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