From a4e2c1af1fd83b22ef4ee57ab19a15616224f8b8 Mon Sep 17 00:00:00 2001 From: Lucas Wojciechowski Date: Thu, 10 May 2018 12:37:14 -0700 Subject: [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 --- benchmark/parse/filter.benchmark.cpp | 1 - cmake/core-files.cmake | 2 - .../mbgl/style/expression/compound_expression.hpp | 15 +- include/mbgl/style/expression/literal.hpp | 3 + include/mbgl/style/filter.hpp | 276 ++--------------- include/mbgl/style/filter_evaluator.hpp | 55 ---- platform/android/src/style/layers/layer.cpp | 12 +- platform/darwin/src/MGLCircleStyleLayer.mm | 2 +- platform/darwin/src/MGLFillExtrusionStyleLayer.mm | 2 +- platform/darwin/src/MGLFillStyleLayer.mm | 2 +- platform/darwin/src/MGLHeatmapStyleLayer.mm | 2 +- platform/darwin/src/MGLLineStyleLayer.mm | 2 +- platform/darwin/src/MGLStyleLayer.mm.ejs | 2 +- platform/darwin/src/MGLSymbolStyleLayer.mm | 2 +- platform/darwin/src/NSPredicate+MGLAdditions.mm | 207 +------------ platform/darwin/test/MGLPredicateTests.mm | 214 ------------- platform/glfw/glfw_view.cpp | 9 +- src/mbgl/geometry/feature_index.cpp | 1 - src/mbgl/layout/symbol_layout.cpp | 1 - src/mbgl/style/conversion/filter.cpp | 343 ++++++++------------- src/mbgl/style/conversion/stringify.hpp | 159 +--------- src/mbgl/style/expression/compound_expression.cpp | 328 +++++++++++++++++--- src/mbgl/style/expression/literal.cpp | 8 + src/mbgl/style/filter.cpp | 12 +- src/mbgl/style/filter_evaluator.cpp | 225 -------------- src/mbgl/tile/custom_geometry_tile.cpp | 1 - src/mbgl/tile/geojson_tile.cpp | 1 - src/mbgl/tile/geometry_tile.cpp | 1 - src/mbgl/tile/geometry_tile_worker.cpp | 1 - test/api/query.test.cpp | 15 +- test/renderer/group_by_layout.test.cpp | 5 +- test/style/conversion/stringify.test.cpp | 9 +- test/style/filter.test.cpp | 65 +++- test/style/style_layer.test.cpp | 2 +- 34 files changed, 578 insertions(+), 1407 deletions(-) delete mode 100644 include/mbgl/style/filter_evaluator.hpp delete mode 100644 src/mbgl/style/filter_evaluator.cpp diff --git a/benchmark/parse/filter.benchmark.cpp b/benchmark/parse/filter.benchmark.cpp index e4cf635256..4e39237138 100644 --- a/benchmark/parse/filter.benchmark.cpp +++ b/benchmark/parse/filter.benchmark.cpp @@ -1,7 +1,6 @@ #include #include -#include #include #include #include diff --git a/cmake/core-files.cmake b/cmake/core-files.cmake index 0853097630..208c598872 100644 --- a/cmake/core-files.cmake +++ b/cmake/core-files.cmake @@ -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 diff --git a/include/mbgl/style/expression/compound_expression.hpp b/include/mbgl/style/expression/compound_expression.hpp index 6baaae862f..431afb4d13 100644 --- a/include/mbgl/style/expression/compound_expression.hpp +++ b/include/mbgl/style/expression/compound_expression.hpp @@ -36,7 +36,7 @@ template struct Varargs : std::vector { using std::vector::vector; }; namespace detail { -// Base class for the Signature structs that are used to determine the +// Base class for the Signature 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> args, ParsingContext& ctx); - + ParseResult createCompoundExpression(const std::string& name, std::vector> 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 arg1, + ParsingContext& ctx); + +ParseResult createCompoundExpression(const std::string& name, + std::unique_ptr arg1, + std::unique_ptr arg2, + ParsingContext& ctx); } // namespace expression } // namespace style diff --git a/include/mbgl/style/expression/literal.hpp b/include/mbgl/style/expression/literal.hpp index a00c468efc..d3b3a20cce 100644 --- a/include/mbgl/style/expression/literal.hpp +++ b/include/mbgl/style/expression/literal.hpp @@ -51,6 +51,9 @@ private: Value value; }; +std::unique_ptr createLiteral(const char* value); +std::unique_ptr createLiteral(Value value); + } // namespace expression } // namespace style } // namespace mbgl diff --git a/include/mbgl/style/filter.hpp b/include/mbgl/style/filter.hpp index ccf8dce188..ce4015bb69 100644 --- a/include/mbgl/style/filter.hpp +++ b/include/mbgl/style/filter.hpp @@ -11,271 +11,31 @@ 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 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 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 filters; - - friend bool operator==(const AnyFilter& lhs, const AnyFilter& rhs) { - return lhs.filters == rhs.filters; - } -}; - -class AllFilter { -public: - std::vector filters; - - friend bool operator==(const AllFilter& lhs, const AllFilter& rhs) { - return lhs.filters == rhs.filters; - } -}; - -class NoneFilter { -public: - std::vector 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 values; - - friend bool operator==(const TypeInFilter& lhs, const TypeInFilter& rhs) { - return lhs.values == rhs.values; - } -}; - -class TypeNotInFilter { -public: - std::vector 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 values; - - friend bool operator==(const IdentifierInFilter& lhs, const IdentifierInFilter& rhs) { - return lhs.values == rhs.values; - } -}; - -class IdentifierNotInFilter { -public: - std::vector values; - - friend bool operator==(const IdentifierNotInFilter& lhs, const IdentifierNotInFilter& rhs) { - return lhs.values == rhs.values; - } -}; - -class HasIdentifierFilter { + +class Filter { public: - friend bool operator==(const HasIdentifierFilter&, const HasIdentifierFilter&) { - return true; + optional> expression; + + Filter() : expression() {} + + Filter(expression::ParseResult _expression) : expression(std::move(*_expression)) { + assert(!expression || *expression != nullptr); } -}; + + bool operator()(const expression::EvaluationContext& context) const; -class NotHasIdentifierFilter { -public: - friend bool operator==(const NotHasIdentifierFilter&, const NotHasIdentifierFilter&) { - return true; + 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); + } } -}; -class ExpressionFilter { -public: - std::shared_ptr expression; - - friend bool operator==(const ExpressionFilter& lhs, const ExpressionFilter& rhs) { - return *(lhs.expression) == *(rhs.expression); + friend bool operator!=(const Filter& lhs, const Filter& rhs) { + return !(lhs == rhs); } }; - -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; -}; - } // namespace style } // namespace mbgl diff --git a/include/mbgl/style/filter_evaluator.hpp b/include/mbgl/style/filter_evaluator.hpp deleted file mode 100644 index a4a4098b3f..0000000000 --- a/include/mbgl/style/filter_evaluator.hpp +++ /dev/null @@ -1,55 +0,0 @@ -#pragma once - -#include -#include - -#include - -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 diff --git a/platform/android/src/style/layers/layer.cpp b/platform/android/src/style/layers/layer.cpp index 6fe6e3cb29..c7a6bcd3a3 100644 --- a/platform/android/src/style/layers/layer.cpp +++ b/platform/android/src/style/layers/layer.cpp @@ -157,14 +157,12 @@ namespace android { using namespace mbgl::style::conversion; Filter filter = layer.accept(GetFilterEvaluator()); - - jni::Object converted; - if (filter.is()) { - ExpressionFilter filterExpression = filter.get(); - 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(); } - return converted; } struct SetSourceLayerEvaluator { diff --git a/platform/darwin/src/MGLCircleStyleLayer.mm b/platform/darwin/src/MGLCircleStyleLayer.mm index b85b3d9d4f..b03ab8a7a6 100644 --- a/platform/darwin/src/MGLCircleStyleLayer.mm +++ b/platform/darwin/src/MGLCircleStyleLayer.mm @@ -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 diff --git a/platform/darwin/src/MGLFillExtrusionStyleLayer.mm b/platform/darwin/src/MGLFillExtrusionStyleLayer.mm index ea29dff62d..4f3bfee18e 100644 --- a/platform/darwin/src/MGLFillExtrusionStyleLayer.mm +++ b/platform/darwin/src/MGLFillExtrusionStyleLayer.mm @@ -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 diff --git a/platform/darwin/src/MGLFillStyleLayer.mm b/platform/darwin/src/MGLFillStyleLayer.mm index 5be0decd4a..12e3643ce6 100644 --- a/platform/darwin/src/MGLFillStyleLayer.mm +++ b/platform/darwin/src/MGLFillStyleLayer.mm @@ -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 diff --git a/platform/darwin/src/MGLHeatmapStyleLayer.mm b/platform/darwin/src/MGLHeatmapStyleLayer.mm index b4bf4c9566..925b3ac750 100644 --- a/platform/darwin/src/MGLHeatmapStyleLayer.mm +++ b/platform/darwin/src/MGLHeatmapStyleLayer.mm @@ -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 diff --git a/platform/darwin/src/MGLLineStyleLayer.mm b/platform/darwin/src/MGLLineStyleLayer.mm index b359c424a2..84412073cd 100644 --- a/platform/darwin/src/MGLLineStyleLayer.mm +++ b/platform/darwin/src/MGLLineStyleLayer.mm @@ -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 diff --git a/platform/darwin/src/MGLStyleLayer.mm.ejs b/platform/darwin/src/MGLStyleLayer.mm.ejs index ead246fb03..e7589c1f62 100644 --- a/platform/darwin/src/MGLStyleLayer.mm.ejs +++ b/platform/darwin/src/MGLStyleLayer.mm.ejs @@ -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 diff --git a/platform/darwin/src/MGLSymbolStyleLayer.mm b/platform/darwin/src/MGLSymbolStyleLayer.mm index 4236e04238..7ec7816c3b 100644 --- a/platform/darwin/src/MGLSymbolStyleLayer.mm +++ b/platform/darwin/src/MGLSymbolStyleLayer.mm @@ -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 diff --git a/platform/darwin/src/NSPredicate+MGLAdditions.mm b/platform/darwin/src/NSPredicate+MGLAdditions.mm index 07154cb246..4b9a4177cb 100644 --- a/platform/darwin/src/NSPredicate+MGLAdditions.mm +++ b/platform/darwin/src/NSPredicate+MGLAdditions.mm @@ -5,205 +5,6 @@ #include -class FilterEvaluator { -public: - - NSArray *getPredicates(std::vector filters) { - NSMutableArray *predicates = [NSMutableArray arrayWithCapacity:filters.size()]; - for (auto filter : filters) { - [predicates addObject:mbgl::style::Filter::visit(filter, FilterEvaluator())]; - } - return predicates; - } - - template - NSExpression *getValues(std::vector 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 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()) { - lowerKey = leftFilter.get().key; - lowerBound = leftFilter.get().value; - } else if (rightFilter.is()) { - lowerKey = rightFilter.get().key; - lowerBound = rightFilter.get().value; - } - - if (leftFilter.is()) { - upperKey = leftFilter.get().key; - upperBound = leftFilter.get().value; - } else if (rightFilter.is()) { - upperKey = rightFilter.get().key; - upperBound = rightFilter.get().value; - } - - if (!lowerBound.is() && !upperBound.is() - && 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 diff --git a/platform/darwin/test/MGLPredicateTests.mm b/platform/darwin/test/MGLPredicateTests.mm index b725c63140..4e7b3e7e4b 100644 --- a/platform/darwin/test/MGLPredicateTests.mm +++ b/platform/darwin/test/MGLPredicateTests.mm @@ -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); diff --git a/platform/glfw/glfw_view.cpp b/platform/glfw/glfw_view.cpp index 3988419265..362269b8e4 100644 --- a/platform/glfw/glfw_view.cpp +++ b/platform/glfw/glfw_view.cpp @@ -7,6 +7,8 @@ #include #include #include +#include +#include #include #include #include @@ -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("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 { "height", mbgl::style::ExponentialStops { diff --git a/src/mbgl/geometry/feature_index.cpp b/src/mbgl/geometry/feature_index.cpp index c67786274a..57719de038 100644 --- a/src/mbgl/geometry/feature_index.cpp +++ b/src/mbgl/geometry/feature_index.cpp @@ -7,7 +7,6 @@ #include #include #include -#include #include #include diff --git a/src/mbgl/layout/symbol_layout.cpp b/src/mbgl/layout/symbol_layout.cpp index 82a9255824..cd9bcbd585 100644 --- a/src/mbgl/layout/symbol_layout.cpp +++ b/src/mbgl/layout/symbol_layout.cpp @@ -2,7 +2,6 @@ #include #include #include -#include #include #include #include diff --git a/src/mbgl/style/conversion/filter.cpp b/src/mbgl/style/conversion/filter.cpp index 3c941945fd..386d85e921 100644 --- a/src/mbgl/style/conversion/filter.cpp +++ b/src/mbgl/style/conversion/filter.cpp @@ -1,17 +1,39 @@ #include +#include #include #include #include -#include +#include +#include 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 convertLegacyFilter(const Convertible& values, Error& error); + +optional Converter::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 = convertLegacyFilter(value, error); + if (!expression) return {}; + return Filter(optional>(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 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 normalizeValue(const optional& value, Error& error) { - if (!value) { - error = { "filter expression value must be a boolean, number, or string" }; - return {}; - } else { - return *value; - } -} +std::unique_ptr createExpression(std::string op, std::vector> args, Error& error) { + if (op == "any") { + return std::make_unique(std::move(args)); + + } else if (op == "all") { + return std::make_unique(std::move(args)); -static optional toFeatureType(const Convertible& value, Error& error) { - optional 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 {}; + ParsingContext parsingContext(type::Boolean); + ParseResult parseResult = createCompoundExpression(op, std::move(args), parsingContext); + if (!parseResult) { + error = { parsingContext.getCombinedErrors() }; + return {}; + } else { + return std::move(*parseResult); + } } } -static optional toFeatureIdentifier(const Convertible& value, Error& error) { - optional 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 { return { t }; }, - [] ( int64_t t) -> optional { return { t }; }, - [] ( double t) -> optional { return { t }; }, - [] (const std::string& t) -> optional { return { t }; }, - [&] (const auto&) -> optional { - error = { "filter expression value must be a boolean, number, or string" }; - return {}; - }); - } +std::unique_ptr createExpression(std::string op, std::unique_ptr expression, Error& error) { + std::vector> args; + args.push_back(std::move(expression)); + return createExpression(op, std::move(args), error); } -template -optional convertUnaryFilter(const Convertible& value, Error& error) { - if (arrayLength(value) < 2) { - error = { "filter expression must have 2 elements" }; - return {}; - } - - optional key = toString(arrayMember(value, 1)); - if (!key) { - error = { "filter expression key must be a string" }; - return {}; - } - - if (*key == "$id") { - return { IdentifierFilterType {} }; +std::unique_ptr convertLiteral(const Convertible& convertible, Error& error) { + ParsingContext parsingContext; + ParseResult parseResult = Literal::parse(convertible, parsingContext); + if (parseResult) { + return std::move(*parseResult); } else { - return { FilterType { *key } }; + error = { parsingContext.getCombinedErrors() }; + return {}; } } -template -optional convertEqualityFilter(const Convertible& value, Error& error) { - if (arrayLength(value) < 3) { - error = { "filter expression must have 3 elements" }; - return {}; +std::vector> convertLiteralArray(const Convertible &input, Error& error, std::size_t startIndex = 0) { + std::vector> output; + for (std::size_t i = startIndex; i < arrayLength(input); i++) { + output.push_back(convertLiteral(arrayMember(input, i), error)); } + return output; +} - optional key = toString(arrayMember(value, 1)); - if (!key) { - error = { "filter expression key must be a string" }; +std::unique_ptr convertLegacyComparisonFilter(const Convertible& values, Error& error, optional opOverride = {}) { + optional op = opOverride ? opOverride : toString(arrayMember(values, 0)); + optional property = toString(arrayMember(values, 1)); + + if (!property) { + error = { "filter property must be a string" }; return {}; - } - - if (*key == "$type") { - optional filterValue = toFeatureType(arrayMember(value, 2), error); - if (!filterValue) { - return {}; - } - - return { TypeFilterType { *filterValue } }; - - } else if (*key == "$id") { - optional filterValue = toFeatureIdentifier(arrayMember(value, 2), error); - if (!filterValue) { - return {}; - } - - return { IdentifierFilterType { *filterValue } }; - + } 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 { - optional filterValue = normalizeValue(toValue(arrayMember(value, 2)), error); - if (!filterValue) { - return {}; - } - - return { FilterType { *key, *filterValue } }; + return createExpression("filter-" + *op, convertLiteralArray(values, error, 1), error); } } - -template -optional convertBinaryFilter(const Convertible& value, Error& error) { - if (arrayLength(value) < 3) { - error = { "filter expression must have 3 elements" }; - return {}; - } - - optional key = toString(arrayMember(value, 1)); - if (!key) { - error = { "filter expression key must be a string" }; - return {}; - } - - optional filterValue = normalizeValue(toValue(arrayMember(value, 2)), error); - if (!filterValue) { + +std::unique_ptr convertLegacyHasFilter(const Convertible& values, Error& error) { + optional property = toString(arrayMember(values, 1)); + + if (!property) { + error = { "filter property must be a string" }; return {}; + } else if (*property == "$type") { + return std::make_unique(true); + } else if (*property == "$id") { + return createExpression("filter-has-id", std::vector>(), error); + } else { + return createExpression("filter-has", std::make_unique(*property), error); } - - return { FilterType { *key, *filterValue } }; } -template -optional convertSetFilter(const Convertible& value, Error& error) { - if (arrayLength(value) < 2) { - error = { "filter expression must at least 2 elements" }; - return {}; - } - - optional key = toString(arrayMember(value, 1)); - if (!key) { - error = { "filter expression key must be a string" }; - return {}; - } - - if (*key == "$type") { - std::vector values; - for (std::size_t i = 2; i < arrayLength(value); ++i) { - optional filterValue = toFeatureType(arrayMember(value, i), error); - if (!filterValue) { - return {}; - } - values.push_back(*filterValue); - } - - return { TypeFilterType { std::move(values) } }; - - } else if (*key == "$id") { - std::vector values; - for (std::size_t i = 2; i < arrayLength(value); ++i) { - optional filterValue = toFeatureIdentifier(arrayMember(value, i), error); - if (!filterValue) { - return {}; - } - values.push_back(*filterValue); - } - - return { IdentifierFilterType { std::move(values) } }; - +std::unique_ptr convertLegacyInFilter(const Convertible& values, Error& error) { + optional property = toString(arrayMember(values, 1)); + + if (!property) { + error = { "filter property must be a string" }; + return {}; + } else if (arrayLength(values) == 0) { + return std::make_unique(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 { - std::vector values; - for (std::size_t i = 2; i < arrayLength(value); ++i) { - optional filterValue = normalizeValue(toValue(arrayMember(value, i)), error); - if (!filterValue) { - return {}; - } - values.push_back(*filterValue); - } - - return { FilterType { *key, std::move(values) } }; + return createExpression("filter-in", convertLiteralArray(values, error, 1), error); } } -template -optional convertCompoundFilter(const Convertible& value, Error& error) { - std::vector filters; - for (std::size_t i = 1; i < arrayLength(value); ++i) { - optional element = convert(arrayMember(value, i), error); - if (!element) { - return {}; - } - filters.push_back(*element); +std::vector> convertLegacyFilterArray(const Convertible &input, Error& error, std::size_t startIndex = 0) { + std::vector> output; + for (std::size_t i = startIndex; i < arrayLength(input); i++) { + output.push_back(convertLegacyFilter(arrayMember(input, i), error)); } - - return { FilterType { std::move(filters) } }; + return output; } -optional convertExpressionFilter(const Convertible& value, Error& error) { - expression::ParsingContext ctx(expression::type::Boolean); - expression::ParseResult expression = ctx.parseExpression(value); - if (!expression) { - error = { ctx.getCombinedErrors() }; - return {}; - } - - return { ExpressionFilter { std::move(*expression) } }; -} - -optional Converter::operator()(const Convertible& value, Error& error) const { - if (isExpressionFilter(value)) { - return convertExpressionFilter(value, error); +std::unique_ptr convertLegacyFilter(const Convertible& values, Error& error) { + if (isUndefined(values)) { + return std::make_unique(true); } - if (!isArray(value)) { - error = { "filter expression must be an array" }; - return {}; - } - - if (arrayLength(value) < 1) { - error = { "filter expression must have at least 1 element" }; - return {}; - } + optional op = toString(arrayMember(values, 0)); - optional op = toString(arrayMember(value, 0)); if (!op) { error = { "filter operator must be a string" }; return {}; + } else if (arrayLength(values) <= 1) { + return std::make_unique(*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(true) + ); } - - if (*op == "==") { - return convertEqualityFilter(value, error); - } else if (*op == "!=") { - return convertEqualityFilter(value, error); - } else if (*op == ">") { - return convertBinaryFilter(value, error); - } else if (*op == ">=") { - return convertBinaryFilter(value, error); - } else if (*op == "<") { - return convertBinaryFilter(value, error); - } else if (*op == "<=") { - return convertBinaryFilter(value, error); - } else if (*op == "in") { - return convertSetFilter(value, error); - } else if (*op == "!in") { - return convertSetFilter(value, error); - } else if (*op == "all") { - return convertCompoundFilter(value, error); - } else if (*op == "any") { - return convertCompoundFilter(value, error); - } else if (*op == "none") { - return convertCompoundFilter(value, error); - } else if (*op == "has") { - return convertUnaryFilter(value, error); - } else if (*op == "!has") { - return convertUnaryFilter(value, error); - } - - error = { R"(filter operator must be one of "==", "!=", ">", ">=", "<", "<=", "in", "!in", "all", "any", "none", "has", or "!has")" }; - return {}; } } // namespace conversion diff --git a/src/mbgl/style/conversion/stringify.hpp b/src/mbgl/style/conversion/stringify.hpp index 7b7727d7c4..74171763a0 100644 --- a/src/mbgl/style/conversion/stringify.hpp +++ b/src/mbgl/style/conversion/stringify.hpp @@ -126,162 +126,9 @@ void stringify(Writer& writer, const FeatureIdentifier& id) { } template -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 - void stringifyBinaryFilter(const F& f, const char * op) { - stringifyBinaryFilter(f, op, f.key); - } - - template - 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 - void stringifySetFilter(const F& f, const char * op) { - stringifySetFilter(f, op, f.key); - } - - template - 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 - 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 -void stringify(Writer& writer, const Filter& f) { - Filter::visit(f, StringifyFilter { writer }); +void stringify(Writer& writer, const Filter& filter) { + if (!filter.expression) writer.Null(); + else stringify(writer, (*filter.expression)->serialize()); } template diff --git a/src/mbgl/style/expression/compound_expression.cpp b/src/mbgl/style/expression/compound_expression.cpp index c36ffa33e3..3bd8a836df 100644 --- a/src/mbgl/style/expression/compound_expression.cpp +++ b/src/mbgl/style/expression/compound_expression.cpp @@ -18,7 +18,7 @@ namespace detail { The Signature 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: Wraps a simple evaluate function (const T0&, const T1&, ...) -> Result @@ -29,9 +29,9 @@ namespace detail { Signature: 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. Needed + (const EvaluationParams&, const T0&, const T1&, ...) -> Result. 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 struct Signature : SignatureBase { using Args = std::array, sizeof...(Params)>; - + Signature(R (*evaluate_)(Params...), std::string name_) : SignatureBase( valueTypeToExpressionType>(), @@ -54,7 +54,7 @@ struct Signature : SignatureBase { EvaluationResult apply(const EvaluationContext& evaluationParameters, const Args& args) const { return applyImpl(evaluationParameters, args, std::index_sequence_for{}); } - + std::unique_ptr makeExpression(std::vector> 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 struct Signature&)> : SignatureBase { using Args = std::vector>; - + Signature(R (*evaluate_)(const Varargs&), std::string name_) : SignatureBase( valueTypeToExpressionType>(), @@ -88,11 +88,11 @@ struct Signature&)> : SignatureBase { ), evaluate(evaluate_) {} - + std::unique_ptr makeExpression(std::vector> args) const override { return std::make_unique>(name, *this, std::move(args)); }; - + EvaluationResult apply(const EvaluationContext& evaluationParameters, const Args& args) const { Varargs evaluated; evaluated.reserve(args.size()); @@ -114,7 +114,7 @@ struct Signature&)> : SignatureBase { template struct Signature : SignatureBase { using Args = std::array, sizeof...(Params)>; - + Signature(R (*evaluate_)(const EvaluationContext&, Params...), std::string name_) : SignatureBase( valueTypeToExpressionType>(), @@ -123,17 +123,17 @@ struct Signature : SignatureBase { ), evaluate(evaluate_) {} - + std::unique_ptr makeExpression(std::vector> args) const override { typename Signature::Args argsArray; std::copy_n(std::make_move_iterator(args.begin()), sizeof...(Params), argsArray.begin()); return std::make_unique>(name, *this, std::move(argsArray)); } - + EvaluationResult apply(const EvaluationContext& evaluationParameters, const Args& args) const { return applyImpl(evaluationParameters, args, std::index_sequence_for{}); } - + private: template EvaluationResult applyImpl(const EvaluationContext& evaluationParameters, const Args& args, std::index_sequence) const { @@ -149,6 +149,41 @@ private: R (*evaluate)(const EvaluationContext&, Params...); }; + +// Evaluate function needing EvaluationContext and Varargs +// (const EvaluationContext&, const Varargs&) -> Result +template +struct Signature&)> : SignatureBase { + using Args = std::vector>; + + Signature(R (*evaluate_)(const EvaluationContext&, const Varargs&), std::string name_) : + SignatureBase( + valueTypeToExpressionType>(), + VarargsType { valueTypeToExpressionType() }, + std::move(name_) + ), + evaluate(evaluate_) + {} + + std::unique_ptr makeExpression(std::vector> args) const override { + return std::make_unique>(name, *this, std::move(args)); + }; + + EvaluationResult apply(const EvaluationContext& evaluationParameters, const Args& args) const { + Varargs 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>(*evaluatedArg)); + } + const R value = evaluate(evaluationParameters, evaluated); + if (!value) return value.error(); + return *value; + } + + R (*evaluate)(const EvaluationContext&, const Varargs&); +}; // Machinery to pull out function types from class methods, lambdas, etc. template @@ -180,18 +215,92 @@ static std::unique_ptr makeSignature(Fn evaluateFunction, return std::make_unique>(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 featurePropertyAsExpressionValue(EvaluationContext params, const std::string& key) { + assert(params.feature); + auto property = params.feature->getValue(key); + return property ? toExpressionValue(*property) : optional(); +}; + +optional featureTypeAsString(FeatureType type) { + switch(type) { + case FeatureType::Point: + return optional("Point"); + case FeatureType::LineString: + return optional("LineString"); + case FeatureType::Polygon: + return optional("Polygon"); + case FeatureType::Unknown: + return optional("Unknown"); + default: + return {}; + } +}; + +optional 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(static_cast(value)); }, + [](int64_t value) { return optional(static_cast(value)); }, + [](auto) { return optional(); } + ); +}; + +optional 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(); } + ); +}; + +optional featureIdAsDouble(EvaluationContext params) { + assert(params.feature); + auto id = params.feature->getID(); + if (!id) return optional(); + return id->match( + [](double value) { return value; }, + [](uint64_t value) { return optional(static_cast(value)); }, + [](int64_t value) { return optional(static_cast(value)); }, + [](auto) { return optional(); } + ); +}; + +optional featureIdAsString(EvaluationContext params) { + assert(params.feature); + auto id = params.feature->getID(); + if (!id) return optional(); + return id->match( + [](std::string value) { return value; }, + [](auto) { return optional(); } + ); +}; + std::unordered_map initializeDefinitions() { std::unordered_map definitions; auto define = [&](std::string name, auto fn) { definitions[name].push_back(makeSignature(fn, name)); }; - + define("e", []() -> Result { return 2.718281828459045; }); define("pi", []() -> Result { return 3.141592653589793; }); define("ln2", []() -> Result { return 0.6931471805599453; }); define("typeof", [](const Value& v) -> Result { return toString(typeOf(v)); }); - + define("to-string", [](const Value& value) -> Result { return value.match( [](const Color& c) -> Result { return c.stringify(); }, // avoid quoting @@ -199,10 +308,10 @@ std::unordered_map initiali [](const auto& v) -> Result { return stringify(v); } ); }); - + define("to-boolean", [](const Value& v) -> Result { return v.match( - [&] (double f) { return (bool)f; }, + [&] (double f) { return static_cast(f); }, [&] (const std::string& s) { return s.length() > 0; }, [&] (bool b) { return b; }, [&] (const NullValue&) { return false; }, @@ -212,10 +321,10 @@ std::unordered_map initiali define("to-rgba", [](const Color& color) -> Result> { 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 { if (!params.zoom) { return EvaluationError { @@ -224,7 +333,7 @@ std::unordered_map initiali } return *(params.zoom); }); - + define("heatmap-density", [](const EvaluationContext& params) -> Result { if (!params.heatmapDensity) { return EvaluationError { @@ -240,7 +349,7 @@ std::unordered_map 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& object) -> Result { @@ -266,7 +375,7 @@ std::unordered_map initiali } return object.at(key); }); - + define("properties", [](const EvaluationContext& params) -> Result> { if (!params.feature) { return EvaluationError { @@ -280,14 +389,14 @@ std::unordered_map initiali } return result; }); - + define("geometry-type", [](const EvaluationContext& params) -> Result { 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 initiali return "Unknown"; } }); - + define("id", [](const EvaluationContext& params) -> Result { 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 initiali } ); }); - + define("+", [](const Varargs& args) -> Result { double sum = 0.0f; for (auto arg : args) { @@ -347,7 +456,7 @@ std::unordered_map initiali define("asin", [](double x) -> Result { return asin(x); }); define("acos", [](double x) -> Result { return acos(x); }); define("atan", [](double x) -> Result { return atan(x); }); - + define("min", [](const Varargs& args) -> Result { double result = std::numeric_limits::infinity(); for (double arg : args) { @@ -362,7 +471,7 @@ std::unordered_map initiali } return result; }); - + define("round", [](double x) -> Result { return std::round(x); }); define("floor", [](double x) -> Result { return std::floor(x); }); define("ceil", [](double x) -> Result { return std::ceil(x); }); @@ -376,9 +485,9 @@ std::unordered_map initiali define("<", [](const std::string& lhs, const std::string& rhs) -> Result { return lhs < rhs; }); define("<=", [](double lhs, double rhs) -> Result { return lhs <= rhs; }); define("<=", [](const std::string& lhs, const std::string& rhs) -> Result { return lhs <= rhs; }); - + define("!", [](bool e) -> Result { return !e; }); - + define("upcase", [](const std::string& input) -> Result { return platform::uppercase(input); }); @@ -395,7 +504,130 @@ std::unordered_map initiali define("error", [](const std::string& input) -> Result { return EvaluationError { input }; }); + + // Legacy Filters + define("filter-==", [](const EvaluationContext& params, const std::string& key, const Value &lhs) -> Result { + const auto rhs = featurePropertyAsExpressionValue(params, key); + return rhs ? lhs == *rhs : false; + }); + + define("filter-id-==", [](const EvaluationContext& params, const Value &lhs) -> Result { + return lhs == featureIdAsExpressionValue(params); + }); + + define("filter-type-==", [](const EvaluationContext& params, const std::string &lhs) -> Result { + if (!params.feature) return false; + return featureTypeAsString(params.feature->getType()) == lhs; + }); + + define("filter-<", [](const EvaluationContext& params, const std::string& key, double lhs) -> Result { + auto rhs = featurePropertyAsDouble(params, key); + return rhs ? rhs < lhs : false; + }); + + define("filter-<", [](const EvaluationContext& params, const std::string& key, std::string lhs) -> Result { + auto rhs = featurePropertyAsString(params, key); + return rhs ? rhs < lhs : false; + }); + + define("filter-id-<", [](const EvaluationContext& params, double lhs) -> Result { + auto rhs = featureIdAsDouble(params); + return rhs ? rhs < lhs : false; + }); + + define("filter-id-<", [](const EvaluationContext& params, std::string lhs) -> Result { + auto rhs = featureIdAsString(params); + return rhs ? rhs < lhs : false; + }); + + define("filter->", [](const EvaluationContext& params, const std::string& key, double lhs) -> Result { + auto rhs = featurePropertyAsDouble(params, key); + return rhs ? rhs > lhs : false; + }); + + define("filter->", [](const EvaluationContext& params, const std::string& key, std::string lhs) -> Result { + auto rhs = featurePropertyAsString(params, key); + return rhs ? rhs > lhs : false; + }); + + define("filter-id->", [](const EvaluationContext& params, double lhs) -> Result { + auto rhs = featureIdAsDouble(params); + return rhs ? rhs > lhs : false; + }); + + define("filter-id->", [](const EvaluationContext& params, std::string lhs) -> Result { + auto rhs = featureIdAsString(params); + return rhs ? rhs > lhs : false; + }); + + define("filter-<=", [](const EvaluationContext& params, const std::string& key, double lhs) -> Result { + auto rhs = featurePropertyAsDouble(params, key); + return rhs ? rhs <= lhs : false; + }); + define("filter-<=", [](const EvaluationContext& params, const std::string& key, std::string lhs) -> Result { + auto rhs = featurePropertyAsString(params, key); + return rhs ? rhs <= lhs : false; + }); + + define("filter-id-<=", [](const EvaluationContext& params, double lhs) -> Result { + auto rhs = featureIdAsDouble(params); + return rhs ? rhs <= lhs : false; + }); + + define("filter-id-<=", [](const EvaluationContext& params, std::string lhs) -> Result { + auto rhs = featureIdAsString(params); + return rhs ? rhs <= lhs : false; + }); + + define("filter->=", [](const EvaluationContext& params, const std::string& key, double lhs) -> Result { + auto rhs = featurePropertyAsDouble(params, key); + return rhs ? rhs >= lhs : false; + }); + + define("filter->=", [](const EvaluationContext& params, const std::string& key, std::string lhs) -> Result { + auto rhs = featurePropertyAsString(params, key); + return rhs ? rhs >= lhs : false; + }); + + define("filter-id->=", [](const EvaluationContext& params, double lhs) -> Result { + auto rhs = featureIdAsDouble(params); + return rhs ? rhs >= lhs : false; + }); + + define("filter-id->=", [](const EvaluationContext& params, std::string lhs) -> Result { + auto rhs = featureIdAsString(params); + return rhs ? rhs >= lhs : false; + }); + + define("filter-has", [](const EvaluationContext& params, const std::string& key) -> Result { + assert(params.feature); + return bool(params.feature->getValue(key)); + }); + + define("filter-has-id", [](const EvaluationContext& params) -> Result { + assert(params.feature); + return bool(params.feature->getID()); + }); + + define("filter-type-in", [](const EvaluationContext& params, const Varargs& types) -> Result { + assert(params.feature); + optional type = featureTypeAsString(params.feature->getType()); + return std::find(types.begin(), types.end(), type) != types.end(); + }); + + define("filter-id-in", [](const EvaluationContext& params, const Varargs& ids) -> Result { + auto id = featureIdAsExpressionValue(params); + return std::find(ids.begin(), ids.end(), id) != ids.end(); + }); + + define("filter-in", [](const EvaluationContext& params, const Varargs& varargs) -> Result { + if (varargs.size() < 2) return false; + assert(varargs[0].is()); + auto value = featurePropertyAsExpressionValue(params, varargs[0].get()); + 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 expected; - + if (singleMatchingSignature) { expected = definition[*singleMatchingSignature]->params.match( [](const VarargsType& varargs) { return varargs.type; }, [&](const std::vector& 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& signature : definition) { signatureContext.clearErrors(); - + if (signature->params.is>()) { const std::vector& params = signature->params.get>(); 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>(), ctx); +} + +ParseResult createCompoundExpression(const std::string& name, + std::unique_ptr arg1, + ParsingContext& ctx) { + std::vector> args; + args.push_back(std::move(arg1)); + return createCompoundExpression(name, std::move(args), ctx); +} + +ParseResult createCompoundExpression(const std::string& name, + std::unique_ptr arg1, + std::unique_ptr arg2, + ParsingContext& ctx) { + std::vector> 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 diff --git a/src/mbgl/style/expression/literal.cpp b/src/mbgl/style/expression/literal.cpp index 8a63980dba..f68cfd5cf5 100644 --- a/src/mbgl/style/expression/literal.cpp +++ b/src/mbgl/style/expression/literal.cpp @@ -109,6 +109,14 @@ mbgl::Value Literal::serialize() const { return *fromExpressionValue(value); } } + +std::unique_ptr createLiteral(const char* value) { + return createLiteral(std::string(value)); +} + +std::unique_ptr createLiteral(Value value) { + return std::make_unique(value); +} } // namespace expression } // namespace style diff --git a/src/mbgl/style/filter.cpp b/src/mbgl/style/filter.cpp index 51aa6bcf82..2559eb4816 100644 --- a/src/mbgl/style/filter.cpp +++ b/src/mbgl/style/filter.cpp @@ -1,12 +1,20 @@ #include -#include #include 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 typed = expression::fromExpressionValue(*result); + return typed ? *typed : false; + } else { + return true; + } } } // namespace style diff --git a/src/mbgl/style/filter_evaluator.cpp b/src/mbgl/style/filter_evaluator.cpp deleted file mode 100644 index 72022172f4..0000000000 --- a/src/mbgl/style/filter_evaluator.cpp +++ /dev/null @@ -1,225 +0,0 @@ -#include -#include -#include - -namespace mbgl { -namespace style { - -template -struct Comparator { - const Op& op; - - template - bool operator()(const T& lhs, const T& rhs) const { - return op(lhs, rhs); - } - - template - auto operator()(const T0& lhs, const T1& rhs) const - -> typename std::enable_if_t::value && !std::is_same::value && - std::is_arithmetic::value && !std::is_same::value, bool> { - return op(double(lhs), double(rhs)); - } - - template - auto operator()(const T0&, const T1&) const - -> typename std::enable_if_t::value || std::is_same::value || - !std::is_arithmetic::value || std::is_same::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&, - const std::vector&) 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 -bool compare(const Value& lhs, const Value& rhs, const Op& op) { - return Value::binary_visit(lhs, rhs, Comparator { 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 actual = context.feature->getValue(filter.key); - return actual && equal(*actual, filter.value); -} - -bool FilterEvaluator::operator()(const NotEqualsFilter& filter) const { - optional actual = context.feature->getValue(filter.key); - return !actual || !equal(*actual, filter.value); -} - -bool FilterEvaluator::operator()(const LessThanFilter& filter) const { - optional 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 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 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 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 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 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 typed = expression::fromExpressionValue(*result); - return typed ? *typed : false; - } - return false; -} - -} // namespace style -} // namespace mbgl diff --git a/src/mbgl/tile/custom_geometry_tile.cpp b/src/mbgl/tile/custom_geometry_tile.cpp index a2fefcfa9f..272a1594d4 100644 --- a/src/mbgl/tile/custom_geometry_tile.cpp +++ b/src/mbgl/tile/custom_geometry_tile.cpp @@ -3,7 +3,6 @@ #include #include #include -#include #include #include #include diff --git a/src/mbgl/tile/geojson_tile.cpp b/src/mbgl/tile/geojson_tile.cpp index f211c03569..7a83da2267 100644 --- a/src/mbgl/tile/geojson_tile.cpp +++ b/src/mbgl/tile/geojson_tile.cpp @@ -2,7 +2,6 @@ #include #include #include -#include namespace mbgl { diff --git a/src/mbgl/tile/geometry_tile.cpp b/src/mbgl/tile/geometry_tile.cpp index a99cb91d26..28f46f6f54 100644 --- a/src/mbgl/tile/geometry_tile.cpp +++ b/src/mbgl/tile/geometry_tile.cpp @@ -16,7 +16,6 @@ #include #include #include -#include #include #include diff --git a/src/mbgl/tile/geometry_tile_worker.cpp b/src/mbgl/tile/geometry_tile_worker.cpp index 1378ad5d3a..61bc0cb597 100644 --- a/src/mbgl/tile/geometry_tile_worker.cpp +++ b/src/mbgl/tile/geometry_tile_worker.cpp @@ -5,7 +5,6 @@ #include #include #include -#include #include #include #include diff --git a/test/api/query.test.cpp b/test/api/query.test.cpp index c67ff9064c..ffab52692b 100644 --- a/test/api/query.test.cpp +++ b/test/api/query.test.cpp @@ -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); } diff --git a/test/renderer/group_by_layout.test.cpp b/test/renderer/group_by_layout.test.cpp index 958f1bdf24..6cf17e2279 100644 --- a/test/renderer/group_by_layout.test.cpp +++ b/test/renderer/group_by_layout.test.cpp @@ -5,9 +5,11 @@ #include #include #include +#include using namespace mbgl; using namespace mbgl::style; +using namespace mbgl::style::expression; static std::vector> toRenderLayers(const std::vector>& layers) { std::vector> result; @@ -39,7 +41,8 @@ TEST(GroupByLayout, UnrelatedFilter) { std::vector> layers; layers.push_back(std::make_unique("a", "source")); layers.push_back(std::make_unique("b", "source")); - layers[0]->as()->setFilter(EqualsFilter()); + ParsingContext context; + layers[0]->as()->setFilter(Filter(createCompoundExpression("filter-has-id", context))); auto result = groupByLayout(toRenderLayers(layers)); ASSERT_EQ(2u, result.size()); } diff --git a/test/style/conversion/stringify.test.cpp b/test/style/conversion/stringify.test.cpp index 136f276aaf..c3faf1f838 100644 --- a/test/style/conversion/stringify.test.cpp +++ b/test/style/conversion/stringify.test.cpp @@ -1,5 +1,6 @@ #include +#include #include #include #include @@ -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) { diff --git a/test/style/filter.test.cpp b/test/style/filter.test.cpp index 49edcaef45..c59a73eab1 100644 --- a/test/style/filter.test.cpp +++ b/test/style/filter.test.cpp @@ -4,7 +4,6 @@ #include #include -#include #include #include @@ -28,6 +27,24 @@ bool filter(const char * json, return (*filter)(context); } +void invalidFilter(const char * json) { + conversion::Error error; + optional filter = conversion::convertJSON(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)); diff --git a/test/style/style_layer.test.cpp b/test/style/style_layer.test.cpp index 77acca2868..624ed088c3 100644 --- a/test/style/style_layer.test.cpp +++ b/test/style/style_layer.test.cpp @@ -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. -- cgit v1.2.1