#pragma once #include #include #include #include #include #include namespace mbgl { namespace style { namespace conversion { template optional> convertStops(const V& value, Error& error) { auto stopsValue = objectMember(value, "stops"); if (!stopsValue) { error = { "function value must specify stops" }; return {}; } if (!isArray(*stopsValue)) { error = { "function stops must be an array" }; return {}; } if (arrayLength(*stopsValue) == 0) { error = { "function must have at least one stop" }; return {}; } std::map stops; for (std::size_t i = 0; i < arrayLength(*stopsValue); ++i) { const auto& stopValue = arrayMember(*stopsValue, i); if (!isArray(stopValue)) { error = { "function stop must be an array" }; return {}; } if (arrayLength(stopValue) != 2) { error = { "function stop must have two elements" }; return {}; } optional d = convert(arrayMember(stopValue, 0), error); if (!d) { return {}; } optional r = convert(arrayMember(stopValue, 1), error); if (!r) { return {}; } stops.emplace(*d, *r); } return stops; } template struct Converter> { static constexpr const char * type = "exponential"; template optional> operator()(const V& value, Error& error) const { auto stops = convertStops(value, error); if (!stops) { return {}; } auto baseValue = objectMember(value, "base"); if (!baseValue) { return ExponentialStops(*stops); } optional base = toNumber(*baseValue); if (!base) { error = { "function base must be a number"}; return {}; } return ExponentialStops(*stops, *base); } }; template struct Converter> { static constexpr const char * type = "interval"; template optional> operator()(const V& value, Error& error) const { auto stops = convertStops(value, error); if (!stops) { return {}; } return IntervalStops(*stops); } }; template <> struct Converter { template optional operator()(const V& value, Error& error) const { auto b = toBool(value); if (b) { return { *b }; } auto n = toNumber(value); if (n) { return { int64_t(*n) }; } auto s = toString(value); if (s) { return { *s }; } error = { "stop domain value must be a number, string, or boolean" }; return {}; } }; template struct Converter> { static constexpr const char * type = "categorical"; template optional> operator()(const V& value, Error& error) const { auto stops = convertStops(value, error); if (!stops) { return {}; } return CategoricalStops( std::map((*stops).begin(), (*stops).end())); } }; template struct Converter> { static constexpr const char * type = "identity"; template optional> operator()(const V&, Error&) const { return IdentityStops(); } }; template struct StopsConverter; template struct StopsConverter> { public: template optional> operator()(const V& value, Error& error) const { std::string type = util::Interpolatable::value ? "exponential" : "interval"; auto typeValue = objectMember(value, "type"); if (typeValue && toString(*typeValue)) { type = *toString(*typeValue); } bool matched = false; optional> result; // Workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47226 auto tryConvert = [&] (auto* tp) { using Stops = std::decay_t; if (type == Converter::type) { matched = true; optional stops = convert(value, error); if (stops) { result = variant(*stops); } } }; util::ignore({ (tryConvert((Ts*)nullptr), 0)... }); if (!matched) { error = { "unsupported function type" }; return {}; } return result; } }; template struct Converter> { template optional> operator()(const V& value, Error& error) const { if (!isObject(value)) { error = { "function must be an object" }; return {}; } auto stops = StopsConverter::Stops>()(value, error); if (!stops) { return {}; } return CameraFunction(*stops); } }; template optional> convertDefaultValue(const V& value, Error& error) { auto defaultValueValue = objectMember(value, "default"); if (!defaultValueValue) { return optional(); } auto defaultValue = convert(*defaultValueValue, error); if (!defaultValue) { error = { R"(wrong type for "default": )" + error.message }; return {}; } return { *defaultValue }; } template struct Converter> { template optional> operator()(const V& value, Error& error) const { if (!isObject(value)) { error = { "function must be an object" }; return {}; } auto propertyValue = objectMember(value, "property"); if (!propertyValue) { error = { "function must specify property" }; return {}; } auto propertyString = toString(*propertyValue); if (!propertyString) { error = { "function property must be a string" }; return {}; } auto stops = StopsConverter::Stops>()(value, error); if (!stops) { return {}; } auto defaultValue = convertDefaultValue(value, error); if (!defaultValue) { return {}; } return SourceFunction(*propertyString, *stops, *defaultValue); } }; template struct CompositeValue : std::pair { using std::pair::pair; }; template struct Converter> { template optional> operator()(const V& value, Error& error) const { if (!isObject(value)) { error = { "stop must be an object" }; return {}; } auto zoomValue = objectMember(value, "zoom"); if (!zoomValue) { error = { "stop must specify zoom" }; return {}; } auto propertyValue = objectMember(value, "value"); if (!propertyValue) { error = { "stop must specify value" }; return {}; } optional z = convert(*zoomValue, error); if (!z) { return {}; } optional s = convert(*propertyValue, error); if (!s) { return {}; } return CompositeValue { *z, *s }; } }; template struct Converter> { static constexpr const char * type = "exponential"; template optional> operator()(const V& value, Error& error) const { auto stops = convertStops, T>(value, error); if (!stops) { return {}; } auto base = 1.0f; auto baseValue = objectMember(value, "base"); if (baseValue && toNumber(*baseValue)) { base = *toNumber(*baseValue); } std::map> convertedStops; for (const auto& stop : *stops) { convertedStops[stop.first.first].emplace(stop.first.second, stop.second); } return CompositeExponentialStops(convertedStops, base); } }; template struct Converter> { static constexpr const char * type = "interval"; template optional> operator()(const V& value, Error& error) const { auto stops = convertStops, T>(value, error); if (!stops) { return {}; } std::map> convertedStops; for (const auto& stop : *stops) { convertedStops[stop.first.first].emplace(stop.first.second, stop.second); } return CompositeIntervalStops(convertedStops); } }; template struct Converter> { static constexpr const char * type = "categorical"; template optional> operator()(const V& value, Error& error) const { auto stops = convertStops, T>(value, error); if (!stops) { return {}; } std::map> convertedStops; for (const auto& stop : *stops) { convertedStops[stop.first.first].emplace(stop.first.second, stop.second); } return CompositeCategoricalStops(convertedStops); } }; template struct Converter> { template optional> operator()(const V& value, Error& error) const { if (!isObject(value)) { error = { "function must be an object" }; return {}; } auto propertyValue = objectMember(value, "property"); if (!propertyValue) { error = { "function must specify property" }; return {}; } auto propertyString = toString(*propertyValue); if (!propertyString) { error = { "function property must be a string" }; return {}; } auto stops = StopsConverter::Stops>()(value, error); if (!stops) { return {}; } auto defaultValue = convertDefaultValue(value, error); if (!defaultValue) { return {}; } return CompositeFunction(*propertyString, *stops, *defaultValue); } }; } // namespace conversion } // namespace style } // namespace mbgl