diff --git a/src/mbgl/style/filter.hpp b/src/mbgl/style/filter.hpp index 0a1dcbd17..6ad6969fb 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 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 ab550ee26..b607a0c65 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 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 27edf15f9..4f081e7d8 100644 --- a/src/mbgl/style/style_parser.cpp +++ b/src/mbgl/style/style_parser.cpp @@ -533,6 +533,25 @@ Value parseValue(const JSValue& value) { } } +template +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 Filter parseBinaryFilter(const JSValue& value) { Filter empty; @@ -635,8 +654,12 @@ Filter parseFilter(const JSValue& value) { return parseCompoundFilter(value); } else if (op == "none") { return parseCompoundFilter(value); + } else if (op == "has") { + return parseUnaryFilter(value); + } else if (op == "!has") { + return parseUnaryFilter(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 da0e5ff4a..f258c3149 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\"]"), + {{}})); +}