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 --- include/mbgl/annotation/annotation.hpp | 11 +- .../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 +- include/mbgl/style/data_driven_property_value.hpp | 51 ++++ include/mbgl/style/function.hpp | 40 --- include/mbgl/style/function/camera_function.hpp | 41 +++ include/mbgl/style/function/categorical_stops.hpp | 42 +++ include/mbgl/style/function/composite_function.hpp | 109 +++++++ include/mbgl/style/function/exponential_stops.hpp | 54 ++++ include/mbgl/style/function/identity_stops.hpp | 20 ++ include/mbgl/style/function/interval_stops.hpp | 49 +++ include/mbgl/style/function/source_function.hpp | 56 ++++ include/mbgl/style/layers/background_layer.hpp | 1 + include/mbgl/style/layers/circle_layer.hpp | 43 +-- include/mbgl/style/layers/fill_extrusion_layer.hpp | 19 +- include/mbgl/style/layers/fill_layer.hpp | 19 +- include/mbgl/style/layers/layer.hpp.ejs | 13 +- include/mbgl/style/layers/line_layer.hpp | 31 +- include/mbgl/style/layers/raster_layer.hpp | 1 + include/mbgl/style/layers/symbol_layer.hpp | 13 +- include/mbgl/style/property_value.hpp | 26 +- include/mbgl/style/undefined.hpp | 12 + include/mbgl/util/feature.hpp | 17 ++ 25 files changed, 897 insertions(+), 164 deletions(-) create mode 100644 include/mbgl/style/conversion/data_driven_property_value.hpp create mode 100644 include/mbgl/style/data_driven_property_value.hpp delete mode 100644 include/mbgl/style/function.hpp create mode 100644 include/mbgl/style/function/camera_function.hpp create mode 100644 include/mbgl/style/function/categorical_stops.hpp create mode 100644 include/mbgl/style/function/composite_function.hpp create mode 100644 include/mbgl/style/function/exponential_stops.hpp create mode 100644 include/mbgl/style/function/identity_stops.hpp create mode 100644 include/mbgl/style/function/interval_stops.hpp create mode 100644 include/mbgl/style/function/source_function.hpp create mode 100644 include/mbgl/style/undefined.hpp (limited to 'include') diff --git a/include/mbgl/annotation/annotation.hpp b/include/mbgl/annotation/annotation.hpp index a72c65b8c4..de83d24712 100644 --- a/include/mbgl/annotation/annotation.hpp +++ b/include/mbgl/annotation/annotation.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -29,17 +30,17 @@ using ShapeAnnotationGeometry = variant< class LineAnnotation { public: ShapeAnnotationGeometry geometry; - style::PropertyValue opacity { 1.0f }; + style::DataDrivenPropertyValue opacity { 1.0f }; style::PropertyValue width { 1.0f }; - style::PropertyValue color { Color::black() }; + style::DataDrivenPropertyValue color { Color::black() }; }; class FillAnnotation { public: ShapeAnnotationGeometry geometry; - style::PropertyValue opacity { 1.0f }; - style::PropertyValue color { Color::black() }; - style::PropertyValue outlineColor {}; + style::DataDrivenPropertyValue opacity { 1.0f }; + style::DataDrivenPropertyValue color { Color::black() }; + style::DataDrivenPropertyValue outlineColor {}; }; // An annotation whose type and properties are sourced from a style layer. 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(); } diff --git a/include/mbgl/style/data_driven_property_value.hpp b/include/mbgl/style/data_driven_property_value.hpp new file mode 100644 index 0000000000..4653224f15 --- /dev/null +++ b/include/mbgl/style/data_driven_property_value.hpp @@ -0,0 +1,51 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace mbgl { +namespace style { + +template +class DataDrivenPropertyValue { +private: + using Value = variant< + Undefined, + T, + CameraFunction, + SourceFunction, + CompositeFunction>; + + Value value; + + friend bool operator==(const DataDrivenPropertyValue& lhs, + const DataDrivenPropertyValue& rhs) { + return lhs.value == rhs.value; + } + +public: + DataDrivenPropertyValue() = default; + DataDrivenPropertyValue( T v) : value(std::move(v)) {} + DataDrivenPropertyValue( CameraFunction v) : value(std::move(v)) {} + DataDrivenPropertyValue( SourceFunction v) : value(std::move(v)) {} + DataDrivenPropertyValue(CompositeFunction v) : value(std::move(v)) {} + + bool isUndefined() const { + return value.template is(); + } + + bool isDataDriven() const { + return value.template is>() || value.template is>(); + } + + template + auto evaluate(const Evaluator& evaluator) const { + return Value::visit(value, evaluator); + } +}; + +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/style/function.hpp b/include/mbgl/style/function.hpp deleted file mode 100644 index b023229e4f..0000000000 --- a/include/mbgl/style/function.hpp +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace mbgl { -namespace style { - -template -class Function { -public: - using Stop = std::pair; - using Stops = std::vector; - - Function(Stops stops_, float base_) - : base(base_), stops(std::move(stops_)) { - assert(stops.size() > 0); - } - - float getBase() const { return base; } - const std::vector>& getStops() const { return stops; } - - T evaluate(float z) const; - - friend bool operator==(const Function& lhs, const Function& rhs) { - return lhs.base == rhs.base && lhs.stops == rhs.stops; - } - - friend bool operator!=(const Function& lhs, const Function& rhs) { - return !(lhs == rhs); - } - -private: - float base = 1; - std::vector> stops; -}; - -} // namespace style -} // namespace mbgl diff --git a/include/mbgl/style/function/camera_function.hpp b/include/mbgl/style/function/camera_function.hpp new file mode 100644 index 0000000000..a96978939d --- /dev/null +++ b/include/mbgl/style/function/camera_function.hpp @@ -0,0 +1,41 @@ +#pragma once + +#include +#include +#include +#include + +namespace mbgl { +namespace style { + +template +class CameraFunction { +public: + using Stops = std::conditional_t< + util::Interpolatable, + variant< + ExponentialStops, + IntervalStops>, + variant< + IntervalStops>>; + + CameraFunction(Stops stops_) + : stops(std::move(stops_)) { + } + + T evaluate(float zoom) const { + return stops.match([&] (const auto& s) { + return s.evaluate(Value(double(zoom))); + }); + } + + friend bool operator==(const CameraFunction& lhs, + const CameraFunction& rhs) { + return lhs.stops == rhs.stops; + } + + Stops stops; +}; + +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/style/function/categorical_stops.hpp b/include/mbgl/style/function/categorical_stops.hpp new file mode 100644 index 0000000000..11ec2a0e5a --- /dev/null +++ b/include/mbgl/style/function/categorical_stops.hpp @@ -0,0 +1,42 @@ +#pragma once + +#include +#include + +#include +#include +#include + +namespace mbgl { +namespace style { + +class CategoricalValue : public variant { +public: + using variant::variant; +}; + +template +class CategoricalStops { +public: + using Stops = std::map; + + Stops stops; + T defaultValue; + + CategoricalStops() = default; + CategoricalStops(Stops stops_, T defaultValue_ = T()) + : stops(std::move(stops_)), + defaultValue(std::move(defaultValue_)) { + assert(stops.size() > 0); + } + + T evaluate(const Value&) const; + + friend bool operator==(const CategoricalStops& lhs, + const CategoricalStops& rhs) { + return lhs.stops == rhs.stops && lhs.defaultValue == rhs.defaultValue; + } +}; + +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/style/function/composite_function.hpp b/include/mbgl/style/function/composite_function.hpp new file mode 100644 index 0000000000..169a455435 --- /dev/null +++ b/include/mbgl/style/function/composite_function.hpp @@ -0,0 +1,109 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace mbgl { + +class GeometryTileFeature; + +namespace style { + +// A CompositeFunction consists of an outer zoom function whose stop range values are +// "inner" source functions. It provides the GL Native implementation of +// "zoom-and-property" functions from the style spec. + +template +class CompositeFunction { +public: + using InnerStops = std::conditional_t< + util::Interpolatable, + variant< + ExponentialStops, + IntervalStops, + CategoricalStops>, + variant< + IntervalStops, + CategoricalStops>>; + + using Stops = std::conditional_t< + util::Interpolatable, + variant< + std::map>, + std::map>, + std::map>>, + variant< + std::map>, + std::map>>>; + + CompositeFunction(std::string property_, Stops stops_) + : property(std::move(property_)), + stops(std::move(stops_)) { + } + + std::tuple, Range> + coveringRanges(float zoom) const { + return stops.match( + [&] (const auto& s) { + assert(!s.empty()); + auto minIt = s.lower_bound(zoom); + auto maxIt = s.upper_bound(zoom); + if (minIt != s.begin()) { + minIt--; + } + return std::make_tuple( + Range { + minIt == s.end() ? s.rbegin()->first : minIt->first, + maxIt == s.end() ? s.rbegin()->first : maxIt->first + }, + Range { + minIt == s.end() ? s.rbegin()->second : minIt->second, + maxIt == s.end() ? s.rbegin()->second : maxIt->second + } + ); + } + ); + } + + Range evaluate(Range coveringStops, + const GeometryTileFeature& feature) const { + optional v = feature.getValue(property); + if (!v) { + return { T(), T() }; + } + auto eval = [&] (const auto& s) { + return s.evaluate(*v); + }; + return Range { + coveringStops.min.match(eval), + coveringStops.max.match(eval) + }; + } + + T evaluate(float zoom, const GeometryTileFeature& feature) const { + std::tuple, Range> ranges = coveringRanges(zoom); + Range resultRange = evaluate(std::get<1>(ranges), feature); + return util::interpolate( + resultRange.min, + resultRange.max, + util::interpolationFactor(1.0f, std::get<0>(ranges), zoom)); + } + + friend bool operator==(const CompositeFunction& lhs, + const CompositeFunction& rhs) { + return lhs.property == rhs.property && lhs.stops == rhs.stops; + } + + std::string property; + Stops stops; +}; + +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/style/function/exponential_stops.hpp b/include/mbgl/style/function/exponential_stops.hpp new file mode 100644 index 0000000000..7bd8783614 --- /dev/null +++ b/include/mbgl/style/function/exponential_stops.hpp @@ -0,0 +1,54 @@ +#pragma once + +#include +#include + +#include + +namespace mbgl { +namespace style { + +template +class ExponentialStops { +public: + using Stops = std::map; + + Stops stops; + float base = 1.0f; + + ExponentialStops() = default; + ExponentialStops(Stops stops_, float base_ = 1.0f) + : stops(std::move(stops_)), + base(base_) { + } + + T evaluate(const Value& value) const { + if (stops.empty()) { + assert(false); + return T(); + } + + optional z = numericValue(value); + if (!z) { + return T(); + } + + auto it = stops.upper_bound(*z); + if (it == stops.end()) { + return stops.rbegin()->second; + } else if (it == stops.begin()) { + return stops.begin()->second; + } else { + return util::interpolate(std::prev(it)->second, it->second, + util::interpolationFactor(base, { std::prev(it)->first, it->first }, *z)); + } + } + + friend bool operator==(const ExponentialStops& lhs, + const ExponentialStops& rhs) { + return lhs.stops == rhs.stops && lhs.base == rhs.base; + } +}; + +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/style/function/identity_stops.hpp b/include/mbgl/style/function/identity_stops.hpp new file mode 100644 index 0000000000..4e199f2e15 --- /dev/null +++ b/include/mbgl/style/function/identity_stops.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include + +namespace mbgl { +namespace style { + +template +class IdentityStops { +public: + T evaluate(const Value&) const; + + friend bool operator==(const IdentityStops&, + const IdentityStops&) { + return true; + } +}; + +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/style/function/interval_stops.hpp b/include/mbgl/style/function/interval_stops.hpp new file mode 100644 index 0000000000..cf879d730b --- /dev/null +++ b/include/mbgl/style/function/interval_stops.hpp @@ -0,0 +1,49 @@ +#pragma once + +#include + +#include + +namespace mbgl { +namespace style { + +template +class IntervalStops { +public: + using Stops = std::map; + Stops stops; + + IntervalStops() = default; + IntervalStops(Stops stops_) + : stops(std::move(stops_)) { + } + + T evaluate(const Value& value) const { + if (stops.empty()) { + assert(false); + return T(); + } + + optional z = numericValue(value); + if (!z) { + return T(); + } + + auto it = stops.upper_bound(*z); + if (it == stops.end()) { + return stops.rbegin()->second; + } else if (it == stops.begin()) { + return stops.begin()->second; + } else { + return std::prev(it)->second; + } + } + + friend bool operator==(const IntervalStops& lhs, + const IntervalStops& rhs) { + return lhs.stops == rhs.stops; + } +}; + +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/style/function/source_function.hpp b/include/mbgl/style/function/source_function.hpp new file mode 100644 index 0000000000..e998be082a --- /dev/null +++ b/include/mbgl/style/function/source_function.hpp @@ -0,0 +1,56 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace mbgl { +namespace style { + +template +class SourceFunction { +public: + using Stops = std::conditional_t< + util::Interpolatable, + variant< + ExponentialStops, + IntervalStops, + CategoricalStops, + IdentityStops>, + variant< + IntervalStops, + CategoricalStops, + IdentityStops>>; + + SourceFunction(std::string property_, Stops stops_) + : property(std::move(property_)), + stops(std::move(stops_)) { + } + + T evaluate(const GeometryTileFeature& feature) const { + optional v = feature.getValue(property); + if (!v) { + return T(); + } + return stops.match([&] (const auto& s) { + return s.evaluate(*v); + }); + } + + friend bool operator==(const SourceFunction& lhs, + const SourceFunction& rhs) { + return lhs.property == rhs.property && lhs.stops == rhs.stops; + } + + std::string property; + Stops stops; +}; + +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/style/layers/background_layer.hpp b/include/mbgl/style/layers/background_layer.hpp index c120b7f493..050cb67df6 100644 --- a/include/mbgl/style/layers/background_layer.hpp +++ b/include/mbgl/style/layers/background_layer.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include diff --git a/include/mbgl/style/layers/circle_layer.hpp b/include/mbgl/style/layers/circle_layer.hpp index 5562126c2f..8ffea9946f 100644 --- a/include/mbgl/style/layers/circle_layer.hpp +++ b/include/mbgl/style/layers/circle_layer.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include @@ -26,21 +27,21 @@ public: // Paint properties - static PropertyValue getDefaultCircleRadius(); - PropertyValue getCircleRadius(const optional& klass = {}) const; - void setCircleRadius(PropertyValue, const optional& klass = {}); + static DataDrivenPropertyValue getDefaultCircleRadius(); + DataDrivenPropertyValue getCircleRadius(const optional& klass = {}) const; + void setCircleRadius(DataDrivenPropertyValue, const optional& klass = {}); - static PropertyValue getDefaultCircleColor(); - PropertyValue getCircleColor(const optional& klass = {}) const; - void setCircleColor(PropertyValue, const optional& klass = {}); + static DataDrivenPropertyValue getDefaultCircleColor(); + DataDrivenPropertyValue getCircleColor(const optional& klass = {}) const; + void setCircleColor(DataDrivenPropertyValue, const optional& klass = {}); - static PropertyValue getDefaultCircleBlur(); - PropertyValue getCircleBlur(const optional& klass = {}) const; - void setCircleBlur(PropertyValue, const optional& klass = {}); + static DataDrivenPropertyValue getDefaultCircleBlur(); + DataDrivenPropertyValue getCircleBlur(const optional& klass = {}) const; + void setCircleBlur(DataDrivenPropertyValue, const optional& klass = {}); - static PropertyValue getDefaultCircleOpacity(); - PropertyValue getCircleOpacity(const optional& klass = {}) const; - void setCircleOpacity(PropertyValue, const optional& klass = {}); + static DataDrivenPropertyValue getDefaultCircleOpacity(); + DataDrivenPropertyValue getCircleOpacity(const optional& klass = {}) const; + void setCircleOpacity(DataDrivenPropertyValue, const optional& klass = {}); static PropertyValue> getDefaultCircleTranslate(); PropertyValue> getCircleTranslate(const optional& klass = {}) const; @@ -54,17 +55,17 @@ public: PropertyValue getCirclePitchScale(const optional& klass = {}) const; void setCirclePitchScale(PropertyValue, const optional& klass = {}); - static PropertyValue getDefaultCircleStrokeWidth(); - PropertyValue getCircleStrokeWidth(const optional& klass = {}) const; - void setCircleStrokeWidth(PropertyValue, const optional& klass = {}); + static DataDrivenPropertyValue getDefaultCircleStrokeWidth(); + DataDrivenPropertyValue getCircleStrokeWidth(const optional& klass = {}) const; + void setCircleStrokeWidth(DataDrivenPropertyValue, const optional& klass = {}); - static PropertyValue getDefaultCircleStrokeColor(); - PropertyValue getCircleStrokeColor(const optional& klass = {}) const; - void setCircleStrokeColor(PropertyValue, const optional& klass = {}); + static DataDrivenPropertyValue getDefaultCircleStrokeColor(); + DataDrivenPropertyValue getCircleStrokeColor(const optional& klass = {}) const; + void setCircleStrokeColor(DataDrivenPropertyValue, const optional& klass = {}); - static PropertyValue getDefaultCircleStrokeOpacity(); - PropertyValue getCircleStrokeOpacity(const optional& klass = {}) const; - void setCircleStrokeOpacity(PropertyValue, const optional& klass = {}); + static DataDrivenPropertyValue getDefaultCircleStrokeOpacity(); + DataDrivenPropertyValue getCircleStrokeOpacity(const optional& klass = {}) const; + void setCircleStrokeOpacity(DataDrivenPropertyValue, const optional& klass = {}); // Private implementation diff --git a/include/mbgl/style/layers/fill_extrusion_layer.hpp b/include/mbgl/style/layers/fill_extrusion_layer.hpp index 08728af309..09a0040ff3 100644 --- a/include/mbgl/style/layers/fill_extrusion_layer.hpp +++ b/include/mbgl/style/layers/fill_extrusion_layer.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include @@ -30,9 +31,9 @@ public: PropertyValue getFillExtrusionOpacity(const optional& klass = {}) const; void setFillExtrusionOpacity(PropertyValue, const optional& klass = {}); - static PropertyValue getDefaultFillExtrusionColor(); - PropertyValue getFillExtrusionColor(const optional& klass = {}) const; - void setFillExtrusionColor(PropertyValue, const optional& klass = {}); + static DataDrivenPropertyValue getDefaultFillExtrusionColor(); + DataDrivenPropertyValue getFillExtrusionColor(const optional& klass = {}) const; + void setFillExtrusionColor(DataDrivenPropertyValue, const optional& klass = {}); static PropertyValue> getDefaultFillExtrusionTranslate(); PropertyValue> getFillExtrusionTranslate(const optional& klass = {}) const; @@ -46,13 +47,13 @@ public: PropertyValue getFillExtrusionPattern(const optional& klass = {}) const; void setFillExtrusionPattern(PropertyValue, const optional& klass = {}); - static PropertyValue getDefaultFillExtrusionHeight(); - PropertyValue getFillExtrusionHeight(const optional& klass = {}) const; - void setFillExtrusionHeight(PropertyValue, const optional& klass = {}); + static DataDrivenPropertyValue getDefaultFillExtrusionHeight(); + DataDrivenPropertyValue getFillExtrusionHeight(const optional& klass = {}) const; + void setFillExtrusionHeight(DataDrivenPropertyValue, const optional& klass = {}); - static PropertyValue getDefaultFillExtrusionBase(); - PropertyValue getFillExtrusionBase(const optional& klass = {}) const; - void setFillExtrusionBase(PropertyValue, const optional& klass = {}); + static DataDrivenPropertyValue getDefaultFillExtrusionBase(); + DataDrivenPropertyValue getFillExtrusionBase(const optional& klass = {}) const; + void setFillExtrusionBase(DataDrivenPropertyValue, const optional& klass = {}); // Private implementation diff --git a/include/mbgl/style/layers/fill_layer.hpp b/include/mbgl/style/layers/fill_layer.hpp index 4b9201641d..079541ec39 100644 --- a/include/mbgl/style/layers/fill_layer.hpp +++ b/include/mbgl/style/layers/fill_layer.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include @@ -30,17 +31,17 @@ public: PropertyValue getFillAntialias(const optional& klass = {}) const; void setFillAntialias(PropertyValue, const optional& klass = {}); - static PropertyValue getDefaultFillOpacity(); - PropertyValue getFillOpacity(const optional& klass = {}) const; - void setFillOpacity(PropertyValue, const optional& klass = {}); + static DataDrivenPropertyValue getDefaultFillOpacity(); + DataDrivenPropertyValue getFillOpacity(const optional& klass = {}) const; + void setFillOpacity(DataDrivenPropertyValue, const optional& klass = {}); - static PropertyValue getDefaultFillColor(); - PropertyValue getFillColor(const optional& klass = {}) const; - void setFillColor(PropertyValue, const optional& klass = {}); + static DataDrivenPropertyValue getDefaultFillColor(); + DataDrivenPropertyValue getFillColor(const optional& klass = {}) const; + void setFillColor(DataDrivenPropertyValue, const optional& klass = {}); - static PropertyValue getDefaultFillOutlineColor(); - PropertyValue getFillOutlineColor(const optional& klass = {}) const; - void setFillOutlineColor(PropertyValue, const optional& klass = {}); + static DataDrivenPropertyValue getDefaultFillOutlineColor(); + DataDrivenPropertyValue getFillOutlineColor(const optional& klass = {}) const; + void setFillOutlineColor(DataDrivenPropertyValue, const optional& klass = {}); static PropertyValue> getDefaultFillTranslate(); PropertyValue> getFillTranslate(const optional& klass = {}) const; diff --git a/include/mbgl/style/layers/layer.hpp.ejs b/include/mbgl/style/layers/layer.hpp.ejs index 15d0fcee61..0c902de5af 100644 --- a/include/mbgl/style/layers/layer.hpp.ejs +++ b/include/mbgl/style/layers/layer.hpp.ejs @@ -10,6 +10,7 @@ #include #include #include +#include #include @@ -45,18 +46,18 @@ public: // Layout properties <% for (const property of layoutProperties) { -%> - static PropertyValue<<%- propertyType(property) %>> getDefault<%- camelize(property.name) %>(); - PropertyValue<<%- propertyType(property) %>> get<%- camelize(property.name) %>() const; - void set<%- camelize(property.name) %>(PropertyValue<<%- propertyType(property) %>>); + static <%- propertyValueType(property) %> getDefault<%- camelize(property.name) %>(); + <%- propertyValueType(property) %> get<%- camelize(property.name) %>() const; + void set<%- camelize(property.name) %>(<%- propertyValueType(property) %>); <% } -%> <% } -%> // Paint properties <% for (const property of paintProperties) { -%> - static PropertyValue<<%- propertyType(property) %>> getDefault<%- camelize(property.name) %>(); - PropertyValue<<%- propertyType(property) %>> get<%- camelize(property.name) %>(const optional& klass = {}) const; - void set<%- camelize(property.name) %>(PropertyValue<<%- propertyType(property) %>>, const optional& klass = {}); + static <%- propertyValueType(property) %> getDefault<%- camelize(property.name) %>(); + <%- propertyValueType(property) %> get<%- camelize(property.name) %>(const optional& klass = {}) const; + void set<%- camelize(property.name) %>(<%- propertyValueType(property) %>, const optional& klass = {}); <% } -%> // Private implementation diff --git a/include/mbgl/style/layers/line_layer.hpp b/include/mbgl/style/layers/line_layer.hpp index c3c1026bcd..c9413f1096 100644 --- a/include/mbgl/style/layers/line_layer.hpp +++ b/include/mbgl/style/layers/line_layer.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include @@ -46,13 +47,13 @@ public: // Paint properties - static PropertyValue getDefaultLineOpacity(); - PropertyValue getLineOpacity(const optional& klass = {}) const; - void setLineOpacity(PropertyValue, const optional& klass = {}); + static DataDrivenPropertyValue getDefaultLineOpacity(); + DataDrivenPropertyValue getLineOpacity(const optional& klass = {}) const; + void setLineOpacity(DataDrivenPropertyValue, const optional& klass = {}); - static PropertyValue getDefaultLineColor(); - PropertyValue getLineColor(const optional& klass = {}) const; - void setLineColor(PropertyValue, const optional& klass = {}); + static DataDrivenPropertyValue getDefaultLineColor(); + DataDrivenPropertyValue getLineColor(const optional& klass = {}) const; + void setLineColor(DataDrivenPropertyValue, const optional& klass = {}); static PropertyValue> getDefaultLineTranslate(); PropertyValue> getLineTranslate(const optional& klass = {}) const; @@ -66,17 +67,17 @@ public: PropertyValue getLineWidth(const optional& klass = {}) const; void setLineWidth(PropertyValue, const optional& klass = {}); - static PropertyValue getDefaultLineGapWidth(); - PropertyValue getLineGapWidth(const optional& klass = {}) const; - void setLineGapWidth(PropertyValue, const optional& klass = {}); + static DataDrivenPropertyValue getDefaultLineGapWidth(); + DataDrivenPropertyValue getLineGapWidth(const optional& klass = {}) const; + void setLineGapWidth(DataDrivenPropertyValue, const optional& klass = {}); - static PropertyValue getDefaultLineOffset(); - PropertyValue getLineOffset(const optional& klass = {}) const; - void setLineOffset(PropertyValue, const optional& klass = {}); + static DataDrivenPropertyValue getDefaultLineOffset(); + DataDrivenPropertyValue getLineOffset(const optional& klass = {}) const; + void setLineOffset(DataDrivenPropertyValue, const optional& klass = {}); - static PropertyValue getDefaultLineBlur(); - PropertyValue getLineBlur(const optional& klass = {}) const; - void setLineBlur(PropertyValue, const optional& klass = {}); + static DataDrivenPropertyValue getDefaultLineBlur(); + DataDrivenPropertyValue getLineBlur(const optional& klass = {}) const; + void setLineBlur(DataDrivenPropertyValue, const optional& klass = {}); static PropertyValue> getDefaultLineDasharray(); PropertyValue> getLineDasharray(const optional& klass = {}) const; diff --git a/include/mbgl/style/layers/raster_layer.hpp b/include/mbgl/style/layers/raster_layer.hpp index ae6ec7f91c..e998abf12a 100644 --- a/include/mbgl/style/layers/raster_layer.hpp +++ b/include/mbgl/style/layers/raster_layer.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include diff --git a/include/mbgl/style/layers/symbol_layer.hpp b/include/mbgl/style/layers/symbol_layer.hpp index 1e2e6ac454..8826408e81 100644 --- a/include/mbgl/style/layers/symbol_layer.hpp +++ b/include/mbgl/style/layers/symbol_layer.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include @@ -72,9 +73,9 @@ public: PropertyValue getIconImage() const; void setIconImage(PropertyValue); - static PropertyValue getDefaultIconRotate(); - PropertyValue getIconRotate() const; - void setIconRotate(PropertyValue); + static DataDrivenPropertyValue getDefaultIconRotate(); + DataDrivenPropertyValue getIconRotate() const; + void setIconRotate(DataDrivenPropertyValue); static PropertyValue getDefaultIconPadding(); PropertyValue getIconPadding() const; @@ -84,9 +85,9 @@ public: PropertyValue getIconKeepUpright() const; void setIconKeepUpright(PropertyValue); - static PropertyValue> getDefaultIconOffset(); - PropertyValue> getIconOffset() const; - void setIconOffset(PropertyValue>); + static DataDrivenPropertyValue> getDefaultIconOffset(); + DataDrivenPropertyValue> getIconOffset() const; + void setIconOffset(DataDrivenPropertyValue>); static PropertyValue getDefaultTextPitchAlignment(); PropertyValue getTextPitchAlignment() const; diff --git a/include/mbgl/style/property_value.hpp b/include/mbgl/style/property_value.hpp index 83c4b4cf1b..e784633aa7 100644 --- a/include/mbgl/style/property_value.hpp +++ b/include/mbgl/style/property_value.hpp @@ -1,20 +1,16 @@ #pragma once #include -#include +#include +#include namespace mbgl { namespace style { -class Undefined {}; - -inline bool operator==(const Undefined&, const Undefined&) { return true; } -inline bool operator!=(const Undefined&, const Undefined&) { return false; } - template class PropertyValue { private: - using Value = variant>; + using Value = variant>; Value value; friend bool operator==(const PropertyValue& lhs, const PropertyValue& rhs) { @@ -26,16 +22,16 @@ private: } public: - PropertyValue() : value() {} - PropertyValue( T constant) : value(constant) {} - PropertyValue(Function function) : value(function) {} + PropertyValue() : value() {} + PropertyValue( T constant) : value(constant) {} + PropertyValue(CameraFunction function) : value(function) {} - bool isUndefined() const { return value.which() == 0; } - bool isConstant() const { return value.which() == 1; } - bool isFunction() const { return value.which() == 2; } + bool isUndefined() const { return value.which() == 0; } + bool isConstant() const { return value.which() == 1; } + bool isCameraFunction() const { return value.which() == 2; } - const T & asConstant() const { return value.template get< T >(); } - const Function& asFunction() const { return value.template get>(); } + const T & asConstant() const { return value.template get< T >(); } + const CameraFunction& asCameraFunction() const { return value.template get>(); } explicit operator bool() const { return !isUndefined(); }; diff --git a/include/mbgl/style/undefined.hpp b/include/mbgl/style/undefined.hpp new file mode 100644 index 0000000000..e43f132a80 --- /dev/null +++ b/include/mbgl/style/undefined.hpp @@ -0,0 +1,12 @@ +#pragma once + +namespace mbgl { +namespace style { + +class Undefined {}; + +inline bool operator==(const Undefined&, const Undefined&) { return true; } +inline bool operator!=(const Undefined&, const Undefined&) { return false; } + +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/util/feature.hpp b/include/mbgl/util/feature.hpp index b72aa15ddd..4eeceda944 100644 --- a/include/mbgl/util/feature.hpp +++ b/include/mbgl/util/feature.hpp @@ -12,4 +12,21 @@ using PropertyMap = mapbox::geometry::property_map; using FeatureIdentifier = mapbox::geometry::identifier; using Feature = mapbox::geometry::feature; +template +optional numericValue(const Value& value) { + return value.match( + [] (uint64_t t) { + return optional(t); + }, + [] (int64_t t) { + return optional(t); + }, + [] (double t) { + return optional(t); + }, + [] (auto) { + return optional(); + }); +} + } // namespace mbgl -- cgit v1.2.1