From 68b25f217dc02732bce0c440ab7e09cfb3629bbb Mon Sep 17 00:00:00 2001 From: Lucas Wojciechowski Date: Fri, 9 Feb 2018 10:10:23 -0800 Subject: [PATCH 01/24] WIP --- include/mbgl/style/filter.hpp | 13 ++++++++++++- include/mbgl/style/filter_evaluator.hpp | 4 ++++ platform/darwin/src/NSPredicate+MGLAdditions.mm | 4 ++++ src/mbgl/style/conversion/stringify.hpp | 4 ++++ 4 files changed, 24 insertions(+), 1 deletion(-) diff --git a/include/mbgl/style/filter.hpp b/include/mbgl/style/filter.hpp index a204a2b17ac..31fef05a4a6 100644 --- a/include/mbgl/style/filter.hpp +++ b/include/mbgl/style/filter.hpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -232,6 +233,15 @@ class NotHasIdentifierFilter { return true; } }; + +class ExpressionFilter { +public: + std::shared_ptr expression; + + friend bool operator==(const ExpressionFilter& lhs, const ExpressionFilter& rhs) { + return lhs.expression == rhs.expression; + } +}; using FilterBase = variant< @@ -258,7 +268,8 @@ using FilterBase = variant< class IdentifierInFilter, class IdentifierNotInFilter, class HasIdentifierFilter, - class NotHasIdentifierFilter>; + class NotHasIdentifierFilter, + class ExpressionFilter>; class Filter : public FilterBase { public: diff --git a/include/mbgl/style/filter_evaluator.hpp b/include/mbgl/style/filter_evaluator.hpp index 66223d7282a..bd63caa0a0e 100644 --- a/include/mbgl/style/filter_evaluator.hpp +++ b/include/mbgl/style/filter_evaluator.hpp @@ -180,6 +180,10 @@ class FilterEvaluator { bool operator()(const NotHasIdentifierFilter&) const { return !featureIdentifier; } + + bool operator()(const ExpressionFilter&) const { + return false; + } private: template diff --git a/platform/darwin/src/NSPredicate+MGLAdditions.mm b/platform/darwin/src/NSPredicate+MGLAdditions.mm index 3ffe200d017..1a643d41d0f 100644 --- a/platform/darwin/src/NSPredicate+MGLAdditions.mm +++ b/platform/darwin/src/NSPredicate+MGLAdditions.mm @@ -194,6 +194,10 @@ NSPredicate *operator()(mbgl::style::NotHasIdentifierFilter filter) { return [NSPredicate predicateWithFormat:@"%K == nil", @"$id"]; } + + NSPredicate *operator()(mbgl::style::ExpressionFilter filter) { + return nil; + } }; @implementation NSPredicate (MGLAdditions) diff --git a/src/mbgl/style/conversion/stringify.hpp b/src/mbgl/style/conversion/stringify.hpp index 6ae6fede42d..fd1af4ba0cb 100644 --- a/src/mbgl/style/conversion/stringify.hpp +++ b/src/mbgl/style/conversion/stringify.hpp @@ -225,6 +225,10 @@ class StringifyFilter { void operator()(const NotHasIdentifierFilter&) { stringifyUnaryFilter("!has", "$id"); } + + void operator()(const ExpressionFilter&) { + stringifyUnaryFilter("herp", "derp"); + } private: template From aab8b157f1b96b8e1c0444cc1fbe4908dd63d7d2 Mon Sep 17 00:00:00 2001 From: Lucas Wojciechowski Date: Fri, 9 Feb 2018 12:14:33 -0800 Subject: [PATCH 02/24] WIP --- include/mbgl/style/filter.hpp | 2 +- include/mbgl/style/filter_evaluator.hpp | 8 +++++--- src/mbgl/layout/symbol_layout.cpp | 2 +- src/mbgl/tile/geometry_tile_worker.cpp | 2 +- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/include/mbgl/style/filter.hpp b/include/mbgl/style/filter.hpp index 31fef05a4a6..4728b8a1c3e 100644 --- a/include/mbgl/style/filter.hpp +++ b/include/mbgl/style/filter.hpp @@ -281,7 +281,7 @@ class Filter : public FilterBase { bool operator()(const GeometryTileFeature&) const; template - bool operator()(FeatureType type, optional id, PropertyAccessor accessor) const; + bool operator()(FeatureType type, optional id, PropertyAccessor accessor, float zoom) const; }; } // namespace style diff --git a/include/mbgl/style/filter_evaluator.hpp b/include/mbgl/style/filter_evaluator.hpp index bd63caa0a0e..67882d2c0dd 100644 --- a/include/mbgl/style/filter_evaluator.hpp +++ b/include/mbgl/style/filter_evaluator.hpp @@ -247,16 +247,18 @@ inline bool Filter::operator()(const Feature& feature) const { if (it == feature.properties.end()) return {}; return it->second; - }); + }, 0); } template bool Filter::operator()(const GeometryTileFeature& feature) const { - return operator()(feature.getType(), feature.getID(), [&] (const auto& key) { return feature.getValue(key); }); + return operator()(feature.getType(), feature.getID(), [&] (const auto& key) { return feature.getValue(key); }, 0); } template -bool Filter::operator()(FeatureType type, optional id, PropertyAccessor accessor) const { +// TODO add zoom & expression-compatible feature reference to this call +bool Filter::operator()(FeatureType type, optional id, PropertyAccessor accessor, float zoom) const { + (void) zoom; return FilterBase::visit(*this, FilterEvaluator { type, id, accessor }); } diff --git a/src/mbgl/layout/symbol_layout.cpp b/src/mbgl/layout/symbol_layout.cpp index a41a98fcaf9..8a70f9ce56d 100644 --- a/src/mbgl/layout/symbol_layout.cpp +++ b/src/mbgl/layout/symbol_layout.cpp @@ -100,7 +100,7 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters, const size_t featureCount = sourceLayer->featureCount(); for (size_t i = 0; i < featureCount; ++i) { auto feature = sourceLayer->getFeature(i); - if (!leader.filter(feature->getType(), feature->getID(), [&] (const auto& key) { return feature->getValue(key); })) + if (!leader.filter(feature->getType(), feature->getID(), [&] (const auto& key) { return feature->getValue(key); }, 0)) continue; SymbolFeature ft(std::move(feature)); diff --git a/src/mbgl/tile/geometry_tile_worker.cpp b/src/mbgl/tile/geometry_tile_worker.cpp index 24841dd125c..ecbbcb53084 100644 --- a/src/mbgl/tile/geometry_tile_worker.cpp +++ b/src/mbgl/tile/geometry_tile_worker.cpp @@ -339,7 +339,7 @@ void GeometryTileWorker::redoLayout() { for (std::size_t i = 0; !obsolete && i < geometryLayer->featureCount(); i++) { std::unique_ptr feature = geometryLayer->getFeature(i); - if (!filter(feature->getType(), feature->getID(), [&] (const auto& key) { return feature->getValue(key); })) + if (!filter(feature->getType(), feature->getID(), [&] (const auto& key) { return feature->getValue(key); }, 0)) continue; GeometryCollection geometries = feature->getGeometries(); From bbdce800a597ea52abe6db23fa9ecf60ac3df4b3 Mon Sep 17 00:00:00 2001 From: Lucas Wojciechowski Date: Tue, 13 Feb 2018 13:50:30 -0800 Subject: [PATCH 03/24] WIP --- include/mbgl/style/filter.hpp | 4 ++-- include/mbgl/style/filter_evaluator.hpp | 10 ++++++---- src/mbgl/layout/symbol_layout.cpp | 2 +- src/mbgl/tile/geometry_tile_worker.cpp | 2 +- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/include/mbgl/style/filter.hpp b/include/mbgl/style/filter.hpp index 4728b8a1c3e..e27c3bfc102 100644 --- a/include/mbgl/style/filter.hpp +++ b/include/mbgl/style/filter.hpp @@ -236,7 +236,7 @@ class NotHasIdentifierFilter { class ExpressionFilter { public: - std::shared_ptr expression; + std::shared_ptr expression; friend bool operator==(const ExpressionFilter& lhs, const ExpressionFilter& rhs) { return lhs.expression == rhs.expression; @@ -281,7 +281,7 @@ class Filter : public FilterBase { bool operator()(const GeometryTileFeature&) const; template - bool operator()(FeatureType type, optional id, PropertyAccessor accessor, float zoom) const; + bool operator()(FeatureType type, optional id, PropertyAccessor accessor, expression::EvaluationContext context) const; }; } // namespace style diff --git a/include/mbgl/style/filter_evaluator.hpp b/include/mbgl/style/filter_evaluator.hpp index 67882d2c0dd..747627210d9 100644 --- a/include/mbgl/style/filter_evaluator.hpp +++ b/include/mbgl/style/filter_evaluator.hpp @@ -247,18 +247,20 @@ inline bool Filter::operator()(const Feature& feature) const { if (it == feature.properties.end()) return {}; return it->second; - }, 0); + + // TODO include GeoJSONFeature-wrapped feature + }, expression::EvaluationContext { 0.0 } ); } template bool Filter::operator()(const GeometryTileFeature& feature) const { - return operator()(feature.getType(), feature.getID(), [&] (const auto& key) { return feature.getValue(key); }, 0); + return operator()(feature.getType(), feature.getID(), [&] (const auto& key) { return feature.getValue(key); }, expression::EvaluationContext { &feature }); } template // TODO add zoom & expression-compatible feature reference to this call -bool Filter::operator()(FeatureType type, optional id, PropertyAccessor accessor, float zoom) const { - (void) zoom; + bool Filter::operator()(FeatureType type, optional id, PropertyAccessor accessor, expression::EvaluationContext context) const { + (void) context; return FilterBase::visit(*this, FilterEvaluator { type, id, accessor }); } diff --git a/src/mbgl/layout/symbol_layout.cpp b/src/mbgl/layout/symbol_layout.cpp index 8a70f9ce56d..049c68a764e 100644 --- a/src/mbgl/layout/symbol_layout.cpp +++ b/src/mbgl/layout/symbol_layout.cpp @@ -100,7 +100,7 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters, const size_t featureCount = sourceLayer->featureCount(); for (size_t i = 0; i < featureCount; ++i) { auto feature = sourceLayer->getFeature(i); - if (!leader.filter(feature->getType(), feature->getID(), [&] (const auto& key) { return feature->getValue(key); }, 0)) + if (!leader.filter(feature->getType(), feature->getID(), [&] (const auto& key) { return feature->getValue(key); }, expression::EvaluationContext { feature.get() })) continue; SymbolFeature ft(std::move(feature)); diff --git a/src/mbgl/tile/geometry_tile_worker.cpp b/src/mbgl/tile/geometry_tile_worker.cpp index ecbbcb53084..c80bd5ecd5c 100644 --- a/src/mbgl/tile/geometry_tile_worker.cpp +++ b/src/mbgl/tile/geometry_tile_worker.cpp @@ -339,7 +339,7 @@ void GeometryTileWorker::redoLayout() { for (std::size_t i = 0; !obsolete && i < geometryLayer->featureCount(); i++) { std::unique_ptr feature = geometryLayer->getFeature(i); - if (!filter(feature->getType(), feature->getID(), [&] (const auto& key) { return feature->getValue(key); }, 0)) + if (!filter(feature->getType(), feature->getID(), [&] (const auto& key) { return feature->getValue(key); }, expression::EvaluationContext { feature.get() })) continue; GeometryCollection geometries = feature->getGeometries(); From c9618fd8ecf43b222237d0de9fcabb0e0fa458dc Mon Sep 17 00:00:00 2001 From: Lucas Wojciechowski Date: Tue, 13 Feb 2018 16:00:22 -0800 Subject: [PATCH 04/24] Remove Filter::operator()(const Feature&) --- include/mbgl/style/filter.hpp | 2 -- include/mbgl/style/filter_evaluator.hpp | 11 ----------- test/style/filter.test.cpp | 25 +++++++++++-------------- 3 files changed, 11 insertions(+), 27 deletions(-) diff --git a/include/mbgl/style/filter.hpp b/include/mbgl/style/filter.hpp index e27c3bfc102..9ab7e2e950c 100644 --- a/include/mbgl/style/filter.hpp +++ b/include/mbgl/style/filter.hpp @@ -275,8 +275,6 @@ class Filter : public FilterBase { public: using FilterBase::FilterBase; - bool operator()(const Feature&) const; - template bool operator()(const GeometryTileFeature&) const; diff --git a/include/mbgl/style/filter_evaluator.hpp b/include/mbgl/style/filter_evaluator.hpp index 747627210d9..6d5a7581d9d 100644 --- a/include/mbgl/style/filter_evaluator.hpp +++ b/include/mbgl/style/filter_evaluator.hpp @@ -241,17 +241,6 @@ class FilterEvaluator { } }; -inline bool Filter::operator()(const Feature& feature) const { - return operator()(apply_visitor(ToFeatureType(), feature.geometry), feature.id, [&] (const std::string& key) -> optional { - auto it = feature.properties.find(key); - if (it == feature.properties.end()) - return {}; - return it->second; - - // TODO include GeoJSONFeature-wrapped feature - }, expression::EvaluationContext { 0.0 } ); -} - template bool Filter::operator()(const GeometryTileFeature& feature) const { return operator()(feature.getType(), feature.getID(), [&] (const auto& key) { return feature.getValue(key); }, expression::EvaluationContext { &feature }); diff --git a/test/style/filter.test.cpp b/test/style/filter.test.cpp index 73f8e7626db..a67e4184568 100644 --- a/test/style/filter.test.cpp +++ b/test/style/filter.test.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include @@ -17,10 +18,8 @@ Filter parse(const char * expression) { return *filter; } -Feature feature(const PropertyMap& properties, const Geometry& geometry = Point()) { - Feature result { geometry }; - result.properties = properties; - return result; +StubGeometryTileFeature feature(const PropertyMap& properties) { + return StubGeometryTileFeature { properties }; } TEST(Filter, EqualsString) { @@ -46,15 +45,15 @@ TEST(Filter, EqualsNumber) { TEST(Filter, EqualsType) { Filter f = parse(R"(["==", "$type", "LineString"])"); - ASSERT_FALSE(f(feature({{}}, Point()))); - ASSERT_TRUE(f(feature({{}}, LineString()))); + ASSERT_FALSE(f(StubGeometryTileFeature({}, FeatureType::Point, {}, {}))); + ASSERT_TRUE(f(StubGeometryTileFeature({}, FeatureType::LineString, {}, {}))); } TEST(Filter, InType) { Filter f = parse(R"(["in", "$type", "LineString", "Polygon"])"); - ASSERT_FALSE(f(feature({{}}, Point()))); - ASSERT_TRUE(f(feature({{}}, LineString()))); - ASSERT_TRUE(f(feature({{}}, Polygon()))); + ASSERT_FALSE(f(StubGeometryTileFeature({}, FeatureType::Point, {}, {}))); + ASSERT_TRUE(f(StubGeometryTileFeature({}, FeatureType::LineString, {}, {}))); + ASSERT_TRUE(f(StubGeometryTileFeature({}, FeatureType::Polygon, {}, {}))); } TEST(Filter, Any) { @@ -110,14 +109,12 @@ TEST(Filter, NotHas) { } TEST(Filter, ID) { - Feature feature1 { Point() }; - feature1.id = { uint64_t(1234) }; + StubGeometryTileFeature feature1 { FeatureIdentifier{ int64_t{ 1234 } }, {}, {}, {} }; ASSERT_TRUE(parse("[\"==\", \"$id\", 1234]")(feature1)); ASSERT_FALSE(parse("[\"==\", \"$id\", \"1234\"]")(feature1)); - - Feature feature2 { Point() }; - feature2.properties["id"] = { uint64_t(1234) }; + + StubGeometryTileFeature feature2 { {{ "id", uint64_t(1234) }} }; ASSERT_FALSE(parse("[\"==\", \"$id\", 1234]")(feature2)); } From 9a51bc432489198f7aa238201ece0d01615402e6 Mon Sep 17 00:00:00 2001 From: Lucas Wojciechowski Date: Wed, 14 Feb 2018 15:12:12 -0800 Subject: [PATCH 05/24] WIP --- include/mbgl/style/filter_evaluator.hpp | 50 ++++++++++++------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/include/mbgl/style/filter_evaluator.hpp b/include/mbgl/style/filter_evaluator.hpp index 6d5a7581d9d..3292ecbb16d 100644 --- a/include/mbgl/style/filter_evaluator.hpp +++ b/include/mbgl/style/filter_evaluator.hpp @@ -22,46 +22,47 @@ namespace style { template class FilterEvaluator { public: - const FeatureType featureType; - const optional featureIdentifier; - const PropertyAccessor propertyAccessor; + const FeatureType _featureType; + const optional _featureIdentifier; + const PropertyAccessor _propertyAccessor; + const expression::EvaluationContext context; bool operator()(const NullFilter&) const { return true; } bool operator()(const EqualsFilter& filter) const { - optional actual = propertyAccessor(filter.key); + optional actual = context.feature->getValue(filter.key); return actual && equal(*actual, filter.value); } bool operator()(const NotEqualsFilter& filter) const { - optional actual = propertyAccessor(filter.key); + optional actual = context.feature->getValue(filter.key); return !actual || !equal(*actual, filter.value); } bool operator()(const LessThanFilter& filter) const { - optional actual = propertyAccessor(filter.key); + optional actual = context.feature->getValue(filter.key); return actual && compare(*actual, filter.value, [] (const auto& lhs_, const auto& rhs_) { return lhs_ < rhs_; }); } bool operator()(const LessThanEqualsFilter& filter) const { - optional actual = propertyAccessor(filter.key); + optional actual = context.feature->getValue(filter.key); return actual && compare(*actual, filter.value, [] (const auto& lhs_, const auto& rhs_) { return lhs_ <= rhs_; }); } bool operator()(const GreaterThanFilter& filter) const { - optional actual = propertyAccessor(filter.key); + optional actual = context.feature->getValue(filter.key); return actual && compare(*actual, filter.value, [] (const auto& lhs_, const auto& rhs_) { return lhs_ > rhs_; }); } bool operator()(const GreaterThanEqualsFilter& filter) const { - optional actual = propertyAccessor(filter.key); + optional actual = context.feature->getValue(filter.key); return actual && compare(*actual, filter.value, [] (const auto& lhs_, const auto& rhs_) { return lhs_ >= rhs_; }); } bool operator()(const InFilter& filter) const { - optional actual = propertyAccessor(filter.key); + optional actual = context.feature->getValue(filter.key); if (!actual) return false; for (const auto& v: filter.values) { @@ -73,7 +74,7 @@ class FilterEvaluator { } bool operator()(const NotInFilter& filter) const { - optional actual = propertyAccessor(filter.key); + optional actual = context.feature->getValue(filter.key); if (!actual) return true; for (const auto& v: filter.values) { @@ -112,25 +113,25 @@ class FilterEvaluator { } bool operator()(const HasFilter& filter) const { - return bool(propertyAccessor(filter.key)); + return bool(context.feature->getValue(filter.key)); } bool operator()(const NotHasFilter& filter) const { - return !propertyAccessor(filter.key); + return !context.feature->getValue(filter.key); } bool operator()(const TypeEqualsFilter& filter) const { - return featureType == filter.value; + return context.feature->getType() == filter.value; } bool operator()(const TypeNotEqualsFilter& filter) const { - return featureType != filter.value; + return context.feature->getType() != filter.value; } bool operator()(const TypeInFilter& filter) const { for (const auto& v: filter.values) { - if (featureType == v) { + if (context.feature->getType() == v) { return true; } } @@ -139,7 +140,7 @@ class FilterEvaluator { bool operator()(const TypeNotInFilter& filter) const { for (const auto& v: filter.values) { - if (featureType == v) { + if (context.feature->getType() == v) { return false; } } @@ -148,16 +149,16 @@ class FilterEvaluator { bool operator()(const IdentifierEqualsFilter& filter) const { - return featureIdentifier == filter.value; + return context.feature->getID() == filter.value; } bool operator()(const IdentifierNotEqualsFilter& filter) const { - return featureIdentifier != filter.value; + return context.feature->getID() != filter.value; } bool operator()(const IdentifierInFilter& filter) const { for (const auto& v: filter.values) { - if (featureIdentifier == v) { + if (context.feature->getID() == v) { return true; } } @@ -166,7 +167,7 @@ class FilterEvaluator { bool operator()(const IdentifierNotInFilter& filter) const { for (const auto& v: filter.values) { - if (featureIdentifier == v) { + if (context.feature->getID() == v) { return false; } } @@ -174,11 +175,11 @@ class FilterEvaluator { } bool operator()(const HasIdentifierFilter&) const { - return bool(featureIdentifier); + return bool(context.feature->getID()); } bool operator()(const NotHasIdentifierFilter&) const { - return !featureIdentifier; + return !context.feature->getID(); } bool operator()(const ExpressionFilter&) const { @@ -249,8 +250,7 @@ bool Filter::operator()(const GeometryTileFeature& feature) const { template // TODO add zoom & expression-compatible feature reference to this call bool Filter::operator()(FeatureType type, optional id, PropertyAccessor accessor, expression::EvaluationContext context) const { - (void) context; - return FilterBase::visit(*this, FilterEvaluator { type, id, accessor }); + return FilterBase::visit(*this, FilterEvaluator { type, id, accessor, context }); } } // namespace style From 228ca2df0730ac394b95176d176504f17b4dc366 Mon Sep 17 00:00:00 2001 From: Lucas Wojciechowski Date: Mon, 19 Feb 2018 13:41:59 -0800 Subject: [PATCH 06/24] WIP --- cmake/core-files.cmake | 1 + include/mbgl/style/filter.hpp | 3 +- include/mbgl/style/filter_evaluator.hpp | 195 ++++-------------------- src/mbgl/layout/symbol_layout.cpp | 2 +- src/mbgl/style/filter_evaluator.cpp | 170 +++++++++++++++++++++ src/mbgl/tile/geometry_tile_worker.cpp | 2 +- 6 files changed, 200 insertions(+), 173 deletions(-) create mode 100644 src/mbgl/style/filter_evaluator.cpp diff --git a/cmake/core-files.cmake b/cmake/core-files.cmake index f24482e301f..90f74ecf0e9 100644 --- a/cmake/core-files.cmake +++ b/cmake/core-files.cmake @@ -376,6 +376,7 @@ set(MBGL_CORE_FILES src/mbgl/style/collection.hpp src/mbgl/style/custom_tile_loader.cpp src/mbgl/style/custom_tile_loader.hpp + 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/filter.hpp b/include/mbgl/style/filter.hpp index 9ab7e2e950c..5bca3ed47db 100644 --- a/include/mbgl/style/filter.hpp +++ b/include/mbgl/style/filter.hpp @@ -278,8 +278,7 @@ class Filter : public FilterBase { template bool operator()(const GeometryTileFeature&) const; - template - bool operator()(FeatureType type, optional id, PropertyAccessor accessor, expression::EvaluationContext context) const; + bool operator()(expression::EvaluationContext context) const; }; } // namespace style diff --git a/include/mbgl/style/filter_evaluator.hpp b/include/mbgl/style/filter_evaluator.hpp index 3292ecbb16d..7b883ef5fdd 100644 --- a/include/mbgl/style/filter_evaluator.hpp +++ b/include/mbgl/style/filter_evaluator.hpp @@ -19,172 +19,35 @@ namespace style { // does not match } */ -template class FilterEvaluator { public: - const FeatureType _featureType; - const optional _featureIdentifier; - const PropertyAccessor _propertyAccessor; const expression::EvaluationContext context; - bool operator()(const NullFilter&) const { - return true; - } - - bool operator()(const EqualsFilter& filter) const { - optional actual = context.feature->getValue(filter.key); - return actual && equal(*actual, filter.value); - } - - bool operator()(const NotEqualsFilter& filter) const { - optional actual = context.feature->getValue(filter.key); - return !actual || !equal(*actual, filter.value); - } - - bool 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 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 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 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 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 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 operator()(const AnyFilter& filter) const { - for (const auto& f: filter.filters) { - if (Filter::visit(f, *this)) { - return true; - } - } - return false; - } - - bool operator()(const AllFilter& filter) const { - for (const auto& f: filter.filters) { - if (!Filter::visit(f, *this)) { - return false; - } - } - return true; - } - - bool operator()(const NoneFilter& filter) const { - for (const auto& f: filter.filters) { - if (Filter::visit(f, *this)) { - return false; - } - } - return true; - } - - bool operator()(const HasFilter& filter) const { - return bool(context.feature->getValue(filter.key)); - } - - bool operator()(const NotHasFilter& filter) const { - return !context.feature->getValue(filter.key); - } - - - bool operator()(const TypeEqualsFilter& filter) const { - return context.feature->getType() == filter.value; - } - - bool operator()(const TypeNotEqualsFilter& filter) const { - return context.feature->getType() != filter.value; - } - - bool operator()(const TypeInFilter& filter) const { - for (const auto& v: filter.values) { - if (context.feature->getType() == v) { - return true; - } - } - return false; - } - - bool operator()(const TypeNotInFilter& filter) const { - for (const auto& v: filter.values) { - if (context.feature->getType() == v) { - return false; - } - } - return true; - } - - - bool operator()(const IdentifierEqualsFilter& filter) const { - return context.feature->getID() == filter.value; - } - - bool operator()(const IdentifierNotEqualsFilter& filter) const { - return context.feature->getID() != filter.value; - } - - bool operator()(const IdentifierInFilter& filter) const { - for (const auto& v: filter.values) { - if (context.feature->getID() == v) { - return true; - } - } - return false; - } - - bool operator()(const IdentifierNotInFilter& filter) const { - for (const auto& v: filter.values) { - if (context.feature->getID() == v) { - return false; - } - } - return true; - } - - bool operator()(const HasIdentifierFilter&) const { - return bool(context.feature->getID()); - } - - bool operator()(const NotHasIdentifierFilter&) const { - return !context.feature->getID(); - } - - bool operator()(const ExpressionFilter&) const { - return false; - } + 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; private: template @@ -244,13 +107,7 @@ class FilterEvaluator { template bool Filter::operator()(const GeometryTileFeature& feature) const { - return operator()(feature.getType(), feature.getID(), [&] (const auto& key) { return feature.getValue(key); }, expression::EvaluationContext { &feature }); -} - -template -// TODO add zoom & expression-compatible feature reference to this call - bool Filter::operator()(FeatureType type, optional id, PropertyAccessor accessor, expression::EvaluationContext context) const { - return FilterBase::visit(*this, FilterEvaluator { type, id, accessor, context }); + return operator()(expression::EvaluationContext { &feature }); } } // namespace style diff --git a/src/mbgl/layout/symbol_layout.cpp b/src/mbgl/layout/symbol_layout.cpp index 049c68a764e..f455a9ddef6 100644 --- a/src/mbgl/layout/symbol_layout.cpp +++ b/src/mbgl/layout/symbol_layout.cpp @@ -100,7 +100,7 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters, const size_t featureCount = sourceLayer->featureCount(); for (size_t i = 0; i < featureCount; ++i) { auto feature = sourceLayer->getFeature(i); - if (!leader.filter(feature->getType(), feature->getID(), [&] (const auto& key) { return feature->getValue(key); }, expression::EvaluationContext { feature.get() })) + if (!leader.filter(expression::EvaluationContext { feature.get() })) continue; SymbolFeature ft(std::move(feature)); diff --git a/src/mbgl/style/filter_evaluator.cpp b/src/mbgl/style/filter_evaluator.cpp new file mode 100644 index 00000000000..c1df1adc386 --- /dev/null +++ b/src/mbgl/style/filter_evaluator.cpp @@ -0,0 +1,170 @@ +#include +#include +#include + +namespace mbgl { +namespace style { + +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&) const { + return false; +} + +bool Filter::operator()(expression::EvaluationContext context) const { + return FilterBase::visit(*this, FilterEvaluator { context }); +} + +} // namespace style +} // namespace mbgl diff --git a/src/mbgl/tile/geometry_tile_worker.cpp b/src/mbgl/tile/geometry_tile_worker.cpp index c80bd5ecd5c..19e32a46bf4 100644 --- a/src/mbgl/tile/geometry_tile_worker.cpp +++ b/src/mbgl/tile/geometry_tile_worker.cpp @@ -339,7 +339,7 @@ void GeometryTileWorker::redoLayout() { for (std::size_t i = 0; !obsolete && i < geometryLayer->featureCount(); i++) { std::unique_ptr feature = geometryLayer->getFeature(i); - if (!filter(feature->getType(), feature->getID(), [&] (const auto& key) { return feature->getValue(key); }, expression::EvaluationContext { feature.get() })) + if (!filter(expression::EvaluationContext { feature.get() })) continue; GeometryCollection geometries = feature->getGeometries(); From 57e1df1513e54d375adf307c4a0cf89d40f58174 Mon Sep 17 00:00:00 2001 From: Lucas Wojciechowski Date: Mon, 19 Feb 2018 13:44:27 -0800 Subject: [PATCH 07/24] WIP --- include/mbgl/style/filter_evaluator.hpp | 54 ------------------------- src/mbgl/style/filter_evaluator.cpp | 54 +++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 54 deletions(-) diff --git a/include/mbgl/style/filter_evaluator.hpp b/include/mbgl/style/filter_evaluator.hpp index 7b883ef5fdd..d2c680d3bc4 100644 --- a/include/mbgl/style/filter_evaluator.hpp +++ b/include/mbgl/style/filter_evaluator.hpp @@ -49,60 +49,6 @@ class FilterEvaluator { bool operator()(const NotHasIdentifierFilter&) const; bool operator()(const ExpressionFilter&) const; -private: - 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) const { - return Value::binary_visit(lhs, rhs, Comparator { op }); - } - - bool equal(const Value& lhs, const Value& rhs) const { - return compare(lhs, rhs, [] (const auto& lhs_, const auto& rhs_) { return lhs_ == rhs_; }); - } }; template diff --git a/src/mbgl/style/filter_evaluator.cpp b/src/mbgl/style/filter_evaluator.cpp index c1df1adc386..3ec99e6c983 100644 --- a/src/mbgl/style/filter_evaluator.cpp +++ b/src/mbgl/style/filter_evaluator.cpp @@ -4,6 +4,60 @@ 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; From d4aac8e8830c401ab8fa8ee6700cc5f60bf56e9a Mon Sep 17 00:00:00 2001 From: Lucas Wojciechowski Date: Mon, 19 Feb 2018 14:03:20 -0800 Subject: [PATCH 08/24] WIP --- cmake/core-files.cmake | 1 + include/mbgl/style/filter.hpp | 2 -- include/mbgl/style/filter_evaluator.hpp | 5 ----- src/mbgl/style/filter.cpp | 17 +++++++++++++++++ src/mbgl/style/filter_evaluator.cpp | 4 ---- 5 files changed, 18 insertions(+), 11 deletions(-) create mode 100644 src/mbgl/style/filter.cpp diff --git a/cmake/core-files.cmake b/cmake/core-files.cmake index 90f74ecf0e9..35eb9c9400f 100644 --- a/cmake/core-files.cmake +++ b/cmake/core-files.cmake @@ -376,6 +376,7 @@ set(MBGL_CORE_FILES src/mbgl/style/collection.hpp 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 diff --git a/include/mbgl/style/filter.hpp b/include/mbgl/style/filter.hpp index 5bca3ed47db..a5bd32b71e8 100644 --- a/include/mbgl/style/filter.hpp +++ b/include/mbgl/style/filter.hpp @@ -275,9 +275,7 @@ class Filter : public FilterBase { public: using FilterBase::FilterBase; - template bool operator()(const GeometryTileFeature&) const; - bool operator()(expression::EvaluationContext context) const; }; diff --git a/include/mbgl/style/filter_evaluator.hpp b/include/mbgl/style/filter_evaluator.hpp index d2c680d3bc4..a4a4098b3f8 100644 --- a/include/mbgl/style/filter_evaluator.hpp +++ b/include/mbgl/style/filter_evaluator.hpp @@ -51,10 +51,5 @@ class FilterEvaluator { }; -template -bool Filter::operator()(const GeometryTileFeature& feature) const { - return operator()(expression::EvaluationContext { &feature }); -} - } // namespace style } // namespace mbgl diff --git a/src/mbgl/style/filter.cpp b/src/mbgl/style/filter.cpp new file mode 100644 index 00000000000..4aa8d9622e6 --- /dev/null +++ b/src/mbgl/style/filter.cpp @@ -0,0 +1,17 @@ +#include +#include +#include + +namespace mbgl { +namespace style { + +bool Filter::operator()(expression::EvaluationContext context) const { + return FilterBase::visit(*this, FilterEvaluator { context }); +} + +bool Filter::operator()(const GeometryTileFeature& feature) const { + return operator()(expression::EvaluationContext { &feature }); +} + +} // namespace style +} // namespace mbgl diff --git a/src/mbgl/style/filter_evaluator.cpp b/src/mbgl/style/filter_evaluator.cpp index 3ec99e6c983..3645a017284 100644 --- a/src/mbgl/style/filter_evaluator.cpp +++ b/src/mbgl/style/filter_evaluator.cpp @@ -215,10 +215,6 @@ bool FilterEvaluator::operator()(const NotHasIdentifierFilter&) const { bool FilterEvaluator::operator()(const ExpressionFilter&) const { return false; } - -bool Filter::operator()(expression::EvaluationContext context) const { - return FilterBase::visit(*this, FilterEvaluator { context }); -} } // namespace style } // namespace mbgl From 392fe113b529f705cee4a1757eedc6ef48023574 Mon Sep 17 00:00:00 2001 From: Lucas Wojciechowski Date: Mon, 19 Feb 2018 14:16:05 -0800 Subject: [PATCH 09/24] Hook up expression filter evaluator --- src/mbgl/style/filter_evaluator.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/mbgl/style/filter_evaluator.cpp b/src/mbgl/style/filter_evaluator.cpp index 3645a017284..72022172f44 100644 --- a/src/mbgl/style/filter_evaluator.cpp +++ b/src/mbgl/style/filter_evaluator.cpp @@ -212,7 +212,12 @@ bool FilterEvaluator::operator()(const NotHasIdentifierFilter&) const { return !context.feature->getID(); } -bool FilterEvaluator::operator()(const ExpressionFilter&) const { +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; } From 4eae9bb0c6d2d2d9a739ba6ad1b5ceaad106e854 Mon Sep 17 00:00:00 2001 From: Lucas Wojciechowski Date: Tue, 20 Feb 2018 10:18:55 -0800 Subject: [PATCH 10/24] Replace `shared_ptr` with &reference --- include/mbgl/style/filter.hpp | 2 +- src/mbgl/style/filter_evaluator.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/mbgl/style/filter.hpp b/include/mbgl/style/filter.hpp index a5bd32b71e8..99a5426c2b5 100644 --- a/include/mbgl/style/filter.hpp +++ b/include/mbgl/style/filter.hpp @@ -236,7 +236,7 @@ class NotHasIdentifierFilter { class ExpressionFilter { public: - std::shared_ptr expression; + expression::Expression &expression; friend bool operator==(const ExpressionFilter& lhs, const ExpressionFilter& rhs) { return lhs.expression == rhs.expression; diff --git a/src/mbgl/style/filter_evaluator.cpp b/src/mbgl/style/filter_evaluator.cpp index 72022172f44..39f356a8b1c 100644 --- a/src/mbgl/style/filter_evaluator.cpp +++ b/src/mbgl/style/filter_evaluator.cpp @@ -213,7 +213,7 @@ bool FilterEvaluator::operator()(const NotHasIdentifierFilter&) const { } bool FilterEvaluator::operator()(const ExpressionFilter& filter) const { - const expression::EvaluationResult result = filter.expression->evaluate(context); + const expression::EvaluationResult result = filter.expression.evaluate(context); if (result) { const optional typed = expression::fromExpressionValue(*result); return typed ? *typed : false; From 9c55db89312428d809e90021e51b86c70e547306 Mon Sep 17 00:00:00 2001 From: Lucas Wojciechowski Date: Tue, 20 Feb 2018 11:40:50 -0800 Subject: [PATCH 11/24] Fill in implementation of `void operator()(const ExpressionFilter&)` --- src/mbgl/style/conversion/stringify.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mbgl/style/conversion/stringify.hpp b/src/mbgl/style/conversion/stringify.hpp index fd1af4ba0cb..0d1d4d85fbe 100644 --- a/src/mbgl/style/conversion/stringify.hpp +++ b/src/mbgl/style/conversion/stringify.hpp @@ -226,8 +226,8 @@ class StringifyFilter { stringifyUnaryFilter("!has", "$id"); } - void operator()(const ExpressionFilter&) { - stringifyUnaryFilter("herp", "derp"); + void operator()(const ExpressionFilter& filter) { + stringify(writer, filter.expression.serialize()); } private: From bce45e89a777642a9a9e98f2be9bb6342083450f Mon Sep 17 00:00:00 2001 From: Lucas Wojciechowski Date: Tue, 20 Feb 2018 15:24:31 -0800 Subject: [PATCH 12/24] Fix failing tests --- test/style/filter.test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/style/filter.test.cpp b/test/style/filter.test.cpp index a67e4184568..8f53ca04c0f 100644 --- a/test/style/filter.test.cpp +++ b/test/style/filter.test.cpp @@ -109,7 +109,7 @@ TEST(Filter, NotHas) { } TEST(Filter, ID) { - StubGeometryTileFeature feature1 { FeatureIdentifier{ int64_t{ 1234 } }, {}, {}, {} }; + StubGeometryTileFeature feature1 { FeatureIdentifier{ uint64_t{ 1234 } }, {}, {}, {} }; ASSERT_TRUE(parse("[\"==\", \"$id\", 1234]")(feature1)); ASSERT_FALSE(parse("[\"==\", \"$id\", \"1234\"]")(feature1)); From f4eb02aebc94fb16b9e87c711e7c62b077e294e7 Mon Sep 17 00:00:00 2001 From: Lucas Wojciechowski Date: Wed, 21 Feb 2018 14:14:08 -0800 Subject: [PATCH 13/24] Switch back to a shared_ptr per chat with @anandthakker --- include/mbgl/style/filter.hpp | 4 ++-- src/mbgl/style/conversion/stringify.hpp | 2 +- src/mbgl/style/filter_evaluator.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/mbgl/style/filter.hpp b/include/mbgl/style/filter.hpp index 99a5426c2b5..040f7808a72 100644 --- a/include/mbgl/style/filter.hpp +++ b/include/mbgl/style/filter.hpp @@ -236,10 +236,10 @@ class NotHasIdentifierFilter { class ExpressionFilter { public: - expression::Expression &expression; + std::shared_ptr expression; friend bool operator==(const ExpressionFilter& lhs, const ExpressionFilter& rhs) { - return lhs.expression == rhs.expression; + return *(lhs.expression) == *(rhs.expression); } }; diff --git a/src/mbgl/style/conversion/stringify.hpp b/src/mbgl/style/conversion/stringify.hpp index 0d1d4d85fbe..7924a442c46 100644 --- a/src/mbgl/style/conversion/stringify.hpp +++ b/src/mbgl/style/conversion/stringify.hpp @@ -227,7 +227,7 @@ class StringifyFilter { } void operator()(const ExpressionFilter& filter) { - stringify(writer, filter.expression.serialize()); + stringify(writer, filter.expression->serialize()); } private: diff --git a/src/mbgl/style/filter_evaluator.cpp b/src/mbgl/style/filter_evaluator.cpp index 39f356a8b1c..72022172f44 100644 --- a/src/mbgl/style/filter_evaluator.cpp +++ b/src/mbgl/style/filter_evaluator.cpp @@ -213,7 +213,7 @@ bool FilterEvaluator::operator()(const NotHasIdentifierFilter&) const { } bool FilterEvaluator::operator()(const ExpressionFilter& filter) const { - const expression::EvaluationResult result = filter.expression.evaluate(context); + const expression::EvaluationResult result = filter.expression->evaluate(context); if (result) { const optional typed = expression::fromExpressionValue(*result); return typed ? *typed : false; From bf59bc637a09333051bdbf60a8c153f81c7c5188 Mon Sep 17 00:00:00 2001 From: Lucas Wojciechowski Date: Wed, 21 Feb 2018 14:29:29 -0800 Subject: [PATCH 14/24] Fix benchmark compilation --- benchmark/parse/filter.benchmark.cpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/benchmark/parse/filter.benchmark.cpp b/benchmark/parse/filter.benchmark.cpp index 49846684003..20267df867b 100644 --- a/benchmark/parse/filter.benchmark.cpp +++ b/benchmark/parse/filter.benchmark.cpp @@ -6,6 +6,7 @@ #include #include #include +#include using namespace mbgl; @@ -22,15 +23,10 @@ static void Parse_Filter(benchmark::State& state) { static void Parse_EvaluateFilter(benchmark::State& state) { const style::Filter filter = parse(R"FILTER(["==", "foo", "bar"])FILTER"); - const PropertyMap properties = { { "foo", std::string("bar") } }; + const StubGeometryTileFeature feature = { {}, FeatureType::Unknown , {}, {{ "foo", std::string("bar") }} }; while (state.KeepRunning()) { - filter(FeatureType::Unknown, {}, [&] (const std::string& key) -> optional { - auto it = properties.find(key); - if (it == properties.end()) - return {}; - return it->second; - }); + filter(feature); } } From 6c1c93fbbb1d175002d142087a1d8b55d7d49115 Mon Sep 17 00:00:00 2001 From: Lucas Wojciechowski Date: Wed, 21 Feb 2018 16:00:05 -0800 Subject: [PATCH 15/24] Shot in the dark to fix CI --- test/style/filter.test.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/style/filter.test.cpp b/test/style/filter.test.cpp index 8f53ca04c0f..c450a12eb24 100644 --- a/test/style/filter.test.cpp +++ b/test/style/filter.test.cpp @@ -45,15 +45,15 @@ TEST(Filter, EqualsNumber) { TEST(Filter, EqualsType) { Filter f = parse(R"(["==", "$type", "LineString"])"); - ASSERT_FALSE(f(StubGeometryTileFeature({}, FeatureType::Point, {}, {}))); - ASSERT_TRUE(f(StubGeometryTileFeature({}, FeatureType::LineString, {}, {}))); + ASSERT_FALSE(f(StubGeometryTileFeature({}, FeatureType::Point, {}, {{}}))); + ASSERT_TRUE(f(StubGeometryTileFeature({}, FeatureType::LineString, {}, {{}}))); } TEST(Filter, InType) { Filter f = parse(R"(["in", "$type", "LineString", "Polygon"])"); - ASSERT_FALSE(f(StubGeometryTileFeature({}, FeatureType::Point, {}, {}))); - ASSERT_TRUE(f(StubGeometryTileFeature({}, FeatureType::LineString, {}, {}))); - ASSERT_TRUE(f(StubGeometryTileFeature({}, FeatureType::Polygon, {}, {}))); + ASSERT_FALSE(f(StubGeometryTileFeature({}, FeatureType::Point, {}, {{}}))); + ASSERT_TRUE(f(StubGeometryTileFeature({}, FeatureType::LineString, {}, {{}}))); + ASSERT_TRUE(f(StubGeometryTileFeature({}, FeatureType::Polygon, {}, {{}}))); } TEST(Filter, Any) { From fa9ae2a9b6c0a32f8ada48f5c28bef8dfa7fc1fa Mon Sep 17 00:00:00 2001 From: Lucas Wojciechowski Date: Thu, 22 Feb 2018 14:45:16 -0800 Subject: [PATCH 16/24] Shot in the dark to fix CI (part 2) --- test/style/filter.test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/style/filter.test.cpp b/test/style/filter.test.cpp index c450a12eb24..debb0dde5a2 100644 --- a/test/style/filter.test.cpp +++ b/test/style/filter.test.cpp @@ -109,7 +109,7 @@ TEST(Filter, NotHas) { } TEST(Filter, ID) { - StubGeometryTileFeature feature1 { FeatureIdentifier{ uint64_t{ 1234 } }, {}, {}, {} }; + StubGeometryTileFeature feature1 { FeatureIdentifier{ uint64_t{ 1234 } }, {}, {}, {{} }; ASSERT_TRUE(parse("[\"==\", \"$id\", 1234]")(feature1)); ASSERT_FALSE(parse("[\"==\", \"$id\", \"1234\"]")(feature1)); From 562d5536c535cfc4020c10774dbd46c2f03cb560 Mon Sep 17 00:00:00 2001 From: Lucas Wojciechowski Date: Thu, 22 Feb 2018 15:10:17 -0800 Subject: [PATCH 17/24] Shot in the dark to fix CI (part 3) --- test/style/filter.test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/style/filter.test.cpp b/test/style/filter.test.cpp index debb0dde5a2..7994059f529 100644 --- a/test/style/filter.test.cpp +++ b/test/style/filter.test.cpp @@ -109,7 +109,7 @@ TEST(Filter, NotHas) { } TEST(Filter, ID) { - StubGeometryTileFeature feature1 { FeatureIdentifier{ uint64_t{ 1234 } }, {}, {}, {{} }; + StubGeometryTileFeature feature1 { FeatureIdentifier{ uint64_t{ 1234 } }, {}, {}, {{}}}; ASSERT_TRUE(parse("[\"==\", \"$id\", 1234]")(feature1)); ASSERT_FALSE(parse("[\"==\", \"$id\", \"1234\"]")(feature1)); From 3246b7d834bea3ed411640f0eb944481383d36a0 Mon Sep 17 00:00:00 2001 From: Lucas Wojciechowski Date: Fri, 23 Feb 2018 15:27:00 -0800 Subject: [PATCH 18/24] In src/mbgl/style/conversion/filter.cpp, add a port of isExpressionFilter and use it to decide in Converter::operator() whether to parse the incoming JSON as an ExpressionFilter or one of the legacy filter types --- src/mbgl/style/conversion/filter.cpp | 61 +++++++++++++++++++++++++--- 1 file changed, 55 insertions(+), 6 deletions(-) diff --git a/src/mbgl/style/conversion/filter.cpp b/src/mbgl/style/conversion/filter.cpp index bb7bb6ea980..75f6fffbae4 100644 --- a/src/mbgl/style/conversion/filter.cpp +++ b/src/mbgl/style/conversion/filter.cpp @@ -1,11 +1,48 @@ #include #include +#include +#include +#include namespace mbgl { namespace style { namespace conversion { + +using GeometryValue = mapbox::geometry::value; -static optional normalizeValue(const optional& value, Error& error) { +// 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) { + if (!isArray(filter) || arrayLength(filter) == 0) { + return false; + } + + optional op = toString(arrayMember(filter, 0)); + + if (*op == "has") { + auto operand = toString(arrayMember(filter, 1)); + return arrayLength(filter) >= 2 && operand && *operand != "$id" && *operand != "$type"; + + } else if (*op == "in" || *op == "!in" || *op == "!has" || *op == "none") { + return false; + + } else if (*op == "==" || *op == "!=" || *op == ">" || *op == ">=" || *op == "<" || *op == "<=") { + return arrayLength(filter) == 3 && (isArray(arrayMember(filter, 1)) || isArray(arrayMember(filter, 2))); + + } 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)) { + return false; + } + } + return true; + + } else { + return true; + } +} + +static optional normalizeValue(const optional& value, Error& error) { if (!value) { error = { "filter expression value must be a boolean, number, or string" }; return {}; @@ -32,7 +69,7 @@ static optional toFeatureType(const Convertible& value, Error& erro } static optional toFeatureIdentifier(const Convertible& value, Error& error) { - optional identifier = toValue(value); + optional identifier = toValue(value); if (!identifier) { error = { "filter expression value must be a boolean, number, or string" }; return {}; @@ -99,7 +136,7 @@ optional convertEqualityFilter(const Convertible& value, Error& error) { return { IdentifierFilterType { *filterValue } }; } else { - optional filterValue = normalizeValue(toValue(arrayMember(value, 2)), error); + optional filterValue = normalizeValue(toValue(arrayMember(value, 2)), error); if (!filterValue) { return {}; } @@ -121,7 +158,7 @@ optional convertBinaryFilter(const Convertible& value, Error& error) { return {}; } - optional filterValue = normalizeValue(toValue(arrayMember(value, 2)), error); + optional filterValue = normalizeValue(toValue(arrayMember(value, 2)), error); if (!filterValue) { return {}; } @@ -167,9 +204,9 @@ optional convertSetFilter(const Convertible& value, Error& error) { return { IdentifierFilterType { std::move(values) } }; } else { - std::vector values; + std::vector values; for (std::size_t i = 2; i < arrayLength(value); ++i) { - optional filterValue = normalizeValue(toValue(arrayMember(value, i)), error); + optional filterValue = normalizeValue(toValue(arrayMember(value, i)), error); if (!filterValue) { return {}; } @@ -193,8 +230,20 @@ optional convertCompoundFilter(const Convertible& value, Error& error) { return { FilterType { std::move(filters) } }; } + +optional convertExpressionFilter(const Convertible& value, Error& error) { + optional> expression = convert>(value, error, expression::type::Boolean); + if (!expression) { + return {}; + } + return { ExpressionFilter { std::move(*expression) } }; +} optional Converter::operator()(const Convertible& value, Error& error) const { + if (isExpressionFilter(value)) { + return convertExpressionFilter(value, error); + } + if (!isArray(value)) { error = { "filter expression must be an array" }; return {}; From 00b5c2735a5c0ec38e81e06fc7f3329ecb7cded8 Mon Sep 17 00:00:00 2001 From: Lucas Wojciechowski Date: Mon, 26 Feb 2018 11:02:14 -0800 Subject: [PATCH 19/24] Remove bool Filter::operator()(const GeometryTileFeature&) const --- benchmark/fixtures/api/cache.db | Bin 1298432 -> 1298432 bytes benchmark/parse/filter.benchmark.cpp | 3 +- include/mbgl/style/filter.hpp | 2 - src/mbgl/geometry/feature_index.cpp | 2 +- src/mbgl/style/filter.cpp | 4 - src/mbgl/tile/custom_geometry_tile.cpp | 2 +- src/mbgl/tile/geojson_tile.cpp | 2 +- src/mbgl/tile/geometry_tile.cpp | 2 +- test/style/filter.test.cpp | 127 +++++++++++-------------- 9 files changed, 60 insertions(+), 84 deletions(-) diff --git a/benchmark/fixtures/api/cache.db b/benchmark/fixtures/api/cache.db index 6a1d60421f8d0ff10f9ec399642b39cf7af8c148..10452b2714806432e06447a7d7c982a599e80a3c 100644 GIT binary patch delta 2720 zcmZ{lZE#f88OP7L_uPHoy_p%S)gLl7L29iLF>=#uhY9vC|hI z*-f$v0m*f#0SmfTVT)+Ux+E7xN2eXXw2s}5Gj;q@%fz8GG9B}w(+N#R`v2d3As=jJ zp5OC4=RW7$=jH58ceY@ao6@-6bh=jPT*qmxaE(%Wu191^j zW5RXX{2;VOjb-;fseC=Fl)I*;MkXK1^#|HzIJ)PlpY8o&=VL$Lx%=@)ckX>GH59B( zeGzQ%B_5RSn%Y{~r{`V_Reyb8sBO!}t!>TGx~DpK*Zb;onx{*t&@uUZt}kF}%AAl( za>qk|kX^`9ep6$fI%HwlI!gC7;(KLl;|+_&)zWq1o~1j7h!XRN*~D$c0^(Mp-T-uN zTS)gCN~AswuVM~jK37-Kx`|lfMAEv0SV_zx)(~rn8e%!Ip4d>{QAhU{ViU2MXeMqZ zT8LJsPFh=(s1)LM$G?Hp*@}x@bNPH9aUZdtSWVnRtRrHClmGp+?j@RttwfSINE{;i zhy>GgIiP!l7$ACx=ZF;1tGdA7(0u+zUTo4!Tzi4|Epd`KHybXCVodeL%d3y zCgQ|lB2&(zo9U}d>lMd$0*UXpALGy^&WBxK% z5o$?%no+@-%PH<$34yR_sg8Ai#_ONQpg zn;4v{`|R&A)hzVptktB?Jh4m*inx@9+L{e`m1ob+jz^yS#odcj@!d=9qx0mRK#047 zXpfYGLJnrFBhoy%%NGz2Nkg<VS_!q%$e0St> zNE764$af&iAm4`E1-TRQEyTVHc?&WMNkdLRjzb=R%!F)%v_ZDp?E^An%4S;)$m^!u zW~*-M6}E~&YipjfRjZsdF(=VttFVS=3v4y4LvI>Ubm!TsgQ3x#wt5($yk_$oZS@cN zl_~exD$Uq>TfNV7Z-a)fH3?gd7tg)jR(XBOc|{$Lb|dWm5v8E+K{VYvkOci0-k_$; z5F4o*Cxme#6bj7FTKz^v--q)eL9qkf+}=OvLpyD>^QSIZW4~}(`&=I#*7R}h>0jHb zQ7crlzF6r>RAu1o)u`B*GJVdF#8n}#TGr*N_^Ew8KMslbCXOlb)@IJ>=wY?ZZoRCZ z4;dJR=t2ERiXG}kemH|2SbGiUsX{l_eS!*$cH;N8iW-F!wjc#|a08mf^1h3nsEOKe z>8@zLL&A3`6v|qYuBybD?Y$Z@S<6g%|Ll~AOgi-c|76B3@wB_|N7%H{N&nqPi|N-7 z#yX9UR)P0oh4nP^*Ey3DK#@%qDo=@!#Eb z?llqeTOxGWeLJ+GHlF**6DpT&w)2)hs+_?FKI^tg9 z7GeX@M>G=am8ciuVVVyS4-gyEC#Ri{cGKKLtS0)2D6yQFPeh2#M67cARUED%_7eMu z{lqX4XPPx7aQHMadb6aJD4SFiE6;NI7sOM<7)$pets}(q#0$jHO2Fqh93);MUM3zP zULlSVL&O1Myt3_A9G)Zwh%>-4@fyt$B1@dEBzKm>6U1>ML!2TC#6-z2f3%^+syWYz zH`9}it?5vc(}|H}d*q$OH;ARgUBowuZxMGB?4azzJEk^kv%ZuzQRua(#nQyQmi!wH?~n5qaG$2WhU_&})=uO}bmKS2QA(uR}T^ z9S{VRZIC69J0Oc8iy#Z(`xfLPBo8?ZNkg82+zM%iv_R%Ul4fh49Cu}l(O2bHt|jIg z-EJY3}x3p0@sOd&BR|ZmboLqRN{`GES+?^UWWQS!-(KjKVp~zAD#F zC=^BRL1^Z+%%~QQm*WWQhU=7?CO#A5vsCJDUi|2jgFmbj@wIGQ;=;O2>MwdxdS=hk zO!S}%n#Vq}Ga-EovKd4ejWBUEh2d5jb(tBjGU_Vz-U-F!^+tONm;W=i+=qVCb+u8W zI2o^B48YsI;np$<%NY^1{muwJh^XVmvo*utMI!f?5_#);r9?83-MTf?_mMr_>_4b> z%f=np7fu}CG-SbbC0&Q!b;>y}F#q1KX4-ldoSs5=nASQ}!3X^S#?+gTHub|sw=mBW z@a;3IkG>D$YU)}uUs^w=H)4y8=||8*x^nInT+a{tQ`no)E8L~f?zcTTG@;P9!uDy} z@)8%PTiKBoMh zQSB_NFnYUxK}hjS%BB7&$L*reljEo+FLPjAZFjtzF8y?o`w$oL#_c}s$^8iAz;y(# zfchK`r#^umIa9qE#gf1LKzuueZztZ}HeY_{=5~K3^P&H-y4y-^vpyI4nDu!s^$)8) z@wT!(5$_5}%ffHP@ZPPN9WQ&2nhRI_y;eh4;*B<|YI*3B&Z*^XAN#KaG(K68LVu8? zv3EH)zwxxumsn*w9L8rw`xDst2_5(nPp%LN*e%kaU4h9%GKVSzCfnqxan-^qRJIBFl64t*mwy;9;+Y0foi7Skt7hLskr@8M)T;gh zC;l3tgI~GhRyOG2FPqV^;QOV31F6(vW1S0HsA74pKb2a7hZMXvKpF=02SF!KL7v# diff --git a/benchmark/parse/filter.benchmark.cpp b/benchmark/parse/filter.benchmark.cpp index 20267df867b..e4cf6352567 100644 --- a/benchmark/parse/filter.benchmark.cpp +++ b/benchmark/parse/filter.benchmark.cpp @@ -24,9 +24,10 @@ static void Parse_Filter(benchmark::State& state) { static void Parse_EvaluateFilter(benchmark::State& state) { const style::Filter filter = parse(R"FILTER(["==", "foo", "bar"])FILTER"); const StubGeometryTileFeature feature = { {}, FeatureType::Unknown , {}, {{ "foo", std::string("bar") }} }; + const style::expression::EvaluationContext context = { &feature }; while (state.KeepRunning()) { - filter(feature); + filter(context); } } diff --git a/include/mbgl/style/filter.hpp b/include/mbgl/style/filter.hpp index 040f7808a72..9347ae93810 100644 --- a/include/mbgl/style/filter.hpp +++ b/include/mbgl/style/filter.hpp @@ -274,8 +274,6 @@ using FilterBase = variant< class Filter : public FilterBase { public: using FilterBase::FilterBase; - - bool operator()(const GeometryTileFeature&) const; bool operator()(expression::EvaluationContext context) const; }; diff --git a/src/mbgl/geometry/feature_index.cpp b/src/mbgl/geometry/feature_index.cpp index 3b5e12b54a7..8b397fad480 100644 --- a/src/mbgl/geometry/feature_index.cpp +++ b/src/mbgl/geometry/feature_index.cpp @@ -125,7 +125,7 @@ void FeatureIndex::addFeature( continue; } - if (options.filter && !(*options.filter)(*geometryTileFeature)) { + if (options.filter && !(*options.filter)(style::expression::EvaluationContext { geometryTileFeature.get() })) { continue; } diff --git a/src/mbgl/style/filter.cpp b/src/mbgl/style/filter.cpp index 4aa8d9622e6..99d91cbcb43 100644 --- a/src/mbgl/style/filter.cpp +++ b/src/mbgl/style/filter.cpp @@ -9,9 +9,5 @@ bool Filter::operator()(expression::EvaluationContext context) const { return FilterBase::visit(*this, FilterEvaluator { context }); } -bool Filter::operator()(const GeometryTileFeature& feature) const { - return operator()(expression::EvaluationContext { &feature }); -} - } // namespace style } // namespace mbgl diff --git a/src/mbgl/tile/custom_geometry_tile.cpp b/src/mbgl/tile/custom_geometry_tile.cpp index 33962ad87d4..e431b3c355f 100644 --- a/src/mbgl/tile/custom_geometry_tile.cpp +++ b/src/mbgl/tile/custom_geometry_tile.cpp @@ -79,7 +79,7 @@ void CustomGeometryTile::querySourceFeatures( auto feature = layer->getFeature(i); // Apply filter, if any - if (queryOptions.filter && !(*queryOptions.filter)(*feature)) { + if (queryOptions.filter && !(*queryOptions.filter)(style::expression::EvaluationContext { feature.get() })) { continue; } diff --git a/src/mbgl/tile/geojson_tile.cpp b/src/mbgl/tile/geojson_tile.cpp index bbec8999508..f63d4e315ce 100644 --- a/src/mbgl/tile/geojson_tile.cpp +++ b/src/mbgl/tile/geojson_tile.cpp @@ -30,7 +30,7 @@ void GeoJSONTile::querySourceFeatures( auto feature = layer->getFeature(i); // Apply filter, if any - if (options.filter && !(*options.filter)(*feature)) { + if (options.filter && !(*options.filter)(style::expression::EvaluationContext { feature.get() })) { continue; } diff --git a/src/mbgl/tile/geometry_tile.cpp b/src/mbgl/tile/geometry_tile.cpp index a58c7440652..30730501020 100644 --- a/src/mbgl/tile/geometry_tile.cpp +++ b/src/mbgl/tile/geometry_tile.cpp @@ -281,7 +281,7 @@ void GeometryTile::querySourceFeatures( auto feature = layer->getFeature(i); // Apply filter, if any - if (options.filter && !(*options.filter)(*feature)) { + if (options.filter && !(*options.filter)(style::expression::EvaluationContext { feature.get() })) { continue; } diff --git a/test/style/filter.test.cpp b/test/style/filter.test.cpp index 7994059f529..ac911829349 100644 --- a/test/style/filter.test.cpp +++ b/test/style/filter.test.cpp @@ -11,110 +11,91 @@ using namespace mbgl; using namespace mbgl::style; -Filter parse(const char * expression) { +bool filter(const char * json, const PropertyMap& featureProperties = {{}}, optional featureId = {}, FeatureType featureType = FeatureType::Point, GeometryCollection featureGeometry = {}) { conversion::Error error; - optional filter = conversion::convertJSON(expression, error); + optional filter = conversion::convertJSON(json, error); EXPECT_TRUE(bool(filter)); - return *filter; -} - -StubGeometryTileFeature feature(const PropertyMap& properties) { - return StubGeometryTileFeature { properties }; + EXPECT_EQ(error.message, ""); + + StubGeometryTileFeature feature { featureId, featureType, featureGeometry, featureProperties }; + expression::EvaluationContext context = { &feature }; + + return (*filter)(context); } TEST(Filter, EqualsString) { - Filter f = parse(R"(["==", "foo", "bar"])"); - ASSERT_TRUE(f(feature({{ "foo", std::string("bar") }}))); - ASSERT_FALSE(f(feature({{ "foo", std::string("baz") }}))); + auto f = R"(["==", "foo", "bar"])"; + ASSERT_TRUE(filter(f, {{ "foo", std::string("bar") }})); + ASSERT_FALSE(filter(f, {{ "foo", std::string("baz") }})); } TEST(Filter, EqualsNumber) { - Filter f = parse(R"(["==", "foo", 0])"); - ASSERT_TRUE(f(feature({{ "foo", int64_t(0) }}))); - ASSERT_TRUE(f(feature({{ "foo", uint64_t(0) }}))); - ASSERT_TRUE(f(feature({{ "foo", double(0) }}))); - ASSERT_FALSE(f(feature({{ "foo", int64_t(1) }}))); - ASSERT_FALSE(f(feature({{ "foo", uint64_t(1) }}))); - ASSERT_FALSE(f(feature({{ "foo", double(1) }}))); - ASSERT_FALSE(f(feature({{ "foo", std::string("0") }}))); - ASSERT_FALSE(f(feature({{ "foo", false }}))); - ASSERT_FALSE(f(feature({{ "foo", true }}))); - ASSERT_FALSE(f(feature({{ "foo", mapbox::geometry::null_value }}))); - ASSERT_FALSE(f(feature({{}}))); + auto f = R"(["==", "foo", 0])"; + ASSERT_TRUE(filter(f, {{ "foo", int64_t(0) }})); + ASSERT_TRUE(filter(f, {{ "foo", uint64_t(0) }})); + ASSERT_TRUE(filter(f, {{ "foo", double(0) }})); + ASSERT_FALSE(filter(f, {{ "foo", int64_t(1) }})); + ASSERT_FALSE(filter(f, {{ "foo", uint64_t(1) }})); + ASSERT_FALSE(filter(f, {{ "foo", double(1) }})); + ASSERT_FALSE(filter(f, {{ "foo", std::string("0") }})); + ASSERT_FALSE(filter(f, {{ "foo", false }})); + ASSERT_FALSE(filter(f, {{ "foo", true }})); + ASSERT_FALSE(filter(f, {{ "foo", mapbox::geometry::null_value }})); + ASSERT_FALSE(filter(f, {{}})); } TEST(Filter, EqualsType) { - Filter f = parse(R"(["==", "$type", "LineString"])"); - ASSERT_FALSE(f(StubGeometryTileFeature({}, FeatureType::Point, {}, {{}}))); - ASSERT_TRUE(f(StubGeometryTileFeature({}, FeatureType::LineString, {}, {{}}))); + auto f = R"(["==", "$type", "LineString"])"; + ASSERT_FALSE(filter(f, {{}}, {}, FeatureType::Point, {})); + ASSERT_TRUE(filter(f, {{}}, {}, FeatureType::LineString, {})); } TEST(Filter, InType) { - Filter f = parse(R"(["in", "$type", "LineString", "Polygon"])"); - ASSERT_FALSE(f(StubGeometryTileFeature({}, FeatureType::Point, {}, {{}}))); - ASSERT_TRUE(f(StubGeometryTileFeature({}, FeatureType::LineString, {}, {{}}))); - ASSERT_TRUE(f(StubGeometryTileFeature({}, FeatureType::Polygon, {}, {{}}))); + auto f = R"(["in", "$type", "LineString", "Polygon"])"; + ASSERT_FALSE(filter(f, {{}}, {}, FeatureType::Point)); + ASSERT_TRUE(filter(f, {{}}, {}, FeatureType::LineString)); + ASSERT_TRUE(filter(f, {{}}, {}, FeatureType::Polygon)); } TEST(Filter, Any) { - ASSERT_FALSE(parse("[\"any\"]")(feature({{}}))); - ASSERT_TRUE(parse("[\"any\", [\"==\", \"foo\", 1]]")( - feature({{ std::string("foo"), int64_t(1) }}))); - ASSERT_FALSE(parse("[\"any\", [\"==\", \"foo\", 0]]")( - feature({{ std::string("foo"), int64_t(1) }}))); - ASSERT_TRUE(parse("[\"any\", [\"==\", \"foo\", 0], [\"==\", \"foo\", 1]]")( - feature({{ std::string("foo"), int64_t(1) }}))); + ASSERT_FALSE(filter("[\"any\"]")); + ASSERT_TRUE(filter("[\"any\", [\"==\", \"foo\", 1]]", {{ std::string("foo"), int64_t(1) }})); + ASSERT_FALSE(filter("[\"any\", [\"==\", \"foo\", 0]]", {{ std::string("foo"), int64_t(1) }})); + ASSERT_TRUE(filter("[\"any\", [\"==\", \"foo\", 0], [\"==\", \"foo\", 1]]", {{ std::string("foo"), int64_t(1) }})); } TEST(Filter, All) { - ASSERT_TRUE(parse("[\"all\"]")(feature({{}}))); - ASSERT_TRUE(parse("[\"all\", [\"==\", \"foo\", 1]]")( - feature({{ std::string("foo"), int64_t(1) }}))); - ASSERT_FALSE(parse("[\"all\", [\"==\", \"foo\", 0]]")( - feature({{ std::string("foo"), int64_t(1) }}))); - ASSERT_FALSE(parse("[\"all\", [\"==\", \"foo\", 0], [\"==\", \"foo\", 1]]")( - feature({{ std::string("foo"), int64_t(1) }}))); + ASSERT_TRUE(filter("[\"all\"]", {{}})); + ASSERT_TRUE(filter("[\"all\", [\"==\", \"foo\", 1]]", {{ std::string("foo"), int64_t(1) }})); + ASSERT_FALSE(filter("[\"all\", [\"==\", \"foo\", 0]]", {{ std::string("foo"), int64_t(1) }})); + ASSERT_FALSE(filter("[\"all\", [\"==\", \"foo\", 0], [\"==\", \"foo\", 1]]", {{ std::string("foo"), int64_t(1) }})); } TEST(Filter, None) { - ASSERT_TRUE(parse("[\"none\"]")(feature({{}}))); - ASSERT_FALSE(parse("[\"none\", [\"==\", \"foo\", 1]]")( - feature({{ std::string("foo"), int64_t(1) }}))); - ASSERT_TRUE(parse("[\"none\", [\"==\", \"foo\", 0]]")( - feature({{ std::string("foo"), int64_t(1) }}))); - ASSERT_FALSE(parse("[\"none\", [\"==\", \"foo\", 0], [\"==\", \"foo\", 1]]")( - feature({{ std::string("foo"), int64_t(1) }}))); + ASSERT_TRUE(filter("[\"none\"]")); + ASSERT_FALSE(filter("[\"none\", [\"==\", \"foo\", 1]]", {{ std::string("foo"), int64_t(1) }})); + ASSERT_TRUE(filter("[\"none\", [\"==\", \"foo\", 0]]", {{ std::string("foo"), int64_t(1) }})); + ASSERT_FALSE(filter("[\"none\", [\"==\", \"foo\", 0], [\"==\", \"foo\", 1]]", {{ std::string("foo"), int64_t(1) }})); } TEST(Filter, Has) { - ASSERT_TRUE(parse("[\"has\", \"foo\"]")( - feature({{ std::string("foo"), int64_t(1) }}))); - ASSERT_TRUE(parse("[\"has\", \"foo\"]")( - feature({{ std::string("foo"), int64_t(0) }}))); - ASSERT_TRUE(parse("[\"has\", \"foo\"]")( - feature({{ std::string("foo"), false }}))); - ASSERT_FALSE(parse("[\"has\", \"foo\"]")( - feature({{}}))); + ASSERT_TRUE(filter("[\"has\", \"foo\"]", {{ std::string("foo"), int64_t(1) }})); + 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\"]")); } TEST(Filter, NotHas) { - ASSERT_FALSE(parse("[\"!has\", \"foo\"]")( - feature({{ std::string("foo"), int64_t(1) }}))); - ASSERT_FALSE(parse("[\"!has\", \"foo\"]")( - feature({{ std::string("foo"), int64_t(0) }}))); - ASSERT_FALSE(parse("[\"!has\", \"foo\"]")( - feature({{ std::string("foo"), false }}))); - ASSERT_TRUE(parse("[\"!has\", \"foo\"]")( - feature({{}}))); + ASSERT_FALSE(filter("[\"!has\", \"foo\"]", {{ std::string("foo"), int64_t(1) }})); + ASSERT_FALSE(filter("[\"!has\", \"foo\"]", {{ std::string("foo"), int64_t(0) }})); + ASSERT_FALSE(filter("[\"!has\", \"foo\"]", {{ std::string("foo"), false }})); + ASSERT_TRUE(filter("[\"!has\", \"foo\"]")); } TEST(Filter, ID) { - StubGeometryTileFeature feature1 { FeatureIdentifier{ uint64_t{ 1234 } }, {}, {}, {{}}}; - - ASSERT_TRUE(parse("[\"==\", \"$id\", 1234]")(feature1)); - ASSERT_FALSE(parse("[\"==\", \"$id\", \"1234\"]")(feature1)); + FeatureIdentifier id1 { uint64_t{ 1234 } }; + ASSERT_TRUE(filter("[\"==\", \"$id\", 1234]", {{}}, id1)); + ASSERT_FALSE(filter("[\"==\", \"$id\", \"1234\"]", {{}}, id1)); - StubGeometryTileFeature feature2 { {{ "id", uint64_t(1234) }} }; - - ASSERT_FALSE(parse("[\"==\", \"$id\", 1234]")(feature2)); + ASSERT_FALSE(filter("[\"==\", \"$id\", 1234]", {{ "id", uint64_t(1234) }})); } From 777dd63a08f17832cc340e30f8629a3a7a000af8 Mon Sep 17 00:00:00 2001 From: Lucas Wojciechowski Date: Tue, 27 Feb 2018 10:42:15 -0800 Subject: [PATCH 20/24] Ensure the map zoom is passed into filtering operations wherever applicable --- src/mbgl/geometry/feature_index.cpp | 2 +- src/mbgl/layout/symbol_layout.cpp | 2 +- src/mbgl/tile/custom_geometry_tile.cpp | 2 +- src/mbgl/tile/geojson_tile.cpp | 2 +- src/mbgl/tile/geometry_tile.cpp | 2 +- src/mbgl/tile/geometry_tile_worker.cpp | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/mbgl/geometry/feature_index.cpp b/src/mbgl/geometry/feature_index.cpp index 8b397fad480..6fb0d5e4465 100644 --- a/src/mbgl/geometry/feature_index.cpp +++ b/src/mbgl/geometry/feature_index.cpp @@ -125,7 +125,7 @@ void FeatureIndex::addFeature( continue; } - if (options.filter && !(*options.filter)(style::expression::EvaluationContext { geometryTileFeature.get() })) { + if (options.filter && !(*options.filter)(style::expression::EvaluationContext { static_cast(tileID.z), geometryTileFeature.get() })) { continue; } diff --git a/src/mbgl/layout/symbol_layout.cpp b/src/mbgl/layout/symbol_layout.cpp index f455a9ddef6..3bf85407c6b 100644 --- a/src/mbgl/layout/symbol_layout.cpp +++ b/src/mbgl/layout/symbol_layout.cpp @@ -100,7 +100,7 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters, const size_t featureCount = sourceLayer->featureCount(); for (size_t i = 0; i < featureCount; ++i) { auto feature = sourceLayer->getFeature(i); - if (!leader.filter(expression::EvaluationContext { feature.get() })) + if (!leader.filter(expression::EvaluationContext { this->zoom, feature.get() })) continue; SymbolFeature ft(std::move(feature)); diff --git a/src/mbgl/tile/custom_geometry_tile.cpp b/src/mbgl/tile/custom_geometry_tile.cpp index e431b3c355f..a2fefcfa9f6 100644 --- a/src/mbgl/tile/custom_geometry_tile.cpp +++ b/src/mbgl/tile/custom_geometry_tile.cpp @@ -79,7 +79,7 @@ void CustomGeometryTile::querySourceFeatures( auto feature = layer->getFeature(i); // Apply filter, if any - if (queryOptions.filter && !(*queryOptions.filter)(style::expression::EvaluationContext { feature.get() })) { + if (queryOptions.filter && !(*queryOptions.filter)(style::expression::EvaluationContext { static_cast(id.overscaledZ), feature.get() })) { continue; } diff --git a/src/mbgl/tile/geojson_tile.cpp b/src/mbgl/tile/geojson_tile.cpp index f63d4e315ce..f211c035699 100644 --- a/src/mbgl/tile/geojson_tile.cpp +++ b/src/mbgl/tile/geojson_tile.cpp @@ -30,7 +30,7 @@ void GeoJSONTile::querySourceFeatures( auto feature = layer->getFeature(i); // Apply filter, if any - if (options.filter && !(*options.filter)(style::expression::EvaluationContext { feature.get() })) { + if (options.filter && !(*options.filter)(style::expression::EvaluationContext { static_cast(this->id.overscaledZ), feature.get() })) { continue; } diff --git a/src/mbgl/tile/geometry_tile.cpp b/src/mbgl/tile/geometry_tile.cpp index 30730501020..82d0c918061 100644 --- a/src/mbgl/tile/geometry_tile.cpp +++ b/src/mbgl/tile/geometry_tile.cpp @@ -281,7 +281,7 @@ void GeometryTile::querySourceFeatures( auto feature = layer->getFeature(i); // Apply filter, if any - if (options.filter && !(*options.filter)(style::expression::EvaluationContext { feature.get() })) { + if (options.filter && !(*options.filter)(style::expression::EvaluationContext { static_cast(this->id.overscaledZ), feature.get() })) { continue; } diff --git a/src/mbgl/tile/geometry_tile_worker.cpp b/src/mbgl/tile/geometry_tile_worker.cpp index 19e32a46bf4..8b6f1f63bf5 100644 --- a/src/mbgl/tile/geometry_tile_worker.cpp +++ b/src/mbgl/tile/geometry_tile_worker.cpp @@ -339,7 +339,7 @@ void GeometryTileWorker::redoLayout() { for (std::size_t i = 0; !obsolete && i < geometryLayer->featureCount(); i++) { std::unique_ptr feature = geometryLayer->getFeature(i); - if (!filter(expression::EvaluationContext { feature.get() })) + if (!filter(expression::EvaluationContext { static_cast(this->id.overscaledZ), feature.get() })) continue; GeometryCollection geometries = feature->getGeometries(); From bd9e8b615013af903884249da4054f969c8cfadc Mon Sep 17 00:00:00 2001 From: Lucas Wojciechowski Date: Wed, 28 Feb 2018 12:40:55 -0800 Subject: [PATCH 21/24] Add expression filter tests --- test/style/filter.test.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/style/filter.test.cpp b/test/style/filter.test.cpp index ac911829349..6f261c43ec3 100644 --- a/test/style/filter.test.cpp +++ b/test/style/filter.test.cpp @@ -99,3 +99,13 @@ TEST(Filter, ID) { ASSERT_FALSE(filter("[\"==\", \"$id\", 1234]", {{ "id", uint64_t(1234) }})); } + +TEST(Filter, Expression) { + ASSERT_TRUE(filter("[\"==\", [\"+\", 1, 1], 2]")); + ASSERT_FALSE(filter("[\"==\", [\"+\", 1, 1], 4]")); +} + +TEST(Filter, PropertyExpression) { + ASSERT_TRUE(filter("[\"==\", [\"get\", \"two\"], 2]", {{"two", int64_t(2)}})); + ASSERT_FALSE(filter("[\"==\", [\"get\", \"two\"], 4]", {{"two", int64_t(2)}})); +} From c3eafed00c32bfcbd64475175ebc0c97df0dba9f Mon Sep 17 00:00:00 2001 From: Lucas Wojciechowski Date: Fri, 2 Mar 2018 13:56:34 -0800 Subject: [PATCH 22/24] Addressed PR feedback --- include/mbgl/style/filter.hpp | 4 ++-- src/mbgl/style/conversion/filter.cpp | 10 +++++++--- src/mbgl/style/filter.cpp | 2 +- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/include/mbgl/style/filter.hpp b/include/mbgl/style/filter.hpp index 9347ae93810..00a81636677 100644 --- a/include/mbgl/style/filter.hpp +++ b/include/mbgl/style/filter.hpp @@ -236,7 +236,7 @@ class NotHasIdentifierFilter { class ExpressionFilter { public: - std::shared_ptr expression; + std::shared_ptr expression; friend bool operator==(const ExpressionFilter& lhs, const ExpressionFilter& rhs) { return *(lhs.expression) == *(rhs.expression); @@ -274,7 +274,7 @@ using FilterBase = variant< class Filter : public FilterBase { public: using FilterBase::FilterBase; - bool operator()(expression::EvaluationContext context) const; + bool operator()(const expression::EvaluationContext &context) const; }; } // namespace style diff --git a/src/mbgl/style/conversion/filter.cpp b/src/mbgl/style/conversion/filter.cpp index 75f6fffbae4..fba149da12d 100644 --- a/src/mbgl/style/conversion/filter.cpp +++ b/src/mbgl/style/conversion/filter.cpp @@ -18,9 +18,13 @@ static bool isExpressionFilter(const Convertible& filter) { optional op = toString(arrayMember(filter, 0)); - if (*op == "has") { - auto operand = toString(arrayMember(filter, 1)); - return arrayLength(filter) >= 2 && operand && *operand != "$id" && *operand != "$type"; + if (!op) { + return false; + + } else if (*op == "has") { + if (arrayLength(filter) < 2) return false; + optional operand = toString(arrayMember(filter, 1)); + return operand && *operand != "$id" && *operand != "$type"; } else if (*op == "in" || *op == "!in" || *op == "!has" || *op == "none") { return false; diff --git a/src/mbgl/style/filter.cpp b/src/mbgl/style/filter.cpp index 99d91cbcb43..51aa6bcf82b 100644 --- a/src/mbgl/style/filter.cpp +++ b/src/mbgl/style/filter.cpp @@ -5,7 +5,7 @@ namespace mbgl { namespace style { -bool Filter::operator()(expression::EvaluationContext context) const { +bool Filter::operator()(const expression::EvaluationContext &context) const { return FilterBase::visit(*this, FilterEvaluator { context }); } From a50c9f8ad508b506f299cb6d6cde62a5aa6960ac Mon Sep 17 00:00:00 2001 From: Lucas Wojciechowski Date: Fri, 2 Mar 2018 16:07:07 -0800 Subject: [PATCH 23/24] Implement `NSPredicate *operator()(mbgl::style::ExpressionFilter filter)` --- platform/darwin/src/NSPredicate+MGLAdditions.mm | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/platform/darwin/src/NSPredicate+MGLAdditions.mm b/platform/darwin/src/NSPredicate+MGLAdditions.mm index 1a643d41d0f..63c8307803a 100644 --- a/platform/darwin/src/NSPredicate+MGLAdditions.mm +++ b/platform/darwin/src/NSPredicate+MGLAdditions.mm @@ -1,6 +1,7 @@ #import "NSPredicate+MGLAdditions.h" #import "MGLValueEvaluator.h" +#import "MGLStyleValue_Private.h" class FilterEvaluator { public: @@ -196,7 +197,8 @@ } NSPredicate *operator()(mbgl::style::ExpressionFilter filter) { - return nil; + id jsonObject = MGLJSONObjectFromMBGLExpression(*filter.expression); + return [NSPredicate mgl_predicateWithJSONObject:jsonObject]; } }; From 594bfd1615af3dd909700a820f03ecb27c8c8c04 Mon Sep 17 00:00:00 2001 From: Lucas Wojciechowski Date: Wed, 7 Mar 2018 11:45:58 -0800 Subject: [PATCH 24/24] Fix formatting& nit --- include/mbgl/style/filter.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/mbgl/style/filter.hpp b/include/mbgl/style/filter.hpp index 00a81636677..ccf8dce1883 100644 --- a/include/mbgl/style/filter.hpp +++ b/include/mbgl/style/filter.hpp @@ -274,7 +274,7 @@ using FilterBase = variant< class Filter : public FilterBase { public: using FilterBase::FilterBase; - bool operator()(const expression::EvaluationContext &context) const; + bool operator()(const expression::EvaluationContext& context) const; }; } // namespace style