summaryrefslogtreecommitdiff
path: root/include
diff options
context:
space:
mode:
Diffstat (limited to 'include')
-rw-r--r--include/mbgl/style/conversion.hpp132
1 files changed, 131 insertions, 1 deletions
diff --git a/include/mbgl/style/conversion.hpp b/include/mbgl/style/conversion.hpp
index 0507d452fd..18326c8dd1 100644
--- a/include/mbgl/style/conversion.hpp
+++ b/include/mbgl/style/conversion.hpp
@@ -2,7 +2,7 @@
#include <mbgl/style/types.hpp>
#include <mbgl/style/property_value.hpp>
-#include <mbgl/style/transition_options.hpp>
+#include <mbgl/style/filter.hpp>
#include <mbgl/util/variant.hpp>
#include <mbgl/util/optional.hpp>
@@ -27,6 +27,12 @@ namespace conversion {
This is used concretely for conversions from RapidJSON types in mbgl core, and from v8 types in
the node bindings.
+ It also defines a convertion from `V` to `Filter`, representing a JSON object conforming to a Style
+ Specification filter object:
+
+ template <class V>
+ Result<Filter> convertFilter(const V& value);
+
The requirements are that the following are legal expressions for a value `v` of type `const V&`:
* `isArray(v)` -- returns a boolean indicating whether `v` represents a JSON array
@@ -41,6 +47,9 @@ namespace conversion {
* `toBool(v)` -- returns `optional<bool>`, absence indicating `v` is not a JSON boolean
* `toNumber(v)` -- returns `optional<float>`, absence indicating `v` is not a JSON number
* `toString(v)` -- returns `optional<std::string>`, absence indicating `v` is not a JSON string
+ * `toValue(v)` -- returns `optional<mbgl::Value>`, a variant type, for generic conversion,
+ absence indicating `v` is not a boolean, number, or string. Numbers should be converted to
+ unsigned integer, signed integer, or floating point, in descending preference.
If for any reason the conversion fails, the result of `convertPropertyValue` will be the `Error` variant,
which includes explanatory text.
@@ -258,6 +267,127 @@ Result<PropertyValue<T>> convertPropertyValue(const V& value) {
return Function<T>(stops, *base);
}
+Result<Value> normalizeFilterValue(const std::string& key, const optional<Value>&);
+
+template <class V>
+Result<Filter> convertFilter(const V& value);
+
+template <class FilterType, class V>
+Result<Filter> parseUnaryFilter(const V& value) {
+ if (arrayLength(value) < 2) {
+ return Error { "filter expression must have 2 elements" };
+ }
+
+ optional<std::string> key = toString(arrayMember(value, 1));
+ if (!key) {
+ return Error { "filter expression key must be a string" };
+ }
+
+ return FilterType { *key };
+}
+
+template <class FilterType, class V>
+Result<Filter> parseBinaryFilter(const V& value) {
+ if (arrayLength(value) < 3) {
+ return Error { "filter expression must have 3 elements" };
+ }
+
+ optional<std::string> key = toString(arrayMember(value, 1));
+ if (!key) {
+ return Error { "filter expression key must be a string" };
+ }
+
+ Result<Value> filterValue = normalizeFilterValue(*key, toValue(arrayMember(value, 2)));
+ if (filterValue.is<Error>()) {
+ return filterValue.get<Error>();
+ }
+
+ return FilterType { *key, filterValue.get<Value>() };
+}
+
+template <class FilterType, class V>
+Result<Filter> parseSetFilter(const V& value) {
+ if (arrayLength(value) < 2) {
+ return Error { "filter expression must at least 2 elements" };
+ }
+
+ optional<std::string> key = toString(arrayMember(value, 1));
+ if (!key) {
+ return Error { "filter expression key must be a string" };
+ }
+
+ std::vector<Value> values;
+ for (std::size_t i = 2; i < arrayLength(value); ++i) {
+ Result<Value> filterValue = normalizeFilterValue(*key, toValue(arrayMember(value, i)));
+ if (filterValue.is<Error>()) {
+ return filterValue.get<Error>();
+ }
+ values.push_back(filterValue.get<Value>());
+ }
+
+ return FilterType { *key, std::move(values) };
+}
+
+template <class FilterType, class V>
+Result<Filter> parseCompoundFilter(const V& value) {
+ std::vector<Filter> filters;
+ for (std::size_t i = 1; i < arrayLength(value); ++i) {
+ Result<Filter> element = convertFilter(arrayMember(value, i));
+ if (element.is<Error>()) {
+ return element;
+ }
+ filters.push_back(element.get<Filter>());
+ }
+
+ return FilterType { std::move(filters) };
+}
+
+template <class V>
+Result<Filter> convertFilter(const V& value) {
+ if (!isArray(value)) {
+ return Error { "filter expression must be an array" };
+ }
+
+ if (arrayLength(value) < 1) {
+ return Error { "filter expression must have at least 1 element" };
+ }
+
+ optional<std::string> op = toString(arrayMember(value, 0));
+ if (!op) {
+ return Error { "filter operator must be a string" };
+ }
+
+ if (*op == "==") {
+ return parseBinaryFilter<EqualsFilter>(value);
+ } else if (*op == "!=") {
+ return parseBinaryFilter<NotEqualsFilter>(value);
+ } else if (*op == ">") {
+ return parseBinaryFilter<GreaterThanFilter>(value);
+ } else if (*op == ">=") {
+ return parseBinaryFilter<GreaterThanEqualsFilter>(value);
+ } else if (*op == "<") {
+ return parseBinaryFilter<LessThanFilter>(value);
+ } else if (*op == "<=") {
+ return parseBinaryFilter<LessThanEqualsFilter>(value);
+ } else if (*op == "in") {
+ return parseSetFilter<InFilter>(value);
+ } else if (*op == "!in") {
+ return parseSetFilter<NotInFilter>(value);
+ } else if (*op == "all") {
+ return parseCompoundFilter<AllFilter>(value);
+ } else if (*op == "any") {
+ 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);
+ }
+
+ return Error { "filter operator must be one of \"==\", \"!=\", \">\", \">=\", \"<\", \"<=\", \"in\", \"!in\", \"all\", \"any\", \"none\", \"has\", or \"!has\"" };
+}
+
} // namespace conversion
} // namespace style
} // namespace mbgl