[core] Add expression filter support (#11251)
* WIP * WIP * WIP * Remove Filter::operator()(const Feature&) * WIP * WIP * WIP * WIP * Hook up expression filter evaluator * Replace `shared_ptr` with &reference * Fill in implementation of `void operator()(const ExpressionFilter&)` * Fix failing tests * Switch back to a shared_ptr per chat with @anandthakker * Fix benchmark compilation * Shot in the dark to fix CI * Shot in the dark to fix CI (part 2) * Shot in the dark to fix CI (part 3) * In src/mbgl/style/conversion/filter.cpp, add a port of isExpressionFilter and use it to decide in Converter<Filter>::operator() whether to parse the incoming JSON as an ExpressionFilter or one of the legacy filter types * Remove bool Filter::operator()(const GeometryTileFeature&) const * Ensure the map zoom is passed into filtering operations wherever applicable * Add expression filter tests * Addressed PR feedback * Implement `NSPredicate *operator()(mbgl::style::ExpressionFilter filter)` * Fix formatting& nit
This commit is contained in:
committed by
Tobrun
parent
6179110b6b
commit
8a3deab733
@@ -1,6 +1,7 @@
|
||||
#include <mbgl/test/util.hpp>
|
||||
#include <mbgl/util/feature.hpp>
|
||||
#include <mbgl/util/geometry.hpp>
|
||||
#include <mbgl/test/stub_geometry_tile_feature.hpp>
|
||||
|
||||
#include <mbgl/style/filter.hpp>
|
||||
#include <mbgl/style/filter_evaluator.hpp>
|
||||
@@ -10,114 +11,101 @@
|
||||
using namespace mbgl;
|
||||
using namespace mbgl::style;
|
||||
|
||||
Filter parse(const char * expression) {
|
||||
bool filter(const char * json, const PropertyMap& featureProperties = {{}}, optional<FeatureIdentifier> featureId = {}, FeatureType featureType = FeatureType::Point, GeometryCollection featureGeometry = {}) {
|
||||
conversion::Error error;
|
||||
optional<Filter> filter = conversion::convertJSON<Filter>(expression, error);
|
||||
optional<Filter> filter = conversion::convertJSON<Filter>(json, error);
|
||||
EXPECT_TRUE(bool(filter));
|
||||
return *filter;
|
||||
}
|
||||
|
||||
Feature feature(const PropertyMap& properties, const Geometry<double>& geometry = Point<double>()) {
|
||||
Feature result { geometry };
|
||||
result.properties = properties;
|
||||
return result;
|
||||
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(feature({{}}, Point<double>())));
|
||||
ASSERT_TRUE(f(feature({{}}, LineString<double>())));
|
||||
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(feature({{}}, Point<double>())));
|
||||
ASSERT_TRUE(f(feature({{}}, LineString<double>())));
|
||||
ASSERT_TRUE(f(feature({{}}, Polygon<double>())));
|
||||
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) {
|
||||
Feature feature1 { Point<double>() };
|
||||
feature1.id = { uint64_t(1234) };
|
||||
|
||||
ASSERT_TRUE(parse("[\"==\", \"$id\", 1234]")(feature1));
|
||||
ASSERT_FALSE(parse("[\"==\", \"$id\", \"1234\"]")(feature1));
|
||||
|
||||
Feature feature2 { Point<double>() };
|
||||
feature2.properties["id"] = { uint64_t(1234) };
|
||||
|
||||
ASSERT_FALSE(parse("[\"==\", \"$id\", 1234]")(feature2));
|
||||
FeatureIdentifier id1 { uint64_t{ 1234 } };
|
||||
ASSERT_TRUE(filter("[\"==\", \"$id\", 1234]", {{}}, id1));
|
||||
ASSERT_FALSE(filter("[\"==\", \"$id\", \"1234\"]", {{}}, id1));
|
||||
|
||||
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)}}));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user