[core] Convert "legacy" filters directly into expressions (#11610)
Ports the specialized filter-* expressions from GL JS, adding them to src/mbgl/style/expression/compound_expression.cpp
This commit is contained in:
committed by
Asheem Mamoowala
parent
ee10088702
commit
a4e2c1af1f
@@ -1,7 +1,6 @@
|
||||
#include <benchmark/benchmark.h>
|
||||
|
||||
#include <mbgl/style/filter.hpp>
|
||||
#include <mbgl/style/filter_evaluator.hpp>
|
||||
#include <mbgl/style/conversion.hpp>
|
||||
#include <mbgl/style/conversion/json.hpp>
|
||||
#include <mbgl/style/conversion/filter.hpp>
|
||||
|
||||
@@ -360,7 +360,6 @@ set(MBGL_CORE_FILES
|
||||
include/mbgl/style/conversion.hpp
|
||||
include/mbgl/style/data_driven_property_value.hpp
|
||||
include/mbgl/style/filter.hpp
|
||||
include/mbgl/style/filter_evaluator.hpp
|
||||
include/mbgl/style/heatmap_color_property_value.hpp
|
||||
include/mbgl/style/image.hpp
|
||||
include/mbgl/style/layer.hpp
|
||||
@@ -377,7 +376,6 @@ set(MBGL_CORE_FILES
|
||||
src/mbgl/style/custom_tile_loader.cpp
|
||||
src/mbgl/style/custom_tile_loader.hpp
|
||||
src/mbgl/style/filter.cpp
|
||||
src/mbgl/style/filter_evaluator.cpp
|
||||
src/mbgl/style/image.cpp
|
||||
src/mbgl/style/image_impl.cpp
|
||||
src/mbgl/style/image_impl.hpp
|
||||
|
||||
@@ -36,7 +36,7 @@ template <typename T>
|
||||
struct Varargs : std::vector<T> { using std::vector<T>::vector; };
|
||||
|
||||
namespace detail {
|
||||
// Base class for the Signature<Fn> structs that are used to determine the
|
||||
// Base class for the Signature<Fn> structs that are used to determine
|
||||
// each CompoundExpression definition's type::Type data from the type of its
|
||||
// "evaluate" function.
|
||||
struct SignatureBase {
|
||||
@@ -137,10 +137,21 @@ ParseResult parseCompoundExpression(const std::string name, const mbgl::style::c
|
||||
ParseResult createCompoundExpression(const CompoundExpressionRegistry::Definition& definition,
|
||||
std::vector<std::unique_ptr<Expression>> args,
|
||||
ParsingContext& ctx);
|
||||
|
||||
|
||||
ParseResult createCompoundExpression(const std::string& name,
|
||||
std::vector<std::unique_ptr<Expression>> args,
|
||||
ParsingContext& ctx);
|
||||
// Convenience method for use expressions that have 0, 1, or 2 args.
|
||||
ParseResult createCompoundExpression(const std::string& name, ParsingContext& ctx);
|
||||
|
||||
ParseResult createCompoundExpression(const std::string& name,
|
||||
std::unique_ptr<Expression> arg1,
|
||||
ParsingContext& ctx);
|
||||
|
||||
ParseResult createCompoundExpression(const std::string& name,
|
||||
std::unique_ptr<Expression> arg1,
|
||||
std::unique_ptr<Expression> arg2,
|
||||
ParsingContext& ctx);
|
||||
|
||||
} // namespace expression
|
||||
} // namespace style
|
||||
|
||||
@@ -51,6 +51,9 @@ private:
|
||||
Value value;
|
||||
};
|
||||
|
||||
std::unique_ptr<Literal> createLiteral(const char* value);
|
||||
std::unique_ptr<Literal> createLiteral(Value value);
|
||||
|
||||
} // namespace expression
|
||||
} // namespace style
|
||||
} // namespace mbgl
|
||||
|
||||
@@ -11,270 +11,30 @@
|
||||
|
||||
namespace mbgl {
|
||||
namespace style {
|
||||
|
||||
class Filter;
|
||||
|
||||
class NullFilter {
|
||||
public:
|
||||
friend bool operator==(const NullFilter&, const NullFilter&) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class EqualsFilter {
|
||||
public:
|
||||
std::string key;
|
||||
Value value;
|
||||
|
||||
friend bool operator==(const EqualsFilter& lhs, const EqualsFilter& rhs) {
|
||||
return std::tie(lhs.key, lhs.value) == std::tie(rhs.key, rhs.value);
|
||||
}
|
||||
};
|
||||
|
||||
class NotEqualsFilter {
|
||||
public:
|
||||
std::string key;
|
||||
Value value;
|
||||
|
||||
friend bool operator==(const NotEqualsFilter& lhs, const NotEqualsFilter& rhs) {
|
||||
return std::tie(lhs.key, lhs.value) == std::tie(rhs.key, rhs.value);
|
||||
}
|
||||
};
|
||||
|
||||
class LessThanFilter {
|
||||
public:
|
||||
std::string key;
|
||||
Value value;
|
||||
|
||||
friend bool operator==(const LessThanFilter& lhs, const LessThanFilter& rhs) {
|
||||
return std::tie(lhs.key, lhs.value) == std::tie(rhs.key, rhs.value);
|
||||
}
|
||||
};
|
||||
|
||||
class LessThanEqualsFilter {
|
||||
public:
|
||||
std::string key;
|
||||
Value value;
|
||||
|
||||
friend bool operator==(const LessThanEqualsFilter& lhs, const LessThanEqualsFilter& rhs) {
|
||||
return std::tie(lhs.key, lhs.value) == std::tie(rhs.key, rhs.value);
|
||||
}
|
||||
};
|
||||
|
||||
class GreaterThanFilter {
|
||||
public:
|
||||
std::string key;
|
||||
Value value;
|
||||
|
||||
friend bool operator==(const GreaterThanFilter& lhs, const GreaterThanFilter& rhs) {
|
||||
return std::tie(lhs.key, lhs.value) == std::tie(rhs.key, rhs.value);
|
||||
}
|
||||
};
|
||||
|
||||
class GreaterThanEqualsFilter {
|
||||
public:
|
||||
std::string key;
|
||||
Value value;
|
||||
|
||||
friend bool operator==(const GreaterThanEqualsFilter& lhs, const GreaterThanEqualsFilter& rhs) {
|
||||
return std::tie(lhs.key, lhs.value) == std::tie(rhs.key, rhs.value);
|
||||
}
|
||||
};
|
||||
|
||||
class InFilter {
|
||||
public:
|
||||
std::string key;
|
||||
std::vector<Value> values;
|
||||
|
||||
friend bool operator==(const InFilter& lhs, const InFilter& rhs) {
|
||||
return std::tie(lhs.key, lhs.values) == std::tie(rhs.key, rhs.values);
|
||||
}
|
||||
};
|
||||
|
||||
class NotInFilter {
|
||||
public:
|
||||
std::string key;
|
||||
std::vector<Value> values;
|
||||
|
||||
friend bool operator==(const NotInFilter& lhs, const NotInFilter& rhs) {
|
||||
return std::tie(lhs.key, lhs.values) == std::tie(rhs.key, rhs.values);
|
||||
}
|
||||
};
|
||||
|
||||
class AnyFilter {
|
||||
public:
|
||||
std::vector<Filter> filters;
|
||||
|
||||
friend bool operator==(const AnyFilter& lhs, const AnyFilter& rhs) {
|
||||
return lhs.filters == rhs.filters;
|
||||
}
|
||||
};
|
||||
|
||||
class AllFilter {
|
||||
public:
|
||||
std::vector<Filter> filters;
|
||||
|
||||
friend bool operator==(const AllFilter& lhs, const AllFilter& rhs) {
|
||||
return lhs.filters == rhs.filters;
|
||||
}
|
||||
};
|
||||
|
||||
class NoneFilter {
|
||||
public:
|
||||
std::vector<Filter> filters;
|
||||
|
||||
friend bool operator==(const NoneFilter& lhs, const NoneFilter& rhs) {
|
||||
return lhs.filters == rhs.filters;
|
||||
}
|
||||
};
|
||||
|
||||
class HasFilter {
|
||||
public:
|
||||
std::string key;
|
||||
|
||||
friend bool operator==(const HasFilter& lhs, const HasFilter& rhs) {
|
||||
return lhs.key == rhs.key;
|
||||
}
|
||||
};
|
||||
|
||||
class NotHasFilter {
|
||||
public:
|
||||
std::string key;
|
||||
|
||||
friend bool operator==(const NotHasFilter& lhs, const NotHasFilter& rhs) {
|
||||
return lhs.key == rhs.key;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class TypeEqualsFilter {
|
||||
public:
|
||||
FeatureType value;
|
||||
|
||||
friend bool operator==(const TypeEqualsFilter& lhs, const TypeEqualsFilter& rhs) {
|
||||
return lhs.value == rhs.value;
|
||||
}
|
||||
};
|
||||
|
||||
class TypeNotEqualsFilter {
|
||||
public:
|
||||
FeatureType value;
|
||||
|
||||
friend bool operator==(const TypeNotEqualsFilter& lhs, const TypeNotEqualsFilter& rhs) {
|
||||
return lhs.value == rhs.value;
|
||||
}
|
||||
};
|
||||
|
||||
class TypeInFilter {
|
||||
public:
|
||||
std::vector<FeatureType> values;
|
||||
|
||||
friend bool operator==(const TypeInFilter& lhs, const TypeInFilter& rhs) {
|
||||
return lhs.values == rhs.values;
|
||||
}
|
||||
};
|
||||
|
||||
class TypeNotInFilter {
|
||||
public:
|
||||
std::vector<FeatureType> values;
|
||||
|
||||
friend bool operator==(const TypeNotInFilter& lhs, const TypeNotInFilter& rhs) {
|
||||
return lhs.values == rhs.values;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class IdentifierEqualsFilter {
|
||||
public:
|
||||
FeatureIdentifier value;
|
||||
|
||||
friend bool operator==(const IdentifierEqualsFilter& lhs, const IdentifierEqualsFilter& rhs) {
|
||||
return lhs.value == rhs.value;
|
||||
}
|
||||
};
|
||||
|
||||
class IdentifierNotEqualsFilter {
|
||||
public:
|
||||
FeatureIdentifier value;
|
||||
|
||||
friend bool operator==(const IdentifierNotEqualsFilter& lhs, const IdentifierNotEqualsFilter& rhs) {
|
||||
return lhs.value == rhs.value;
|
||||
}
|
||||
};
|
||||
|
||||
class IdentifierInFilter {
|
||||
public:
|
||||
std::vector<FeatureIdentifier> values;
|
||||
|
||||
friend bool operator==(const IdentifierInFilter& lhs, const IdentifierInFilter& rhs) {
|
||||
return lhs.values == rhs.values;
|
||||
}
|
||||
};
|
||||
|
||||
class IdentifierNotInFilter {
|
||||
public:
|
||||
std::vector<FeatureIdentifier> values;
|
||||
|
||||
friend bool operator==(const IdentifierNotInFilter& lhs, const IdentifierNotInFilter& rhs) {
|
||||
return lhs.values == rhs.values;
|
||||
}
|
||||
};
|
||||
|
||||
class HasIdentifierFilter {
|
||||
public:
|
||||
friend bool operator==(const HasIdentifierFilter&, const HasIdentifierFilter&) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class NotHasIdentifierFilter {
|
||||
public:
|
||||
friend bool operator==(const NotHasIdentifierFilter&, const NotHasIdentifierFilter&) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class ExpressionFilter {
|
||||
class Filter {
|
||||
public:
|
||||
std::shared_ptr<const expression::Expression> expression;
|
||||
optional<std::shared_ptr<const expression::Expression>> expression;
|
||||
|
||||
friend bool operator==(const ExpressionFilter& lhs, const ExpressionFilter& rhs) {
|
||||
return *(lhs.expression) == *(rhs.expression);
|
||||
Filter() : expression() {}
|
||||
|
||||
Filter(expression::ParseResult _expression) : expression(std::move(*_expression)) {
|
||||
assert(!expression || *expression != nullptr);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
using FilterBase = variant<
|
||||
class NullFilter,
|
||||
class EqualsFilter,
|
||||
class NotEqualsFilter,
|
||||
class LessThanFilter,
|
||||
class LessThanEqualsFilter,
|
||||
class GreaterThanFilter,
|
||||
class GreaterThanEqualsFilter,
|
||||
class InFilter,
|
||||
class NotInFilter,
|
||||
class AnyFilter,
|
||||
class AllFilter,
|
||||
class NoneFilter,
|
||||
class HasFilter,
|
||||
class NotHasFilter,
|
||||
class TypeEqualsFilter,
|
||||
class TypeNotEqualsFilter,
|
||||
class TypeInFilter,
|
||||
class TypeNotInFilter,
|
||||
class IdentifierEqualsFilter,
|
||||
class IdentifierNotEqualsFilter,
|
||||
class IdentifierInFilter,
|
||||
class IdentifierNotInFilter,
|
||||
class HasIdentifierFilter,
|
||||
class NotHasIdentifierFilter,
|
||||
class ExpressionFilter>;
|
||||
|
||||
class Filter : public FilterBase {
|
||||
public:
|
||||
using FilterBase::FilterBase;
|
||||
|
||||
bool operator()(const expression::EvaluationContext& context) const;
|
||||
|
||||
friend bool operator==(const Filter& lhs, const Filter& rhs) {
|
||||
if (!lhs.expression || !rhs.expression) {
|
||||
return lhs.expression == rhs.expression;
|
||||
} else {
|
||||
return *(lhs.expression) == *(rhs.expression);
|
||||
}
|
||||
}
|
||||
|
||||
friend bool operator!=(const Filter& lhs, const Filter& rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace style
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <mbgl/style/filter.hpp>
|
||||
#include <mbgl/util/geometry.hpp>
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace mbgl {
|
||||
namespace style {
|
||||
|
||||
/*
|
||||
A visitor that evaluates a `Filter` for a given feature.
|
||||
|
||||
Use via `Filter::operator()`. For example:
|
||||
|
||||
if (filter(feature)) {
|
||||
// matches the filter
|
||||
} else {
|
||||
// does not match
|
||||
}
|
||||
*/
|
||||
class FilterEvaluator {
|
||||
public:
|
||||
const expression::EvaluationContext context;
|
||||
|
||||
bool operator()(const NullFilter&) const;
|
||||
bool operator()(const EqualsFilter& filter) const;
|
||||
bool operator()(const NotEqualsFilter& filter) const;
|
||||
bool operator()(const LessThanFilter& filter) const;
|
||||
bool operator()(const LessThanEqualsFilter& filter) const;
|
||||
bool operator()(const GreaterThanFilter& filter) const;
|
||||
bool operator()(const GreaterThanEqualsFilter& filter) const;
|
||||
bool operator()(const InFilter& filter) const;
|
||||
bool operator()(const NotInFilter& filter) const;
|
||||
bool operator()(const AnyFilter& filter) const;
|
||||
bool operator()(const AllFilter& filter) const;
|
||||
bool operator()(const NoneFilter& filter) const;
|
||||
bool operator()(const HasFilter& filter) const;
|
||||
bool operator()(const NotHasFilter& filter) const;
|
||||
bool operator()(const TypeEqualsFilter& filter) const;
|
||||
bool operator()(const TypeNotEqualsFilter& filter) const;
|
||||
bool operator()(const TypeInFilter& filter) const;
|
||||
bool operator()(const TypeNotInFilter& filter) const;
|
||||
bool operator()(const IdentifierEqualsFilter& filter) const;
|
||||
bool operator()(const IdentifierNotEqualsFilter& filter) const;
|
||||
bool operator()(const IdentifierInFilter& filter) const;
|
||||
bool operator()(const IdentifierNotInFilter& filter) const;
|
||||
bool operator()(const HasIdentifierFilter&) const;
|
||||
bool operator()(const NotHasIdentifierFilter&) const;
|
||||
bool operator()(const ExpressionFilter&) const;
|
||||
|
||||
};
|
||||
|
||||
} // namespace style
|
||||
} // namespace mbgl
|
||||
@@ -157,14 +157,12 @@ namespace android {
|
||||
using namespace mbgl::style::conversion;
|
||||
|
||||
Filter filter = layer.accept(GetFilterEvaluator());
|
||||
|
||||
jni::Object<gson::JsonElement> converted;
|
||||
if (filter.is<ExpressionFilter>()) {
|
||||
ExpressionFilter filterExpression = filter.get<ExpressionFilter>();
|
||||
mbgl::Value expressionValue = filterExpression.expression.get()->serialize();
|
||||
converted = gson::JsonElement::New(env, expressionValue);
|
||||
if (filter.expression) {
|
||||
mbgl::Value expressionValue = (*filter.expression)->serialize();
|
||||
return gson::JsonElement::New(env, expressionValue);
|
||||
} else {
|
||||
return jni::Object<gson::JsonElement>();
|
||||
}
|
||||
return converted;
|
||||
}
|
||||
|
||||
struct SetSourceLayerEvaluator {
|
||||
|
||||
@@ -75,7 +75,7 @@ namespace mbgl {
|
||||
{
|
||||
MGLAssertStyleLayerIsValid();
|
||||
|
||||
self.rawLayer->setFilter(predicate ? predicate.mgl_filter : mbgl::style::NullFilter());
|
||||
self.rawLayer->setFilter(predicate ? predicate.mgl_filter : mbgl::style::Filter());
|
||||
}
|
||||
|
||||
- (NSPredicate *)predicate
|
||||
|
||||
@@ -65,7 +65,7 @@ namespace mbgl {
|
||||
{
|
||||
MGLAssertStyleLayerIsValid();
|
||||
|
||||
self.rawLayer->setFilter(predicate ? predicate.mgl_filter : mbgl::style::NullFilter());
|
||||
self.rawLayer->setFilter(predicate ? predicate.mgl_filter : mbgl::style::Filter());
|
||||
}
|
||||
|
||||
- (NSPredicate *)predicate
|
||||
|
||||
@@ -65,7 +65,7 @@ namespace mbgl {
|
||||
{
|
||||
MGLAssertStyleLayerIsValid();
|
||||
|
||||
self.rawLayer->setFilter(predicate ? predicate.mgl_filter : mbgl::style::NullFilter());
|
||||
self.rawLayer->setFilter(predicate ? predicate.mgl_filter : mbgl::style::Filter());
|
||||
}
|
||||
|
||||
- (NSPredicate *)predicate
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
{
|
||||
MGLAssertStyleLayerIsValid();
|
||||
|
||||
self.rawLayer->setFilter(predicate ? predicate.mgl_filter : mbgl::style::NullFilter());
|
||||
self.rawLayer->setFilter(predicate ? predicate.mgl_filter : mbgl::style::Filter());
|
||||
}
|
||||
|
||||
- (NSPredicate *)predicate
|
||||
|
||||
@@ -77,7 +77,7 @@ namespace mbgl {
|
||||
{
|
||||
MGLAssertStyleLayerIsValid();
|
||||
|
||||
self.rawLayer->setFilter(predicate ? predicate.mgl_filter : mbgl::style::NullFilter());
|
||||
self.rawLayer->setFilter(predicate ? predicate.mgl_filter : mbgl::style::Filter());
|
||||
}
|
||||
|
||||
- (NSPredicate *)predicate
|
||||
|
||||
@@ -103,7 +103,7 @@ namespace mbgl {
|
||||
{
|
||||
MGLAssertStyleLayerIsValid();
|
||||
|
||||
self.rawLayer->setFilter(predicate ? predicate.mgl_filter : mbgl::style::NullFilter());
|
||||
self.rawLayer->setFilter(predicate ? predicate.mgl_filter : mbgl::style::Filter());
|
||||
}
|
||||
|
||||
- (NSPredicate *)predicate
|
||||
|
||||
@@ -142,7 +142,7 @@ namespace mbgl {
|
||||
{
|
||||
MGLAssertStyleLayerIsValid();
|
||||
|
||||
self.rawLayer->setFilter(predicate ? predicate.mgl_filter : mbgl::style::NullFilter());
|
||||
self.rawLayer->setFilter(predicate ? predicate.mgl_filter : mbgl::style::Filter());
|
||||
}
|
||||
|
||||
- (NSPredicate *)predicate
|
||||
|
||||
@@ -5,205 +5,6 @@
|
||||
|
||||
#include <mbgl/style/conversion/filter.hpp>
|
||||
|
||||
class FilterEvaluator {
|
||||
public:
|
||||
|
||||
NSArray *getPredicates(std::vector<mbgl::style::Filter> filters) {
|
||||
NSMutableArray *predicates = [NSMutableArray arrayWithCapacity:filters.size()];
|
||||
for (auto filter : filters) {
|
||||
[predicates addObject:mbgl::style::Filter::visit(filter, FilterEvaluator())];
|
||||
}
|
||||
return predicates;
|
||||
}
|
||||
|
||||
template <typename MBGLType>
|
||||
NSExpression *getValues(std::vector<MBGLType> values) {
|
||||
NSMutableArray *array = [NSMutableArray arrayWithCapacity:values.size()];
|
||||
for (auto value : values) {
|
||||
id constantValue = MBGLType::visit(value, ValueEvaluator());
|
||||
[array addObject:[NSExpression expressionForConstantValue:constantValue]];
|
||||
}
|
||||
return [NSExpression expressionForAggregate:array];
|
||||
}
|
||||
|
||||
NSString *getFeatureTypeString(mbgl::FeatureType type) {
|
||||
switch (type) {
|
||||
case mbgl::FeatureType::Point:
|
||||
return @"Point";
|
||||
|
||||
case mbgl::FeatureType::LineString:
|
||||
return @"LineString";
|
||||
|
||||
case mbgl::FeatureType::Polygon:
|
||||
return @"Polygon";
|
||||
|
||||
default:
|
||||
NSCAssert(NO, @"Unrecognized feature type %hhu", type);
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
NSExpression *getFeatureTypeStrings(std::vector<mbgl::FeatureType> values) {
|
||||
NSMutableArray *array = [NSMutableArray arrayWithCapacity:values.size()];
|
||||
for (auto value : values) {
|
||||
id typeString = getFeatureTypeString(value);
|
||||
[array addObject:[NSExpression expressionForConstantValue:typeString]];
|
||||
}
|
||||
return [NSExpression expressionForAggregate:array];
|
||||
}
|
||||
|
||||
NSPredicate *operator()(mbgl::style::NullFilter filter) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSPredicate *operator()(mbgl::style::EqualsFilter filter) {
|
||||
return [NSPredicate predicateWithFormat:@"%K == %@", @(filter.key.c_str()), mbgl::Value::visit(filter.value, ValueEvaluator())];
|
||||
}
|
||||
|
||||
NSPredicate *operator()(mbgl::style::NotEqualsFilter filter) {
|
||||
return [NSPredicate predicateWithFormat:@"%K != %@", @(filter.key.c_str()), mbgl::Value::visit(filter.value, ValueEvaluator())];
|
||||
}
|
||||
|
||||
NSPredicate *operator()(mbgl::style::GreaterThanFilter filter) {
|
||||
return [NSPredicate predicateWithFormat:@"%K > %@", @(filter.key.c_str()), mbgl::Value::visit(filter.value, ValueEvaluator())];
|
||||
}
|
||||
|
||||
NSPredicate *operator()(mbgl::style::GreaterThanEqualsFilter filter) {
|
||||
return [NSPredicate predicateWithFormat:@"%K >= %@", @(filter.key.c_str()), mbgl::Value::visit(filter.value, ValueEvaluator())];
|
||||
}
|
||||
|
||||
NSPredicate *operator()(mbgl::style::LessThanFilter filter) {
|
||||
return [NSPredicate predicateWithFormat:@"%K < %@", @(filter.key.c_str()), mbgl::Value::visit(filter.value, ValueEvaluator())];
|
||||
}
|
||||
|
||||
NSPredicate *operator()(mbgl::style::LessThanEqualsFilter filter) {
|
||||
return [NSPredicate predicateWithFormat:@"%K <= %@", @(filter.key.c_str()), mbgl::Value::visit(filter.value, ValueEvaluator())];
|
||||
}
|
||||
|
||||
NSPredicate *operator()(mbgl::style::InFilter filter) {
|
||||
return [NSPredicate predicateWithFormat:@"%K IN %@", @(filter.key.c_str()), getValues(filter.values)];
|
||||
}
|
||||
|
||||
NSPredicate *operator()(mbgl::style::NotInFilter filter) {
|
||||
return [NSPredicate predicateWithFormat:@"NOT %K IN %@", @(filter.key.c_str()), getValues(filter.values)];
|
||||
}
|
||||
|
||||
NSPredicate *operator()(mbgl::style::TypeEqualsFilter filter) {
|
||||
return [NSPredicate predicateWithFormat:@"%K == %@", @"$type", getFeatureTypeString(filter.value)];
|
||||
}
|
||||
|
||||
NSPredicate *operator()(mbgl::style::TypeNotEqualsFilter filter) {
|
||||
return [NSPredicate predicateWithFormat:@"%K != %@", @"$type", getFeatureTypeString(filter.value)];
|
||||
}
|
||||
|
||||
NSPredicate *operator()(mbgl::style::TypeInFilter filter) {
|
||||
return [NSPredicate predicateWithFormat:@"%K IN %@", @"$type", getFeatureTypeStrings(filter.values)];
|
||||
}
|
||||
|
||||
NSPredicate *operator()(mbgl::style::TypeNotInFilter filter) {
|
||||
return [NSPredicate predicateWithFormat:@"NOT %K IN %@", @"$type", getFeatureTypeStrings(filter.values)];
|
||||
}
|
||||
|
||||
NSPredicate *operator()(mbgl::style::IdentifierEqualsFilter filter) {
|
||||
return [NSPredicate predicateWithFormat:@"%K == %@", @"$id", mbgl::FeatureIdentifier::visit(filter.value, ValueEvaluator())];
|
||||
}
|
||||
|
||||
NSPredicate *operator()(mbgl::style::IdentifierNotEqualsFilter filter) {
|
||||
return [NSPredicate predicateWithFormat:@"%K != %@", @"$id", mbgl::FeatureIdentifier::visit(filter.value, ValueEvaluator())];
|
||||
}
|
||||
|
||||
NSPredicate *operator()(mbgl::style::IdentifierInFilter filter) {
|
||||
return [NSPredicate predicateWithFormat:@"%K IN %@", @"$id", getValues(filter.values)];
|
||||
}
|
||||
|
||||
NSPredicate *operator()(mbgl::style::IdentifierNotInFilter filter) {
|
||||
return [NSPredicate predicateWithFormat:@"NOT %K IN %@", @"$id", getValues(filter.values)];
|
||||
}
|
||||
|
||||
NSPredicate *operator()(mbgl::style::AnyFilter filter) {
|
||||
NSArray *subpredicates = getPredicates(filter.filters);
|
||||
if (subpredicates.count) {
|
||||
return [NSCompoundPredicate orPredicateWithSubpredicates:subpredicates];
|
||||
}
|
||||
return [NSPredicate predicateWithValue:NO];
|
||||
}
|
||||
|
||||
NSPredicate *operator()(mbgl::style::AllFilter filter) {
|
||||
// Convert [all, [>=, key, lower], [<=, key, upper]] to key BETWEEN {lower, upper}
|
||||
if (filter.filters.size() == 2) {
|
||||
auto leftFilter = filter.filters[0];
|
||||
auto rightFilter = filter.filters[1];
|
||||
|
||||
std::string lowerKey;
|
||||
std::string upperKey;
|
||||
mbgl::Value lowerBound;
|
||||
mbgl::Value upperBound;
|
||||
if (leftFilter.is<mbgl::style::GreaterThanEqualsFilter>()) {
|
||||
lowerKey = leftFilter.get<mbgl::style::GreaterThanEqualsFilter>().key;
|
||||
lowerBound = leftFilter.get<mbgl::style::GreaterThanEqualsFilter>().value;
|
||||
} else if (rightFilter.is<mbgl::style::GreaterThanEqualsFilter>()) {
|
||||
lowerKey = rightFilter.get<mbgl::style::GreaterThanEqualsFilter>().key;
|
||||
lowerBound = rightFilter.get<mbgl::style::GreaterThanEqualsFilter>().value;
|
||||
}
|
||||
|
||||
if (leftFilter.is<mbgl::style::LessThanEqualsFilter>()) {
|
||||
upperKey = leftFilter.get<mbgl::style::LessThanEqualsFilter>().key;
|
||||
upperBound = leftFilter.get<mbgl::style::LessThanEqualsFilter>().value;
|
||||
} else if (rightFilter.is<mbgl::style::LessThanEqualsFilter>()) {
|
||||
upperKey = rightFilter.get<mbgl::style::LessThanEqualsFilter>().key;
|
||||
upperBound = rightFilter.get<mbgl::style::LessThanEqualsFilter>().value;
|
||||
}
|
||||
|
||||
if (!lowerBound.is<mbgl::NullValue>() && !upperBound.is<mbgl::NullValue>()
|
||||
&& lowerKey == upperKey) {
|
||||
return [NSPredicate predicateWithFormat:@"%K BETWEEN {%@, %@}",
|
||||
@(lowerKey.c_str()),
|
||||
mbgl::Value::visit(lowerBound, ValueEvaluator()),
|
||||
mbgl::Value::visit(upperBound, ValueEvaluator())];
|
||||
}
|
||||
}
|
||||
|
||||
NSArray *subpredicates = getPredicates(filter.filters);
|
||||
if (subpredicates.count) {
|
||||
return [NSCompoundPredicate andPredicateWithSubpredicates:subpredicates];
|
||||
}
|
||||
return [NSPredicate predicateWithValue:YES];
|
||||
}
|
||||
|
||||
NSPredicate *operator()(mbgl::style::NoneFilter filter) {
|
||||
NSArray *subpredicates = getPredicates(filter.filters);
|
||||
if (subpredicates.count > 1) {
|
||||
NSCompoundPredicate *predicate = [NSCompoundPredicate orPredicateWithSubpredicates:subpredicates];
|
||||
return [NSCompoundPredicate notPredicateWithSubpredicate:predicate];
|
||||
} else if (subpredicates.count) {
|
||||
return [NSCompoundPredicate notPredicateWithSubpredicate:subpredicates.firstObject];
|
||||
} else {
|
||||
return [NSPredicate predicateWithValue:YES];
|
||||
}
|
||||
}
|
||||
|
||||
NSPredicate *operator()(mbgl::style::HasFilter filter) {
|
||||
return [NSPredicate predicateWithFormat:@"%K != nil", @(filter.key.c_str())];
|
||||
}
|
||||
|
||||
NSPredicate *operator()(mbgl::style::NotHasFilter filter) {
|
||||
return [NSPredicate predicateWithFormat:@"%K == nil", @(filter.key.c_str())];
|
||||
}
|
||||
|
||||
NSPredicate *operator()(mbgl::style::HasIdentifierFilter filter) {
|
||||
return [NSPredicate predicateWithFormat:@"%K != nil", @"$id"];
|
||||
}
|
||||
|
||||
NSPredicate *operator()(mbgl::style::NotHasIdentifierFilter filter) {
|
||||
return [NSPredicate predicateWithFormat:@"%K == nil", @"$id"];
|
||||
}
|
||||
|
||||
NSPredicate *operator()(mbgl::style::ExpressionFilter filter) {
|
||||
id jsonObject = MGLJSONObjectFromMBGLExpression(*filter.expression);
|
||||
return [NSPredicate predicateWithMGLJSONObject:jsonObject];
|
||||
}
|
||||
};
|
||||
|
||||
@implementation NSPredicate (MGLPrivateAdditions)
|
||||
|
||||
- (mbgl::style::Filter)mgl_filter
|
||||
@@ -224,8 +25,12 @@ public:
|
||||
|
||||
+ (instancetype)mgl_predicateWithFilter:(mbgl::style::Filter)filter
|
||||
{
|
||||
FilterEvaluator evaluator;
|
||||
return mbgl::style::Filter::visit(filter, evaluator);
|
||||
if (filter.expression) {
|
||||
id jsonObject = MGLJSONObjectFromMBGLExpression(**filter.expression);
|
||||
return [NSPredicate predicateWithMGLJSONObject:jsonObject];
|
||||
} else {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -4,226 +4,12 @@
|
||||
#import "NSPredicate+MGLPrivateAdditions.h"
|
||||
#import "MGLValueEvaluator.h"
|
||||
|
||||
namespace mbgl {
|
||||
namespace style {
|
||||
bool operator!=(const Filter &a, const Filter &b) {
|
||||
return !(a == b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define MGLAssertEqualFilters(actual, expected, ...) \
|
||||
XCTAssertTrue(actual.is<__typeof__(expected)>()); \
|
||||
if (actual.is<__typeof__(expected)>()) { \
|
||||
XCTAssertEqual(actual.get<__typeof__(expected)>(), expected, __VA_ARGS__); \
|
||||
}
|
||||
|
||||
@interface MGLPredicateTests : XCTestCase
|
||||
@end
|
||||
|
||||
@implementation MGLPredicateTests
|
||||
|
||||
- (void)testPredication {
|
||||
XCTAssertNil([NSPredicate mgl_predicateWithFilter:mbgl::style::NullFilter()]);
|
||||
|
||||
{
|
||||
mbgl::style::EqualsFilter filter = { .key = "a", .value = std::string("b") };
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a = 'b'"]);
|
||||
}
|
||||
|
||||
{
|
||||
mbgl::style::TypeEqualsFilter filter = { .value = mbgl::FeatureType::Point };
|
||||
NSPredicate *expected = [NSPredicate predicateWithFormat:@"%K = 'Point'", @"$type"];
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], expected);
|
||||
}
|
||||
|
||||
{
|
||||
mbgl::style::TypeEqualsFilter filter = { .value = mbgl::FeatureType::LineString };
|
||||
NSPredicate *expected = [NSPredicate predicateWithFormat:@"%K = 'LineString'", @"$type"];
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], expected);
|
||||
}
|
||||
|
||||
{
|
||||
mbgl::style::TypeEqualsFilter filter = { .value = mbgl::FeatureType::Polygon };
|
||||
NSPredicate *expected = [NSPredicate predicateWithFormat:@"%K = 'Polygon'", @"$type"];
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], expected);
|
||||
}
|
||||
|
||||
{
|
||||
mbgl::style::IdentifierEqualsFilter filter = { .value = UINT64_C(67086180) };
|
||||
NSPredicate *expected = [NSPredicate predicateWithFormat:@"%K = 67086180", @"$id"];
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], expected);
|
||||
}
|
||||
|
||||
{
|
||||
mbgl::style::NotHasIdentifierFilter filter;
|
||||
NSPredicate *expected = [NSPredicate predicateWithFormat:@"%K = nil", @"$id"];
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], expected);
|
||||
}
|
||||
|
||||
{
|
||||
mbgl::style::TypeEqualsFilter filter = { .value = mbgl::FeatureType::Unknown };
|
||||
XCTAssertThrowsSpecificNamed([NSPredicate mgl_predicateWithFilter:filter], NSException, NSInternalInconsistencyException);
|
||||
}
|
||||
|
||||
{
|
||||
mbgl::style::NotHasFilter filter = { .key = "a" };
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a = nil"]);
|
||||
}
|
||||
|
||||
{
|
||||
mbgl::style::NotEqualsFilter filter = { .key = "a", .value = std::string("b") };
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a != 'b'"]);
|
||||
}
|
||||
|
||||
{
|
||||
mbgl::style::TypeNotEqualsFilter filter = { .value = mbgl::FeatureType::Point };
|
||||
NSPredicate *expected = [NSPredicate predicateWithFormat:@"%K != 'Point'", @"$type"];
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], expected);
|
||||
}
|
||||
|
||||
{
|
||||
mbgl::style::IdentifierNotEqualsFilter filter = { .value = UINT64_C(67086180) };
|
||||
NSPredicate *expected = [NSPredicate predicateWithFormat:@"%K != 67086180", @"$id"];
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], expected);
|
||||
}
|
||||
|
||||
{
|
||||
mbgl::style::HasIdentifierFilter filter;
|
||||
NSPredicate *expected = [NSPredicate predicateWithFormat:@"%K != nil", @"$id"];
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], expected);
|
||||
}
|
||||
|
||||
{
|
||||
mbgl::style::HasFilter filter = { .key = "a" };
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a != nil"]);
|
||||
}
|
||||
|
||||
{
|
||||
mbgl::style::LessThanFilter filter = { .key = "a", .value = std::string("b") };
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a < 'b'"]);
|
||||
}
|
||||
|
||||
{
|
||||
mbgl::style::LessThanEqualsFilter filter = { .key = "a", .value = std::string("b") };
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a <= 'b'"]);
|
||||
}
|
||||
|
||||
{
|
||||
mbgl::style::GreaterThanFilter filter = { .key = "a", .value = std::string("b") };
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a > 'b'"]);
|
||||
}
|
||||
|
||||
{
|
||||
mbgl::style::GreaterThanEqualsFilter filter = { .key = "a", .value = std::string("b") };
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a >= 'b'"]);
|
||||
}
|
||||
|
||||
{
|
||||
mbgl::style::AllFilter filter = {
|
||||
.filters = {
|
||||
mbgl::style::GreaterThanEqualsFilter { .key = "a", .value = std::string("b") },
|
||||
mbgl::style::LessThanEqualsFilter { .key = "a", .value = std::string("z") },
|
||||
},
|
||||
};
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a BETWEEN {'b', 'z'}"]);
|
||||
}
|
||||
|
||||
{
|
||||
mbgl::style::AllFilter filter = {
|
||||
.filters = {
|
||||
mbgl::style::LessThanEqualsFilter { .key = "a", .value = std::string("z") },
|
||||
mbgl::style::GreaterThanEqualsFilter { .key = "a", .value = std::string("b") },
|
||||
},
|
||||
};
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a BETWEEN {'b', 'z'}"]);
|
||||
}
|
||||
|
||||
{
|
||||
mbgl::style::InFilter filter = { .key = "a", .values = { std::string("b"), std::string("c") } };
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter].predicateFormat, [NSPredicate predicateWithFormat:@"a IN {'b', 'c'}"].predicateFormat);
|
||||
}
|
||||
|
||||
{
|
||||
mbgl::style::TypeInFilter filter = { .values = { mbgl::FeatureType::LineString, mbgl::FeatureType::Polygon } };
|
||||
NSPredicate *expected = [NSPredicate predicateWithFormat:@"%K IN {'LineString', 'Polygon'}", @"$type"];
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter].predicateFormat, expected.predicateFormat);
|
||||
}
|
||||
|
||||
{
|
||||
mbgl::style::IdentifierInFilter filter = { .values = { UINT64_C(67086180), UINT64_C(3709678893), UINT64_C(3352016856), UINT64_C(4189833989) } };
|
||||
NSPredicate *expected = [NSPredicate predicateWithFormat:@"%K IN {67086180, 3709678893, 3352016856, 4189833989}", @"$id"];
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], expected);
|
||||
}
|
||||
|
||||
{
|
||||
mbgl::style::NotInFilter filter = { .key = "a", .values = { std::string("b"), std::string("c") } };
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter].predicateFormat, [NSPredicate predicateWithFormat:@"NOT a IN {'b', 'c'}"].predicateFormat);
|
||||
}
|
||||
|
||||
{
|
||||
mbgl::style::TypeNotInFilter filter = { .values = { mbgl::FeatureType::LineString, mbgl::FeatureType::Polygon } };
|
||||
NSPredicate *expected = [NSPredicate predicateWithFormat:@"NOT %K IN {'LineString', 'Polygon'}", @"$type"];
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter].predicateFormat, expected.predicateFormat);
|
||||
}
|
||||
|
||||
{
|
||||
mbgl::style::IdentifierNotInFilter filter = { .values = { UINT64_C(67086180), UINT64_C(3709678893), UINT64_C(3352016856), UINT64_C(4189833989) } };
|
||||
NSPredicate *expected = [NSPredicate predicateWithFormat:@"NOT %K IN {67086180, 3709678893, 3352016856, 4189833989}", @"$id"];
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], expected);
|
||||
}
|
||||
|
||||
{
|
||||
mbgl::style::AllFilter filter;
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithValue:YES]);
|
||||
}
|
||||
|
||||
{
|
||||
mbgl::style::AllFilter filter = {
|
||||
.filters = {
|
||||
mbgl::style::EqualsFilter { .key = "a", .value = std::string("b") },
|
||||
mbgl::style::EqualsFilter { .key = "c", .value = std::string("d") },
|
||||
},
|
||||
};
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a == 'b' AND c == 'd'"]);
|
||||
}
|
||||
|
||||
{
|
||||
mbgl::style::AnyFilter filter;
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithValue:NO]);
|
||||
}
|
||||
|
||||
{
|
||||
mbgl::style::AnyFilter filter = {
|
||||
.filters = {
|
||||
mbgl::style::EqualsFilter { .key = "a", .value = std::string("b") },
|
||||
mbgl::style::EqualsFilter { .key = "c", .value = std::string("d") },
|
||||
},
|
||||
};
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a == 'b' OR c == 'd'"]);
|
||||
}
|
||||
|
||||
{
|
||||
mbgl::style::NoneFilter filter;
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithValue:YES]);
|
||||
}
|
||||
|
||||
{
|
||||
mbgl::style::NoneFilter filter = {
|
||||
.filters = {
|
||||
mbgl::style::EqualsFilter { .key = "a", .value = std::string("b") },
|
||||
mbgl::style::EqualsFilter { .key = "c", .value = std::string("d") },
|
||||
},
|
||||
};
|
||||
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"NOT(a == 'b' OR c == 'd')"]);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)testUnsupportedFilterPredicates {
|
||||
XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"1 == 2"].mgl_filter, NSException, NSInvalidArgumentException);
|
||||
XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"1 == 1"].mgl_filter, NSException, NSInvalidArgumentException);
|
||||
XCTAssertThrowsSpecificNamed([NSPredicate predicateWithValue:YES].mgl_filter, NSException, NSInvalidArgumentException);
|
||||
XCTAssertThrowsSpecificNamed([NSPredicate predicateWithValue:NO].mgl_filter, NSException, NSInvalidArgumentException);
|
||||
XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"a BEGINSWITH 'L'"].mgl_filter, NSException, NSInvalidArgumentException);
|
||||
XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"a ENDSWITH 'itude'"].mgl_filter, NSException, NSInvalidArgumentException);
|
||||
XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"a LIKE 'glob?trotter'"].mgl_filter, NSException, NSInvalidArgumentException);
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
#include <mbgl/style/image.hpp>
|
||||
#include <mbgl/style/transition_options.hpp>
|
||||
#include <mbgl/style/layers/fill_extrusion_layer.hpp>
|
||||
#include <mbgl/style/expression/compound_expression.hpp>
|
||||
#include <mbgl/style/expression/literal.hpp>
|
||||
#include <mbgl/util/logging.hpp>
|
||||
#include <mbgl/util/platform.hpp>
|
||||
#include <mbgl/util/string.hpp>
|
||||
@@ -619,6 +621,9 @@ void GLFWView::onDidFinishLoadingStyle() {
|
||||
}
|
||||
|
||||
void GLFWView::toggle3DExtrusions(bool visible) {
|
||||
using namespace mbgl::style;
|
||||
using namespace mbgl::style::expression;
|
||||
|
||||
show3DExtrusions = visible;
|
||||
|
||||
// Satellite-only style does not contain building extrusions data.
|
||||
@@ -634,7 +639,9 @@ void GLFWView::toggle3DExtrusions(bool visible) {
|
||||
auto extrusionLayer = std::make_unique<mbgl::style::FillExtrusionLayer>("3d-buildings", "composite");
|
||||
extrusionLayer->setSourceLayer("building");
|
||||
extrusionLayer->setMinZoom(15.0f);
|
||||
extrusionLayer->setFilter(mbgl::style::EqualsFilter { "extrude", { std::string("true") } });
|
||||
|
||||
ParsingContext parsingContext;
|
||||
extrusionLayer->setFilter(Filter(createCompoundExpression("filter-==", createLiteral("extrude"), createLiteral("true"), parsingContext)));
|
||||
|
||||
auto colorFn = mbgl::style::SourceFunction<mbgl::Color> { "height",
|
||||
mbgl::style::ExponentialStops<mbgl::Color> {
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
#include <mbgl/util/math.hpp>
|
||||
#include <mbgl/math/minmax.hpp>
|
||||
#include <mbgl/style/filter.hpp>
|
||||
#include <mbgl/style/filter_evaluator.hpp>
|
||||
#include <mbgl/tile/tile_id.hpp>
|
||||
|
||||
#include <mapbox/geometry/envelope.hpp>
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
#include <mbgl/layout/merge_lines.hpp>
|
||||
#include <mbgl/layout/clip_lines.hpp>
|
||||
#include <mbgl/renderer/buckets/symbol_bucket.hpp>
|
||||
#include <mbgl/style/filter_evaluator.hpp>
|
||||
#include <mbgl/renderer/bucket_parameters.hpp>
|
||||
#include <mbgl/renderer/layers/render_symbol_layer.hpp>
|
||||
#include <mbgl/renderer/image_atlas.hpp>
|
||||
|
||||
@@ -1,17 +1,39 @@
|
||||
#include <mbgl/style/conversion/filter.hpp>
|
||||
#include <mbgl/style/expression/literal.hpp>
|
||||
#include <mbgl/util/geometry.hpp>
|
||||
#include <mbgl/style/expression/expression.hpp>
|
||||
#include <mbgl/style/expression/type.hpp>
|
||||
#include <mbgl/style/expression/parsing_context.hpp>
|
||||
#include <mbgl/style/expression/compound_expression.hpp>
|
||||
#include <mbgl/style/expression/boolean_operator.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
namespace style {
|
||||
namespace conversion {
|
||||
|
||||
using GeometryValue = mapbox::geometry::value;
|
||||
using namespace mbgl::style::expression;
|
||||
|
||||
static bool isExpression(const Convertible& filter);
|
||||
std::unique_ptr<Expression> convertLegacyFilter(const Convertible& values, Error& error);
|
||||
|
||||
optional<Filter> Converter<Filter>::operator()(const Convertible& value, Error& error) const {
|
||||
if (isExpression(value)) {
|
||||
ParsingContext parsingContext(type::Boolean);
|
||||
ParseResult parseResult = parsingContext.parseExpression(value);
|
||||
if (!parseResult) {
|
||||
error = { parsingContext.getCombinedErrors() };
|
||||
return {};
|
||||
} else {
|
||||
return { Filter(std::move(parseResult)) };
|
||||
}
|
||||
} else {
|
||||
std::unique_ptr<Expression> expression = convertLegacyFilter(value, error);
|
||||
if (!expression) return {};
|
||||
return Filter(optional<std::unique_ptr<Expression>>(std::move(expression)));
|
||||
}
|
||||
}
|
||||
|
||||
// This is a port from https://github.com/mapbox/mapbox-gl-js/blob/master/src/style-spec/feature_filter/index.js
|
||||
static bool isExpressionFilter(const Convertible& filter) {
|
||||
bool isExpression(const Convertible& filter) {
|
||||
if (!isArray(filter) || arrayLength(filter) == 0) {
|
||||
return false;
|
||||
}
|
||||
@@ -20,7 +42,7 @@ static bool isExpressionFilter(const Convertible& filter) {
|
||||
|
||||
if (!op) {
|
||||
return false;
|
||||
|
||||
|
||||
} else if (*op == "has") {
|
||||
if (arrayLength(filter) < 2) return false;
|
||||
optional<std::string> operand = toString(arrayMember(filter, 1));
|
||||
@@ -35,7 +57,7 @@ static bool isExpressionFilter(const Convertible& filter) {
|
||||
} else if (*op == "any" || *op == "all") {
|
||||
for (std::size_t i = 1; i < arrayLength(filter); i++) {
|
||||
Convertible f = arrayMember(filter, i);
|
||||
if (!isExpressionFilter(f) && !toBool(f)) {
|
||||
if (!isExpression(f) && !toBool(f)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -46,257 +68,136 @@ static bool isExpressionFilter(const Convertible& filter) {
|
||||
}
|
||||
}
|
||||
|
||||
static optional<GeometryValue> normalizeValue(const optional<GeometryValue>& value, Error& error) {
|
||||
if (!value) {
|
||||
error = { "filter expression value must be a boolean, number, or string" };
|
||||
return {};
|
||||
std::unique_ptr<Expression> createExpression(std::string op, std::vector<std::unique_ptr<Expression>> args, Error& error) {
|
||||
if (op == "any") {
|
||||
return std::make_unique<Any>(std::move(args));
|
||||
|
||||
} else if (op == "all") {
|
||||
return std::make_unique<All>(std::move(args));
|
||||
|
||||
} else {
|
||||
return *value;
|
||||
}
|
||||
}
|
||||
|
||||
static optional<FeatureType> toFeatureType(const Convertible& value, Error& error) {
|
||||
optional<std::string> type = toString(value);
|
||||
if (!type) {
|
||||
error = { "value for $type filter must be a string" };
|
||||
return {};
|
||||
} else if (*type == "Point") {
|
||||
return FeatureType::Point;
|
||||
} else if (*type == "LineString") {
|
||||
return FeatureType::LineString;
|
||||
} else if (*type == "Polygon") {
|
||||
return FeatureType::Polygon;
|
||||
} else {
|
||||
error = { "value for $type filter must be Point, LineString, or Polygon" };
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
static optional<FeatureIdentifier> toFeatureIdentifier(const Convertible& value, Error& error) {
|
||||
optional<GeometryValue> identifier = toValue(value);
|
||||
if (!identifier) {
|
||||
error = { "filter expression value must be a boolean, number, or string" };
|
||||
return {};
|
||||
} else {
|
||||
return (*identifier).match(
|
||||
[] (uint64_t t) -> optional<FeatureIdentifier> { return { t }; },
|
||||
[] ( int64_t t) -> optional<FeatureIdentifier> { return { t }; },
|
||||
[] ( double t) -> optional<FeatureIdentifier> { return { t }; },
|
||||
[] (const std::string& t) -> optional<FeatureIdentifier> { return { t }; },
|
||||
[&] (const auto&) -> optional<FeatureIdentifier> {
|
||||
error = { "filter expression value must be a boolean, number, or string" };
|
||||
return {};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
template <class FilterType, class IdentifierFilterType>
|
||||
optional<Filter> convertUnaryFilter(const Convertible& value, Error& error) {
|
||||
if (arrayLength(value) < 2) {
|
||||
error = { "filter expression must have 2 elements" };
|
||||
return {};
|
||||
}
|
||||
|
||||
optional<std::string> key = toString(arrayMember(value, 1));
|
||||
if (!key) {
|
||||
error = { "filter expression key must be a string" };
|
||||
return {};
|
||||
}
|
||||
|
||||
if (*key == "$id") {
|
||||
return { IdentifierFilterType {} };
|
||||
} else {
|
||||
return { FilterType { *key } };
|
||||
}
|
||||
}
|
||||
|
||||
template <class FilterType, class TypeFilterType, class IdentifierFilterType>
|
||||
optional<Filter> convertEqualityFilter(const Convertible& value, Error& error) {
|
||||
if (arrayLength(value) < 3) {
|
||||
error = { "filter expression must have 3 elements" };
|
||||
return {};
|
||||
}
|
||||
|
||||
optional<std::string> key = toString(arrayMember(value, 1));
|
||||
if (!key) {
|
||||
error = { "filter expression key must be a string" };
|
||||
return {};
|
||||
}
|
||||
|
||||
if (*key == "$type") {
|
||||
optional<FeatureType> filterValue = toFeatureType(arrayMember(value, 2), error);
|
||||
if (!filterValue) {
|
||||
ParsingContext parsingContext(type::Boolean);
|
||||
ParseResult parseResult = createCompoundExpression(op, std::move(args), parsingContext);
|
||||
if (!parseResult) {
|
||||
error = { parsingContext.getCombinedErrors() };
|
||||
return {};
|
||||
} else {
|
||||
return std::move(*parseResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { TypeFilterType { *filterValue } };
|
||||
|
||||
} else if (*key == "$id") {
|
||||
optional<FeatureIdentifier> filterValue = toFeatureIdentifier(arrayMember(value, 2), error);
|
||||
if (!filterValue) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return { IdentifierFilterType { *filterValue } };
|
||||
std::unique_ptr<Expression> createExpression(std::string op, std::unique_ptr<Expression> expression, Error& error) {
|
||||
std::vector<std::unique_ptr<Expression>> args;
|
||||
args.push_back(std::move(expression));
|
||||
return createExpression(op, std::move(args), error);
|
||||
}
|
||||
|
||||
std::unique_ptr<Expression> convertLiteral(const Convertible& convertible, Error& error) {
|
||||
ParsingContext parsingContext;
|
||||
ParseResult parseResult = Literal::parse(convertible, parsingContext);
|
||||
if (parseResult) {
|
||||
return std::move(*parseResult);
|
||||
} else {
|
||||
optional<GeometryValue> filterValue = normalizeValue(toValue(arrayMember(value, 2)), error);
|
||||
if (!filterValue) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return { FilterType { *key, *filterValue } };
|
||||
error = { parsingContext.getCombinedErrors() };
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
template <class FilterType>
|
||||
optional<Filter> convertBinaryFilter(const Convertible& value, Error& error) {
|
||||
if (arrayLength(value) < 3) {
|
||||
error = { "filter expression must have 3 elements" };
|
||||
return {};
|
||||
std::vector<std::unique_ptr<Expression>> convertLiteralArray(const Convertible &input, Error& error, std::size_t startIndex = 0) {
|
||||
std::vector<std::unique_ptr<Expression>> output;
|
||||
for (std::size_t i = startIndex; i < arrayLength(input); i++) {
|
||||
output.push_back(convertLiteral(arrayMember(input, i), error));
|
||||
}
|
||||
|
||||
optional<std::string> key = toString(arrayMember(value, 1));
|
||||
if (!key) {
|
||||
error = { "filter expression key must be a string" };
|
||||
return {};
|
||||
}
|
||||
|
||||
optional<GeometryValue> filterValue = normalizeValue(toValue(arrayMember(value, 2)), error);
|
||||
if (!filterValue) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return { FilterType { *key, *filterValue } };
|
||||
return output;
|
||||
}
|
||||
|
||||
template <class FilterType, class TypeFilterType, class IdentifierFilterType>
|
||||
optional<Filter> convertSetFilter(const Convertible& value, Error& error) {
|
||||
if (arrayLength(value) < 2) {
|
||||
error = { "filter expression must at least 2 elements" };
|
||||
std::unique_ptr<Expression> convertLegacyComparisonFilter(const Convertible& values, Error& error, optional<std::string> opOverride = {}) {
|
||||
optional<std::string> op = opOverride ? opOverride : toString(arrayMember(values, 0));
|
||||
optional<std::string> property = toString(arrayMember(values, 1));
|
||||
|
||||
if (!property) {
|
||||
error = { "filter property must be a string" };
|
||||
return {};
|
||||
}
|
||||
|
||||
optional<std::string> key = toString(arrayMember(value, 1));
|
||||
if (!key) {
|
||||
error = { "filter expression key must be a string" };
|
||||
return {};
|
||||
}
|
||||
|
||||
if (*key == "$type") {
|
||||
std::vector<FeatureType> values;
|
||||
for (std::size_t i = 2; i < arrayLength(value); ++i) {
|
||||
optional<FeatureType> filterValue = toFeatureType(arrayMember(value, i), error);
|
||||
if (!filterValue) {
|
||||
return {};
|
||||
}
|
||||
values.push_back(*filterValue);
|
||||
}
|
||||
|
||||
return { TypeFilterType { std::move(values) } };
|
||||
|
||||
} else if (*key == "$id") {
|
||||
std::vector<FeatureIdentifier> values;
|
||||
for (std::size_t i = 2; i < arrayLength(value); ++i) {
|
||||
optional<FeatureIdentifier> filterValue = toFeatureIdentifier(arrayMember(value, i), error);
|
||||
if (!filterValue) {
|
||||
return {};
|
||||
}
|
||||
values.push_back(*filterValue);
|
||||
}
|
||||
|
||||
return { IdentifierFilterType { std::move(values) } };
|
||||
|
||||
} else if (*property == "$type") {
|
||||
return createExpression("filter-type-" + *op, convertLiteralArray(values, error, 2), error);
|
||||
} else if (*property == "$id") {
|
||||
return createExpression("filter-id-" + *op, convertLiteralArray(values, error, 2), error);
|
||||
} else {
|
||||
std::vector<GeometryValue> values;
|
||||
for (std::size_t i = 2; i < arrayLength(value); ++i) {
|
||||
optional<GeometryValue> filterValue = normalizeValue(toValue(arrayMember(value, i)), error);
|
||||
if (!filterValue) {
|
||||
return {};
|
||||
}
|
||||
values.push_back(*filterValue);
|
||||
}
|
||||
|
||||
return { FilterType { *key, std::move(values) } };
|
||||
return createExpression("filter-" + *op, convertLiteralArray(values, error, 1), error);
|
||||
}
|
||||
}
|
||||
|
||||
template <class FilterType>
|
||||
optional<Filter> convertCompoundFilter(const Convertible& value, Error& error) {
|
||||
std::vector<Filter> filters;
|
||||
for (std::size_t i = 1; i < arrayLength(value); ++i) {
|
||||
optional<Filter> element = convert<Filter>(arrayMember(value, i), error);
|
||||
if (!element) {
|
||||
return {};
|
||||
}
|
||||
filters.push_back(*element);
|
||||
}
|
||||
|
||||
return { FilterType { std::move(filters) } };
|
||||
}
|
||||
|
||||
optional<Filter> convertExpressionFilter(const Convertible& value, Error& error) {
|
||||
expression::ParsingContext ctx(expression::type::Boolean);
|
||||
expression::ParseResult expression = ctx.parseExpression(value);
|
||||
if (!expression) {
|
||||
error = { ctx.getCombinedErrors() };
|
||||
std::unique_ptr<Expression> convertLegacyHasFilter(const Convertible& values, Error& error) {
|
||||
optional<std::string> property = toString(arrayMember(values, 1));
|
||||
|
||||
if (!property) {
|
||||
error = { "filter property must be a string" };
|
||||
return {};
|
||||
} else if (*property == "$type") {
|
||||
return std::make_unique<Literal>(true);
|
||||
} else if (*property == "$id") {
|
||||
return createExpression("filter-has-id", std::vector<std::unique_ptr<Expression>>(), error);
|
||||
} else {
|
||||
return createExpression("filter-has", std::make_unique<Literal>(*property), error);
|
||||
}
|
||||
|
||||
return { ExpressionFilter { std::move(*expression) } };
|
||||
}
|
||||
|
||||
optional<Filter> Converter<Filter>::operator()(const Convertible& value, Error& error) const {
|
||||
if (isExpressionFilter(value)) {
|
||||
return convertExpressionFilter(value, error);
|
||||
std::unique_ptr<Expression> convertLegacyInFilter(const Convertible& values, Error& error) {
|
||||
optional<std::string> property = toString(arrayMember(values, 1));
|
||||
|
||||
if (!property) {
|
||||
error = { "filter property must be a string" };
|
||||
return {};
|
||||
} else if (arrayLength(values) == 0) {
|
||||
return std::make_unique<Literal>(false);
|
||||
} else if (*property == "$type") {
|
||||
return createExpression("filter-type-in", convertLiteralArray(values, error, 2), error);
|
||||
} else if (*property == "$id") {
|
||||
return createExpression("filter-id-in", convertLiteralArray(values, error, 2), error);
|
||||
} else {
|
||||
return createExpression("filter-in", convertLiteralArray(values, error, 1), error);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<Expression>> convertLegacyFilterArray(const Convertible &input, Error& error, std::size_t startIndex = 0) {
|
||||
std::vector<std::unique_ptr<Expression>> output;
|
||||
for (std::size_t i = startIndex; i < arrayLength(input); i++) {
|
||||
output.push_back(convertLegacyFilter(arrayMember(input, i), error));
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
std::unique_ptr<Expression> convertLegacyFilter(const Convertible& values, Error& error) {
|
||||
if (isUndefined(values)) {
|
||||
return std::make_unique<Literal>(true);
|
||||
}
|
||||
|
||||
if (!isArray(value)) {
|
||||
error = { "filter expression must be an array" };
|
||||
return {};
|
||||
}
|
||||
optional<std::string> op = toString(arrayMember(values, 0));
|
||||
|
||||
if (arrayLength(value) < 1) {
|
||||
error = { "filter expression must have at least 1 element" };
|
||||
return {};
|
||||
}
|
||||
|
||||
optional<std::string> op = toString(arrayMember(value, 0));
|
||||
if (!op) {
|
||||
error = { "filter operator must be a string" };
|
||||
return {};
|
||||
} else if (arrayLength(values) <= 1) {
|
||||
return std::make_unique<Literal>(*op != "any");
|
||||
} else {
|
||||
return (
|
||||
*op == "==" ||
|
||||
*op == "<" ||
|
||||
*op == ">" ||
|
||||
*op == "<=" ||
|
||||
*op == ">=" ? convertLegacyComparisonFilter(values, error) :
|
||||
*op == "!=" ? createExpression("!", convertLegacyComparisonFilter(values, error, {"=="}), error) :
|
||||
*op == "any" ? createExpression("any", convertLegacyFilterArray(values, error, 1), error) :
|
||||
*op == "all" ? createExpression("all", convertLegacyFilterArray(values, error, 1), error) :
|
||||
*op == "none" ? createExpression("!", createExpression("any", convertLegacyFilterArray(values, error, 1), error), error) :
|
||||
*op == "in" ? convertLegacyInFilter(values, error) :
|
||||
*op == "!in" ? createExpression("!", convertLegacyInFilter(values, error), error) :
|
||||
*op == "has" ? convertLegacyHasFilter(values, error) :
|
||||
*op == "!has" ? createExpression("!", convertLegacyHasFilter(values, error), error) :
|
||||
std::make_unique<Literal>(true)
|
||||
);
|
||||
}
|
||||
|
||||
if (*op == "==") {
|
||||
return convertEqualityFilter<EqualsFilter, TypeEqualsFilter, IdentifierEqualsFilter>(value, error);
|
||||
} else if (*op == "!=") {
|
||||
return convertEqualityFilter<NotEqualsFilter, TypeNotEqualsFilter, IdentifierNotEqualsFilter>(value, error);
|
||||
} else if (*op == ">") {
|
||||
return convertBinaryFilter<GreaterThanFilter>(value, error);
|
||||
} else if (*op == ">=") {
|
||||
return convertBinaryFilter<GreaterThanEqualsFilter>(value, error);
|
||||
} else if (*op == "<") {
|
||||
return convertBinaryFilter<LessThanFilter>(value, error);
|
||||
} else if (*op == "<=") {
|
||||
return convertBinaryFilter<LessThanEqualsFilter>(value, error);
|
||||
} else if (*op == "in") {
|
||||
return convertSetFilter<InFilter, TypeInFilter, IdentifierInFilter>(value, error);
|
||||
} else if (*op == "!in") {
|
||||
return convertSetFilter<NotInFilter, TypeNotInFilter, IdentifierNotInFilter>(value, error);
|
||||
} else if (*op == "all") {
|
||||
return convertCompoundFilter<AllFilter>(value, error);
|
||||
} else if (*op == "any") {
|
||||
return convertCompoundFilter<AnyFilter>(value, error);
|
||||
} else if (*op == "none") {
|
||||
return convertCompoundFilter<NoneFilter>(value, error);
|
||||
} else if (*op == "has") {
|
||||
return convertUnaryFilter<HasFilter, HasIdentifierFilter>(value, error);
|
||||
} else if (*op == "!has") {
|
||||
return convertUnaryFilter<NotHasFilter, NotHasIdentifierFilter>(value, error);
|
||||
}
|
||||
|
||||
error = { R"(filter operator must be one of "==", "!=", ">", ">=", "<", "<=", "in", "!in", "all", "any", "none", "has", or "!has")" };
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace conversion
|
||||
|
||||
@@ -126,162 +126,9 @@ void stringify(Writer& writer, const FeatureIdentifier& id) {
|
||||
}
|
||||
|
||||
template <class Writer>
|
||||
class StringifyFilter {
|
||||
public:
|
||||
Writer& writer;
|
||||
|
||||
void operator()(const NullFilter&) {
|
||||
writer.Null();
|
||||
}
|
||||
|
||||
void operator()(const EqualsFilter& f) {
|
||||
stringifyBinaryFilter(f, "==");
|
||||
}
|
||||
|
||||
void operator()(const NotEqualsFilter& f) {
|
||||
stringifyBinaryFilter(f, "!=");
|
||||
}
|
||||
|
||||
void operator()(const LessThanFilter& f) {
|
||||
stringifyBinaryFilter(f, "<");
|
||||
}
|
||||
|
||||
void operator()(const LessThanEqualsFilter& f) {
|
||||
stringifyBinaryFilter(f, "<=");
|
||||
}
|
||||
|
||||
void operator()(const GreaterThanFilter& f) {
|
||||
stringifyBinaryFilter(f, ">");
|
||||
}
|
||||
|
||||
void operator()(const GreaterThanEqualsFilter& f) {
|
||||
stringifyBinaryFilter(f, ">=");
|
||||
}
|
||||
|
||||
void operator()(const InFilter& f) {
|
||||
stringifySetFilter(f, "in");
|
||||
}
|
||||
|
||||
void operator()(const NotInFilter& f) {
|
||||
stringifySetFilter(f, "!in");
|
||||
}
|
||||
|
||||
void operator()(const AllFilter& f) {
|
||||
stringifyCompoundFilter(f, "all");
|
||||
}
|
||||
|
||||
void operator()(const AnyFilter& f) {
|
||||
stringifyCompoundFilter(f, "any");
|
||||
}
|
||||
|
||||
void operator()(const NoneFilter& f) {
|
||||
stringifyCompoundFilter(f, "none");
|
||||
}
|
||||
|
||||
void operator()(const HasFilter& f) {
|
||||
stringifyUnaryFilter("has", f.key);
|
||||
}
|
||||
|
||||
void operator()(const NotHasFilter& f) {
|
||||
stringifyUnaryFilter("!has", f.key);
|
||||
}
|
||||
|
||||
void operator()(const TypeEqualsFilter& f) {
|
||||
stringifyBinaryFilter(f, "==", "$type");
|
||||
}
|
||||
|
||||
void operator()(const TypeNotEqualsFilter& f) {
|
||||
stringifyBinaryFilter(f, "!=", "$type");
|
||||
}
|
||||
|
||||
void operator()(const TypeInFilter& f) {
|
||||
stringifySetFilter(f, "in", "$type");
|
||||
}
|
||||
|
||||
void operator()(const TypeNotInFilter& f) {
|
||||
stringifySetFilter(f, "!in", "$type");
|
||||
}
|
||||
|
||||
void operator()(const IdentifierEqualsFilter& f) {
|
||||
stringifyBinaryFilter(f, "==", "$id");
|
||||
}
|
||||
|
||||
void operator()(const IdentifierNotEqualsFilter& f) {
|
||||
stringifyBinaryFilter(f, "!=", "$id");
|
||||
}
|
||||
|
||||
void operator()(const IdentifierInFilter& f) {
|
||||
stringifySetFilter(f, "in", "$id");
|
||||
}
|
||||
|
||||
void operator()(const IdentifierNotInFilter& f) {
|
||||
stringifySetFilter(f, "!in", "$id");
|
||||
}
|
||||
|
||||
void operator()(const HasIdentifierFilter&) {
|
||||
stringifyUnaryFilter("has", "$id");
|
||||
}
|
||||
|
||||
void operator()(const NotHasIdentifierFilter&) {
|
||||
stringifyUnaryFilter("!has", "$id");
|
||||
}
|
||||
|
||||
void operator()(const ExpressionFilter& filter) {
|
||||
stringify(writer, filter.expression->serialize());
|
||||
}
|
||||
|
||||
private:
|
||||
template <class F>
|
||||
void stringifyBinaryFilter(const F& f, const char * op) {
|
||||
stringifyBinaryFilter(f, op, f.key);
|
||||
}
|
||||
|
||||
template <class F>
|
||||
void stringifyBinaryFilter(const F& f, const char * op, const std::string& key) {
|
||||
writer.StartArray();
|
||||
writer.String(op);
|
||||
writer.String(key);
|
||||
stringify(writer, f.value);
|
||||
writer.EndArray();
|
||||
}
|
||||
|
||||
template <class F>
|
||||
void stringifySetFilter(const F& f, const char * op) {
|
||||
stringifySetFilter(f, op, f.key);
|
||||
}
|
||||
|
||||
template <class F>
|
||||
void stringifySetFilter(const F& f, const char * op, const std::string& key) {
|
||||
writer.StartArray();
|
||||
writer.String(op);
|
||||
writer.String(key);
|
||||
for (const auto& value : f.values) {
|
||||
stringify(writer, value);
|
||||
}
|
||||
writer.EndArray();
|
||||
}
|
||||
|
||||
template <class F>
|
||||
void stringifyCompoundFilter(const F& f, const char * op) {
|
||||
writer.StartArray();
|
||||
writer.String(op);
|
||||
for (const auto& filter : f.filters) {
|
||||
Filter::visit(filter, *this);
|
||||
}
|
||||
writer.EndArray();
|
||||
}
|
||||
|
||||
void stringifyUnaryFilter(const char * op, const std::string& key) {
|
||||
writer.StartArray();
|
||||
writer.String(op);
|
||||
writer.String(key);
|
||||
writer.EndArray();
|
||||
}
|
||||
};
|
||||
|
||||
template <class Writer>
|
||||
void stringify(Writer& writer, const Filter& f) {
|
||||
Filter::visit(f, StringifyFilter<Writer> { writer });
|
||||
void stringify(Writer& writer, const Filter& filter) {
|
||||
if (!filter.expression) writer.Null();
|
||||
else stringify(writer, (*filter.expression)->serialize());
|
||||
}
|
||||
|
||||
template <class Writer>
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace detail {
|
||||
The Signature<Fn> structs are wrappers around an "evaluate()" function whose
|
||||
purpose is to extract the necessary Type data from the evaluate function's
|
||||
type. There are three key (partial) specializations:
|
||||
|
||||
|
||||
Signature<R (Params...)>:
|
||||
Wraps a simple evaluate function (const T0&, const T1&, ...) -> Result<U>
|
||||
|
||||
@@ -29,9 +29,9 @@ namespace detail {
|
||||
Signature<R (const EvaluationContext&, Params...)>:
|
||||
Wraps an evaluate function that needs to access the expression evaluation
|
||||
parameters in addition to its subexpressions, i.e.,
|
||||
(const EvaluationParams& const T0&, const T1&, ...) -> Result<U>. Needed
|
||||
(const EvaluationParams&, const T0&, const T1&, ...) -> Result<U>. Needed
|
||||
for expressions like ["zoom"], ["get", key], etc.
|
||||
|
||||
|
||||
In each of the above evaluate signatures, T0, T1, etc. are the types of
|
||||
the successfully evaluated subexpressions.
|
||||
*/
|
||||
@@ -42,7 +42,7 @@ struct Signature;
|
||||
template <class R, class... Params>
|
||||
struct Signature<R (Params...)> : SignatureBase {
|
||||
using Args = std::array<std::unique_ptr<Expression>, sizeof...(Params)>;
|
||||
|
||||
|
||||
Signature(R (*evaluate_)(Params...), std::string name_) :
|
||||
SignatureBase(
|
||||
valueTypeToExpressionType<std::decay_t<typename R::Value>>(),
|
||||
@@ -54,7 +54,7 @@ struct Signature<R (Params...)> : SignatureBase {
|
||||
EvaluationResult apply(const EvaluationContext& evaluationParameters, const Args& args) const {
|
||||
return applyImpl(evaluationParameters, args, std::index_sequence_for<Params...>{});
|
||||
}
|
||||
|
||||
|
||||
std::unique_ptr<Expression> makeExpression(std::vector<std::unique_ptr<Expression>> args) const override {
|
||||
typename Signature::Args argsArray;
|
||||
std::copy_n(std::make_move_iterator(args.begin()), sizeof...(Params), argsArray.begin());
|
||||
@@ -79,7 +79,7 @@ private:
|
||||
template <class R, typename T>
|
||||
struct Signature<R (const Varargs<T>&)> : SignatureBase {
|
||||
using Args = std::vector<std::unique_ptr<Expression>>;
|
||||
|
||||
|
||||
Signature(R (*evaluate_)(const Varargs<T>&), std::string name_) :
|
||||
SignatureBase(
|
||||
valueTypeToExpressionType<std::decay_t<typename R::Value>>(),
|
||||
@@ -88,11 +88,11 @@ struct Signature<R (const Varargs<T>&)> : SignatureBase {
|
||||
),
|
||||
evaluate(evaluate_)
|
||||
{}
|
||||
|
||||
|
||||
std::unique_ptr<Expression> makeExpression(std::vector<std::unique_ptr<Expression>> args) const override {
|
||||
return std::make_unique<CompoundExpression<Signature>>(name, *this, std::move(args));
|
||||
};
|
||||
|
||||
|
||||
EvaluationResult apply(const EvaluationContext& evaluationParameters, const Args& args) const {
|
||||
Varargs<T> evaluated;
|
||||
evaluated.reserve(args.size());
|
||||
@@ -114,7 +114,7 @@ struct Signature<R (const Varargs<T>&)> : SignatureBase {
|
||||
template <class R, class... Params>
|
||||
struct Signature<R (const EvaluationContext&, Params...)> : SignatureBase {
|
||||
using Args = std::array<std::unique_ptr<Expression>, sizeof...(Params)>;
|
||||
|
||||
|
||||
Signature(R (*evaluate_)(const EvaluationContext&, Params...), std::string name_) :
|
||||
SignatureBase(
|
||||
valueTypeToExpressionType<std::decay_t<typename R::Value>>(),
|
||||
@@ -123,17 +123,17 @@ struct Signature<R (const EvaluationContext&, Params...)> : SignatureBase {
|
||||
),
|
||||
evaluate(evaluate_)
|
||||
{}
|
||||
|
||||
|
||||
std::unique_ptr<Expression> makeExpression(std::vector<std::unique_ptr<Expression>> args) const override {
|
||||
typename Signature::Args argsArray;
|
||||
std::copy_n(std::make_move_iterator(args.begin()), sizeof...(Params), argsArray.begin());
|
||||
return std::make_unique<CompoundExpression<Signature>>(name, *this, std::move(argsArray));
|
||||
}
|
||||
|
||||
|
||||
EvaluationResult apply(const EvaluationContext& evaluationParameters, const Args& args) const {
|
||||
return applyImpl(evaluationParameters, args, std::index_sequence_for<Params...>{});
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
template <std::size_t ...I>
|
||||
EvaluationResult applyImpl(const EvaluationContext& evaluationParameters, const Args& args, std::index_sequence<I...>) const {
|
||||
@@ -149,6 +149,41 @@ private:
|
||||
|
||||
R (*evaluate)(const EvaluationContext&, Params...);
|
||||
};
|
||||
|
||||
// Evaluate function needing EvaluationContext and Varargs
|
||||
// (const EvaluationContext&, const Varargs<T>&) -> Result<U>
|
||||
template <class R, typename T>
|
||||
struct Signature<R (const EvaluationContext&, const Varargs<T>&)> : SignatureBase {
|
||||
using Args = std::vector<std::unique_ptr<Expression>>;
|
||||
|
||||
Signature(R (*evaluate_)(const EvaluationContext&, const Varargs<T>&), std::string name_) :
|
||||
SignatureBase(
|
||||
valueTypeToExpressionType<std::decay_t<typename R::Value>>(),
|
||||
VarargsType { valueTypeToExpressionType<T>() },
|
||||
std::move(name_)
|
||||
),
|
||||
evaluate(evaluate_)
|
||||
{}
|
||||
|
||||
std::unique_ptr<Expression> makeExpression(std::vector<std::unique_ptr<Expression>> args) const override {
|
||||
return std::make_unique<CompoundExpression<Signature>>(name, *this, std::move(args));
|
||||
};
|
||||
|
||||
EvaluationResult apply(const EvaluationContext& evaluationParameters, const Args& args) const {
|
||||
Varargs<T> evaluated;
|
||||
evaluated.reserve(args.size());
|
||||
for (const auto& arg : args) {
|
||||
const EvaluationResult evaluatedArg = arg->evaluate(evaluationParameters);
|
||||
if(!evaluatedArg) return evaluatedArg.error();
|
||||
evaluated.push_back(*fromExpressionValue<std::decay_t<T>>(*evaluatedArg));
|
||||
}
|
||||
const R value = evaluate(evaluationParameters, evaluated);
|
||||
if (!value) return value.error();
|
||||
return *value;
|
||||
}
|
||||
|
||||
R (*evaluate)(const EvaluationContext&, const Varargs<T>&);
|
||||
};
|
||||
|
||||
// Machinery to pull out function types from class methods, lambdas, etc.
|
||||
template <class R, class... Params>
|
||||
@@ -180,18 +215,92 @@ static std::unique_ptr<detail::SignatureBase> makeSignature(Fn evaluateFunction,
|
||||
return std::make_unique<detail::Signature<Fn>>(evaluateFunction, std::move(name));
|
||||
}
|
||||
|
||||
Value featureIdAsExpressionValue(EvaluationContext params) {
|
||||
assert(params.feature);
|
||||
auto id = params.feature->getID();
|
||||
if (!id) return Null;
|
||||
return id->match([](const auto& idid) {
|
||||
return toExpressionValue(mbgl::Value(idid));
|
||||
});
|
||||
};
|
||||
|
||||
optional<Value> featurePropertyAsExpressionValue(EvaluationContext params, const std::string& key) {
|
||||
assert(params.feature);
|
||||
auto property = params.feature->getValue(key);
|
||||
return property ? toExpressionValue(*property) : optional<Value>();
|
||||
};
|
||||
|
||||
optional<std::string> featureTypeAsString(FeatureType type) {
|
||||
switch(type) {
|
||||
case FeatureType::Point:
|
||||
return optional<std::string>("Point");
|
||||
case FeatureType::LineString:
|
||||
return optional<std::string>("LineString");
|
||||
case FeatureType::Polygon:
|
||||
return optional<std::string>("Polygon");
|
||||
case FeatureType::Unknown:
|
||||
return optional<std::string>("Unknown");
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
optional<double> featurePropertyAsDouble(EvaluationContext params, const std::string& key) {
|
||||
assert(params.feature);
|
||||
auto property = params.feature->getValue(key);
|
||||
if (!property) return {};
|
||||
return property->match(
|
||||
[](double value) { return value; },
|
||||
[](uint64_t value) { return optional<double>(static_cast<double>(value)); },
|
||||
[](int64_t value) { return optional<double>(static_cast<double>(value)); },
|
||||
[](auto) { return optional<double>(); }
|
||||
);
|
||||
};
|
||||
|
||||
optional<std::string> featurePropertyAsString(EvaluationContext params, const std::string& key) {
|
||||
assert(params.feature);
|
||||
auto property = params.feature->getValue(key);
|
||||
if (!property) return {};
|
||||
return property->match(
|
||||
[](std::string value) { return value; },
|
||||
[](auto) { return optional<std::string>(); }
|
||||
);
|
||||
};
|
||||
|
||||
optional<double> featureIdAsDouble(EvaluationContext params) {
|
||||
assert(params.feature);
|
||||
auto id = params.feature->getID();
|
||||
if (!id) return optional<double>();
|
||||
return id->match(
|
||||
[](double value) { return value; },
|
||||
[](uint64_t value) { return optional<double>(static_cast<double>(value)); },
|
||||
[](int64_t value) { return optional<double>(static_cast<double>(value)); },
|
||||
[](auto) { return optional<double>(); }
|
||||
);
|
||||
};
|
||||
|
||||
optional<std::string> featureIdAsString(EvaluationContext params) {
|
||||
assert(params.feature);
|
||||
auto id = params.feature->getID();
|
||||
if (!id) return optional<std::string>();
|
||||
return id->match(
|
||||
[](std::string value) { return value; },
|
||||
[](auto) { return optional<std::string>(); }
|
||||
);
|
||||
};
|
||||
|
||||
std::unordered_map<std::string, CompoundExpressionRegistry::Definition> initializeDefinitions() {
|
||||
std::unordered_map<std::string, CompoundExpressionRegistry::Definition> definitions;
|
||||
auto define = [&](std::string name, auto fn) {
|
||||
definitions[name].push_back(makeSignature(fn, name));
|
||||
};
|
||||
|
||||
|
||||
define("e", []() -> Result<double> { return 2.718281828459045; });
|
||||
define("pi", []() -> Result<double> { return 3.141592653589793; });
|
||||
define("ln2", []() -> Result<double> { return 0.6931471805599453; });
|
||||
|
||||
define("typeof", [](const Value& v) -> Result<std::string> { return toString(typeOf(v)); });
|
||||
|
||||
|
||||
define("to-string", [](const Value& value) -> Result<std::string> {
|
||||
return value.match(
|
||||
[](const Color& c) -> Result<std::string> { return c.stringify(); }, // avoid quoting
|
||||
@@ -199,10 +308,10 @@ std::unordered_map<std::string, CompoundExpressionRegistry::Definition> initiali
|
||||
[](const auto& v) -> Result<std::string> { return stringify(v); }
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
define("to-boolean", [](const Value& v) -> Result<bool> {
|
||||
return v.match(
|
||||
[&] (double f) { return (bool)f; },
|
||||
[&] (double f) { return static_cast<bool>(f); },
|
||||
[&] (const std::string& s) { return s.length() > 0; },
|
||||
[&] (bool b) { return b; },
|
||||
[&] (const NullValue&) { return false; },
|
||||
@@ -212,10 +321,10 @@ std::unordered_map<std::string, CompoundExpressionRegistry::Definition> initiali
|
||||
define("to-rgba", [](const Color& color) -> Result<std::array<double, 4>> {
|
||||
return color.toArray();
|
||||
});
|
||||
|
||||
|
||||
define("rgba", rgba);
|
||||
define("rgb", [](double r, double g, double b) { return rgba(r, g, b, 1.0f); });
|
||||
|
||||
|
||||
define("zoom", [](const EvaluationContext& params) -> Result<double> {
|
||||
if (!params.zoom) {
|
||||
return EvaluationError {
|
||||
@@ -224,7 +333,7 @@ std::unordered_map<std::string, CompoundExpressionRegistry::Definition> initiali
|
||||
}
|
||||
return *(params.zoom);
|
||||
});
|
||||
|
||||
|
||||
define("heatmap-density", [](const EvaluationContext& params) -> Result<double> {
|
||||
if (!params.heatmapDensity) {
|
||||
return EvaluationError {
|
||||
@@ -240,7 +349,7 @@ std::unordered_map<std::string, CompoundExpressionRegistry::Definition> initiali
|
||||
"Feature data is unavailable in the current evaluation context."
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
return params.feature->getValue(key) ? true : false;
|
||||
});
|
||||
define("has", [](const std::string& key, const std::unordered_map<std::string, Value>& object) -> Result<bool> {
|
||||
@@ -266,7 +375,7 @@ std::unordered_map<std::string, CompoundExpressionRegistry::Definition> initiali
|
||||
}
|
||||
return object.at(key);
|
||||
});
|
||||
|
||||
|
||||
define("properties", [](const EvaluationContext& params) -> Result<std::unordered_map<std::string, Value>> {
|
||||
if (!params.feature) {
|
||||
return EvaluationError {
|
||||
@@ -280,14 +389,14 @@ std::unordered_map<std::string, CompoundExpressionRegistry::Definition> initiali
|
||||
}
|
||||
return result;
|
||||
});
|
||||
|
||||
|
||||
define("geometry-type", [](const EvaluationContext& params) -> Result<std::string> {
|
||||
if (!params.feature) {
|
||||
return EvaluationError {
|
||||
"Feature data is unavailable in the current evaluation context."
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
auto type = params.feature->getType();
|
||||
if (type == FeatureType::Point) {
|
||||
return "Point";
|
||||
@@ -299,14 +408,14 @@ std::unordered_map<std::string, CompoundExpressionRegistry::Definition> initiali
|
||||
return "Unknown";
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
define("id", [](const EvaluationContext& params) -> Result<Value> {
|
||||
if (!params.feature) {
|
||||
return EvaluationError {
|
||||
"Feature data is unavailable in the current evaluation context."
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
auto id = params.feature->getID();
|
||||
if (!id) {
|
||||
return Null;
|
||||
@@ -317,7 +426,7 @@ std::unordered_map<std::string, CompoundExpressionRegistry::Definition> initiali
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
define("+", [](const Varargs<double>& args) -> Result<double> {
|
||||
double sum = 0.0f;
|
||||
for (auto arg : args) {
|
||||
@@ -347,7 +456,7 @@ std::unordered_map<std::string, CompoundExpressionRegistry::Definition> initiali
|
||||
define("asin", [](double x) -> Result<double> { return asin(x); });
|
||||
define("acos", [](double x) -> Result<double> { return acos(x); });
|
||||
define("atan", [](double x) -> Result<double> { return atan(x); });
|
||||
|
||||
|
||||
define("min", [](const Varargs<double>& args) -> Result<double> {
|
||||
double result = std::numeric_limits<double>::infinity();
|
||||
for (double arg : args) {
|
||||
@@ -362,7 +471,7 @@ std::unordered_map<std::string, CompoundExpressionRegistry::Definition> initiali
|
||||
}
|
||||
return result;
|
||||
});
|
||||
|
||||
|
||||
define("round", [](double x) -> Result<double> { return std::round(x); });
|
||||
define("floor", [](double x) -> Result<double> { return std::floor(x); });
|
||||
define("ceil", [](double x) -> Result<double> { return std::ceil(x); });
|
||||
@@ -376,9 +485,9 @@ std::unordered_map<std::string, CompoundExpressionRegistry::Definition> initiali
|
||||
define("<", [](const std::string& lhs, const std::string& rhs) -> Result<bool> { return lhs < rhs; });
|
||||
define("<=", [](double lhs, double rhs) -> Result<bool> { return lhs <= rhs; });
|
||||
define("<=", [](const std::string& lhs, const std::string& rhs) -> Result<bool> { return lhs <= rhs; });
|
||||
|
||||
|
||||
define("!", [](bool e) -> Result<bool> { return !e; });
|
||||
|
||||
|
||||
define("upcase", [](const std::string& input) -> Result<std::string> {
|
||||
return platform::uppercase(input);
|
||||
});
|
||||
@@ -395,7 +504,130 @@ std::unordered_map<std::string, CompoundExpressionRegistry::Definition> initiali
|
||||
define("error", [](const std::string& input) -> Result<type::ErrorType> {
|
||||
return EvaluationError { input };
|
||||
});
|
||||
|
||||
// Legacy Filters
|
||||
define("filter-==", [](const EvaluationContext& params, const std::string& key, const Value &lhs) -> Result<bool> {
|
||||
const auto rhs = featurePropertyAsExpressionValue(params, key);
|
||||
return rhs ? lhs == *rhs : false;
|
||||
});
|
||||
|
||||
define("filter-id-==", [](const EvaluationContext& params, const Value &lhs) -> Result<bool> {
|
||||
return lhs == featureIdAsExpressionValue(params);
|
||||
});
|
||||
|
||||
define("filter-type-==", [](const EvaluationContext& params, const std::string &lhs) -> Result<bool> {
|
||||
if (!params.feature) return false;
|
||||
return featureTypeAsString(params.feature->getType()) == lhs;
|
||||
});
|
||||
|
||||
define("filter-<", [](const EvaluationContext& params, const std::string& key, double lhs) -> Result<bool> {
|
||||
auto rhs = featurePropertyAsDouble(params, key);
|
||||
return rhs ? rhs < lhs : false;
|
||||
});
|
||||
|
||||
define("filter-<", [](const EvaluationContext& params, const std::string& key, std::string lhs) -> Result<bool> {
|
||||
auto rhs = featurePropertyAsString(params, key);
|
||||
return rhs ? rhs < lhs : false;
|
||||
});
|
||||
|
||||
define("filter-id-<", [](const EvaluationContext& params, double lhs) -> Result<bool> {
|
||||
auto rhs = featureIdAsDouble(params);
|
||||
return rhs ? rhs < lhs : false;
|
||||
});
|
||||
|
||||
define("filter-id-<", [](const EvaluationContext& params, std::string lhs) -> Result<bool> {
|
||||
auto rhs = featureIdAsString(params);
|
||||
return rhs ? rhs < lhs : false;
|
||||
});
|
||||
|
||||
define("filter->", [](const EvaluationContext& params, const std::string& key, double lhs) -> Result<bool> {
|
||||
auto rhs = featurePropertyAsDouble(params, key);
|
||||
return rhs ? rhs > lhs : false;
|
||||
});
|
||||
|
||||
define("filter->", [](const EvaluationContext& params, const std::string& key, std::string lhs) -> Result<bool> {
|
||||
auto rhs = featurePropertyAsString(params, key);
|
||||
return rhs ? rhs > lhs : false;
|
||||
});
|
||||
|
||||
define("filter-id->", [](const EvaluationContext& params, double lhs) -> Result<bool> {
|
||||
auto rhs = featureIdAsDouble(params);
|
||||
return rhs ? rhs > lhs : false;
|
||||
});
|
||||
|
||||
define("filter-id->", [](const EvaluationContext& params, std::string lhs) -> Result<bool> {
|
||||
auto rhs = featureIdAsString(params);
|
||||
return rhs ? rhs > lhs : false;
|
||||
});
|
||||
|
||||
define("filter-<=", [](const EvaluationContext& params, const std::string& key, double lhs) -> Result<bool> {
|
||||
auto rhs = featurePropertyAsDouble(params, key);
|
||||
return rhs ? rhs <= lhs : false;
|
||||
});
|
||||
|
||||
define("filter-<=", [](const EvaluationContext& params, const std::string& key, std::string lhs) -> Result<bool> {
|
||||
auto rhs = featurePropertyAsString(params, key);
|
||||
return rhs ? rhs <= lhs : false;
|
||||
});
|
||||
|
||||
define("filter-id-<=", [](const EvaluationContext& params, double lhs) -> Result<bool> {
|
||||
auto rhs = featureIdAsDouble(params);
|
||||
return rhs ? rhs <= lhs : false;
|
||||
});
|
||||
|
||||
define("filter-id-<=", [](const EvaluationContext& params, std::string lhs) -> Result<bool> {
|
||||
auto rhs = featureIdAsString(params);
|
||||
return rhs ? rhs <= lhs : false;
|
||||
});
|
||||
|
||||
define("filter->=", [](const EvaluationContext& params, const std::string& key, double lhs) -> Result<bool> {
|
||||
auto rhs = featurePropertyAsDouble(params, key);
|
||||
return rhs ? rhs >= lhs : false;
|
||||
});
|
||||
|
||||
define("filter->=", [](const EvaluationContext& params, const std::string& key, std::string lhs) -> Result<bool> {
|
||||
auto rhs = featurePropertyAsString(params, key);
|
||||
return rhs ? rhs >= lhs : false;
|
||||
});
|
||||
|
||||
define("filter-id->=", [](const EvaluationContext& params, double lhs) -> Result<bool> {
|
||||
auto rhs = featureIdAsDouble(params);
|
||||
return rhs ? rhs >= lhs : false;
|
||||
});
|
||||
|
||||
define("filter-id->=", [](const EvaluationContext& params, std::string lhs) -> Result<bool> {
|
||||
auto rhs = featureIdAsString(params);
|
||||
return rhs ? rhs >= lhs : false;
|
||||
});
|
||||
|
||||
define("filter-has", [](const EvaluationContext& params, const std::string& key) -> Result<bool> {
|
||||
assert(params.feature);
|
||||
return bool(params.feature->getValue(key));
|
||||
});
|
||||
|
||||
define("filter-has-id", [](const EvaluationContext& params) -> Result<bool> {
|
||||
assert(params.feature);
|
||||
return bool(params.feature->getID());
|
||||
});
|
||||
|
||||
define("filter-type-in", [](const EvaluationContext& params, const Varargs<std::string>& types) -> Result<bool> {
|
||||
assert(params.feature);
|
||||
optional<std::string> type = featureTypeAsString(params.feature->getType());
|
||||
return std::find(types.begin(), types.end(), type) != types.end();
|
||||
});
|
||||
|
||||
define("filter-id-in", [](const EvaluationContext& params, const Varargs<Value>& ids) -> Result<bool> {
|
||||
auto id = featureIdAsExpressionValue(params);
|
||||
return std::find(ids.begin(), ids.end(), id) != ids.end();
|
||||
});
|
||||
|
||||
define("filter-in", [](const EvaluationContext& params, const Varargs<Value>& varargs) -> Result<bool> {
|
||||
if (varargs.size() < 2) return false;
|
||||
assert(varargs[0].is<std::string>());
|
||||
auto value = featurePropertyAsExpressionValue(params, varargs[0].get<std::string>());
|
||||
return value ? std::find(varargs.begin() + 1, varargs.end(), *value) != varargs.end() : false;
|
||||
});
|
||||
|
||||
return definitions;
|
||||
}
|
||||
|
||||
@@ -414,7 +646,7 @@ ParseResult parseCompoundExpression(const std::string name, const Convertible& v
|
||||
return ParseResult();
|
||||
}
|
||||
const CompoundExpressionRegistry::Definition& definition = it->second;
|
||||
|
||||
|
||||
auto length = arrayLength(value);
|
||||
|
||||
// Check if we have a single signature with the correct number of
|
||||
@@ -440,14 +672,14 @@ ParseResult parseCompoundExpression(const std::string name, const Convertible& v
|
||||
args.reserve(length - 1);
|
||||
for (std::size_t i = 1; i < length; i++) {
|
||||
optional<type::Type> expected;
|
||||
|
||||
|
||||
if (singleMatchingSignature) {
|
||||
expected = definition[*singleMatchingSignature]->params.match(
|
||||
[](const VarargsType& varargs) { return varargs.type; },
|
||||
[&](const std::vector<type::Type>& params_) { return params_[i - 1]; }
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
auto parsed = ctx.parse(arrayMember(value, i), i, expected);
|
||||
if (!parsed) {
|
||||
return parsed;
|
||||
@@ -474,7 +706,7 @@ ParseResult createCompoundExpression(const Definition& definition,
|
||||
|
||||
for (const std::unique_ptr<detail::SignatureBase>& signature : definition) {
|
||||
signatureContext.clearErrors();
|
||||
|
||||
|
||||
if (signature->params.is<std::vector<type::Type>>()) {
|
||||
const std::vector<type::Type>& params = signature->params.get<std::vector<type::Type>>();
|
||||
if (params.size() != args.size()) {
|
||||
@@ -502,12 +734,12 @@ ParseResult createCompoundExpression(const Definition& definition,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (signatureContext.getErrors().size() == 0) {
|
||||
return ParseResult(signature->makeExpression(std::move(args)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (definition.size() == 1) {
|
||||
ctx.appendErrors(std::move(signatureContext));
|
||||
} else {
|
||||
@@ -540,10 +772,32 @@ ParseResult createCompoundExpression(const Definition& definition,
|
||||
}
|
||||
ctx.error("Expected arguments of type " + signatures + ", but found (" + actualTypes + ") instead.");
|
||||
}
|
||||
|
||||
|
||||
return ParseResult();
|
||||
}
|
||||
|
||||
ParseResult createCompoundExpression(const std::string& name, ParsingContext& ctx) {
|
||||
return createCompoundExpression(name, std::vector<std::unique_ptr<Expression>>(), ctx);
|
||||
}
|
||||
|
||||
ParseResult createCompoundExpression(const std::string& name,
|
||||
std::unique_ptr<Expression> arg1,
|
||||
ParsingContext& ctx) {
|
||||
std::vector<std::unique_ptr<Expression>> args;
|
||||
args.push_back(std::move(arg1));
|
||||
return createCompoundExpression(name, std::move(args), ctx);
|
||||
}
|
||||
|
||||
ParseResult createCompoundExpression(const std::string& name,
|
||||
std::unique_ptr<Expression> arg1,
|
||||
std::unique_ptr<Expression> arg2,
|
||||
ParsingContext& ctx) {
|
||||
std::vector<std::unique_ptr<Expression>> args;
|
||||
args.push_back(std::move(arg1));
|
||||
args.push_back(std::move(arg2));
|
||||
return createCompoundExpression(name, std::move(args), ctx);
|
||||
}
|
||||
|
||||
} // namespace expression
|
||||
} // namespace style
|
||||
} // namespace mbgl
|
||||
|
||||
@@ -109,6 +109,14 @@ mbgl::Value Literal::serialize() const {
|
||||
return *fromExpressionValue<mbgl::Value>(value);
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<Literal> createLiteral(const char* value) {
|
||||
return createLiteral(std::string(value));
|
||||
}
|
||||
|
||||
std::unique_ptr<Literal> createLiteral(Value value) {
|
||||
return std::make_unique<Literal>(value);
|
||||
}
|
||||
|
||||
} // namespace expression
|
||||
} // namespace style
|
||||
|
||||
@@ -1,12 +1,20 @@
|
||||
#include <mbgl/style/filter.hpp>
|
||||
#include <mbgl/style/filter_evaluator.hpp>
|
||||
#include <mbgl/tile/geometry_tile_data.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
namespace style {
|
||||
|
||||
bool Filter::operator()(const expression::EvaluationContext &context) const {
|
||||
return FilterBase::visit(*this, FilterEvaluator { context });
|
||||
|
||||
if (!this->expression) return true;
|
||||
|
||||
const expression::EvaluationResult result = (*this->expression)->evaluate(context);
|
||||
if (result) {
|
||||
const optional<bool> typed = expression::fromExpressionValue<bool>(*result);
|
||||
return typed ? *typed : false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace style
|
||||
|
||||
@@ -1,225 +0,0 @@
|
||||
#include <mbgl/style/filter.hpp>
|
||||
#include <mbgl/style/filter_evaluator.hpp>
|
||||
#include <mbgl/tile/geometry_tile_data.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
namespace style {
|
||||
|
||||
template <class Op>
|
||||
struct Comparator {
|
||||
const Op& op;
|
||||
|
||||
template <class T>
|
||||
bool operator()(const T& lhs, const T& rhs) const {
|
||||
return op(lhs, rhs);
|
||||
}
|
||||
|
||||
template <class T0, class T1>
|
||||
auto operator()(const T0& lhs, const T1& rhs) const
|
||||
-> typename std::enable_if_t<std::is_arithmetic<T0>::value && !std::is_same<T0, bool>::value &&
|
||||
std::is_arithmetic<T1>::value && !std::is_same<T1, bool>::value, bool> {
|
||||
return op(double(lhs), double(rhs));
|
||||
}
|
||||
|
||||
template <class T0, class T1>
|
||||
auto operator()(const T0&, const T1&) const
|
||||
-> typename std::enable_if_t<!std::is_arithmetic<T0>::value || std::is_same<T0, bool>::value ||
|
||||
!std::is_arithmetic<T1>::value || std::is_same<T1, bool>::value, bool> {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool operator()(const NullValue&,
|
||||
const NullValue&) const {
|
||||
// Should be unreachable; null is not currently allowed by the style specification.
|
||||
assert(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool operator()(const std::vector<Value>&,
|
||||
const std::vector<Value>&) const {
|
||||
// Should be unreachable; nested values are not currently allowed by the style specification.
|
||||
assert(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool operator()(const PropertyMap&,
|
||||
const PropertyMap&) const {
|
||||
// Should be unreachable; nested values are not currently allowed by the style specification.
|
||||
assert(false);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
template <class Op>
|
||||
bool compare(const Value& lhs, const Value& rhs, const Op& op) {
|
||||
return Value::binary_visit(lhs, rhs, Comparator<Op> { op });
|
||||
}
|
||||
|
||||
bool equal(const Value& lhs, const Value& rhs) {
|
||||
return compare(lhs, rhs, [] (const auto& lhs_, const auto& rhs_) { return lhs_ == rhs_; });
|
||||
}
|
||||
|
||||
bool FilterEvaluator::operator()(const NullFilter&) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FilterEvaluator::operator()(const EqualsFilter& filter) const {
|
||||
optional<Value> actual = context.feature->getValue(filter.key);
|
||||
return actual && equal(*actual, filter.value);
|
||||
}
|
||||
|
||||
bool FilterEvaluator::operator()(const NotEqualsFilter& filter) const {
|
||||
optional<Value> actual = context.feature->getValue(filter.key);
|
||||
return !actual || !equal(*actual, filter.value);
|
||||
}
|
||||
|
||||
bool FilterEvaluator::operator()(const LessThanFilter& filter) const {
|
||||
optional<Value> actual = context.feature->getValue(filter.key);
|
||||
return actual && compare(*actual, filter.value, [] (const auto& lhs_, const auto& rhs_) { return lhs_ < rhs_; });
|
||||
}
|
||||
|
||||
bool FilterEvaluator::operator()(const LessThanEqualsFilter& filter) const {
|
||||
optional<Value> actual = context.feature->getValue(filter.key);
|
||||
return actual && compare(*actual, filter.value, [] (const auto& lhs_, const auto& rhs_) { return lhs_ <= rhs_; });
|
||||
}
|
||||
|
||||
bool FilterEvaluator::operator()(const GreaterThanFilter& filter) const {
|
||||
optional<Value> actual = context.feature->getValue(filter.key);
|
||||
return actual && compare(*actual, filter.value, [] (const auto& lhs_, const auto& rhs_) { return lhs_ > rhs_; });
|
||||
}
|
||||
|
||||
bool FilterEvaluator::operator()(const GreaterThanEqualsFilter& filter) const {
|
||||
optional<Value> actual = context.feature->getValue(filter.key);
|
||||
return actual && compare(*actual, filter.value, [] (const auto& lhs_, const auto& rhs_) { return lhs_ >= rhs_; });
|
||||
}
|
||||
|
||||
bool FilterEvaluator::operator()(const InFilter& filter) const {
|
||||
optional<Value> actual = context.feature->getValue(filter.key);
|
||||
if (!actual)
|
||||
return false;
|
||||
for (const auto& v: filter.values) {
|
||||
if (equal(*actual, v)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FilterEvaluator::operator()(const NotInFilter& filter) const {
|
||||
optional<Value> actual = context.feature->getValue(filter.key);
|
||||
if (!actual)
|
||||
return true;
|
||||
for (const auto& v: filter.values) {
|
||||
if (equal(*actual, v)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FilterEvaluator::operator()(const AnyFilter& filter) const {
|
||||
for (const auto& f: filter.filters) {
|
||||
if (Filter::visit(f, *this)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FilterEvaluator::operator()(const AllFilter& filter) const {
|
||||
for (const auto& f: filter.filters) {
|
||||
if (!Filter::visit(f, *this)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FilterEvaluator::operator()(const NoneFilter& filter) const {
|
||||
for (const auto& f: filter.filters) {
|
||||
if (Filter::visit(f, *this)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FilterEvaluator::operator()(const HasFilter& filter) const {
|
||||
return bool(context.feature->getValue(filter.key));
|
||||
}
|
||||
|
||||
bool FilterEvaluator::operator()(const NotHasFilter& filter) const {
|
||||
return !context.feature->getValue(filter.key);
|
||||
}
|
||||
|
||||
bool FilterEvaluator::operator()(const TypeEqualsFilter& filter) const {
|
||||
return context.feature->getType() == filter.value;
|
||||
}
|
||||
|
||||
bool FilterEvaluator::operator()(const TypeNotEqualsFilter& filter) const {
|
||||
return context.feature->getType() != filter.value;
|
||||
}
|
||||
|
||||
bool FilterEvaluator::operator()(const TypeInFilter& filter) const {
|
||||
for (const auto& v: filter.values) {
|
||||
if (context.feature->getType() == v) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FilterEvaluator::operator()(const TypeNotInFilter& filter) const {
|
||||
for (const auto& v: filter.values) {
|
||||
if (context.feature->getType() == v) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FilterEvaluator::operator()(const IdentifierEqualsFilter& filter) const {
|
||||
return context.feature->getID() == filter.value;
|
||||
}
|
||||
|
||||
bool FilterEvaluator::operator()(const IdentifierNotEqualsFilter& filter) const {
|
||||
return context.feature->getID() != filter.value;
|
||||
}
|
||||
|
||||
bool FilterEvaluator::operator()(const IdentifierInFilter& filter) const {
|
||||
for (const auto& v: filter.values) {
|
||||
if (context.feature->getID() == v) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FilterEvaluator::operator()(const IdentifierNotInFilter& filter) const {
|
||||
for (const auto& v: filter.values) {
|
||||
if (context.feature->getID() == v) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FilterEvaluator::operator()(const HasIdentifierFilter&) const {
|
||||
return bool(context.feature->getID());
|
||||
}
|
||||
|
||||
bool FilterEvaluator::operator()(const NotHasIdentifierFilter&) const {
|
||||
return !context.feature->getID();
|
||||
}
|
||||
|
||||
bool FilterEvaluator::operator()(const ExpressionFilter& filter) const {
|
||||
const expression::EvaluationResult result = filter.expression->evaluate(context);
|
||||
if (result) {
|
||||
const optional<bool> typed = expression::fromExpressionValue<bool>(*result);
|
||||
return typed ? *typed : false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace style
|
||||
} // namespace mbgl
|
||||
@@ -3,7 +3,6 @@
|
||||
#include <mbgl/renderer/query.hpp>
|
||||
#include <mbgl/renderer/tile_parameters.hpp>
|
||||
#include <mbgl/actor/scheduler.hpp>
|
||||
#include <mbgl/style/filter_evaluator.hpp>
|
||||
#include <mbgl/util/string.hpp>
|
||||
#include <mbgl/tile/tile_observer.hpp>
|
||||
#include <mbgl/style/custom_tile_loader.hpp>
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
#include <mbgl/tile/geojson_tile_data.hpp>
|
||||
#include <mbgl/renderer/query.hpp>
|
||||
#include <mbgl/renderer/tile_parameters.hpp>
|
||||
#include <mbgl/style/filter_evaluator.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
#include <mbgl/storage/file_source.hpp>
|
||||
#include <mbgl/geometry/feature_index.hpp>
|
||||
#include <mbgl/map/transform_state.hpp>
|
||||
#include <mbgl/style/filter_evaluator.hpp>
|
||||
#include <mbgl/util/logging.hpp>
|
||||
#include <mbgl/actor/scheduler.hpp>
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include <mbgl/renderer/bucket_parameters.hpp>
|
||||
#include <mbgl/renderer/group_by_layout.hpp>
|
||||
#include <mbgl/style/filter.hpp>
|
||||
#include <mbgl/style/filter_evaluator.hpp>
|
||||
#include <mbgl/style/layers/symbol_layer_impl.hpp>
|
||||
#include <mbgl/renderer/layers/render_symbol_layer.hpp>
|
||||
#include <mbgl/renderer/buckets/symbol_bucket.hpp>
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
|
||||
using namespace mbgl;
|
||||
using namespace mbgl::style;
|
||||
using namespace mbgl::style::expression;
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -67,18 +68,19 @@ TEST(Query, QueryRenderedFeaturesFilterLayer) {
|
||||
|
||||
TEST(Query, QueryRenderedFeaturesFilter) {
|
||||
QueryTest test;
|
||||
ParsingContext context;
|
||||
|
||||
auto zz = test.map.pixelForLatLng({ 0, 0 });
|
||||
|
||||
const EqualsFilter eqFilter = { "key1", std::string("value1") };
|
||||
const Filter eqFilter(createCompoundExpression("filter-==", createLiteral("key1"), createLiteral("value1"), context));
|
||||
auto features1 = test.frontend.getRenderer()->queryRenderedFeatures(zz, {{}, { eqFilter }});
|
||||
EXPECT_EQ(features1.size(), 1u);
|
||||
|
||||
const IdentifierNotEqualsFilter idNotEqFilter = { std::string("feature1") };
|
||||
const Filter idNotEqFilter(createCompoundExpression("!", std::move(*createCompoundExpression("filter-id-==", createLiteral("feature1"), context)), context));
|
||||
auto features2 = test.frontend.getRenderer()->queryRenderedFeatures(zz, {{{ "layer4" }}, { idNotEqFilter }});
|
||||
EXPECT_EQ(features2.size(), 0u);
|
||||
|
||||
const GreaterThanFilter gtFilter = { "key2", 1.0 };
|
||||
const Filter gtFilter(createCompoundExpression("filter->", createLiteral("key2"), createLiteral(1.0), context));
|
||||
auto features3 = test.frontend.getRenderer()->queryRenderedFeatures(zz, {{ }, { gtFilter }});
|
||||
EXPECT_EQ(features3.size(), 1u);
|
||||
}
|
||||
@@ -108,16 +110,17 @@ TEST(Query, QuerySourceFeaturesOptionValidation) {
|
||||
|
||||
TEST(Query, QuerySourceFeaturesFilter) {
|
||||
QueryTest test;
|
||||
ParsingContext context;
|
||||
|
||||
const EqualsFilter eqFilter = { "key1", std::string("value1") };
|
||||
const Filter eqFilter(createCompoundExpression("filter-==", createLiteral("key1"), createLiteral("value1"), context));
|
||||
auto features1 = test.frontend.getRenderer()->querySourceFeatures("source4", {{}, { eqFilter }});
|
||||
EXPECT_EQ(features1.size(), 1u);
|
||||
|
||||
const IdentifierNotEqualsFilter idNotEqFilter = { std::string("feature1") };
|
||||
const Filter idNotEqFilter(createCompoundExpression("!", std::move(*createCompoundExpression("filter-id-==", createLiteral("feature1"), context)), context));
|
||||
auto features2 = test.frontend.getRenderer()->querySourceFeatures("source4", {{}, { idNotEqFilter }});
|
||||
EXPECT_EQ(features2.size(), 0u);
|
||||
|
||||
const GreaterThanFilter gtFilter = { "key2", 1.0 };
|
||||
const Filter gtFilter(createCompoundExpression("filter->", createLiteral("key2"), createLiteral(1.0), context));
|
||||
auto features3 = test.frontend.getRenderer()->querySourceFeatures("source4", {{}, { gtFilter }});
|
||||
EXPECT_EQ(features3.size(), 1u);
|
||||
}
|
||||
|
||||
@@ -5,9 +5,11 @@
|
||||
#include <mbgl/style/layers/background_layer.hpp>
|
||||
#include <mbgl/style/layers/circle_layer.hpp>
|
||||
#include <mbgl/style/layers/line_layer.hpp>
|
||||
#include <mbgl/style/expression/compound_expression.hpp>
|
||||
|
||||
using namespace mbgl;
|
||||
using namespace mbgl::style;
|
||||
using namespace mbgl::style::expression;
|
||||
|
||||
static std::vector<std::unique_ptr<RenderLayer>> toRenderLayers(const std::vector<std::unique_ptr<Layer>>& layers) {
|
||||
std::vector<std::unique_ptr<RenderLayer>> result;
|
||||
@@ -39,7 +41,8 @@ TEST(GroupByLayout, UnrelatedFilter) {
|
||||
std::vector<std::unique_ptr<Layer>> layers;
|
||||
layers.push_back(std::make_unique<LineLayer>("a", "source"));
|
||||
layers.push_back(std::make_unique<LineLayer>("b", "source"));
|
||||
layers[0]->as<LineLayer>()->setFilter(EqualsFilter());
|
||||
ParsingContext context;
|
||||
layers[0]->as<LineLayer>()->setFilter(Filter(createCompoundExpression("filter-has-id", context)));
|
||||
auto result = groupByLayout(toRenderLayers(layers));
|
||||
ASSERT_EQ(2u, result.size());
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include <mbgl/test/util.hpp>
|
||||
|
||||
#include <mbgl/style/expression/literal.hpp>
|
||||
#include <mbgl/style/conversion/stringify.hpp>
|
||||
#include <mbgl/style/types.hpp>
|
||||
#include <mbgl/style/layers/symbol_layer_properties.hpp>
|
||||
@@ -75,8 +76,12 @@ TEST(Stringify, Value) {
|
||||
}
|
||||
|
||||
TEST(Stringify, Filter) {
|
||||
ASSERT_EQ(stringify(NullFilter()), "null");
|
||||
ASSERT_EQ(stringify(EqualsFilter { "a", 1.0 }), "[\"==\",\"a\",1.0]");
|
||||
using namespace mbgl::style::expression;
|
||||
|
||||
ASSERT_EQ(stringify(Filter()), "null");
|
||||
|
||||
ParsingContext context;
|
||||
ASSERT_EQ(stringify(Filter(createCompoundExpression("filter-==", createLiteral("a"), createLiteral(1.0), context))), "[\"filter-==\",\"a\",1.0]");
|
||||
}
|
||||
|
||||
TEST(Stringify, CameraFunction) {
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#include <mbgl/test/stub_geometry_tile_feature.hpp>
|
||||
|
||||
#include <mbgl/style/filter.hpp>
|
||||
#include <mbgl/style/filter_evaluator.hpp>
|
||||
#include <mbgl/style/conversion/json.hpp>
|
||||
#include <mbgl/style/conversion/filter.hpp>
|
||||
|
||||
@@ -28,6 +27,24 @@ bool filter(const char * json,
|
||||
return (*filter)(context);
|
||||
}
|
||||
|
||||
void invalidFilter(const char * json) {
|
||||
conversion::Error error;
|
||||
optional<Filter> filter = conversion::convertJSON<Filter>(json, error);
|
||||
EXPECT_FALSE(bool(filter));
|
||||
EXPECT_NE(error.message, "");
|
||||
}
|
||||
|
||||
TEST(Filter, EqualsNull) {
|
||||
auto f = R"(["==", "foo", null])";
|
||||
ASSERT_TRUE(filter(f, {{ "foo", mapbox::geometry::null_value }}));
|
||||
|
||||
ASSERT_FALSE(filter(f, {{ "foo", int64_t(0) }}));
|
||||
ASSERT_FALSE(filter(f, {{ "foo", int64_t(1) }}));
|
||||
ASSERT_FALSE(filter(f, {{ "foo", std::string("0") }}));
|
||||
ASSERT_FALSE(filter(f, {{ "foo", true }}));
|
||||
ASSERT_FALSE(filter(f, {{ "foo", false }}));
|
||||
ASSERT_FALSE(filter(f, {{ }}));
|
||||
}
|
||||
TEST(Filter, EqualsString) {
|
||||
auto f = R"(["==", "foo", "bar"])";
|
||||
ASSERT_TRUE(filter(f, {{ "foo", std::string("bar") }}));
|
||||
@@ -53,6 +70,12 @@ TEST(Filter, EqualsType) {
|
||||
auto f = R"(["==", "$type", "LineString"])";
|
||||
ASSERT_FALSE(filter(f, {{}}, {}, FeatureType::Point, {}));
|
||||
ASSERT_TRUE(filter(f, {{}}, {}, FeatureType::LineString, {}));
|
||||
ASSERT_FALSE(filter(f, {{}}, {}, FeatureType::Point, {}));
|
||||
|
||||
invalidFilter("[\"==\", \"$type\"]");
|
||||
invalidFilter("[\"==\", \"$type\", null]");
|
||||
invalidFilter("[\"==\", \"$type\", \"foo\", 1]");
|
||||
invalidFilter("[\"==\", \"$type\", \"foo\", \"Point\"]");
|
||||
}
|
||||
|
||||
TEST(Filter, InType) {
|
||||
@@ -62,6 +85,14 @@ TEST(Filter, InType) {
|
||||
ASSERT_TRUE(filter(f, {{}}, {}, FeatureType::Polygon));
|
||||
}
|
||||
|
||||
TEST(Filter, InID) {
|
||||
auto f = R"(["in", "$id", "123", "1234", 1234])";
|
||||
ASSERT_FALSE(filter(f));
|
||||
ASSERT_TRUE(filter(f, {{}}, { uint64_t(1234) }));
|
||||
ASSERT_TRUE(filter(f, {{}}, { std::string("1234") }));
|
||||
ASSERT_FALSE(filter(f, {{}}, { std::string("4321") }));
|
||||
}
|
||||
|
||||
TEST(Filter, Any) {
|
||||
ASSERT_FALSE(filter("[\"any\"]"));
|
||||
ASSERT_TRUE(filter("[\"any\", [\"==\", \"foo\", 1]]", {{ std::string("foo"), int64_t(1) }}));
|
||||
@@ -69,6 +100,13 @@ TEST(Filter, Any) {
|
||||
ASSERT_TRUE(filter("[\"any\", [\"==\", \"foo\", 0], [\"==\", \"foo\", 1]]", {{ std::string("foo"), int64_t(1) }}));
|
||||
}
|
||||
|
||||
TEST(Filter, AnyExpression) {
|
||||
ASSERT_FALSE(filter("[\"any\"]"));
|
||||
ASSERT_TRUE(filter("[\"any\", [\"==\", [\"get\", \"foo\"], 1]]", {{ std::string("foo"), int64_t(1) }}));
|
||||
ASSERT_FALSE(filter("[\"any\", [\"==\", [\"get\", \"foo\"], 0]]", {{ std::string("foo"), int64_t(1) }}));
|
||||
ASSERT_TRUE(filter("[\"any\", [\"==\", [\"get\", \"foo\"], 0], [\"==\", [\"get\", \"foo\"], 1]]", {{ std::string("foo"), int64_t(1) }}));
|
||||
}
|
||||
|
||||
TEST(Filter, All) {
|
||||
ASSERT_TRUE(filter("[\"all\"]", {{}}));
|
||||
ASSERT_TRUE(filter("[\"all\", [\"==\", \"foo\", 1]]", {{ std::string("foo"), int64_t(1) }}));
|
||||
@@ -76,6 +114,12 @@ TEST(Filter, All) {
|
||||
ASSERT_FALSE(filter("[\"all\", [\"==\", \"foo\", 0], [\"==\", \"foo\", 1]]", {{ std::string("foo"), int64_t(1) }}));
|
||||
}
|
||||
|
||||
TEST(Filter, AllExpression) {
|
||||
ASSERT_TRUE(filter("[\"all\", [\"==\", [\"get\", \"foo\"], 1]]", {{ std::string("foo"), int64_t(1) }}));
|
||||
ASSERT_FALSE(filter("[\"all\", [\"==\", [\"get\", \"foo\"], 0]]", {{ std::string("foo"), int64_t(1) }}));
|
||||
ASSERT_FALSE(filter("[\"all\", [\"==\", [\"get\", \"foo\"], 0], [\"==\", [\"get\", \"foo\"], 1]]", {{ std::string("foo"), int64_t(1) }}));
|
||||
}
|
||||
|
||||
TEST(Filter, None) {
|
||||
ASSERT_TRUE(filter("[\"none\"]"));
|
||||
ASSERT_FALSE(filter("[\"none\", [\"==\", \"foo\", 1]]", {{ std::string("foo"), int64_t(1) }}));
|
||||
@@ -88,6 +132,7 @@ TEST(Filter, Has) {
|
||||
ASSERT_TRUE(filter("[\"has\", \"foo\"]", {{ std::string("foo"), int64_t(0) }}));
|
||||
ASSERT_TRUE(filter("[\"has\", \"foo\"]", {{ std::string("foo"), false }}));
|
||||
ASSERT_FALSE(filter("[\"has\", \"foo\"]"));
|
||||
ASSERT_FALSE(filter("[\"has\", \"$id\"]"));
|
||||
}
|
||||
|
||||
TEST(Filter, NotHas) {
|
||||
@@ -101,7 +146,11 @@ TEST(Filter, ID) {
|
||||
FeatureIdentifier id1 { uint64_t{ 1234 } };
|
||||
ASSERT_TRUE(filter("[\"==\", \"$id\", 1234]", {{}}, id1));
|
||||
ASSERT_FALSE(filter("[\"==\", \"$id\", \"1234\"]", {{}}, id1));
|
||||
|
||||
|
||||
FeatureIdentifier id2 { std::string{ "1" } };
|
||||
ASSERT_FALSE(filter("[\"<\", \"$id\", \"0\"]", {{}}, id2));
|
||||
ASSERT_TRUE(filter("[\"<\", \"$id\", \"1234\"]", {{}}, id2));
|
||||
|
||||
ASSERT_FALSE(filter("[\"==\", \"$id\", 1234]", {{ "id", uint64_t(1234) }}));
|
||||
}
|
||||
|
||||
@@ -115,6 +164,18 @@ TEST(Filter, PropertyExpression) {
|
||||
ASSERT_FALSE(filter("[\"==\", [\"get\", \"two\"], 4]", {{"two", int64_t(2)}}));
|
||||
}
|
||||
|
||||
TEST(Filter, LegacyProperty) {
|
||||
ASSERT_TRUE(filter("[\"<=\", \"two\", 2]", {{"two", int64_t(2)}}));
|
||||
ASSERT_FALSE(filter("[\"==\", \"two\", 4]", {{"two", int64_t(2)}}));
|
||||
|
||||
ASSERT_FALSE(filter("[\"<=\", \"two\", \"2\"]", {{"two", int64_t(2)}}));
|
||||
ASSERT_FALSE(filter("[\"==\", \"bool\", false]", {{"two", true}}));
|
||||
|
||||
ASSERT_TRUE(filter("[\"<=\", \"two\", \"2\"]", {{"two", std::string("2")}}));
|
||||
ASSERT_FALSE(filter("[\"<\", \"two\", \"1\"]", {{"two", std::string("2")}}));
|
||||
ASSERT_FALSE(filter("[\"==\", \"two\", 4]", {{"two", std::string("2")}}));
|
||||
}
|
||||
|
||||
TEST(Filter, ZoomExpressionNested) {
|
||||
ASSERT_TRUE(filter(R"(["==", ["get", "two"], ["zoom"]])", {{"two", int64_t(2)}}, {}, FeatureType::Point, {}, 2.0f));
|
||||
ASSERT_FALSE(filter(R"(["==", ["get", "two"], ["+", ["zoom"], 1]])", {{"two", int64_t(2)}}, {}, FeatureType::Point, {}, 2.0f));
|
||||
|
||||
@@ -211,7 +211,7 @@ TEST(Layer, Observer) {
|
||||
EXPECT_EQ(layer.get(), &layer_);
|
||||
filterChanged = true;
|
||||
};
|
||||
layer->setFilter(NullFilter());
|
||||
layer->setFilter(Filter());
|
||||
EXPECT_TRUE(filterChanged);
|
||||
|
||||
// Notifies observer on visibility change.
|
||||
|
||||
Reference in New Issue
Block a user