From 141e995806576364d185626176c1b993fc519291 Mon Sep 17 00:00:00 2001 From: John Firebaugh Date: Fri, 28 Oct 2016 16:39:50 -0700 Subject: [core] Add support for data-driven styling --- .../conversion/data_driven_property_value.hpp | 46 +++ include/mbgl/style/conversion/function.hpp | 338 ++++++++++++++++++--- include/mbgl/style/conversion/property_setter.hpp | 7 +- include/mbgl/style/conversion/property_value.hpp | 2 +- 4 files changed, 355 insertions(+), 38 deletions(-) create mode 100644 include/mbgl/style/conversion/data_driven_property_value.hpp (limited to 'include/mbgl/style/conversion') diff --git a/include/mbgl/style/conversion/data_driven_property_value.hpp b/include/mbgl/style/conversion/data_driven_property_value.hpp new file mode 100644 index 0000000000..83f44fdb27 --- /dev/null +++ b/include/mbgl/style/conversion/data_driven_property_value.hpp @@ -0,0 +1,46 @@ +#pragma once + +#include +#include +#include +#include + +namespace mbgl { +namespace style { +namespace conversion { + +template +struct Converter> { + template + Result> operator()(const V& value) const { + if (isUndefined(value)) { + return {}; + } else if (!isObject(value)) { + Result constant = convert(value); + if (!constant) { + return constant.error(); + } + return DataDrivenPropertyValue(*constant); + } else if (!objectMember(value, "property")) { + Result> function = convert>(value); + if (!function) { + return function.error(); + } + return DataDrivenPropertyValue(*function); + } else { + Result> composite = convert>(value); + if (composite) { + return DataDrivenPropertyValue(*composite); + } + Result> source = convert>(value); + if (!source) { + return source.error(); + } + return DataDrivenPropertyValue(*source); + } + } +}; + +} // namespace conversion +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/style/conversion/function.hpp b/include/mbgl/style/conversion/function.hpp index 6a0b67618f..90b4b95063 100644 --- a/include/mbgl/style/conversion/function.hpp +++ b/include/mbgl/style/conversion/function.hpp @@ -1,70 +1,340 @@ #pragma once -#include +#include +#include +#include #include #include +#include namespace mbgl { namespace style { namespace conversion { +template +Result> convertStops(const V& value) { + auto stopsValue = objectMember(value, "stops"); + if (!stopsValue) { + return Error { "function value must specify stops" }; + } + + if (!isArray(*stopsValue)) { + return Error { "function stops must be an array" }; + } + + if (arrayLength(*stopsValue) == 0) { + return Error { "function must have at least one stop" }; + } + + std::map stops; + for (std::size_t i = 0; i < arrayLength(*stopsValue); ++i) { + const auto& stopValue = arrayMember(*stopsValue, i); + + if (!isArray(stopValue)) { + return Error { "function stop must be an array" }; + } + + if (arrayLength(stopValue) != 2) { + return Error { "function stop must have two elements" }; + } + + Result d = convert(arrayMember(stopValue, 0)); + if (!d) { + return d.error(); + } + + Result r = convert(arrayMember(stopValue, 1)); + if (!r) { + return r.error(); + } + + stops.emplace(*d, *r); + } + + return stops; +} + template -struct Converter> { +struct Converter> { + static constexpr const char * type = "exponential"; + template - Result> operator()(const V& value) const { + Result> operator()(const V& value) const { + auto stops = convertStops(value); + if (!stops) { + return stops.error(); + } + + auto baseValue = objectMember(value, "base"); + if (!baseValue) { + return ExponentialStops(*stops); + } + + optional base = toNumber(*baseValue); + if (!base) { + return Error { "function base must be a number"}; + } + + return ExponentialStops(*stops, *base); + } +}; + +template +struct Converter> { + static constexpr const char * type = "interval"; + + template + Result> operator()(const V& value) const { + auto stops = convertStops(value); + if (!stops) { + return stops.error(); + } + return IntervalStops(*stops); + } +}; + +template <> +struct Converter { + template + Result operator()(const V& value) 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; + } + + return Error { "stop domain value must be a number, string, or boolean" }; + } +}; + +template +struct Converter> { + static constexpr const char * type = "categorical"; + + template + Result> operator()(const V& value) const { + auto stops = convertStops(value); + if (!stops) { + return stops.error(); + } + return CategoricalStops( + std::map((*stops).begin(), (*stops).end())); + } +}; + +template +struct Converter> { + static constexpr const char * type = "identity"; + + template + Result> operator()(const V&) const { + return IdentityStops(); + } +}; + +template +struct StopsConverter; + +template +struct StopsConverter> { +public: + template + Result> operator()(const V& value) const { + std::string type = util::Interpolatable ? "exponential" : "interval"; + + auto typeValue = objectMember(value, "type"); + if (typeValue && toString(*typeValue)) { + type = *toString(*typeValue); + } + + 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) { + auto stops = convert(value); + result = stops + ? Result>(*stops) + : Result>(stops.error()); + } + }; + + util::ignore({ + (tryConvert((Ts*)nullptr), 0)... + }); + + if (!result) { + return Error { "unsupported function type" }; + } + + return *result; + } +}; + +template +struct Converter> { + template + Result> operator()(const V& value) const { + if (!isObject(value)) { + return Error { "function must be an object" }; + } + + auto stops = StopsConverter::Stops>()(value); + if (!stops) { + return stops.error(); + } + + return CameraFunction(*stops); + } +}; + +template +struct Converter> { + template + Result> operator()(const V& value) const { + if (!isObject(value)) { + return Error { "function must be an object" }; + } + + auto propertyValue = objectMember(value, "property"); + if (!propertyValue) { + return Error { "function must specify property" }; + } + + auto propertyString = toString(*propertyValue); + if (!propertyString) { + return Error { "function property must be a string" }; + } + + auto stops = StopsConverter::Stops>()(value); + if (!stops) { + return stops.error(); + } + + return SourceFunction(*propertyString, *stops); + } +}; + +template +struct CompositeValue : std::pair { + using std::pair::pair; +}; + +template +struct Converter> { + template + Result> operator()(const V& value) const { + if (!isObject(value)) { + return Error { "stop must be an object" }; + } + + auto zoomValue = objectMember(value, "zoom"); + if (!zoomValue) { + return Error { "stop must specify zoom" }; + } + + auto propertyValue = objectMember(value, "value"); + if (!propertyValue) { + return Error { "stop must specify value" }; + } + + Result z = convert(*zoomValue); + if (!z) { + return z.error(); + } + + Result s = convert(*propertyValue); + if (!s) { + return s.error(); + } + + return CompositeValue { *z, *s }; + } +}; + +template +struct Converter> { + template + Result> operator()(const V& value) const { if (!isObject(value)) { return Error { "function must be an object" }; } - auto stopsValue = objectMember(value, "stops"); - if (!stopsValue) { - return Error { "function value must specify stops" }; + auto propertyValue = objectMember(value, "property"); + if (!propertyValue) { + return Error { "function must specify property" }; } - if (!isArray(*stopsValue)) { - return Error { "function stops must be an array" }; + auto propertyString = toString(*propertyValue); + if (!propertyString) { + return Error { "function property must be a string" }; } - if (arrayLength(*stopsValue) == 0) { - return Error { "function must have at least one stop" }; + std::string type = "exponential"; + auto typeValue = objectMember(value, "type"); + if (typeValue && toString(*typeValue)) { + type = *toString(*typeValue); } - std::vector> stops; - for (std::size_t i = 0; i < arrayLength(*stopsValue); ++i) { - const auto& stopValue = arrayMember(*stopsValue, i); + if (type == "exponential") { + auto stops = convertStops, T>(value); + if (!stops) { + return stops.error(); + } - if (!isArray(stopValue)) { - return Error { "function stop must be an array" }; + auto base = 1.0f; + auto baseValue = objectMember(value, "base"); + if (baseValue && toNumber(*baseValue)) { + base = *toNumber(*baseValue); } - if (arrayLength(stopValue) != 2) { - return Error { "function stop must have two elements" }; + std::map> convertedStops; + for (const auto& stop : *stops) { + auto& inner = convertedStops[stop.first.first]; + inner.base = base; + inner.stops.emplace(stop.first.second, stop.second); } - optional z = toNumber(arrayMember(stopValue, 0)); - if (!z) { - return Error { "function stop zoom level must be a number" }; + return CompositeFunction(*propertyString, convertedStops); + } else if (type == "interval") { + auto stops = convertStops, T>(value); + if (!stops) { + return stops.error(); } - Result v = convert(arrayMember(stopValue, 1)); - if (!v) { - return v.error(); + std::map> convertedStops; + for (const auto& stop : *stops) { + auto& inner = convertedStops[stop.first.first]; + inner.stops.emplace(stop.first.second, stop.second); } - stops.emplace_back(*z, *v); - } + return CompositeFunction(*propertyString, convertedStops); + } else if (type == "categorical") { + auto stops = convertStops, T>(value); + if (!stops) { + return stops.error(); + } - auto baseValue = objectMember(value, "base"); - if (!baseValue) { - return Function(stops, 1.0f); - } + std::map> convertedStops; + for (const auto& stop : *stops) { + auto& inner = convertedStops[stop.first.first]; + inner.stops.emplace(stop.first.second, stop.second); + } - optional base = toNumber(*baseValue); - if (!base) { - return Error { "function base must be a number"}; + return CompositeFunction(*propertyString, convertedStops); + } else { + return Error { "unsupported function type" }; } - - return Function(stops, *base); } }; diff --git a/include/mbgl/style/conversion/property_setter.hpp b/include/mbgl/style/conversion/property_setter.hpp index 1a601c7c1b..52fb160fde 100644 --- a/include/mbgl/style/conversion/property_setter.hpp +++ b/include/mbgl/style/conversion/property_setter.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -18,15 +19,15 @@ using LayoutPropertySetter = std::function (Layer&, const V&)>; template using PaintPropertySetter = std::function (Layer&, const V&, const optional&)>; -template -auto makePropertySetter(void (L::*setter)(PropertyValue, const Args&...args)) { +template +auto makePropertySetter(void (L::*setter)(PropertyValue, const Args&...args)) { return [setter] (Layer& layer, const V& value, const Args&...args) -> optional { L* typedLayer = layer.as(); if (!typedLayer) { return Error { "layer doesn't support this property" }; } - Result> typedValue = convert>(value); + Result typedValue = convert(value); if (!typedValue) { return typedValue.error(); } diff --git a/include/mbgl/style/conversion/property_value.hpp b/include/mbgl/style/conversion/property_value.hpp index de95b56155..d5e2a5c3c8 100644 --- a/include/mbgl/style/conversion/property_value.hpp +++ b/include/mbgl/style/conversion/property_value.hpp @@ -16,7 +16,7 @@ struct Converter> { if (isUndefined(value)) { return {}; } else if (isObject(value)) { - Result> function = convert>(value); + Result> function = convert>(value); if (!function) { return function.error(); } -- cgit v1.2.1