summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/mbgl/style/filter.hpp14
-rw-r--r--src/mbgl/style/filter_evaluator.hpp8
-rw-r--r--src/mbgl/style/style_parser.cpp25
-rw-r--r--test/style/filter.cpp22
4 files changed, 67 insertions, 2 deletions
diff --git a/src/mbgl/style/filter.hpp b/src/mbgl/style/filter.hpp
index 0a1dcbd17a..6ad6969fbf 100644
--- a/src/mbgl/style/filter.hpp
+++ b/src/mbgl/style/filter.hpp
@@ -20,7 +20,9 @@ typedef variant<
class NotInFilter,
class AnyFilter,
class AllFilter,
- class NoneFilter
+ class NoneFilter,
+ class HasFilter,
+ class NotHasFilter
> Filter;
class NullFilter {};
@@ -88,4 +90,14 @@ public:
std::vector<Filter> filters;
};
+class HasFilter {
+public:
+ std::string key;
+};
+
+class NotHasFilter {
+public:
+ std::string key;
+};
+
} // namespace mbgl
diff --git a/src/mbgl/style/filter_evaluator.hpp b/src/mbgl/style/filter_evaluator.hpp
index ab550ee26c..b607a0c658 100644
--- a/src/mbgl/style/filter_evaluator.hpp
+++ b/src/mbgl/style/filter_evaluator.hpp
@@ -97,6 +97,14 @@ public:
return true;
}
+ bool operator()(const HasFilter& filter) const {
+ return bool(getValue(filter.key));
+ }
+
+ bool operator()(const NotHasFilter& filter) const {
+ return !getValue(filter.key);
+ }
+
private:
optional<Value> getValue(const std::string& key) const {
return key == "$type"
diff --git a/src/mbgl/style/style_parser.cpp b/src/mbgl/style/style_parser.cpp
index 27edf15f97..4f081e7d85 100644
--- a/src/mbgl/style/style_parser.cpp
+++ b/src/mbgl/style/style_parser.cpp
@@ -534,6 +534,25 @@ Value parseValue(const JSValue& value) {
}
template <class Expression>
+Filter parseUnaryFilter(const JSValue& value) {
+ Filter empty;
+
+ if (value.Size() < 2) {
+ Log::Warning(Event::ParseStyle, "filter expression must have 2 elements");
+ return empty;
+ }
+
+ if (!value[1u].IsString()) {
+ Log::Warning(Event::ParseStyle, "filter expression key must be a string");
+ return empty;
+ }
+
+ Expression expression;
+ expression.key = { value[1u].GetString(), value[1u].GetStringLength() };
+ return expression;
+}
+
+template <class Expression>
Filter parseBinaryFilter(const JSValue& value) {
Filter empty;
@@ -635,8 +654,12 @@ Filter parseFilter(const JSValue& value) {
return parseCompoundFilter<AnyFilter>(value);
} else if (op == "none") {
return parseCompoundFilter<NoneFilter>(value);
+ } else if (op == "has") {
+ return parseUnaryFilter<HasFilter>(value);
+ } else if (op == "!has") {
+ return parseUnaryFilter<NotHasFilter>(value);
} else {
- Log::Warning(Event::ParseStyle, "filter operator must be one of \"==\", \"!=\", \">\", \">=\", \"<\", \"<=\", \"in\", \"!in\", \"all\", \"any\", \"none\"");
+ Log::Warning(Event::ParseStyle, "filter operator must be one of \"==\", \"!=\", \">\", \">=\", \"<\", \"<=\", \"in\", \"!in\", \"all\", \"any\", \"none\", \"has\", or \"!has\"");
return empty;
}
}
diff --git a/test/style/filter.cpp b/test/style/filter.cpp
index da0e5ff4a3..f258c31492 100644
--- a/test/style/filter.cpp
+++ b/test/style/filter.cpp
@@ -114,3 +114,25 @@ TEST(Filter, None) {
ASSERT_FALSE(evaluate(parse("[\"none\", [\"==\", \"foo\", 0], [\"==\", \"foo\", 1]]"),
{{ std::string("foo"), int64_t(1) }}));
}
+
+TEST(Filter, Has) {
+ ASSERT_TRUE(evaluate(parse("[\"has\", \"foo\"]"),
+ {{ std::string("foo"), int64_t(1) }}));
+ ASSERT_TRUE(evaluate(parse("[\"has\", \"foo\"]"),
+ {{ std::string("foo"), int64_t(0) }}));
+ ASSERT_TRUE(evaluate(parse("[\"has\", \"foo\"]"),
+ {{ std::string("foo"), false }}));
+ ASSERT_FALSE(evaluate(parse("[\"has\", \"foo\"]"),
+ {{}}));
+}
+
+TEST(Filter, NotHas) {
+ ASSERT_FALSE(evaluate(parse("[\"!has\", \"foo\"]"),
+ {{ std::string("foo"), int64_t(1) }}));
+ ASSERT_FALSE(evaluate(parse("[\"!has\", \"foo\"]"),
+ {{ std::string("foo"), int64_t(0) }}));
+ ASSERT_FALSE(evaluate(parse("[\"!has\", \"foo\"]"),
+ {{ std::string("foo"), false }}));
+ ASSERT_TRUE(evaluate(parse("[\"!has\", \"foo\"]"),
+ {{}}));
+}