diff options
author | John Firebaugh <john.firebaugh@gmail.com> | 2016-10-28 16:39:50 -0700 |
---|---|---|
committer | John Firebaugh <john.firebaugh@gmail.com> | 2017-02-02 09:44:42 -0800 |
commit | 141e995806576364d185626176c1b993fc519291 (patch) | |
tree | ecdc41fc7699f2a1a9e9456157348451ebe99597 /include | |
parent | 6a6bddb4537004cc1bfc506e76772de74d33f3f7 (diff) | |
download | qtlocation-mapboxgl-141e995806576364d185626176c1b993fc519291.tar.gz |
[core] Add support for data-driven styling
Diffstat (limited to 'include')
25 files changed, 897 insertions, 164 deletions
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 <mbgl/util/variant.hpp> #include <mbgl/util/color.hpp> #include <mbgl/style/property_value.hpp> +#include <mbgl/style/data_driven_property_value.hpp> #include <cstdint> #include <vector> @@ -29,17 +30,17 @@ using ShapeAnnotationGeometry = variant< class LineAnnotation { public: ShapeAnnotationGeometry geometry; - style::PropertyValue<float> opacity { 1.0f }; + style::DataDrivenPropertyValue<float> opacity { 1.0f }; style::PropertyValue<float> width { 1.0f }; - style::PropertyValue<Color> color { Color::black() }; + style::DataDrivenPropertyValue<Color> color { Color::black() }; }; class FillAnnotation { public: ShapeAnnotationGeometry geometry; - style::PropertyValue<float> opacity { 1.0f }; - style::PropertyValue<Color> color { Color::black() }; - style::PropertyValue<Color> outlineColor {}; + style::DataDrivenPropertyValue<float> opacity { 1.0f }; + style::DataDrivenPropertyValue<Color> color { Color::black() }; + style::DataDrivenPropertyValue<Color> 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 <mbgl/style/data_driven_property_value.hpp> +#include <mbgl/style/conversion.hpp> +#include <mbgl/style/conversion/constant.hpp> +#include <mbgl/style/conversion/function.hpp> + +namespace mbgl { +namespace style { +namespace conversion { + +template <class T> +struct Converter<DataDrivenPropertyValue<T>> { + template <class V> + Result<DataDrivenPropertyValue<T>> operator()(const V& value) const { + if (isUndefined(value)) { + return {}; + } else if (!isObject(value)) { + Result<T> constant = convert<T>(value); + if (!constant) { + return constant.error(); + } + return DataDrivenPropertyValue<T>(*constant); + } else if (!objectMember(value, "property")) { + Result<CameraFunction<T>> function = convert<CameraFunction<T>>(value); + if (!function) { + return function.error(); + } + return DataDrivenPropertyValue<T>(*function); + } else { + Result<CompositeFunction<T>> composite = convert<CompositeFunction<T>>(value); + if (composite) { + return DataDrivenPropertyValue<T>(*composite); + } + Result<SourceFunction<T>> source = convert<SourceFunction<T>>(value); + if (!source) { + return source.error(); + } + return DataDrivenPropertyValue<T>(*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 <mbgl/style/function.hpp> +#include <mbgl/style/function/camera_function.hpp> +#include <mbgl/style/function/source_function.hpp> +#include <mbgl/style/function/composite_function.hpp> #include <mbgl/style/conversion.hpp> #include <mbgl/style/conversion/constant.hpp> +#include <mbgl/util/ignore.hpp> namespace mbgl { namespace style { namespace conversion { +template <class D, class R, class V> +Result<std::map<D, R>> 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<D, R> 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> d = convert<D>(arrayMember(stopValue, 0)); + if (!d) { + return d.error(); + } + + Result<R> r = convert<R>(arrayMember(stopValue, 1)); + if (!r) { + return r.error(); + } + + stops.emplace(*d, *r); + } + + return stops; +} + template <class T> -struct Converter<Function<T>> { +struct Converter<ExponentialStops<T>> { + static constexpr const char * type = "exponential"; + template <class V> - Result<Function<T>> operator()(const V& value) const { + Result<ExponentialStops<T>> operator()(const V& value) const { + auto stops = convertStops<float, T>(value); + if (!stops) { + return stops.error(); + } + + auto baseValue = objectMember(value, "base"); + if (!baseValue) { + return ExponentialStops<T>(*stops); + } + + optional<float> base = toNumber(*baseValue); + if (!base) { + return Error { "function base must be a number"}; + } + + return ExponentialStops<T>(*stops, *base); + } +}; + +template <class T> +struct Converter<IntervalStops<T>> { + static constexpr const char * type = "interval"; + + template <class V> + Result<IntervalStops<T>> operator()(const V& value) const { + auto stops = convertStops<float, T>(value); + if (!stops) { + return stops.error(); + } + return IntervalStops<T>(*stops); + } +}; + +template <> +struct Converter<CategoricalValue> { + template <class V> + Result<CategoricalValue> 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 <class T> +struct Converter<CategoricalStops<T>> { + static constexpr const char * type = "categorical"; + + template <class V> + Result<CategoricalStops<T>> operator()(const V& value) const { + auto stops = convertStops<CategoricalValue, T>(value); + if (!stops) { + return stops.error(); + } + return CategoricalStops<T>( + std::map<CategoricalValue, T>((*stops).begin(), (*stops).end())); + } +}; + +template <class T> +struct Converter<IdentityStops<T>> { + static constexpr const char * type = "identity"; + + template <class V> + Result<IdentityStops<T>> operator()(const V&) const { + return IdentityStops<T>(); + } +}; + +template <class, class> +struct StopsConverter; + +template <class T, class... Ts> +struct StopsConverter<T, variant<Ts...>> { +public: + template <class V> + Result<variant<Ts...>> operator()(const V& value) const { + std::string type = util::Interpolatable<T> ? "exponential" : "interval"; + + auto typeValue = objectMember(value, "type"); + if (typeValue && toString(*typeValue)) { + type = *toString(*typeValue); + } + + optional<Result<variant<Ts...>>> result; + + // Workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47226 + auto tryConvert = [&] (auto* tp) { + using Stops = std::decay_t<decltype(*tp)>; + if (type == Converter<Stops>::type) { + auto stops = convert<Stops>(value); + result = stops + ? Result<variant<Ts...>>(*stops) + : Result<variant<Ts...>>(stops.error()); + } + }; + + util::ignore({ + (tryConvert((Ts*)nullptr), 0)... + }); + + if (!result) { + return Error { "unsupported function type" }; + } + + return *result; + } +}; + +template <class T> +struct Converter<CameraFunction<T>> { + template <class V> + Result<CameraFunction<T>> operator()(const V& value) const { + if (!isObject(value)) { + return Error { "function must be an object" }; + } + + auto stops = StopsConverter<T, typename CameraFunction<T>::Stops>()(value); + if (!stops) { + return stops.error(); + } + + return CameraFunction<T>(*stops); + } +}; + +template <class T> +struct Converter<SourceFunction<T>> { + template <class V> + Result<SourceFunction<T>> 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<T, typename SourceFunction<T>::Stops>()(value); + if (!stops) { + return stops.error(); + } + + return SourceFunction<T>(*propertyString, *stops); + } +}; + +template <class S> +struct CompositeValue : std::pair<float, S> { + using std::pair<float, S>::pair; +}; + +template <class S> +struct Converter<CompositeValue<S>> { + template <class V> + Result<CompositeValue<S>> 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<float> z = convert<float>(*zoomValue); + if (!z) { + return z.error(); + } + + Result<S> s = convert<S>(*propertyValue); + if (!s) { + return s.error(); + } + + return CompositeValue<S> { *z, *s }; + } +}; + +template <class T> +struct Converter<CompositeFunction<T>> { + template <class V> + Result<CompositeFunction<T>> 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<std::pair<float, T>> stops; - for (std::size_t i = 0; i < arrayLength(*stopsValue); ++i) { - const auto& stopValue = arrayMember(*stopsValue, i); + if (type == "exponential") { + auto stops = convertStops<CompositeValue<float>, 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<float, ExponentialStops<T>> convertedStops; + for (const auto& stop : *stops) { + auto& inner = convertedStops[stop.first.first]; + inner.base = base; + inner.stops.emplace(stop.first.second, stop.second); } - optional<float> z = toNumber(arrayMember(stopValue, 0)); - if (!z) { - return Error { "function stop zoom level must be a number" }; + return CompositeFunction<T>(*propertyString, convertedStops); + } else if (type == "interval") { + auto stops = convertStops<CompositeValue<float>, T>(value); + if (!stops) { + return stops.error(); } - Result<T> v = convert<T>(arrayMember(stopValue, 1)); - if (!v) { - return v.error(); + std::map<float, IntervalStops<T>> 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<T>(*propertyString, convertedStops); + } else if (type == "categorical") { + auto stops = convertStops<CompositeValue<CategoricalValue>, T>(value); + if (!stops) { + return stops.error(); + } - auto baseValue = objectMember(value, "base"); - if (!baseValue) { - return Function<T>(stops, 1.0f); - } + std::map<float, CategoricalStops<T>> convertedStops; + for (const auto& stop : *stops) { + auto& inner = convertedStops[stop.first.first]; + inner.stops.emplace(stop.first.second, stop.second); + } - optional<float> base = toNumber(*baseValue); - if (!base) { - return Error { "function base must be a number"}; + return CompositeFunction<T>(*propertyString, convertedStops); + } else { + return Error { "unsupported function type" }; } - - return Function<T>(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 <mbgl/style/conversion.hpp> #include <mbgl/style/conversion/constant.hpp> #include <mbgl/style/conversion/property_value.hpp> +#include <mbgl/style/conversion/data_driven_property_value.hpp> #include <functional> #include <string> @@ -18,15 +19,15 @@ using LayoutPropertySetter = std::function<optional<Error> (Layer&, const V&)>; template <class V> using PaintPropertySetter = std::function<optional<Error> (Layer&, const V&, const optional<std::string>&)>; -template <class V, class L, class T, class...Args> -auto makePropertySetter(void (L::*setter)(PropertyValue<T>, const Args&...args)) { +template <class V, class L, class PropertyValue, class...Args> +auto makePropertySetter(void (L::*setter)(PropertyValue, const Args&...args)) { return [setter] (Layer& layer, const V& value, const Args&...args) -> optional<Error> { L* typedLayer = layer.as<L>(); if (!typedLayer) { return Error { "layer doesn't support this property" }; } - Result<PropertyValue<T>> typedValue = convert<PropertyValue<T>>(value); + Result<PropertyValue> typedValue = convert<PropertyValue>(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<PropertyValue<T>> { if (isUndefined(value)) { return {}; } else if (isObject(value)) { - Result<Function<T>> function = convert<Function<T>>(value); + Result<CameraFunction<T>> function = convert<CameraFunction<T>>(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 <mbgl/util/variant.hpp> +#include <mbgl/style/undefined.hpp> +#include <mbgl/style/function/camera_function.hpp> +#include <mbgl/style/function/source_function.hpp> +#include <mbgl/style/function/composite_function.hpp> + +namespace mbgl { +namespace style { + +template <class T> +class DataDrivenPropertyValue { +private: + using Value = variant< + Undefined, + T, + CameraFunction<T>, + SourceFunction<T>, + CompositeFunction<T>>; + + 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<T> v) : value(std::move(v)) {} + DataDrivenPropertyValue( SourceFunction<T> v) : value(std::move(v)) {} + DataDrivenPropertyValue(CompositeFunction<T> v) : value(std::move(v)) {} + + bool isUndefined() const { + return value.template is<Undefined>(); + } + + bool isDataDriven() const { + return value.template is<SourceFunction<T>>() || value.template is<CompositeFunction<T>>(); + } + + template <typename Evaluator> + 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 <cassert> -#include <utility> -#include <vector> - -namespace mbgl { -namespace style { - -template <typename T> -class Function { -public: - using Stop = std::pair<float, T>; - using Stops = std::vector<Stop>; - - Function(Stops stops_, float base_) - : base(base_), stops(std::move(stops_)) { - assert(stops.size() > 0); - } - - float getBase() const { return base; } - const std::vector<std::pair<float, T>>& 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<std::pair<float, T>> 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 <mbgl/style/function/exponential_stops.hpp> +#include <mbgl/style/function/interval_stops.hpp> +#include <mbgl/util/interpolate.hpp> +#include <mbgl/util/variant.hpp> + +namespace mbgl { +namespace style { + +template <class T> +class CameraFunction { +public: + using Stops = std::conditional_t< + util::Interpolatable<T>, + variant< + ExponentialStops<T>, + IntervalStops<T>>, + variant< + IntervalStops<T>>>; + + 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 <mbgl/util/feature.hpp> +#include <mbgl/util/variant.hpp> + +#include <cassert> +#include <utility> +#include <map> + +namespace mbgl { +namespace style { + +class CategoricalValue : public variant<bool, int64_t, std::string> { +public: + using variant<bool, int64_t, std::string>::variant; +}; + +template <class T> +class CategoricalStops { +public: + using Stops = std::map<CategoricalValue, T>; + + 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 <mbgl/style/function/exponential_stops.hpp> +#include <mbgl/style/function/interval_stops.hpp> +#include <mbgl/style/function/categorical_stops.hpp> +#include <mbgl/util/interpolate.hpp> +#include <mbgl/util/range.hpp> +#include <mbgl/util/variant.hpp> + +#include <string> +#include <tuple> + +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 T> +class CompositeFunction { +public: + using InnerStops = std::conditional_t< + util::Interpolatable<T>, + variant< + ExponentialStops<T>, + IntervalStops<T>, + CategoricalStops<T>>, + variant< + IntervalStops<T>, + CategoricalStops<T>>>; + + using Stops = std::conditional_t< + util::Interpolatable<T>, + variant< + std::map<float, ExponentialStops<T>>, + std::map<float, IntervalStops<T>>, + std::map<float, CategoricalStops<T>>>, + variant< + std::map<float, IntervalStops<T>>, + std::map<float, CategoricalStops<T>>>>; + + CompositeFunction(std::string property_, Stops stops_) + : property(std::move(property_)), + stops(std::move(stops_)) { + } + + std::tuple<Range<float>, Range<InnerStops>> + 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<float> { + minIt == s.end() ? s.rbegin()->first : minIt->first, + maxIt == s.end() ? s.rbegin()->first : maxIt->first + }, + Range<InnerStops> { + minIt == s.end() ? s.rbegin()->second : minIt->second, + maxIt == s.end() ? s.rbegin()->second : maxIt->second + } + ); + } + ); + } + + Range<T> evaluate(Range<InnerStops> coveringStops, + const GeometryTileFeature& feature) const { + optional<Value> v = feature.getValue(property); + if (!v) { + return { T(), T() }; + } + auto eval = [&] (const auto& s) { + return s.evaluate(*v); + }; + return Range<T> { + coveringStops.min.match(eval), + coveringStops.max.match(eval) + }; + } + + T evaluate(float zoom, const GeometryTileFeature& feature) const { + std::tuple<Range<float>, Range<InnerStops>> ranges = coveringRanges(zoom); + Range<T> 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 <mbgl/util/feature.hpp> +#include <mbgl/util/interpolate.hpp> + +#include <map> + +namespace mbgl { +namespace style { + +template <class T> +class ExponentialStops { +public: + using Stops = std::map<float, T>; + + 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<float> z = numericValue<float>(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 <mbgl/util/feature.hpp> + +namespace mbgl { +namespace style { + +template <class T> +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 <mbgl/util/feature.hpp> + +#include <map> + +namespace mbgl { +namespace style { + +template <class T> +class IntervalStops { +public: + using Stops = std::map<float, T>; + 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<float> z = numericValue<float>(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 <mbgl/style/function/exponential_stops.hpp> +#include <mbgl/style/function/interval_stops.hpp> +#include <mbgl/style/function/categorical_stops.hpp> +#include <mbgl/style/function/identity_stops.hpp> +#include <mbgl/tile/geometry_tile_data.hpp> +#include <mbgl/util/interpolate.hpp> +#include <mbgl/util/variant.hpp> + +#include <string> + +namespace mbgl { +namespace style { + +template <class T> +class SourceFunction { +public: + using Stops = std::conditional_t< + util::Interpolatable<T>, + variant< + ExponentialStops<T>, + IntervalStops<T>, + CategoricalStops<T>, + IdentityStops<T>>, + variant< + IntervalStops<T>, + CategoricalStops<T>, + IdentityStops<T>>>; + + SourceFunction(std::string property_, Stops stops_) + : property(std::move(property_)), + stops(std::move(stops_)) { + } + + T evaluate(const GeometryTileFeature& feature) const { + optional<Value> 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 <mbgl/style/layer.hpp> #include <mbgl/style/filter.hpp> #include <mbgl/style/property_value.hpp> +#include <mbgl/style/data_driven_property_value.hpp> #include <mbgl/util/color.hpp> 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 <mbgl/style/layer.hpp> #include <mbgl/style/filter.hpp> #include <mbgl/style/property_value.hpp> +#include <mbgl/style/data_driven_property_value.hpp> #include <mbgl/util/color.hpp> @@ -26,21 +27,21 @@ public: // Paint properties - static PropertyValue<float> getDefaultCircleRadius(); - PropertyValue<float> getCircleRadius(const optional<std::string>& klass = {}) const; - void setCircleRadius(PropertyValue<float>, const optional<std::string>& klass = {}); + static DataDrivenPropertyValue<float> getDefaultCircleRadius(); + DataDrivenPropertyValue<float> getCircleRadius(const optional<std::string>& klass = {}) const; + void setCircleRadius(DataDrivenPropertyValue<float>, const optional<std::string>& klass = {}); - static PropertyValue<Color> getDefaultCircleColor(); - PropertyValue<Color> getCircleColor(const optional<std::string>& klass = {}) const; - void setCircleColor(PropertyValue<Color>, const optional<std::string>& klass = {}); + static DataDrivenPropertyValue<Color> getDefaultCircleColor(); + DataDrivenPropertyValue<Color> getCircleColor(const optional<std::string>& klass = {}) const; + void setCircleColor(DataDrivenPropertyValue<Color>, const optional<std::string>& klass = {}); - static PropertyValue<float> getDefaultCircleBlur(); - PropertyValue<float> getCircleBlur(const optional<std::string>& klass = {}) const; - void setCircleBlur(PropertyValue<float>, const optional<std::string>& klass = {}); + static DataDrivenPropertyValue<float> getDefaultCircleBlur(); + DataDrivenPropertyValue<float> getCircleBlur(const optional<std::string>& klass = {}) const; + void setCircleBlur(DataDrivenPropertyValue<float>, const optional<std::string>& klass = {}); - static PropertyValue<float> getDefaultCircleOpacity(); - PropertyValue<float> getCircleOpacity(const optional<std::string>& klass = {}) const; - void setCircleOpacity(PropertyValue<float>, const optional<std::string>& klass = {}); + static DataDrivenPropertyValue<float> getDefaultCircleOpacity(); + DataDrivenPropertyValue<float> getCircleOpacity(const optional<std::string>& klass = {}) const; + void setCircleOpacity(DataDrivenPropertyValue<float>, const optional<std::string>& klass = {}); static PropertyValue<std::array<float, 2>> getDefaultCircleTranslate(); PropertyValue<std::array<float, 2>> getCircleTranslate(const optional<std::string>& klass = {}) const; @@ -54,17 +55,17 @@ public: PropertyValue<CirclePitchScaleType> getCirclePitchScale(const optional<std::string>& klass = {}) const; void setCirclePitchScale(PropertyValue<CirclePitchScaleType>, const optional<std::string>& klass = {}); - static PropertyValue<float> getDefaultCircleStrokeWidth(); - PropertyValue<float> getCircleStrokeWidth(const optional<std::string>& klass = {}) const; - void setCircleStrokeWidth(PropertyValue<float>, const optional<std::string>& klass = {}); + static DataDrivenPropertyValue<float> getDefaultCircleStrokeWidth(); + DataDrivenPropertyValue<float> getCircleStrokeWidth(const optional<std::string>& klass = {}) const; + void setCircleStrokeWidth(DataDrivenPropertyValue<float>, const optional<std::string>& klass = {}); - static PropertyValue<Color> getDefaultCircleStrokeColor(); - PropertyValue<Color> getCircleStrokeColor(const optional<std::string>& klass = {}) const; - void setCircleStrokeColor(PropertyValue<Color>, const optional<std::string>& klass = {}); + static DataDrivenPropertyValue<Color> getDefaultCircleStrokeColor(); + DataDrivenPropertyValue<Color> getCircleStrokeColor(const optional<std::string>& klass = {}) const; + void setCircleStrokeColor(DataDrivenPropertyValue<Color>, const optional<std::string>& klass = {}); - static PropertyValue<float> getDefaultCircleStrokeOpacity(); - PropertyValue<float> getCircleStrokeOpacity(const optional<std::string>& klass = {}) const; - void setCircleStrokeOpacity(PropertyValue<float>, const optional<std::string>& klass = {}); + static DataDrivenPropertyValue<float> getDefaultCircleStrokeOpacity(); + DataDrivenPropertyValue<float> getCircleStrokeOpacity(const optional<std::string>& klass = {}) const; + void setCircleStrokeOpacity(DataDrivenPropertyValue<float>, const optional<std::string>& 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 <mbgl/style/layer.hpp> #include <mbgl/style/filter.hpp> #include <mbgl/style/property_value.hpp> +#include <mbgl/style/data_driven_property_value.hpp> #include <mbgl/util/color.hpp> @@ -30,9 +31,9 @@ public: PropertyValue<float> getFillExtrusionOpacity(const optional<std::string>& klass = {}) const; void setFillExtrusionOpacity(PropertyValue<float>, const optional<std::string>& klass = {}); - static PropertyValue<Color> getDefaultFillExtrusionColor(); - PropertyValue<Color> getFillExtrusionColor(const optional<std::string>& klass = {}) const; - void setFillExtrusionColor(PropertyValue<Color>, const optional<std::string>& klass = {}); + static DataDrivenPropertyValue<Color> getDefaultFillExtrusionColor(); + DataDrivenPropertyValue<Color> getFillExtrusionColor(const optional<std::string>& klass = {}) const; + void setFillExtrusionColor(DataDrivenPropertyValue<Color>, const optional<std::string>& klass = {}); static PropertyValue<std::array<float, 2>> getDefaultFillExtrusionTranslate(); PropertyValue<std::array<float, 2>> getFillExtrusionTranslate(const optional<std::string>& klass = {}) const; @@ -46,13 +47,13 @@ public: PropertyValue<std::string> getFillExtrusionPattern(const optional<std::string>& klass = {}) const; void setFillExtrusionPattern(PropertyValue<std::string>, const optional<std::string>& klass = {}); - static PropertyValue<float> getDefaultFillExtrusionHeight(); - PropertyValue<float> getFillExtrusionHeight(const optional<std::string>& klass = {}) const; - void setFillExtrusionHeight(PropertyValue<float>, const optional<std::string>& klass = {}); + static DataDrivenPropertyValue<float> getDefaultFillExtrusionHeight(); + DataDrivenPropertyValue<float> getFillExtrusionHeight(const optional<std::string>& klass = {}) const; + void setFillExtrusionHeight(DataDrivenPropertyValue<float>, const optional<std::string>& klass = {}); - static PropertyValue<float> getDefaultFillExtrusionBase(); - PropertyValue<float> getFillExtrusionBase(const optional<std::string>& klass = {}) const; - void setFillExtrusionBase(PropertyValue<float>, const optional<std::string>& klass = {}); + static DataDrivenPropertyValue<float> getDefaultFillExtrusionBase(); + DataDrivenPropertyValue<float> getFillExtrusionBase(const optional<std::string>& klass = {}) const; + void setFillExtrusionBase(DataDrivenPropertyValue<float>, const optional<std::string>& 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 <mbgl/style/layer.hpp> #include <mbgl/style/filter.hpp> #include <mbgl/style/property_value.hpp> +#include <mbgl/style/data_driven_property_value.hpp> #include <mbgl/util/color.hpp> @@ -30,17 +31,17 @@ public: PropertyValue<bool> getFillAntialias(const optional<std::string>& klass = {}) const; void setFillAntialias(PropertyValue<bool>, const optional<std::string>& klass = {}); - static PropertyValue<float> getDefaultFillOpacity(); - PropertyValue<float> getFillOpacity(const optional<std::string>& klass = {}) const; - void setFillOpacity(PropertyValue<float>, const optional<std::string>& klass = {}); + static DataDrivenPropertyValue<float> getDefaultFillOpacity(); + DataDrivenPropertyValue<float> getFillOpacity(const optional<std::string>& klass = {}) const; + void setFillOpacity(DataDrivenPropertyValue<float>, const optional<std::string>& klass = {}); - static PropertyValue<Color> getDefaultFillColor(); - PropertyValue<Color> getFillColor(const optional<std::string>& klass = {}) const; - void setFillColor(PropertyValue<Color>, const optional<std::string>& klass = {}); + static DataDrivenPropertyValue<Color> getDefaultFillColor(); + DataDrivenPropertyValue<Color> getFillColor(const optional<std::string>& klass = {}) const; + void setFillColor(DataDrivenPropertyValue<Color>, const optional<std::string>& klass = {}); - static PropertyValue<Color> getDefaultFillOutlineColor(); - PropertyValue<Color> getFillOutlineColor(const optional<std::string>& klass = {}) const; - void setFillOutlineColor(PropertyValue<Color>, const optional<std::string>& klass = {}); + static DataDrivenPropertyValue<Color> getDefaultFillOutlineColor(); + DataDrivenPropertyValue<Color> getFillOutlineColor(const optional<std::string>& klass = {}) const; + void setFillOutlineColor(DataDrivenPropertyValue<Color>, const optional<std::string>& klass = {}); static PropertyValue<std::array<float, 2>> getDefaultFillTranslate(); PropertyValue<std::array<float, 2>> getFillTranslate(const optional<std::string>& 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 <mbgl/style/layer.hpp> #include <mbgl/style/filter.hpp> #include <mbgl/style/property_value.hpp> +#include <mbgl/style/data_driven_property_value.hpp> #include <mbgl/util/color.hpp> @@ -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<std::string>& klass = {}) const; - void set<%- camelize(property.name) %>(PropertyValue<<%- propertyType(property) %>>, const optional<std::string>& klass = {}); + static <%- propertyValueType(property) %> getDefault<%- camelize(property.name) %>(); + <%- propertyValueType(property) %> get<%- camelize(property.name) %>(const optional<std::string>& klass = {}) const; + void set<%- camelize(property.name) %>(<%- propertyValueType(property) %>, const optional<std::string>& 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 <mbgl/style/layer.hpp> #include <mbgl/style/filter.hpp> #include <mbgl/style/property_value.hpp> +#include <mbgl/style/data_driven_property_value.hpp> #include <mbgl/util/color.hpp> @@ -46,13 +47,13 @@ public: // Paint properties - static PropertyValue<float> getDefaultLineOpacity(); - PropertyValue<float> getLineOpacity(const optional<std::string>& klass = {}) const; - void setLineOpacity(PropertyValue<float>, const optional<std::string>& klass = {}); + static DataDrivenPropertyValue<float> getDefaultLineOpacity(); + DataDrivenPropertyValue<float> getLineOpacity(const optional<std::string>& klass = {}) const; + void setLineOpacity(DataDrivenPropertyValue<float>, const optional<std::string>& klass = {}); - static PropertyValue<Color> getDefaultLineColor(); - PropertyValue<Color> getLineColor(const optional<std::string>& klass = {}) const; - void setLineColor(PropertyValue<Color>, const optional<std::string>& klass = {}); + static DataDrivenPropertyValue<Color> getDefaultLineColor(); + DataDrivenPropertyValue<Color> getLineColor(const optional<std::string>& klass = {}) const; + void setLineColor(DataDrivenPropertyValue<Color>, const optional<std::string>& klass = {}); static PropertyValue<std::array<float, 2>> getDefaultLineTranslate(); PropertyValue<std::array<float, 2>> getLineTranslate(const optional<std::string>& klass = {}) const; @@ -66,17 +67,17 @@ public: PropertyValue<float> getLineWidth(const optional<std::string>& klass = {}) const; void setLineWidth(PropertyValue<float>, const optional<std::string>& klass = {}); - static PropertyValue<float> getDefaultLineGapWidth(); - PropertyValue<float> getLineGapWidth(const optional<std::string>& klass = {}) const; - void setLineGapWidth(PropertyValue<float>, const optional<std::string>& klass = {}); + static DataDrivenPropertyValue<float> getDefaultLineGapWidth(); + DataDrivenPropertyValue<float> getLineGapWidth(const optional<std::string>& klass = {}) const; + void setLineGapWidth(DataDrivenPropertyValue<float>, const optional<std::string>& klass = {}); - static PropertyValue<float> getDefaultLineOffset(); - PropertyValue<float> getLineOffset(const optional<std::string>& klass = {}) const; - void setLineOffset(PropertyValue<float>, const optional<std::string>& klass = {}); + static DataDrivenPropertyValue<float> getDefaultLineOffset(); + DataDrivenPropertyValue<float> getLineOffset(const optional<std::string>& klass = {}) const; + void setLineOffset(DataDrivenPropertyValue<float>, const optional<std::string>& klass = {}); - static PropertyValue<float> getDefaultLineBlur(); - PropertyValue<float> getLineBlur(const optional<std::string>& klass = {}) const; - void setLineBlur(PropertyValue<float>, const optional<std::string>& klass = {}); + static DataDrivenPropertyValue<float> getDefaultLineBlur(); + DataDrivenPropertyValue<float> getLineBlur(const optional<std::string>& klass = {}) const; + void setLineBlur(DataDrivenPropertyValue<float>, const optional<std::string>& klass = {}); static PropertyValue<std::vector<float>> getDefaultLineDasharray(); PropertyValue<std::vector<float>> getLineDasharray(const optional<std::string>& 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 <mbgl/style/layer.hpp> #include <mbgl/style/filter.hpp> #include <mbgl/style/property_value.hpp> +#include <mbgl/style/data_driven_property_value.hpp> #include <mbgl/util/color.hpp> 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 <mbgl/style/layer.hpp> #include <mbgl/style/filter.hpp> #include <mbgl/style/property_value.hpp> +#include <mbgl/style/data_driven_property_value.hpp> #include <mbgl/util/color.hpp> @@ -72,9 +73,9 @@ public: PropertyValue<std::string> getIconImage() const; void setIconImage(PropertyValue<std::string>); - static PropertyValue<float> getDefaultIconRotate(); - PropertyValue<float> getIconRotate() const; - void setIconRotate(PropertyValue<float>); + static DataDrivenPropertyValue<float> getDefaultIconRotate(); + DataDrivenPropertyValue<float> getIconRotate() const; + void setIconRotate(DataDrivenPropertyValue<float>); static PropertyValue<float> getDefaultIconPadding(); PropertyValue<float> getIconPadding() const; @@ -84,9 +85,9 @@ public: PropertyValue<bool> getIconKeepUpright() const; void setIconKeepUpright(PropertyValue<bool>); - static PropertyValue<std::array<float, 2>> getDefaultIconOffset(); - PropertyValue<std::array<float, 2>> getIconOffset() const; - void setIconOffset(PropertyValue<std::array<float, 2>>); + static DataDrivenPropertyValue<std::array<float, 2>> getDefaultIconOffset(); + DataDrivenPropertyValue<std::array<float, 2>> getIconOffset() const; + void setIconOffset(DataDrivenPropertyValue<std::array<float, 2>>); static PropertyValue<AlignmentType> getDefaultTextPitchAlignment(); PropertyValue<AlignmentType> 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 <mbgl/util/variant.hpp> -#include <mbgl/style/function.hpp> +#include <mbgl/style/undefined.hpp> +#include <mbgl/style/function/camera_function.hpp> 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 T> class PropertyValue { private: - using Value = variant<Undefined, T, Function<T>>; + using Value = variant<Undefined, T, CameraFunction<T>>; 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<T> function) : value(function) {} + PropertyValue() : value() {} + PropertyValue( T constant) : value(constant) {} + PropertyValue(CameraFunction<T> 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<T>& asFunction() const { return value.template get<Function<T>>(); } + const T & asConstant() const { return value.template get< T >(); } + const CameraFunction<T>& asCameraFunction() const { return value.template get<CameraFunction<T>>(); } 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<double>; +template <class T> +optional<T> numericValue(const Value& value) { + return value.match( + [] (uint64_t t) { + return optional<T>(t); + }, + [] (int64_t t) { + return optional<T>(t); + }, + [] (double t) { + return optional<T>(t); + }, + [] (auto) { + return optional<T>(); + }); +} + } // namespace mbgl |