#pragma once #include #include #include namespace mbgl { namespace style { namespace conversion { template <> struct Converter { public: template Result operator()(const V& value) const { 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 op = toString(arrayMember(value, 0)); if (!op) { return Error { "filter operator must be a string" }; } if (*op == "==") { return convertEqualityFilter(value); } else if (*op == "!=") { return convertEqualityFilter(value); } else if (*op == ">") { return convertBinaryFilter(value); } else if (*op == ">=") { return convertBinaryFilter(value); } else if (*op == "<") { return convertBinaryFilter(value); } else if (*op == "<=") { return convertBinaryFilter(value); } else if (*op == "in") { return convertSetFilter(value); } else if (*op == "!in") { return convertSetFilter(value); } else if (*op == "all") { return convertCompoundFilter(value); } else if (*op == "any") { return convertCompoundFilter(value); } else if (*op == "none") { return convertCompoundFilter(value); } else if (*op == "has") { return convertUnaryFilter(value); } else if (*op == "!has") { return convertUnaryFilter(value); } return Error { "filter operator must be one of \"==\", \"!=\", \">\", \">=\", \"<\", \"<=\", \"in\", \"!in\", \"all\", \"any\", \"none\", \"has\", or \"!has\"" }; } private: Result normalizeValue(const optional& value) const { if (!value) { return Error { "filter expression value must be a boolean, number, or string" }; } else { return *value; } } template Result toFeatureType(const V& value) const { optional type = toString(value); if (!type) { return Error { "value for $type filter must be a string" }; } else if (*type == "Point") { return FeatureType::Point; } else if (*type == "LineString") { return FeatureType::LineString; } else if (*type == "Polygon") { return FeatureType::Polygon; } else { return Error { "value for $type filter must be Point, LineString, or Polygon" }; } } template Result toFeatureIdentifier(const V& value) const { optional identifier = toValue(value); if (!identifier) { return Error { "filter expression value must be a boolean, number, or string" }; } else { return (*identifier).match( [] (uint64_t t) -> Result { return t; }, [] ( int64_t t) -> Result { return t; }, [] ( double t) -> Result { return t; }, [] (const std::string& t) -> Result { return t; }, [] (const auto&) -> Result { return Error { "filter expression value must be a boolean, number, or string" }; }); } } template Result convertUnaryFilter(const V& value) const { if (arrayLength(value) < 2) { return Error { "filter expression must have 2 elements" }; } optional key = toString(arrayMember(value, 1)); if (!key) { return Error { "filter expression key must be a string" }; } if (*key == "$id") { return IdentifierFilterType {}; } else { return FilterType { *key }; } } template Result convertEqualityFilter(const V& value) const { if (arrayLength(value) < 3) { return Error { "filter expression must have 3 elements" }; } optional key = toString(arrayMember(value, 1)); if (!key) { return Error { "filter expression key must be a string" }; } if (*key == "$type") { Result filterValue = toFeatureType(arrayMember(value, 2)); if (!filterValue) { return filterValue.error(); } return TypeFilterType { *filterValue }; } else if (*key == "$id") { Result filterValue = toFeatureIdentifier(arrayMember(value, 2)); if (!filterValue) { return filterValue.error(); } return IdentifierFilterType { *filterValue }; } else { Result filterValue = normalizeValue(toValue(arrayMember(value, 2))); if (!filterValue) { return filterValue.error(); } return FilterType { *key, *filterValue }; } } template Result convertBinaryFilter(const V& value) const { if (arrayLength(value) < 3) { return Error { "filter expression must have 3 elements" }; } optional key = toString(arrayMember(value, 1)); if (!key) { return Error { "filter expression key must be a string" }; } Result filterValue = normalizeValue(toValue(arrayMember(value, 2))); if (!filterValue) { return filterValue.error(); } return FilterType { *key, *filterValue }; } template Result convertSetFilter(const V& value) const { if (arrayLength(value) < 2) { return Error { "filter expression must at least 2 elements" }; } optional key = toString(arrayMember(value, 1)); if (!key) { return Error { "filter expression key must be a string" }; } if (*key == "$type") { std::vector values; for (std::size_t i = 2; i < arrayLength(value); ++i) { Result filterValue = toFeatureType(arrayMember(value, i)); if (!filterValue) { return filterValue.error(); } values.push_back(*filterValue); } return TypeFilterType { std::move(values) }; } else if (*key == "$id") { std::vector values; for (std::size_t i = 2; i < arrayLength(value); ++i) { Result filterValue = toFeatureIdentifier(arrayMember(value, i)); if (!filterValue) { return filterValue.error(); } values.push_back(*filterValue); } return IdentifierFilterType { std::move(values) }; } else { std::vector values; for (std::size_t i = 2; i < arrayLength(value); ++i) { Result filterValue = normalizeValue(toValue(arrayMember(value, i))); if (!filterValue) { return filterValue.error(); } values.push_back(*filterValue); } return FilterType { *key, std::move(values) }; } } template Result convertCompoundFilter(const V& value) const { std::vector filters; for (std::size_t i = 1; i < arrayLength(value); ++i) { Result element = operator()(arrayMember(value, i)); if (!element) { return element.error(); } filters.push_back(*element); } return FilterType { std::move(filters) }; } }; } // namespace conversion } // namespace style } // namespace mbgl