summaryrefslogtreecommitdiff
path: root/include
diff options
context:
space:
mode:
Diffstat (limited to 'include')
-rw-r--r--include/mbgl/map/map.hpp8
-rw-r--r--include/mbgl/map/mode.hpp12
-rw-r--r--include/mbgl/renderer/mode.hpp18
-rw-r--r--include/mbgl/renderer/renderer.hpp2
-rw-r--r--include/mbgl/renderer/renderer_backend.hpp2
-rw-r--r--include/mbgl/renderer/renderer_observer.hpp35
-rw-r--r--include/mbgl/storage/offline.hpp6
-rw-r--r--include/mbgl/style/conversion.hpp259
-rw-r--r--include/mbgl/style/conversion/constant.hpp98
-rw-r--r--include/mbgl/style/conversion/coordinate.hpp21
-rw-r--r--include/mbgl/style/conversion/custom_geometry_source_options.hpp84
-rw-r--r--include/mbgl/style/conversion/data_driven_property_value.hpp28
-rw-r--r--include/mbgl/style/conversion/expression.hpp39
-rw-r--r--include/mbgl/style/conversion/filter.hpp243
-rw-r--r--include/mbgl/style/conversion/function.hpp47
-rw-r--r--include/mbgl/style/conversion/geojson.hpp10
-rw-r--r--include/mbgl/style/conversion/geojson_options.hpp79
-rw-r--r--include/mbgl/style/conversion/get_json_type.hpp14
-rw-r--r--include/mbgl/style/conversion/layer.hpp213
-rw-r--r--include/mbgl/style/conversion/light.hpp106
-rw-r--r--include/mbgl/style/conversion/make_property_setters.hpp211
-rw-r--r--include/mbgl/style/conversion/make_property_setters.hpp.ejs48
-rw-r--r--include/mbgl/style/conversion/position.hpp14
-rw-r--r--include/mbgl/style/conversion/property_setter.hpp72
-rw-r--r--include/mbgl/style/conversion/property_value.hpp19
-rw-r--r--include/mbgl/style/conversion/source.hpp176
-rw-r--r--include/mbgl/style/conversion/tileset.hpp96
-rw-r--r--include/mbgl/style/conversion/transition_options.hpp32
-rw-r--r--include/mbgl/style/expression/array_assertion.hpp43
-rw-r--r--include/mbgl/style/expression/assertion.hpp36
-rw-r--r--include/mbgl/style/expression/at.hpp42
-rw-r--r--include/mbgl/style/expression/boolean_operator.hpp50
-rw-r--r--include/mbgl/style/expression/case.hpp38
-rw-r--r--include/mbgl/style/expression/check_subtype.hpp17
-rw-r--r--include/mbgl/style/expression/coalesce.hpp47
-rw-r--r--include/mbgl/style/expression/coercion.hpp39
-rw-r--r--include/mbgl/style/expression/compound_expression.hpp142
-rw-r--r--include/mbgl/style/expression/equals.hpp32
-rw-r--r--include/mbgl/style/expression/expression.hpp179
-rw-r--r--include/mbgl/style/expression/find_zoom_curve.hpp20
-rw-r--r--include/mbgl/style/expression/get_covering_stops.hpp18
-rw-r--r--include/mbgl/style/expression/interpolate.hpp192
-rw-r--r--include/mbgl/style/expression/is_constant.hpp35
-rw-r--r--include/mbgl/style/expression/is_expression.hpp13
-rw-r--r--include/mbgl/style/expression/let.hpp76
-rw-r--r--include/mbgl/style/expression/literal.hpp42
-rw-r--r--include/mbgl/style/expression/match.hpp46
-rw-r--r--include/mbgl/style/expression/parsing_context.hpp156
-rw-r--r--include/mbgl/style/expression/step.hpp48
-rw-r--r--include/mbgl/style/expression/type.hpp112
-rw-r--r--include/mbgl/style/expression/value.hpp162
-rw-r--r--include/mbgl/style/function/camera_function.hpp63
-rw-r--r--include/mbgl/style/function/composite_function.hpp140
-rw-r--r--include/mbgl/style/function/convert.hpp355
-rw-r--r--include/mbgl/style/function/source_function.hpp45
-rw-r--r--include/mbgl/style/layer.hpp7
-rw-r--r--include/mbgl/style/layer_type.hpp3
-rw-r--r--include/mbgl/style/layers/hillshade_layer.hpp86
-rw-r--r--include/mbgl/style/layers/layer.hpp.ejs2
-rw-r--r--include/mbgl/style/layers/symbol_layer.hpp6
-rw-r--r--include/mbgl/style/source.hpp5
-rw-r--r--include/mbgl/style/sources/custom_geometry_source.hpp58
-rw-r--r--include/mbgl/style/sources/geojson_source.hpp2
-rw-r--r--include/mbgl/style/sources/raster_dem_source.hpp25
-rw-r--r--include/mbgl/style/sources/raster_source.hpp4
-rw-r--r--include/mbgl/style/types.hpp15
-rw-r--r--include/mbgl/tile/tile_id.hpp6
-rw-r--r--include/mbgl/util/any.hpp10
-rw-r--r--include/mbgl/util/enum.hpp1
-rw-r--r--include/mbgl/util/indexed_tuple.hpp10
-rw-r--r--include/mbgl/util/interpolate.hpp33
-rw-r--r--include/mbgl/util/optional.hpp3
-rw-r--r--include/mbgl/util/projection.hpp4
-rw-r--r--include/mbgl/util/run_loop.hpp54
-rw-r--r--include/mbgl/util/string.hpp8
-rw-r--r--include/mbgl/util/thread.hpp21
-rw-r--r--include/mbgl/util/tileset.hpp4
-rw-r--r--include/mbgl/util/tuple.hpp19
-rw-r--r--include/mbgl/util/unique_any.hpp275
-rw-r--r--include/mbgl/util/unitbezier.hpp17
80 files changed, 3239 insertions, 1619 deletions
diff --git a/include/mbgl/map/map.hpp b/include/mbgl/map/map.hpp
index c5f90d99e1..5ba23a76dd 100644
--- a/include/mbgl/map/map.hpp
+++ b/include/mbgl/map/map.hpp
@@ -127,6 +127,14 @@ public:
void setViewportMode(ViewportMode);
ViewportMode getViewportMode() const;
+ // Projection mode
+ void setAxonometric(bool);
+ bool getAxonometric() const;
+ void setXSkew(double ySkew);
+ double getXSkew() const;
+ void setYSkew(double ySkew);
+ double getYSkew() const;
+
// Size
void setSize(Size);
Size getSize() const;
diff --git a/include/mbgl/map/mode.hpp b/include/mbgl/map/mode.hpp
index 05de2df22c..4ee289d855 100644
--- a/include/mbgl/map/mode.hpp
+++ b/include/mbgl/map/mode.hpp
@@ -11,16 +11,8 @@ using EnumType = uint32_t;
enum class MapMode : EnumType {
Continuous, // continually updating map
- Still, // a once-off still image
-};
-
-// We can avoid redundant GL calls when it is known that the GL context is not
-// being shared. In a shared GL context case, we need to make sure that the
-// correct GL configurations are in use - they might have changed between render
-// calls.
-enum class GLContextMode : EnumType {
- Unique,
- Shared,
+ Static, // a once-off still image of an arbitrary viewport
+ Tile // a once-off still image of a single tile
};
// We can choose to constrain the map both horizontally or vertically, or only
diff --git a/include/mbgl/renderer/mode.hpp b/include/mbgl/renderer/mode.hpp
new file mode 100644
index 0000000000..6ff42d8058
--- /dev/null
+++ b/include/mbgl/renderer/mode.hpp
@@ -0,0 +1,18 @@
+#pragma once
+
+#include <cstdint>
+
+namespace mbgl {
+
+using EnumType = uint32_t;
+
+// We can avoid redundant GL calls when it is known that the GL context is not
+// being shared. In a shared GL context case, we need to make sure that the
+// correct GL configurations are in use - they might have changed between render
+// calls.
+enum class GLContextMode : EnumType {
+ Unique,
+ Shared,
+};
+
+} // namespace mbgl
diff --git a/include/mbgl/renderer/renderer.hpp b/include/mbgl/renderer/renderer.hpp
index a60f3f6e4d..db28ee92fc 100644
--- a/include/mbgl/renderer/renderer.hpp
+++ b/include/mbgl/renderer/renderer.hpp
@@ -1,7 +1,7 @@
#pragma once
-#include <mbgl/map/mode.hpp>
#include <mbgl/renderer/query.hpp>
+#include <mbgl/renderer/mode.hpp>
#include <mbgl/annotation/annotation.hpp>
#include <mbgl/util/geo.hpp>
#include <mbgl/util/geo.hpp>
diff --git a/include/mbgl/renderer/renderer_backend.hpp b/include/mbgl/renderer/renderer_backend.hpp
index 295838c71b..b83c128169 100644
--- a/include/mbgl/renderer/renderer_backend.hpp
+++ b/include/mbgl/renderer/renderer_backend.hpp
@@ -41,7 +41,7 @@ protected:
// Called with the name of an OpenGL extension that should be loaded. RendererBackend implementations
// must call the API-specific version that obtains the function pointer for this function,
// or a null pointer if unsupported/unavailable.
- virtual gl::ProcAddress initializeExtension(const char*) = 0;
+ virtual gl::ProcAddress getExtensionFunctionPointer(const char*) = 0;
// Called when the backend's GL context needs to be made active or inactive. These are called,
// as a matched pair, exclusively through BackendScope, in two situations:
diff --git a/include/mbgl/renderer/renderer_observer.hpp b/include/mbgl/renderer/renderer_observer.hpp
new file mode 100644
index 0000000000..551b5c803e
--- /dev/null
+++ b/include/mbgl/renderer/renderer_observer.hpp
@@ -0,0 +1,35 @@
+#pragma once
+
+#include <exception>
+
+namespace mbgl {
+
+class RendererObserver {
+public:
+ virtual ~RendererObserver() = default;
+
+ enum class RenderMode : uint32_t {
+ Partial,
+ Full
+ };
+
+ // Signals that a repaint is required
+ virtual void onInvalidate() {}
+
+ // Resource failed to download / parse
+ virtual void onResourceError(std::exception_ptr) {}
+
+ // First frame
+ virtual void onWillStartRenderingMap() {}
+
+ // Start of frame, initial is the first frame for this map
+ virtual void onWillStartRenderingFrame() {}
+
+ // End of frame, boolean flags that a repaint is required
+ virtual void onDidFinishRenderingFrame(RenderMode, bool) {}
+
+ // Final frame
+ virtual void onDidFinishRenderingMap() {}
+};
+
+} // namespace mbgl
diff --git a/include/mbgl/storage/offline.hpp b/include/mbgl/storage/offline.hpp
index 117dd0591b..ef4a499e83 100644
--- a/include/mbgl/storage/offline.hpp
+++ b/include/mbgl/storage/offline.hpp
@@ -30,15 +30,15 @@ public:
OfflineTilePyramidRegionDefinition(std::string, LatLngBounds, double, double, float);
/* Private */
- std::vector<CanonicalTileID> tileCover(SourceType, uint16_t tileSize, const Range<uint8_t>& zoomRange) const;
- uint64_t tileCount(SourceType, uint16_t tileSize, const Range<uint8_t>& zoomRange) const;
+ std::vector<CanonicalTileID> tileCover(style::SourceType, uint16_t tileSize, const Range<uint8_t>& zoomRange) const;
+ uint64_t tileCount(style::SourceType, uint16_t tileSize, const Range<uint8_t>& zoomRange) const;
const std::string styleURL;
const LatLngBounds bounds;
const double minZoom;
const double maxZoom;
const float pixelRatio;
private:
- Range<uint8_t> coveringZoomRange(SourceType, uint16_t tileSize, const Range<uint8_t>& zoomRange) const;
+ Range<uint8_t> coveringZoomRange(style::SourceType, uint16_t tileSize, const Range<uint8_t>& zoomRange) const;
};
/*
diff --git a/include/mbgl/style/conversion.hpp b/include/mbgl/style/conversion.hpp
index 27504a89b1..71c2cec237 100644
--- a/include/mbgl/style/conversion.hpp
+++ b/include/mbgl/style/conversion.hpp
@@ -1,6 +1,8 @@
#pragma once
#include <mbgl/util/optional.hpp>
+#include <mbgl/util/feature.hpp>
+#include <mbgl/util/geojson.hpp>
#include <string>
@@ -9,9 +11,8 @@ namespace style {
namespace conversion {
/*
- The `conversion` namespace defines conversions from a templated type `V` representing a JSON
- object conforming to the schema defined by the Mapbox Style Specification, to the various C++
- types that form the C++ model of that domain:
+ The `conversion` namespace defines conversions from JSON structures conforming to the schema defined by
+ the Mapbox Style Specification, to the various C++ types that form the C++ model of that domain:
* `std::unique_ptr<Source>`
* `std::unique_ptr<Layer>`
@@ -20,15 +21,31 @@ namespace conversion {
A single template function serves as the public interface:
- template <class T, class V>
- optional<T> convert(const V& value, Error& error);
+ template <class T>
+ optional<T> convert(const Convertible& input, Error& error);
Where `T` is one of the above types. If the conversion fails, the result is empty, and the
error parameter includes diagnostic text suitable for presentation to a library user. Otherwise,
a filled optional is returned.
- The implementation of `convert` requires that the following are legal expressions for a value `v`
- of type `const V&`:
+ `Convertible` is a type that encapsulates a special form of polymorphism over various underlying types that
+ can serve as input to the conversion algorithm. For instance, on macOS, we need to support
+ conversion from both RapidJSON types, and a JSON structure represented with `NSArray`/`NSDictionary`/etc.
+ On Qt, we need to support conversion from RapidJSON types and QVariant.
+
+ We don't want to use traditional forms of polymorphism to accomplish this:
+
+ * Compile time polymorphism using a template parameter for the actual value type leads to
+ excessive code bloat and long compile times.
+ * Runtime polymorphism using virtual methods requires extra heap allocation and ubiquitous
+ use of std::unique_ptr, unsuitable for this performance-sensitive code.
+
+ Therefore, we're using a custom implementation of runtime polymorphism where we manually create and
+ dispatch through a table of function pointers (vtable), while keeping the storage for any of the possible
+ underlying types inline on the stack, using `std::aligned_storage`.
+
+ For a given underlying type T, an explicit specialization of `ConversionTraits<T>` must be provided. This
+ specialization must provide the following static methods:
* `isUndefined(v)` -- returns a boolean indication whether `v` is undefined or a JSON null
@@ -48,21 +65,239 @@ namespace conversion {
* `toNumber(v)` -- returns `optional<float>`, absence indicating `v` is not a JSON number
* `toDouble(v)` -- returns `optional<double>`, absence indicating `v` is not a JSON number
* `toString(v)` -- returns `optional<std::string>`, absence indicating `v` is not a JSON string
- * `toValue(v)` -- returns `optional<mbgl::Value>`, a variant type, for generic conversion,
+ * `toValue(v)` -- returns `optional<Value>`, a variant type, for generic conversion,
absence indicating `v` is not a boolean, number, or string. Numbers should be converted to
unsigned integer, signed integer, or floating point, in descending preference.
- The mbgl core implements these requirements for RapidJSON types, and the node bindings implement
- them for v8 types.
+ In addition, the type T must be move-constructable. And finally, `Convertible::Storage`, a typedef for
+ `std::aligned_storage_t`, must be large enough to satisfy the memory requirements for any of the
+ possible underlying types. (A static assert will fail if this is not the case.)
+
+ `Convertible` itself is movable, but not copyable. A moved-from `Convertible` is in an invalid state;
+ you must not do anything with it except let it go out of scope.
*/
struct Error { std::string message; };
+template <typename T>
+class ConversionTraits;
+
+class Convertible {
+public:
+ template <typename T>
+ Convertible(T&& value) : vtable(vtableForType<std::decay_t<T>>()) {
+ static_assert(sizeof(Storage) >= sizeof(std::decay_t<T>), "Storage must be large enough to hold value type");
+ new (static_cast<void*>(&storage)) std::decay_t<T>(std::forward<T>(value));
+ }
+
+ Convertible(Convertible&& v)
+ : vtable(v.vtable)
+ {
+ if (vtable) {
+ vtable->move(std::move(v.storage), this->storage);
+ }
+ }
+
+ ~Convertible() {
+ if (vtable) {
+ vtable->destroy(storage);
+ }
+ }
+
+ Convertible& operator=(Convertible&& v) {
+ if (vtable) {
+ vtable->destroy(storage);
+ }
+ vtable = v.vtable;
+ if (vtable) {
+ vtable->move(std::move(v.storage), this->storage);
+ }
+ v.vtable = nullptr;
+ return *this;
+ }
+
+ Convertible() = delete;
+ Convertible(const Convertible&) = delete;
+ Convertible& operator=(const Convertible&) = delete;
+
+ friend inline bool isUndefined(const Convertible& v) {
+ assert(v.vtable);
+ return v.vtable->isUndefined(v.storage);
+ }
+
+ friend inline bool isArray(const Convertible& v) {
+ assert(v.vtable);
+ return v.vtable->isArray(v.storage);
+ }
+
+ friend inline std::size_t arrayLength(const Convertible& v) {
+ assert(v.vtable);
+ return v.vtable->arrayLength(v.storage);
+ }
+
+ friend inline Convertible arrayMember(const Convertible& v, std::size_t i) {
+ assert(v.vtable);
+ return v.vtable->arrayMember(v.storage, i);
+ }
+
+ friend inline bool isObject(const Convertible& v) {
+ assert(v.vtable);
+ return v.vtable->isObject(v.storage);
+ }
+
+ friend inline optional<Convertible> objectMember(const Convertible& v, const char * name) {
+ assert(v.vtable);
+ return v.vtable->objectMember(v.storage, name);
+ }
+
+ friend inline optional<Error> eachMember(const Convertible& v, const std::function<optional<Error> (const std::string&, const Convertible&)>& fn) {
+ assert(v.vtable);
+ return v.vtable->eachMember(v.storage, fn);
+ }
+
+ friend inline optional<bool> toBool(const Convertible& v) {
+ assert(v.vtable);
+ return v.vtable->toBool(v.storage);
+ }
+
+ friend inline optional<float> toNumber(const Convertible& v) {
+ assert(v.vtable);
+ return v.vtable->toNumber(v.storage);
+ }
+
+ friend inline optional<double> toDouble(const Convertible& v) {
+ assert(v.vtable);
+ return v.vtable->toDouble(v.storage);
+ }
+
+ friend inline optional<std::string> toString(const Convertible& v) {
+ assert(v.vtable);
+ return v.vtable->toString(v.storage);
+ }
+
+ friend inline optional<Value> toValue(const Convertible& v) {
+ assert(v.vtable);
+ return v.vtable->toValue(v.storage);
+ }
+
+ friend inline optional<GeoJSON> toGeoJSON(const Convertible& v, Error& error) {
+ assert(v.vtable);
+ return v.vtable->toGeoJSON(v.storage, error);
+ }
+
+private:
+#if __ANDROID__
+ // Android: JSValue* or mbgl::android::Value
+ using Storage = std::aligned_storage_t<32, 8>;
+#elif __QT__
+ // Qt: JSValue* or QVariant
+ using Storage = std::aligned_storage_t<32, 8>;
+#else
+ // Node: JSValue* or v8::Local<v8::Value>
+ // iOS/macOS: JSValue* or id
+ using Storage = std::aligned_storage_t<8, 8>;
+#endif
+
+ struct VTable {
+ void (*move) (Storage&& src, Storage& dest);
+ void (*destroy) (Storage&);
+
+ bool (*isUndefined) (const Storage&);
+
+ bool (*isArray) (const Storage&);
+ std::size_t (*arrayLength) (const Storage&);
+ Convertible (*arrayMember) (const Storage&, std::size_t);
+
+ bool (*isObject) (const Storage&);
+ optional<Convertible> (*objectMember) (const Storage&, const char *);
+ optional<Error> (*eachMember) (const Storage&, const std::function<optional<Error> (const std::string&, const Convertible&)>&);
+
+ optional<bool> (*toBool) (const Storage&);
+ optional<float> (*toNumber) (const Storage&);
+ optional<double> (*toDouble) (const Storage&);
+ optional<std::string> (*toString) (const Storage&);
+ optional<Value> (*toValue) (const Storage&);
+
+ // https://github.com/mapbox/mapbox-gl-native/issues/5623
+ optional<GeoJSON> (*toGeoJSON) (const Storage&, Error&);
+ };
+
+ // Extracted this function from the table below to work around a GCC bug with differing
+ // visibility settings for capturing lambdas: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80947
+ template <typename T>
+ static auto vtableEachMember(const Storage& s, const std::function<optional<Error>(const std::string&, const Convertible&)>& fn) {
+ return ConversionTraits<T>::eachMember(reinterpret_cast<const T&>(s), [&](const std::string& k, T&& v) {
+ return fn(k, Convertible(std::move(v)));
+ });
+ }
+
+ template <typename T>
+ static VTable* vtableForType() {
+ using Traits = ConversionTraits<T>;
+ static VTable vtable = {
+ [] (Storage&& src, Storage& dest) {
+ auto srcValue = reinterpret_cast<T&&>(src);
+ new (static_cast<void*>(&dest)) T(std::move(srcValue));
+ srcValue.~T();
+ },
+ [] (Storage& s) {
+ reinterpret_cast<T&>(s).~T();
+ },
+ [] (const Storage& s) {
+ return Traits::isUndefined(reinterpret_cast<const T&>(s));
+ },
+ [] (const Storage& s) {
+ return Traits::isArray(reinterpret_cast<const T&>(s));
+ },
+ [] (const Storage& s) {
+ return Traits::arrayLength(reinterpret_cast<const T&>(s));
+ },
+ [] (const Storage& s, std::size_t i) {
+ return Convertible(Traits::arrayMember(reinterpret_cast<const T&>(s), i));
+ },
+ [] (const Storage& s) {
+ return Traits::isObject(reinterpret_cast<const T&>(s));
+ },
+ [] (const Storage& s, const char * key) {
+ optional<T> member = Traits::objectMember(reinterpret_cast<const T&>(s), key);
+ if (member) {
+ return optional<Convertible>(Convertible(std::move(*member)));
+ } else {
+ return optional<Convertible>();
+ }
+ },
+ vtableEachMember<T>,
+ [] (const Storage& s) {
+ return Traits::toBool(reinterpret_cast<const T&>(s));
+ },
+ [] (const Storage& s) {
+ return Traits::toNumber(reinterpret_cast<const T&>(s));
+ },
+ [] (const Storage& s) {
+ return Traits::toDouble(reinterpret_cast<const T&>(s));
+ },
+ [] (const Storage& s) {
+ return Traits::toString(reinterpret_cast<const T&>(s));
+ },
+ [] (const Storage& s) {
+ return Traits::toValue(reinterpret_cast<const T&>(s));
+ },
+ [] (const Storage& s, Error& err) {
+ return Traits::toGeoJSON(reinterpret_cast<const T&>(s), err);
+ }
+ };
+ return &vtable;
+ }
+
+ VTable* vtable;
+ Storage storage;
+};
+
template <class T, class Enable = void>
struct Converter;
-template <class T, class V, class...Args>
-optional<T> convert(const V& value, Error& error, Args&&...args) {
+template <class T, class...Args>
+optional<T> convert(const Convertible& value, Error& error, Args&&...args) {
return Converter<T>()(value, error, std::forward<Args>(args)...);
}
diff --git a/include/mbgl/style/conversion/constant.hpp b/include/mbgl/style/conversion/constant.hpp
index 07c0a35fae..7b3249da52 100644
--- a/include/mbgl/style/conversion/constant.hpp
+++ b/include/mbgl/style/conversion/constant.hpp
@@ -1,7 +1,6 @@
#pragma once
#include <mbgl/style/conversion.hpp>
-#include <mbgl/util/optional.hpp>
#include <mbgl/util/color.hpp>
#include <mbgl/util/enum.hpp>
#include <mbgl/util/string.hpp>
@@ -16,47 +15,22 @@ namespace conversion {
template <>
struct Converter<bool> {
- template <class V>
- optional<bool> operator()(const V& value, Error& error) const {
- optional<bool> converted = toBool(value);
- if (!converted) {
- error = { "value must be a boolean" };
- return {};
- }
- return *converted;
- }
+ optional<bool> operator()(const Convertible& value, Error& error) const;
};
template <>
struct Converter<float> {
- template <class V>
- optional<float> operator()(const V& value, Error& error) const {
- optional<float> converted = toNumber(value);
- if (!converted) {
- error = { "value must be a number" };
- return {};
- }
- return *converted;
- }
+ optional<float> operator()(const Convertible& value, Error& error) const;
};
template <>
struct Converter<std::string> {
- template <class V>
- optional<std::string> operator()(const V& value, Error& error) const {
- optional<std::string> converted = toString(value);
- if (!converted) {
- error = { "value must be a string" };
- return {};
- }
- return *converted;
- }
+ optional<std::string> operator()(const Convertible& value, Error& error) const;
};
template <class T>
struct Converter<T, typename std::enable_if_t<std::is_enum<T>::value>> {
- template <class V>
- optional<T> operator()(const V& value, Error& error) const {
+ optional<T> operator()(const Convertible& value, Error& error) const {
optional<std::string> string = toString(value);
if (!string) {
error = { "value must be a string" };
@@ -75,28 +49,12 @@ struct Converter<T, typename std::enable_if_t<std::is_enum<T>::value>> {
template <>
struct Converter<Color> {
- template <class V>
- optional<Color> operator()(const V& value, Error& error) const {
- optional<std::string> string = toString(value);
- if (!string) {
- error = { "value must be a string" };
- return {};
- }
-
- optional<Color> color = Color::parse(*string);
- if (!color) {
- error = { "value must be a valid color" };
- return {};
- }
-
- return *color;
- }
+ optional<Color> operator()(const Convertible& value, Error& error) const;
};
template <size_t N>
struct Converter<std::array<float, N>> {
- template <class V>
- optional<std::array<float, N>> operator()(const V& value, Error& error) const {
+ optional<std::array<float, N>> operator()(const Convertible& value, Error& error) const {
if (!isArray(value) || arrayLength(value) != N) {
error = { "value must be an array of " + util::toString(N) + " numbers" };
return {};
@@ -117,52 +75,12 @@ struct Converter<std::array<float, N>> {
template <>
struct Converter<std::vector<float>> {
- template <class V>
- optional<std::vector<float>> operator()(const V& value, Error& error) const {
- if (!isArray(value)) {
- error = { "value must be an array" };
- return {};
- }
-
- std::vector<float> result;
- result.reserve(arrayLength(value));
-
- for (std::size_t i = 0; i < arrayLength(value); ++i) {
- optional<float> number = toNumber(arrayMember(value, i));
- if (!number) {
- error = { "value must be an array of numbers" };
- return {};
- }
- result.push_back(*number);
- }
-
- return result;
- }
+ optional<std::vector<float>> operator()(const Convertible& value, Error& error) const;
};
template <>
struct Converter<std::vector<std::string>> {
- template <class V>
- optional<std::vector<std::string>> operator()(const V& value, Error& error) const {
- if (!isArray(value)) {
- error = { "value must be an array" };
- return {};
- }
-
- std::vector<std::string> result;
- result.reserve(arrayLength(value));
-
- for (std::size_t i = 0; i < arrayLength(value); ++i) {
- optional<std::string> string = toString(arrayMember(value, i));
- if (!string) {
- error = { "value must be an array of strings" };
- return {};
- }
- result.push_back(*string);
- }
-
- return result;
- }
+ optional<std::vector<std::string>> operator()(const Convertible& value, Error& error) const;
};
} // namespace conversion
diff --git a/include/mbgl/style/conversion/coordinate.hpp b/include/mbgl/style/conversion/coordinate.hpp
index 732624e77f..e11db5e32f 100644
--- a/include/mbgl/style/conversion/coordinate.hpp
+++ b/include/mbgl/style/conversion/coordinate.hpp
@@ -10,26 +10,7 @@ namespace conversion {
template<>
struct Converter<LatLng> {
public:
- template <class V>
- optional<LatLng> operator() (const V& value, Error& error) const {
- if (!isArray(value) || arrayLength(value) < 2 ) {
- error = { "coordinate array must contain numeric longitude and latitude values" };
- return {};
- }
- //Style spec uses GeoJSON convention for specifying coordinates
- optional<double> latitude = toDouble(arrayMember(value, 1));
- optional<double> longitude = toDouble(arrayMember(value, 0));
-
- if (!latitude || !longitude) {
- error = { "coordinate array must contain numeric longitude and latitude values" };
- return {};
- }
- if (*latitude < -90 || *latitude > 90 ){
- error = { "coordinate latitude must be between -90 and 90" };
- return {};
- }
- return LatLng(*latitude, *longitude);
- }
+ optional<LatLng> operator() (const Convertible& value, Error& error) const;
};
} // namespace conversion
diff --git a/include/mbgl/style/conversion/custom_geometry_source_options.hpp b/include/mbgl/style/conversion/custom_geometry_source_options.hpp
new file mode 100644
index 0000000000..dedecd1aa4
--- /dev/null
+++ b/include/mbgl/style/conversion/custom_geometry_source_options.hpp
@@ -0,0 +1,84 @@
+#pragma once
+
+#include <mbgl/style/conversion.hpp>
+#include <mbgl/style/sources/custom_geometry_source.hpp>
+
+namespace mbgl {
+namespace style {
+namespace conversion {
+
+template <>
+struct Converter<CustomGeometrySource::Options> {
+
+ template <class V>
+ optional<CustomGeometrySource::Options> operator()(const V& value, Error& error) const {
+ CustomGeometrySource::Options options;
+
+ const auto minzoomValue = objectMember(value, "minzoom");
+ if (minzoomValue) {
+ if (toNumber(*minzoomValue)) {
+ options.zoomRange.min = static_cast<uint8_t>(*toNumber(*minzoomValue));
+ } else {
+ error = { "GeoJSON source minzoom value must be a number" };
+ return {};
+ }
+ }
+
+ const auto maxzoomValue = objectMember(value, "maxzoom");
+ if (maxzoomValue) {
+ if (toNumber(*maxzoomValue)) {
+ options.zoomRange.max = static_cast<uint8_t>(*toNumber(*maxzoomValue));
+ } else {
+ error = { "GeoJSON source maxzoom value must be a number" };
+ return {};
+ }
+ }
+
+ const auto bufferValue = objectMember(value, "buffer");
+ if (bufferValue) {
+ if (toNumber(*bufferValue)) {
+ options.tileOptions.buffer = static_cast<uint16_t>(*toNumber(*bufferValue));
+ } else {
+ error = { "GeoJSON source buffer value must be a number" };
+ return {};
+ }
+ }
+
+ const auto toleranceValue = objectMember(value, "tolerance");
+ if (toleranceValue) {
+ if (toNumber(*toleranceValue)) {
+ options.tileOptions.tolerance = static_cast<double>(*toNumber(*toleranceValue));
+ } else {
+ error = { "GeoJSON source tolerance value must be a number" };
+ return {};
+ }
+ }
+
+ const auto wrapValue = objectMember(value, "wrap");
+ if (wrapValue) {
+ if (toBool(*wrapValue)) {
+ options.tileOptions.wrap = static_cast<bool>(*toBool(*wrapValue));
+ } else {
+ error = { "CustomGeometrySource TileOptions wrap value must be a boolean" };
+ return {};
+ }
+ }
+
+ const auto clipValue = objectMember(value, "clip");
+ if (clipValue) {
+ if (toBool(*clipValue)) {
+ options.tileOptions.clip = static_cast<double>(*toBool(*clipValue));
+ } else {
+ error = { "CustomGeometrySource TileOptiosn clip value must be a boolean" };
+ return {};
+ }
+ }
+
+ return { options };
+ }
+
+};
+
+} // namespace conversion
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/conversion/data_driven_property_value.hpp b/include/mbgl/style/conversion/data_driven_property_value.hpp
index 79b15dcfb0..8880d28fb1 100644
--- a/include/mbgl/style/conversion/data_driven_property_value.hpp
+++ b/include/mbgl/style/conversion/data_driven_property_value.hpp
@@ -4,6 +4,13 @@
#include <mbgl/style/conversion.hpp>
#include <mbgl/style/conversion/constant.hpp>
#include <mbgl/style/conversion/function.hpp>
+#include <mbgl/style/conversion/expression.hpp>
+#include <mbgl/style/expression/is_expression.hpp>
+#include <mbgl/style/expression/is_constant.hpp>
+#include <mbgl/style/expression/find_zoom_curve.hpp>
+
+#include <unordered_set>
+
namespace mbgl {
namespace style {
@@ -11,10 +18,27 @@ namespace conversion {
template <class T>
struct Converter<DataDrivenPropertyValue<T>> {
- template <class V>
- optional<DataDrivenPropertyValue<T>> operator()(const V& value, Error& error) const {
+
+ optional<DataDrivenPropertyValue<T>> operator()(const Convertible& value, Error& error) const {
if (isUndefined(value)) {
return DataDrivenPropertyValue<T>();
+ } else if (expression::isExpression(value)) {
+ optional<std::unique_ptr<Expression>> expression = convert<std::unique_ptr<Expression>>(
+ value,
+ error,
+ valueTypeToExpressionType<T>());
+
+ if (!expression) {
+ return {};
+ }
+
+ if (isFeatureConstant(**expression)) {
+ return DataDrivenPropertyValue<T>(CameraFunction<T>(std::move(*expression)));
+ } else if (isZoomConstant(**expression)) {
+ return DataDrivenPropertyValue<T>(SourceFunction<T>(std::move(*expression)));
+ } else {
+ return DataDrivenPropertyValue<T>(CompositeFunction<T>(std::move(*expression)));
+ }
} else if (!isObject(value)) {
optional<T> constant = convert<T>(value, error);
if (!constant) {
diff --git a/include/mbgl/style/conversion/expression.hpp b/include/mbgl/style/conversion/expression.hpp
new file mode 100644
index 0000000000..c5fcf906a7
--- /dev/null
+++ b/include/mbgl/style/conversion/expression.hpp
@@ -0,0 +1,39 @@
+#pragma once
+
+#include <mbgl/style/expression/parsing_context.hpp>
+#include <mbgl/style/expression/type.hpp>
+#include <mbgl/style/conversion.hpp>
+
+#include <memory>
+
+namespace mbgl {
+namespace style {
+namespace conversion {
+
+using namespace mbgl::style::expression;
+
+template<> struct Converter<std::unique_ptr<Expression>> {
+ optional<std::unique_ptr<Expression>> operator()(const Convertible& value, Error& error, type::Type expected) const {
+ ParsingContext ctx(optional<type::Type> {expected});
+ ParseResult parsed = ctx.parse(value);
+ if (parsed) {
+ return std::move(*parsed);
+ }
+ std::string combinedError;
+ for (const ParsingError& parsingError : ctx.getErrors()) {
+ if (combinedError.size() > 0) {
+ combinedError += "\n";
+ }
+ if (parsingError.key.size() > 0) {
+ combinedError += parsingError.key + ": ";
+ }
+ combinedError += parsingError.message;
+ }
+ error = { combinedError };
+ return {};
+ };
+};
+
+} // namespace conversion
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/conversion/filter.hpp b/include/mbgl/style/conversion/filter.hpp
index 986d1bf80d..9daf6ea7a4 100644
--- a/include/mbgl/style/conversion/filter.hpp
+++ b/include/mbgl/style/conversion/filter.hpp
@@ -2,7 +2,6 @@
#include <mbgl/style/filter.hpp>
#include <mbgl/style/conversion.hpp>
-#include <mbgl/util/geometry.hpp>
namespace mbgl {
namespace style {
@@ -11,247 +10,7 @@ namespace conversion {
template <>
struct Converter<Filter> {
public:
- template <class V>
- optional<Filter> operator()(const V& value, Error& error) const {
- if (!isArray(value)) {
- error = { "filter expression must be an array" };
- return {};
- }
-
- if (arrayLength(value) < 1) {
- error = { "filter expression must have at least 1 element" };
- return {};
- }
-
- optional<std::string> op = toString(arrayMember(value, 0));
- if (!op) {
- error = { "filter operator must be a string" };
- return {};
- }
-
- if (*op == "==") {
- return convertEqualityFilter<EqualsFilter, TypeEqualsFilter, IdentifierEqualsFilter>(value, error);
- } else if (*op == "!=") {
- return convertEqualityFilter<NotEqualsFilter, TypeNotEqualsFilter, IdentifierNotEqualsFilter>(value, error);
- } else if (*op == ">") {
- return convertBinaryFilter<GreaterThanFilter>(value, error);
- } else if (*op == ">=") {
- return convertBinaryFilter<GreaterThanEqualsFilter>(value, error);
- } else if (*op == "<") {
- return convertBinaryFilter<LessThanFilter>(value, error);
- } else if (*op == "<=") {
- return convertBinaryFilter<LessThanEqualsFilter>(value, error);
- } else if (*op == "in") {
- return convertSetFilter<InFilter, TypeInFilter, IdentifierInFilter>(value, error);
- } else if (*op == "!in") {
- return convertSetFilter<NotInFilter, TypeNotInFilter, IdentifierNotInFilter>(value, error);
- } else if (*op == "all") {
- return convertCompoundFilter<AllFilter>(value, error);
- } else if (*op == "any") {
- return convertCompoundFilter<AnyFilter>(value, error);
- } else if (*op == "none") {
- return convertCompoundFilter<NoneFilter>(value, error);
- } else if (*op == "has") {
- return convertUnaryFilter<HasFilter, HasIdentifierFilter>(value, error);
- } else if (*op == "!has") {
- return convertUnaryFilter<NotHasFilter, NotHasIdentifierFilter>(value, error);
- }
-
- error = { R"(filter operator must be one of "==", "!=", ">", ">=", "<", "<=", "in", "!in", "all", "any", "none", "has", or "!has")" };
- return {};
- }
-
-private:
- optional<Value> normalizeValue(const optional<Value>& value, Error& error) const {
- if (!value) {
- error = { "filter expression value must be a boolean, number, or string" };
- return {};
- } else {
- return *value;
- }
- }
-
- template <class V>
- optional<FeatureType> toFeatureType(const V& value, Error& error) const {
- optional<std::string> type = toString(value);
- if (!type) {
- error = { "value for $type filter must be a string" };
- return {};
- } else if (*type == "Point") {
- return FeatureType::Point;
- } else if (*type == "LineString") {
- return FeatureType::LineString;
- } else if (*type == "Polygon") {
- return FeatureType::Polygon;
- } else {
- error = { "value for $type filter must be Point, LineString, or Polygon" };
- return {};
- }
- }
-
- template <class V>
- optional<FeatureIdentifier> toFeatureIdentifier(const V& value, Error& error) const {
- optional<Value> identifier = toValue(value);
- if (!identifier) {
- error = { "filter expression value must be a boolean, number, or string" };
- return {};
- } else {
- return (*identifier).match(
- [] (uint64_t t) -> optional<FeatureIdentifier> { return { t }; },
- [] ( int64_t t) -> optional<FeatureIdentifier> { return { t }; },
- [] ( double t) -> optional<FeatureIdentifier> { return { t }; },
- [] (const std::string& t) -> optional<FeatureIdentifier> { return { t }; },
- [&] (const auto&) -> optional<FeatureIdentifier> {
- error = { "filter expression value must be a boolean, number, or string" };
- return {};
- });
- }
- }
-
- template <class FilterType, class IdentifierFilterType, class V>
- optional<Filter> convertUnaryFilter(const V& value, Error& error) const {
- if (arrayLength(value) < 2) {
- error = { "filter expression must have 2 elements" };
- return {};
- }
-
- optional<std::string> key = toString(arrayMember(value, 1));
- if (!key) {
- error = { "filter expression key must be a string" };
- return {};
- }
-
- if (*key == "$id") {
- return { IdentifierFilterType {} };
- } else {
- return { FilterType { *key } };
- }
- }
-
- template <class FilterType, class TypeFilterType, class IdentifierFilterType, class V>
- optional<Filter> convertEqualityFilter(const V& value, Error& error) const {
- if (arrayLength(value) < 3) {
- error = { "filter expression must have 3 elements" };
- return {};
- }
-
- optional<std::string> key = toString(arrayMember(value, 1));
- if (!key) {
- error = { "filter expression key must be a string" };
- return {};
- }
-
- if (*key == "$type") {
- optional<FeatureType> filterValue = toFeatureType(arrayMember(value, 2), error);
- if (!filterValue) {
- return {};
- }
-
- return { TypeFilterType { *filterValue } };
-
- } else if (*key == "$id") {
- optional<FeatureIdentifier> filterValue = toFeatureIdentifier(arrayMember(value, 2), error);
- if (!filterValue) {
- return {};
- }
-
- return { IdentifierFilterType { *filterValue } };
-
- } else {
- optional<Value> filterValue = normalizeValue(toValue(arrayMember(value, 2)), error);
- if (!filterValue) {
- return {};
- }
-
- return { FilterType { *key, *filterValue } };
- }
- }
-
- template <class FilterType, class V>
- optional<Filter> convertBinaryFilter(const V& value, Error& error) const {
- if (arrayLength(value) < 3) {
- error = { "filter expression must have 3 elements" };
- return {};
- }
-
- optional<std::string> key = toString(arrayMember(value, 1));
- if (!key) {
- error = { "filter expression key must be a string" };
- return {};
- }
-
- optional<Value> filterValue = normalizeValue(toValue(arrayMember(value, 2)), error);
- if (!filterValue) {
- return {};
- }
-
- return { FilterType { *key, *filterValue } };
- }
-
- template <class FilterType, class TypeFilterType, class IdentifierFilterType, class V>
- optional<Filter> convertSetFilter(const V& value, Error& error) const {
- if (arrayLength(value) < 2) {
- error = { "filter expression must at least 2 elements" };
- return {};
- }
-
- optional<std::string> key = toString(arrayMember(value, 1));
- if (!key) {
- error = { "filter expression key must be a string" };
- return {};
- }
-
- if (*key == "$type") {
- std::vector<FeatureType> values;
- for (std::size_t i = 2; i < arrayLength(value); ++i) {
- optional<FeatureType> filterValue = toFeatureType(arrayMember(value, i), error);
- if (!filterValue) {
- return {};
- }
- values.push_back(*filterValue);
- }
-
- return { TypeFilterType { std::move(values) } };
-
- } else if (*key == "$id") {
- std::vector<FeatureIdentifier> values;
- for (std::size_t i = 2; i < arrayLength(value); ++i) {
- optional<FeatureIdentifier> filterValue = toFeatureIdentifier(arrayMember(value, i), error);
- if (!filterValue) {
- return {};
- }
- values.push_back(*filterValue);
- }
-
- return { IdentifierFilterType { std::move(values) } };
-
- } else {
- std::vector<Value> values;
- for (std::size_t i = 2; i < arrayLength(value); ++i) {
- optional<Value> filterValue = normalizeValue(toValue(arrayMember(value, i)), error);
- if (!filterValue) {
- return {};
- }
- values.push_back(*filterValue);
- }
-
- return { FilterType { *key, std::move(values) } };
- }
- }
-
- template <class FilterType, class V>
- optional<Filter> convertCompoundFilter(const V& value, Error& error) const {
- std::vector<Filter> filters;
- for (std::size_t i = 1; i < arrayLength(value); ++i) {
- optional<Filter> element = operator()(arrayMember(value, i), error);
- if (!element) {
- return {};
- }
- filters.push_back(*element);
- }
-
- return { FilterType { std::move(filters) } };
- }
+ optional<Filter> operator()(const Convertible& value, Error& error) const;
};
} // namespace conversion
diff --git a/include/mbgl/style/conversion/function.hpp b/include/mbgl/style/conversion/function.hpp
index 752b6dd045..e230884944 100644
--- a/include/mbgl/style/conversion/function.hpp
+++ b/include/mbgl/style/conversion/function.hpp
@@ -11,8 +11,8 @@ namespace mbgl {
namespace style {
namespace conversion {
-template <class D, class R, class V>
-optional<std::map<D, R>> convertStops(const V& value, Error& error) {
+template <class D, class R>
+optional<std::map<D, R>> convertStops(const Convertible& value, Error& error) {
auto stopsValue = objectMember(value, "stops");
if (!stopsValue) {
error = { "function value must specify stops" };
@@ -63,8 +63,7 @@ template <class T>
struct Converter<ExponentialStops<T>> {
static constexpr const char * type = "exponential";
- template <class V>
- optional<ExponentialStops<T>> operator()(const V& value, Error& error) const {
+ optional<ExponentialStops<T>> operator()(const Convertible& value, Error& error) const {
auto stops = convertStops<float, T>(value, error);
if (!stops) {
return {};
@@ -89,8 +88,7 @@ template <class T>
struct Converter<IntervalStops<T>> {
static constexpr const char * type = "interval";
- template <class V>
- optional<IntervalStops<T>> operator()(const V& value, Error& error) const {
+ optional<IntervalStops<T>> operator()(const Convertible& value, Error& error) const {
auto stops = convertStops<float, T>(value, error);
if (!stops) {
return {};
@@ -101,8 +99,7 @@ struct Converter<IntervalStops<T>> {
template <>
struct Converter<CategoricalValue> {
- template <class V>
- optional<CategoricalValue> operator()(const V& value, Error& error) const {
+ optional<CategoricalValue> operator()(const Convertible& value, Error& error) const {
auto b = toBool(value);
if (b) {
return { *b };
@@ -127,8 +124,7 @@ template <class T>
struct Converter<CategoricalStops<T>> {
static constexpr const char * type = "categorical";
- template <class V>
- optional<CategoricalStops<T>> operator()(const V& value, Error& error) const {
+ optional<CategoricalStops<T>> operator()(const Convertible& value, Error& error) const {
auto stops = convertStops<CategoricalValue, T>(value, error);
if (!stops) {
return {};
@@ -142,8 +138,7 @@ template <class T>
struct Converter<IdentityStops<T>> {
static constexpr const char * type = "identity";
- template <class V>
- optional<IdentityStops<T>> operator()(const V&, Error&) const {
+ optional<IdentityStops<T>> operator()(const Convertible&, Error&) const {
return IdentityStops<T>();
}
};
@@ -154,8 +149,7 @@ struct StopsConverter;
template <class T, class... Ts>
struct StopsConverter<T, variant<Ts...>> {
public:
- template <class V>
- optional<variant<Ts...>> operator()(const V& value, Error& error) const {
+ optional<variant<Ts...>> operator()(const Convertible& value, Error& error) const {
std::string type = util::Interpolatable<T>::value ? "exponential" : "interval";
auto typeValue = objectMember(value, "type");
@@ -193,8 +187,7 @@ public:
template <class T>
struct Converter<CameraFunction<T>> {
- template <class V>
- optional<CameraFunction<T>> operator()(const V& value, Error& error) const {
+ optional<CameraFunction<T>> operator()(const Convertible& value, Error& error) const {
if (!isObject(value)) {
error = { "function must be an object" };
return {};
@@ -209,8 +202,8 @@ struct Converter<CameraFunction<T>> {
}
};
-template <class T, class V>
-optional<optional<T>> convertDefaultValue(const V& value, Error& error) {
+template <class T>
+optional<optional<T>> convertDefaultValue(const Convertible& value, Error& error) {
auto defaultValueValue = objectMember(value, "default");
if (!defaultValueValue) {
return optional<T>();
@@ -227,8 +220,7 @@ optional<optional<T>> convertDefaultValue(const V& value, Error& error) {
template <class T>
struct Converter<SourceFunction<T>> {
- template <class V>
- optional<SourceFunction<T>> operator()(const V& value, Error& error) const {
+ optional<SourceFunction<T>> operator()(const Convertible& value, Error& error) const {
if (!isObject(value)) {
error = { "function must be an object" };
return {};
@@ -267,8 +259,7 @@ struct CompositeValue : std::pair<float, S> {
template <class S>
struct Converter<CompositeValue<S>> {
- template <class V>
- optional<CompositeValue<S>> operator()(const V& value, Error& error) const {
+ optional<CompositeValue<S>> operator()(const Convertible& value, Error& error) const {
if (!isObject(value)) {
error = { "stop must be an object" };
return {};
@@ -304,8 +295,7 @@ template <class T>
struct Converter<CompositeExponentialStops<T>> {
static constexpr const char * type = "exponential";
- template <class V>
- optional<CompositeExponentialStops<T>> operator()(const V& value, Error& error) const {
+ optional<CompositeExponentialStops<T>> operator()(const Convertible& value, Error& error) const {
auto stops = convertStops<CompositeValue<float>, T>(value, error);
if (!stops) {
return {};
@@ -330,8 +320,7 @@ template <class T>
struct Converter<CompositeIntervalStops<T>> {
static constexpr const char * type = "interval";
- template <class V>
- optional<CompositeIntervalStops<T>> operator()(const V& value, Error& error) const {
+ optional<CompositeIntervalStops<T>> operator()(const Convertible& value, Error& error) const {
auto stops = convertStops<CompositeValue<float>, T>(value, error);
if (!stops) {
return {};
@@ -350,8 +339,7 @@ template <class T>
struct Converter<CompositeCategoricalStops<T>> {
static constexpr const char * type = "categorical";
- template <class V>
- optional<CompositeCategoricalStops<T>> operator()(const V& value, Error& error) const {
+ optional<CompositeCategoricalStops<T>> operator()(const Convertible& value, Error& error) const {
auto stops = convertStops<CompositeValue<CategoricalValue>, T>(value, error);
if (!stops) {
return {};
@@ -368,8 +356,7 @@ struct Converter<CompositeCategoricalStops<T>> {
template <class T>
struct Converter<CompositeFunction<T>> {
- template <class V>
- optional<CompositeFunction<T>> operator()(const V& value, Error& error) const {
+ optional<CompositeFunction<T>> operator()(const Convertible& value, Error& error) const {
if (!isObject(value)) {
error = { "function must be an object" };
return {};
diff --git a/include/mbgl/style/conversion/geojson.hpp b/include/mbgl/style/conversion/geojson.hpp
index 0b594f066c..403c5f953b 100644
--- a/include/mbgl/style/conversion/geojson.hpp
+++ b/include/mbgl/style/conversion/geojson.hpp
@@ -7,15 +7,13 @@ namespace mbgl {
namespace style {
namespace conversion {
+// Workaround until https://github.com/mapbox/mapbox-gl-native/issues/5623 is done.
+optional<GeoJSON> parseGeoJSON(const std::string&, Error&);
+
template <>
struct Converter<GeoJSON> {
public:
- optional<GeoJSON> operator()(const std::string&, Error&) const;
-
- // This is explicitly specialized in the .cpp file for JSValue. It may also be explicitly
- // specialized for SDK-specific types (e.g. mbgl::android::Value).
- template <class V>
- optional<GeoJSON> operator()(const V&, Error&) const;
+ optional<GeoJSON> operator()(const Convertible&, Error&) const;
};
} // namespace conversion
diff --git a/include/mbgl/style/conversion/geojson_options.hpp b/include/mbgl/style/conversion/geojson_options.hpp
index 1c9c18250c..3f625babb6 100644
--- a/include/mbgl/style/conversion/geojson_options.hpp
+++ b/include/mbgl/style/conversion/geojson_options.hpp
@@ -9,84 +9,7 @@ namespace conversion {
template <>
struct Converter<GeoJSONOptions> {
-
- template <class V>
- optional<GeoJSONOptions> operator()(const V& value, Error& error) const {
- GeoJSONOptions options;
-
- const auto minzoomValue = objectMember(value, "minzoom");
- if (minzoomValue) {
- if (toNumber(*minzoomValue)) {
- options.minzoom = static_cast<uint8_t>(*toNumber(*minzoomValue));
- } else {
- error = { "GeoJSON source minzoom value must be a number" };
- return {};
- }
- }
-
- const auto maxzoomValue = objectMember(value, "maxzoom");
- if (maxzoomValue) {
- if (toNumber(*maxzoomValue)) {
- options.maxzoom = static_cast<uint8_t>(*toNumber(*maxzoomValue));
- } else {
- error = { "GeoJSON source maxzoom value must be a number" };
- return {};
- }
- }
-
- const auto bufferValue = objectMember(value, "buffer");
- if (bufferValue) {
- if (toNumber(*bufferValue)) {
- options.buffer = static_cast<uint16_t>(*toNumber(*bufferValue));
- } else {
- error = { "GeoJSON source buffer value must be a number" };
- return {};
- }
- }
-
- const auto toleranceValue = objectMember(value, "tolerance");
- if (toleranceValue) {
- if (toNumber(*toleranceValue)) {
- options.tolerance = static_cast<double>(*toNumber(*toleranceValue));
- } else {
- error = { "GeoJSON source tolerance value must be a number" };
- return {};
- }
- }
-
- const auto clusterValue = objectMember(value, "cluster");
- if (clusterValue) {
- if (toBool(*clusterValue)) {
- options.cluster = *toBool(*clusterValue);
- } else {
- error = { "GeoJSON source cluster value must be a boolean" };
- return {};
- }
- }
-
- const auto clusterMaxZoomValue = objectMember(value, "clusterMaxZoom");
- if (clusterMaxZoomValue) {
- if (toNumber(*clusterMaxZoomValue)) {
- options.clusterMaxZoom = static_cast<uint8_t>(*toNumber(*clusterMaxZoomValue));
- } else {
- error = { "GeoJSON source clusterMaxZoom value must be a number" };
- return {};
- }
- }
-
- const auto clusterRadiusValue = objectMember(value, "clusterRadius");
- if (clusterRadiusValue) {
- if (toNumber(*clusterRadiusValue)) {
- options.clusterRadius = static_cast<double>(*toNumber(*clusterRadiusValue));
- } else {
- error = { "GeoJSON source clusterRadius value must be a number" };
- return {};
- }
- }
-
- return { options };
- }
-
+ optional<GeoJSONOptions> operator()(const Convertible& value, Error& error) const;
};
} // namespace conversion
diff --git a/include/mbgl/style/conversion/get_json_type.hpp b/include/mbgl/style/conversion/get_json_type.hpp
new file mode 100644
index 0000000000..f7efebccce
--- /dev/null
+++ b/include/mbgl/style/conversion/get_json_type.hpp
@@ -0,0 +1,14 @@
+#pragma once
+
+#include <mbgl/style/conversion.hpp>
+#include <string>
+
+namespace mbgl {
+namespace style {
+namespace conversion {
+
+std::string getJSONType(const Convertible& value);
+
+} // namespace conversion
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/conversion/layer.hpp b/include/mbgl/style/conversion/layer.hpp
index c694c3162a..1c0e2e2f07 100644
--- a/include/mbgl/style/conversion/layer.hpp
+++ b/include/mbgl/style/conversion/layer.hpp
@@ -1,223 +1,24 @@
#pragma once
#include <mbgl/style/layer.hpp>
-#include <mbgl/style/layers/background_layer.hpp>
-#include <mbgl/style/layers/circle_layer.hpp>
-#include <mbgl/style/layers/fill_layer.hpp>
-#include <mbgl/style/layers/fill_extrusion_layer.hpp>
-#include <mbgl/style/layers/line_layer.hpp>
-#include <mbgl/style/layers/raster_layer.hpp>
-#include <mbgl/style/layers/symbol_layer.hpp>
#include <mbgl/style/conversion.hpp>
-#include <mbgl/style/conversion/constant.hpp>
-#include <mbgl/style/conversion/filter.hpp>
-#include <mbgl/style/conversion/make_property_setters.hpp>
+
+#include <memory>
namespace mbgl {
namespace style {
namespace conversion {
-template <class V>
-optional<Error> setLayoutProperty(Layer& layer, const std::string& name, const V& value) {
- static const auto setters = makeLayoutPropertySetters<V>();
- auto it = setters.find(name);
- if (it == setters.end()) {
- return Error { "property not found" };
- }
- return it->second(layer, value);
-}
-
-template <class V>
-optional<Error> setPaintProperty(Layer& layer, const std::string& name, const V& value) {
- static const auto setters = makePaintPropertySetters<V>();
- auto it = setters.find(name);
- if (it == setters.end()) {
- return Error { "property not found" };
- }
- return it->second(layer, value);
-}
-
-template <class V>
-optional<Error> setPaintProperties(Layer& layer, const V& value) {
- auto paintValue = objectMember(value, "paint");
- if (!paintValue) {
- return {};
- }
- if (!isObject(*paintValue)) {
- return { { "paint must be an object" } };
- }
- return eachMember(*paintValue, [&] (const std::string& k, const V& v) {
- return setPaintProperty(layer, k, v);
- });
-}
-
template <>
struct Converter<std::unique_ptr<Layer>> {
public:
- template <class V>
- optional<std::unique_ptr<Layer>> operator()(const V& value, Error& error) const {
- if (!isObject(value)) {
- error = { "layer must be an object" };
- return {};
- }
-
- auto idValue = objectMember(value, "id");
- if (!idValue) {
- error = { "layer must have an id" };
- return {};
- }
-
- optional<std::string> id = toString(*idValue);
- if (!id) {
- error = { "layer id must be a string" };
- return {};
- }
-
- auto typeValue = objectMember(value, "type");
- if (!typeValue) {
- error = { "layer must have a type" };
- return {};
- }
-
- optional<std::string> type = toString(*typeValue);
- if (!type) {
- error = { "layer type must be a string" };
- return {};
- }
-
- optional<std::unique_ptr<Layer>> converted;
-
- if (*type == "fill") {
- converted = convertVectorLayer<FillLayer>(*id, value, error);
- } else if (*type == "fill-extrusion") {
- converted = convertVectorLayer<FillExtrusionLayer>(*id, value, error);
- } else if (*type == "line") {
- converted = convertVectorLayer<LineLayer>(*id, value, error);
- } else if (*type == "circle") {
- converted = convertVectorLayer<CircleLayer>(*id, value, error);
- } else if (*type == "symbol") {
- converted = convertVectorLayer<SymbolLayer>(*id, value, error);
- } else if (*type == "raster") {
- converted = convertRasterLayer(*id, value, error);
- } else if (*type == "background") {
- converted = convertBackgroundLayer(*id, value, error);
- } else {
- error = { "invalid layer type" };
- return {};
- }
-
- if (!converted) {
- return converted;
- }
-
- std::unique_ptr<Layer> layer = std::move(*converted);
-
- auto minzoomValue = objectMember(value, "minzoom");
- if (minzoomValue) {
- optional<float> minzoom = toNumber(*minzoomValue);
- if (!minzoom) {
- error = { "minzoom must be numeric" };
- return {};
- }
- layer->setMinZoom(*minzoom);
- }
-
- auto maxzoomValue = objectMember(value, "maxzoom");
- if (maxzoomValue) {
- optional<float> maxzoom = toNumber(*maxzoomValue);
- if (!maxzoom) {
- error = { "maxzoom must be numeric" };
- return {};
- }
- layer->setMaxZoom(*maxzoom);
- }
-
- auto layoutValue = objectMember(value, "layout");
- if (layoutValue) {
- if (!isObject(*layoutValue)) {
- error = { "layout must be an object" };
- return {};
- }
- optional<Error> error_ = eachMember(*layoutValue, [&] (const std::string& k, const V& v) {
- return setLayoutProperty(*layer, k, v);
- });
- if (error_) {
- error = *error_;
- return {};
- }
- }
-
- optional<Error> error_ = setPaintProperties(*layer, value);
- if (error_) {
- error = *error_;
- return {};
- }
-
- return std::move(layer);
- }
-
-private:
- template <class LayerType, class V>
- optional<std::unique_ptr<Layer>> convertVectorLayer(const std::string& id, const V& value, Error& error) const {
- auto sourceValue = objectMember(value, "source");
- if (!sourceValue) {
- error = { "layer must have a source" };
- return {};
- }
-
- optional<std::string> source = toString(*sourceValue);
- if (!source) {
- error = { "layer source must be a string" };
- return {};
- }
-
- std::unique_ptr<LayerType> layer = std::make_unique<LayerType>(id, *source);
-
- auto sourceLayerValue = objectMember(value, "source-layer");
- if (sourceLayerValue) {
- optional<std::string> sourceLayer = toString(*sourceLayerValue);
- if (!sourceLayer) {
- error = { "layer source-layer must be a string" };
- return {};
- }
- layer->setSourceLayer(*sourceLayer);
- }
-
- auto filterValue = objectMember(value, "filter");
- if (filterValue) {
- optional<Filter> filter = convert<Filter>(*filterValue, error);
- if (!filter) {
- return {};
- }
- layer->setFilter(*filter);
- }
-
- return { std::move(layer) };
- }
-
- template <class V>
- optional<std::unique_ptr<Layer>> convertRasterLayer(const std::string& id, const V& value, Error& error) const {
- auto sourceValue = objectMember(value, "source");
- if (!sourceValue) {
- error = { "layer must have a source" };
- return {};
- }
-
- optional<std::string> source = toString(*sourceValue);
- if (!source) {
- error = { "layer source must be a string" };
- return {};
- }
-
- return { std::make_unique<RasterLayer>(id, *source) };
- }
-
- template <class V>
- optional<std::unique_ptr<Layer>> convertBackgroundLayer(const std::string& id, const V&, Error&) const {
- return { std::make_unique<BackgroundLayer>(id) };
- }
+ optional<std::unique_ptr<Layer>> operator()(const Convertible& value, Error& error) const;
};
+optional<Error> setLayoutProperty(Layer& layer, const std::string& name, const Convertible& value);
+optional<Error> setPaintProperty(Layer& layer, const std::string& name, const Convertible& value);
+optional<Error> setPaintProperties(Layer& layer, const Convertible& value);
+
} // namespace conversion
} // namespace style
} // namespace mbgl
diff --git a/include/mbgl/style/conversion/light.hpp b/include/mbgl/style/conversion/light.hpp
index ba162516c0..289fca2e31 100644
--- a/include/mbgl/style/conversion/light.hpp
+++ b/include/mbgl/style/conversion/light.hpp
@@ -2,9 +2,6 @@
#include <mbgl/style/light.hpp>
#include <mbgl/style/conversion.hpp>
-#include <mbgl/style/conversion/position.hpp>
-#include <mbgl/style/conversion/property_value.hpp>
-#include <mbgl/style/conversion/transition_options.hpp>
namespace mbgl {
namespace style {
@@ -13,108 +10,7 @@ namespace conversion {
template <>
struct Converter<Light> {
public:
- template <class V>
- optional<Light> operator()(const V& value, Error& error) const {
- if (!isObject(value)) {
- error = { "light must be an object" };
- return {};
- }
-
- Light light;
-
- const auto anchor = objectMember(value, "anchor");
- if (anchor) {
- optional<PropertyValue<LightAnchorType>> convertedAnchor =
- convert<PropertyValue<LightAnchorType>>(*anchor, error);
-
- if (convertedAnchor) {
- light.setAnchor(*convertedAnchor);
- } else {
- return {};
- }
- }
-
- const auto anchorTransition = objectMember(value, "anchor-transition");
- if (anchorTransition) {
- optional<TransitionOptions> transition =
- convert<TransitionOptions>(*anchorTransition, error);
- if (transition) {
- light.setAnchorTransition(*transition);
- } else {
- return {};
- }
- }
-
- const auto color = objectMember(value, "color");
- if (color) {
- optional<PropertyValue<Color>> convertedColor =
- convert<PropertyValue<Color>>(*color, error);
-
- if (convertedColor) {
- light.setColor(*convertedColor);
- } else {
- return {};
- }
- }
-
- const auto colorTransition = objectMember(value, "color-transition");
- if (colorTransition) {
- optional<TransitionOptions> transition =
- convert<TransitionOptions>(*colorTransition, error);
- if (transition) {
- light.setColorTransition(*transition);
- } else {
- return {};
- }
- }
-
- const auto position = objectMember(value, "position");
- if (position) {
- optional<PropertyValue<Position>> convertedPosition =
- convert<PropertyValue<Position>>(*position, error);
-
- if (convertedPosition) {
- light.setPosition(*convertedPosition);
- } else {
- return {};
- }
- }
-
- const auto positionTransition = objectMember(value, "position-transition");
- if (positionTransition) {
- optional<TransitionOptions> transition =
- convert<TransitionOptions>(*positionTransition, error);
- if (transition) {
- light.setPositionTransition(*transition);
- } else {
- return {};
- }
- }
-
- const auto intensity = objectMember(value, "intensity");
- if (intensity) {
- optional<PropertyValue<float>> convertedIntensity =
- convert<PropertyValue<float>>(*intensity, error);
-
- if (convertedIntensity) {
- light.setIntensity(*convertedIntensity);
- } else {
- return {};
- }
- }
-
- const auto intensityTransition = objectMember(value, "intensity-transition");
- if (intensityTransition) {
- optional<TransitionOptions> transition =
- convert<TransitionOptions>(*intensityTransition, error);
- if (transition) {
- light.setIntensityTransition(*transition);
- } else {
- return {};
- }
- }
- return { std::move(light) };
- };
+ optional<Light> operator()(const Convertible& value, Error& error) const;
};
} // namespace conversion
diff --git a/include/mbgl/style/conversion/make_property_setters.hpp b/include/mbgl/style/conversion/make_property_setters.hpp
deleted file mode 100644
index 59b0e7be32..0000000000
--- a/include/mbgl/style/conversion/make_property_setters.hpp
+++ /dev/null
@@ -1,211 +0,0 @@
-#pragma once
-
-// This file is generated. Edit make_property_setters.hpp.ejs, then run `make style-code`.
-
-#include <mbgl/style/conversion/property_setter.hpp>
-
-#include <mbgl/style/layers/fill_layer.hpp>
-#include <mbgl/style/layers/line_layer.hpp>
-#include <mbgl/style/layers/symbol_layer.hpp>
-#include <mbgl/style/layers/circle_layer.hpp>
-#include <mbgl/style/layers/fill_extrusion_layer.hpp>
-#include <mbgl/style/layers/raster_layer.hpp>
-#include <mbgl/style/layers/background_layer.hpp>
-
-#include <unordered_map>
-
-namespace mbgl {
-namespace style {
-namespace conversion {
-
-template <class V>
-auto makeLayoutPropertySetters() {
- std::unordered_map<std::string, PropertySetter<V>> result;
-
- result["visibility"] = &setVisibility<V>;
-
-
- result["line-cap"] = &setProperty<V, LineLayer, PropertyValue<LineCapType>, &LineLayer::setLineCap>;
- result["line-join"] = &setProperty<V, LineLayer, DataDrivenPropertyValue<LineJoinType>, &LineLayer::setLineJoin>;
- result["line-miter-limit"] = &setProperty<V, LineLayer, PropertyValue<float>, &LineLayer::setLineMiterLimit>;
- result["line-round-limit"] = &setProperty<V, LineLayer, PropertyValue<float>, &LineLayer::setLineRoundLimit>;
-
- result["symbol-placement"] = &setProperty<V, SymbolLayer, PropertyValue<SymbolPlacementType>, &SymbolLayer::setSymbolPlacement>;
- result["symbol-spacing"] = &setProperty<V, SymbolLayer, PropertyValue<float>, &SymbolLayer::setSymbolSpacing>;
- result["symbol-avoid-edges"] = &setProperty<V, SymbolLayer, PropertyValue<bool>, &SymbolLayer::setSymbolAvoidEdges>;
- result["icon-allow-overlap"] = &setProperty<V, SymbolLayer, PropertyValue<bool>, &SymbolLayer::setIconAllowOverlap>;
- result["icon-ignore-placement"] = &setProperty<V, SymbolLayer, PropertyValue<bool>, &SymbolLayer::setIconIgnorePlacement>;
- result["icon-optional"] = &setProperty<V, SymbolLayer, PropertyValue<bool>, &SymbolLayer::setIconOptional>;
- result["icon-rotation-alignment"] = &setProperty<V, SymbolLayer, PropertyValue<AlignmentType>, &SymbolLayer::setIconRotationAlignment>;
- result["icon-size"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setIconSize>;
- result["icon-text-fit"] = &setProperty<V, SymbolLayer, PropertyValue<IconTextFitType>, &SymbolLayer::setIconTextFit>;
- result["icon-text-fit-padding"] = &setProperty<V, SymbolLayer, PropertyValue<std::array<float, 4>>, &SymbolLayer::setIconTextFitPadding>;
- result["icon-image"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<std::string>, &SymbolLayer::setIconImage>;
- result["icon-rotate"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setIconRotate>;
- result["icon-padding"] = &setProperty<V, SymbolLayer, PropertyValue<float>, &SymbolLayer::setIconPadding>;
- result["icon-keep-upright"] = &setProperty<V, SymbolLayer, PropertyValue<bool>, &SymbolLayer::setIconKeepUpright>;
- result["icon-offset"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<std::array<float, 2>>, &SymbolLayer::setIconOffset>;
- result["icon-anchor"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<SymbolAnchorType>, &SymbolLayer::setIconAnchor>;
- result["icon-pitch-alignment"] = &setProperty<V, SymbolLayer, PropertyValue<AlignmentType>, &SymbolLayer::setIconPitchAlignment>;
- result["text-pitch-alignment"] = &setProperty<V, SymbolLayer, PropertyValue<AlignmentType>, &SymbolLayer::setTextPitchAlignment>;
- result["text-rotation-alignment"] = &setProperty<V, SymbolLayer, PropertyValue<AlignmentType>, &SymbolLayer::setTextRotationAlignment>;
- result["text-field"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<std::string>, &SymbolLayer::setTextField>;
- result["text-font"] = &setProperty<V, SymbolLayer, PropertyValue<std::vector<std::string>>, &SymbolLayer::setTextFont>;
- result["text-size"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextSize>;
- result["text-max-width"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextMaxWidth>;
- result["text-line-height"] = &setProperty<V, SymbolLayer, PropertyValue<float>, &SymbolLayer::setTextLineHeight>;
- result["text-letter-spacing"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextLetterSpacing>;
- result["text-justify"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<TextJustifyType>, &SymbolLayer::setTextJustify>;
- result["text-anchor"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<SymbolAnchorType>, &SymbolLayer::setTextAnchor>;
- result["text-max-angle"] = &setProperty<V, SymbolLayer, PropertyValue<float>, &SymbolLayer::setTextMaxAngle>;
- result["text-rotate"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextRotate>;
- result["text-padding"] = &setProperty<V, SymbolLayer, PropertyValue<float>, &SymbolLayer::setTextPadding>;
- result["text-keep-upright"] = &setProperty<V, SymbolLayer, PropertyValue<bool>, &SymbolLayer::setTextKeepUpright>;
- result["text-transform"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<TextTransformType>, &SymbolLayer::setTextTransform>;
- result["text-offset"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<std::array<float, 2>>, &SymbolLayer::setTextOffset>;
- result["text-allow-overlap"] = &setProperty<V, SymbolLayer, PropertyValue<bool>, &SymbolLayer::setTextAllowOverlap>;
- result["text-ignore-placement"] = &setProperty<V, SymbolLayer, PropertyValue<bool>, &SymbolLayer::setTextIgnorePlacement>;
- result["text-optional"] = &setProperty<V, SymbolLayer, PropertyValue<bool>, &SymbolLayer::setTextOptional>;
-
-
-
-
-
- return result;
-}
-
-template <class V>
-auto makePaintPropertySetters() {
- std::unordered_map<std::string, PropertySetter<V>> result;
-
- result["fill-antialias"] = &setProperty<V, FillLayer, PropertyValue<bool>, &FillLayer::setFillAntialias>;
- result["fill-antialias-transition"] = &setTransition<V, FillLayer, &FillLayer::setFillAntialiasTransition>;
- result["fill-opacity"] = &setProperty<V, FillLayer, DataDrivenPropertyValue<float>, &FillLayer::setFillOpacity>;
- result["fill-opacity-transition"] = &setTransition<V, FillLayer, &FillLayer::setFillOpacityTransition>;
- result["fill-color"] = &setProperty<V, FillLayer, DataDrivenPropertyValue<Color>, &FillLayer::setFillColor>;
- result["fill-color-transition"] = &setTransition<V, FillLayer, &FillLayer::setFillColorTransition>;
- result["fill-outline-color"] = &setProperty<V, FillLayer, DataDrivenPropertyValue<Color>, &FillLayer::setFillOutlineColor>;
- result["fill-outline-color-transition"] = &setTransition<V, FillLayer, &FillLayer::setFillOutlineColorTransition>;
- result["fill-translate"] = &setProperty<V, FillLayer, PropertyValue<std::array<float, 2>>, &FillLayer::setFillTranslate>;
- result["fill-translate-transition"] = &setTransition<V, FillLayer, &FillLayer::setFillTranslateTransition>;
- result["fill-translate-anchor"] = &setProperty<V, FillLayer, PropertyValue<TranslateAnchorType>, &FillLayer::setFillTranslateAnchor>;
- result["fill-translate-anchor-transition"] = &setTransition<V, FillLayer, &FillLayer::setFillTranslateAnchorTransition>;
- result["fill-pattern"] = &setProperty<V, FillLayer, PropertyValue<std::string>, &FillLayer::setFillPattern>;
- result["fill-pattern-transition"] = &setTransition<V, FillLayer, &FillLayer::setFillPatternTransition>;
-
- result["line-opacity"] = &setProperty<V, LineLayer, DataDrivenPropertyValue<float>, &LineLayer::setLineOpacity>;
- result["line-opacity-transition"] = &setTransition<V, LineLayer, &LineLayer::setLineOpacityTransition>;
- result["line-color"] = &setProperty<V, LineLayer, DataDrivenPropertyValue<Color>, &LineLayer::setLineColor>;
- result["line-color-transition"] = &setTransition<V, LineLayer, &LineLayer::setLineColorTransition>;
- result["line-translate"] = &setProperty<V, LineLayer, PropertyValue<std::array<float, 2>>, &LineLayer::setLineTranslate>;
- result["line-translate-transition"] = &setTransition<V, LineLayer, &LineLayer::setLineTranslateTransition>;
- result["line-translate-anchor"] = &setProperty<V, LineLayer, PropertyValue<TranslateAnchorType>, &LineLayer::setLineTranslateAnchor>;
- result["line-translate-anchor-transition"] = &setTransition<V, LineLayer, &LineLayer::setLineTranslateAnchorTransition>;
- result["line-width"] = &setProperty<V, LineLayer, DataDrivenPropertyValue<float>, &LineLayer::setLineWidth>;
- result["line-width-transition"] = &setTransition<V, LineLayer, &LineLayer::setLineWidthTransition>;
- result["line-gap-width"] = &setProperty<V, LineLayer, DataDrivenPropertyValue<float>, &LineLayer::setLineGapWidth>;
- result["line-gap-width-transition"] = &setTransition<V, LineLayer, &LineLayer::setLineGapWidthTransition>;
- result["line-offset"] = &setProperty<V, LineLayer, DataDrivenPropertyValue<float>, &LineLayer::setLineOffset>;
- result["line-offset-transition"] = &setTransition<V, LineLayer, &LineLayer::setLineOffsetTransition>;
- result["line-blur"] = &setProperty<V, LineLayer, DataDrivenPropertyValue<float>, &LineLayer::setLineBlur>;
- result["line-blur-transition"] = &setTransition<V, LineLayer, &LineLayer::setLineBlurTransition>;
- result["line-dasharray"] = &setProperty<V, LineLayer, PropertyValue<std::vector<float>>, &LineLayer::setLineDasharray>;
- result["line-dasharray-transition"] = &setTransition<V, LineLayer, &LineLayer::setLineDasharrayTransition>;
- result["line-pattern"] = &setProperty<V, LineLayer, PropertyValue<std::string>, &LineLayer::setLinePattern>;
- result["line-pattern-transition"] = &setTransition<V, LineLayer, &LineLayer::setLinePatternTransition>;
-
- result["icon-opacity"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setIconOpacity>;
- result["icon-opacity-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setIconOpacityTransition>;
- result["icon-color"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<Color>, &SymbolLayer::setIconColor>;
- result["icon-color-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setIconColorTransition>;
- result["icon-halo-color"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<Color>, &SymbolLayer::setIconHaloColor>;
- result["icon-halo-color-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setIconHaloColorTransition>;
- result["icon-halo-width"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setIconHaloWidth>;
- result["icon-halo-width-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setIconHaloWidthTransition>;
- result["icon-halo-blur"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setIconHaloBlur>;
- result["icon-halo-blur-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setIconHaloBlurTransition>;
- result["icon-translate"] = &setProperty<V, SymbolLayer, PropertyValue<std::array<float, 2>>, &SymbolLayer::setIconTranslate>;
- result["icon-translate-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setIconTranslateTransition>;
- result["icon-translate-anchor"] = &setProperty<V, SymbolLayer, PropertyValue<TranslateAnchorType>, &SymbolLayer::setIconTranslateAnchor>;
- result["icon-translate-anchor-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setIconTranslateAnchorTransition>;
- result["text-opacity"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextOpacity>;
- result["text-opacity-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setTextOpacityTransition>;
- result["text-color"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<Color>, &SymbolLayer::setTextColor>;
- result["text-color-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setTextColorTransition>;
- result["text-halo-color"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<Color>, &SymbolLayer::setTextHaloColor>;
- result["text-halo-color-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setTextHaloColorTransition>;
- result["text-halo-width"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextHaloWidth>;
- result["text-halo-width-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setTextHaloWidthTransition>;
- result["text-halo-blur"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextHaloBlur>;
- result["text-halo-blur-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setTextHaloBlurTransition>;
- result["text-translate"] = &setProperty<V, SymbolLayer, PropertyValue<std::array<float, 2>>, &SymbolLayer::setTextTranslate>;
- result["text-translate-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setTextTranslateTransition>;
- result["text-translate-anchor"] = &setProperty<V, SymbolLayer, PropertyValue<TranslateAnchorType>, &SymbolLayer::setTextTranslateAnchor>;
- result["text-translate-anchor-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setTextTranslateAnchorTransition>;
-
- result["circle-radius"] = &setProperty<V, CircleLayer, DataDrivenPropertyValue<float>, &CircleLayer::setCircleRadius>;
- result["circle-radius-transition"] = &setTransition<V, CircleLayer, &CircleLayer::setCircleRadiusTransition>;
- result["circle-color"] = &setProperty<V, CircleLayer, DataDrivenPropertyValue<Color>, &CircleLayer::setCircleColor>;
- result["circle-color-transition"] = &setTransition<V, CircleLayer, &CircleLayer::setCircleColorTransition>;
- result["circle-blur"] = &setProperty<V, CircleLayer, DataDrivenPropertyValue<float>, &CircleLayer::setCircleBlur>;
- result["circle-blur-transition"] = &setTransition<V, CircleLayer, &CircleLayer::setCircleBlurTransition>;
- result["circle-opacity"] = &setProperty<V, CircleLayer, DataDrivenPropertyValue<float>, &CircleLayer::setCircleOpacity>;
- result["circle-opacity-transition"] = &setTransition<V, CircleLayer, &CircleLayer::setCircleOpacityTransition>;
- result["circle-translate"] = &setProperty<V, CircleLayer, PropertyValue<std::array<float, 2>>, &CircleLayer::setCircleTranslate>;
- result["circle-translate-transition"] = &setTransition<V, CircleLayer, &CircleLayer::setCircleTranslateTransition>;
- result["circle-translate-anchor"] = &setProperty<V, CircleLayer, PropertyValue<TranslateAnchorType>, &CircleLayer::setCircleTranslateAnchor>;
- result["circle-translate-anchor-transition"] = &setTransition<V, CircleLayer, &CircleLayer::setCircleTranslateAnchorTransition>;
- result["circle-pitch-scale"] = &setProperty<V, CircleLayer, PropertyValue<CirclePitchScaleType>, &CircleLayer::setCirclePitchScale>;
- result["circle-pitch-scale-transition"] = &setTransition<V, CircleLayer, &CircleLayer::setCirclePitchScaleTransition>;
- result["circle-pitch-alignment"] = &setProperty<V, CircleLayer, PropertyValue<AlignmentType>, &CircleLayer::setCirclePitchAlignment>;
- result["circle-pitch-alignment-transition"] = &setTransition<V, CircleLayer, &CircleLayer::setCirclePitchAlignmentTransition>;
- result["circle-stroke-width"] = &setProperty<V, CircleLayer, DataDrivenPropertyValue<float>, &CircleLayer::setCircleStrokeWidth>;
- result["circle-stroke-width-transition"] = &setTransition<V, CircleLayer, &CircleLayer::setCircleStrokeWidthTransition>;
- result["circle-stroke-color"] = &setProperty<V, CircleLayer, DataDrivenPropertyValue<Color>, &CircleLayer::setCircleStrokeColor>;
- result["circle-stroke-color-transition"] = &setTransition<V, CircleLayer, &CircleLayer::setCircleStrokeColorTransition>;
- result["circle-stroke-opacity"] = &setProperty<V, CircleLayer, DataDrivenPropertyValue<float>, &CircleLayer::setCircleStrokeOpacity>;
- result["circle-stroke-opacity-transition"] = &setTransition<V, CircleLayer, &CircleLayer::setCircleStrokeOpacityTransition>;
-
- result["fill-extrusion-opacity"] = &setProperty<V, FillExtrusionLayer, PropertyValue<float>, &FillExtrusionLayer::setFillExtrusionOpacity>;
- result["fill-extrusion-opacity-transition"] = &setTransition<V, FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionOpacityTransition>;
- result["fill-extrusion-color"] = &setProperty<V, FillExtrusionLayer, DataDrivenPropertyValue<Color>, &FillExtrusionLayer::setFillExtrusionColor>;
- result["fill-extrusion-color-transition"] = &setTransition<V, FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionColorTransition>;
- result["fill-extrusion-translate"] = &setProperty<V, FillExtrusionLayer, PropertyValue<std::array<float, 2>>, &FillExtrusionLayer::setFillExtrusionTranslate>;
- result["fill-extrusion-translate-transition"] = &setTransition<V, FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionTranslateTransition>;
- result["fill-extrusion-translate-anchor"] = &setProperty<V, FillExtrusionLayer, PropertyValue<TranslateAnchorType>, &FillExtrusionLayer::setFillExtrusionTranslateAnchor>;
- result["fill-extrusion-translate-anchor-transition"] = &setTransition<V, FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionTranslateAnchorTransition>;
- result["fill-extrusion-pattern"] = &setProperty<V, FillExtrusionLayer, PropertyValue<std::string>, &FillExtrusionLayer::setFillExtrusionPattern>;
- result["fill-extrusion-pattern-transition"] = &setTransition<V, FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionPatternTransition>;
- result["fill-extrusion-height"] = &setProperty<V, FillExtrusionLayer, DataDrivenPropertyValue<float>, &FillExtrusionLayer::setFillExtrusionHeight>;
- result["fill-extrusion-height-transition"] = &setTransition<V, FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionHeightTransition>;
- result["fill-extrusion-base"] = &setProperty<V, FillExtrusionLayer, DataDrivenPropertyValue<float>, &FillExtrusionLayer::setFillExtrusionBase>;
- result["fill-extrusion-base-transition"] = &setTransition<V, FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionBaseTransition>;
-
- result["raster-opacity"] = &setProperty<V, RasterLayer, PropertyValue<float>, &RasterLayer::setRasterOpacity>;
- result["raster-opacity-transition"] = &setTransition<V, RasterLayer, &RasterLayer::setRasterOpacityTransition>;
- result["raster-hue-rotate"] = &setProperty<V, RasterLayer, PropertyValue<float>, &RasterLayer::setRasterHueRotate>;
- result["raster-hue-rotate-transition"] = &setTransition<V, RasterLayer, &RasterLayer::setRasterHueRotateTransition>;
- result["raster-brightness-min"] = &setProperty<V, RasterLayer, PropertyValue<float>, &RasterLayer::setRasterBrightnessMin>;
- result["raster-brightness-min-transition"] = &setTransition<V, RasterLayer, &RasterLayer::setRasterBrightnessMinTransition>;
- result["raster-brightness-max"] = &setProperty<V, RasterLayer, PropertyValue<float>, &RasterLayer::setRasterBrightnessMax>;
- result["raster-brightness-max-transition"] = &setTransition<V, RasterLayer, &RasterLayer::setRasterBrightnessMaxTransition>;
- result["raster-saturation"] = &setProperty<V, RasterLayer, PropertyValue<float>, &RasterLayer::setRasterSaturation>;
- result["raster-saturation-transition"] = &setTransition<V, RasterLayer, &RasterLayer::setRasterSaturationTransition>;
- result["raster-contrast"] = &setProperty<V, RasterLayer, PropertyValue<float>, &RasterLayer::setRasterContrast>;
- result["raster-contrast-transition"] = &setTransition<V, RasterLayer, &RasterLayer::setRasterContrastTransition>;
- result["raster-fade-duration"] = &setProperty<V, RasterLayer, PropertyValue<float>, &RasterLayer::setRasterFadeDuration>;
- result["raster-fade-duration-transition"] = &setTransition<V, RasterLayer, &RasterLayer::setRasterFadeDurationTransition>;
-
- result["background-color"] = &setProperty<V, BackgroundLayer, PropertyValue<Color>, &BackgroundLayer::setBackgroundColor>;
- result["background-color-transition"] = &setTransition<V, BackgroundLayer, &BackgroundLayer::setBackgroundColorTransition>;
- result["background-pattern"] = &setProperty<V, BackgroundLayer, PropertyValue<std::string>, &BackgroundLayer::setBackgroundPattern>;
- result["background-pattern-transition"] = &setTransition<V, BackgroundLayer, &BackgroundLayer::setBackgroundPatternTransition>;
- result["background-opacity"] = &setProperty<V, BackgroundLayer, PropertyValue<float>, &BackgroundLayer::setBackgroundOpacity>;
- result["background-opacity-transition"] = &setTransition<V, BackgroundLayer, &BackgroundLayer::setBackgroundOpacityTransition>;
-
- return result;
-}
-
-} // namespace conversion
-} // namespace style
-} // namespace mbgl
diff --git a/include/mbgl/style/conversion/make_property_setters.hpp.ejs b/include/mbgl/style/conversion/make_property_setters.hpp.ejs
deleted file mode 100644
index 19c9f70538..0000000000
--- a/include/mbgl/style/conversion/make_property_setters.hpp.ejs
+++ /dev/null
@@ -1,48 +0,0 @@
-#pragma once
-
-// This file is generated. Edit make_property_setters.hpp.ejs, then run `make style-code`.
-
-#include <mbgl/style/conversion/property_setter.hpp>
-
-<% for (const layer of locals.layers) { -%>
-#include <mbgl/style/layers/<%- layer.type.replace('-', '_') %>_layer.hpp>
-<% } -%>
-
-#include <unordered_map>
-
-namespace mbgl {
-namespace style {
-namespace conversion {
-
-template <class V>
-auto makeLayoutPropertySetters() {
- std::unordered_map<std::string, PropertySetter<V>> result;
-
- result["visibility"] = &setVisibility<V>;
-
-<% for (const layer of locals.layers) { -%>
-<% for (const property of layer.layoutProperties) { -%>
- result["<%- property.name %>"] = &setProperty<V, <%- camelize(layer.type) %>Layer, <%- propertyValueType(property) %>, &<%- camelize(layer.type) %>Layer::set<%- camelize(property.name) %>>;
-<% } -%>
-
-<% } -%>
- return result;
-}
-
-template <class V>
-auto makePaintPropertySetters() {
- std::unordered_map<std::string, PropertySetter<V>> result;
-
-<% for (const layer of locals.layers) { -%>
-<% for (const property of layer.paintProperties) { -%>
- result["<%- property.name %>"] = &setProperty<V, <%- camelize(layer.type) %>Layer, <%- propertyValueType(property) %>, &<%- camelize(layer.type) %>Layer::set<%- camelize(property.name) %>>;
- result["<%- property.name %>-transition"] = &setTransition<V, <%- camelize(layer.type) %>Layer, &<%- camelize(layer.type) %>Layer::set<%- camelize(property.name) %>Transition>;
-<% } -%>
-
-<% } -%>
- return result;
-}
-
-} // namespace conversion
-} // namespace style
-} // namespace mbgl
diff --git a/include/mbgl/style/conversion/position.hpp b/include/mbgl/style/conversion/position.hpp
index 7036b03822..044c45862d 100644
--- a/include/mbgl/style/conversion/position.hpp
+++ b/include/mbgl/style/conversion/position.hpp
@@ -2,9 +2,6 @@
#include <mbgl/style/conversion.hpp>
#include <mbgl/style/position.hpp>
-#include <mbgl/util/optional.hpp>
-
-#include <array>
namespace mbgl {
namespace style {
@@ -12,16 +9,7 @@ namespace conversion {
template <>
struct Converter<Position> {
- template <class V>
- optional<Position> operator()(const V& value, Error& error) const {
- optional<std::array<float, 3>> spherical = convert<std::array<float, 3>>(value, error);
-
- if (!spherical) {
- return {};
- }
-
- return Position(*spherical);
- }
+ optional<Position> operator()(const Convertible& value, Error& error) const;
};
} // namespace conversion
diff --git a/include/mbgl/style/conversion/property_setter.hpp b/include/mbgl/style/conversion/property_setter.hpp
deleted file mode 100644
index 759c4512cc..0000000000
--- a/include/mbgl/style/conversion/property_setter.hpp
+++ /dev/null
@@ -1,72 +0,0 @@
-#pragma once
-
-#include <mbgl/style/layer.hpp>
-#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 <mbgl/style/conversion/transition_options.hpp>
-
-#include <string>
-
-namespace mbgl {
-namespace style {
-namespace conversion {
-
-template <class V>
-using PropertySetter = optional<Error> (*) (Layer&, const V&);
-
-template <class V, class L, class PropertyValue, void (L::*setter)(PropertyValue)>
-optional<Error> setProperty(Layer& layer, const V& value) {
- auto* typedLayer = layer.as<L>();
- if (!typedLayer) {
- return Error { "layer doesn't support this property" };
- }
-
- Error error;
- optional<PropertyValue> typedValue = convert<PropertyValue>(value, error);
- if (!typedValue) {
- return error;
- }
-
- (typedLayer->*setter)(*typedValue);
- return {};
-}
-
-template <class V, class L, void (L::*setter)(const TransitionOptions&)>
-optional<Error> setTransition(Layer& layer, const V& value) {
- auto* typedLayer = layer.as<L>();
- if (!typedLayer) {
- return Error { "layer doesn't support this property" };
- }
-
- Error error;
- optional<TransitionOptions> transition = convert<TransitionOptions>(value, error);
- if (!transition) {
- return error;
- }
-
- (typedLayer->*setter)(*transition);
- return {};
-}
-
-template <class V>
-optional<Error> setVisibility(Layer& layer, const V& value) {
- if (isUndefined(value)) {
- layer.setVisibility(VisibilityType::Visible);
- return {};
- }
-
- Error error;
- optional<VisibilityType> visibility = convert<VisibilityType>(value, error);
- if (!visibility) {
- return error;
- }
-
- layer.setVisibility(*visibility);
- return {};
-}
-
-} // namespace conversion
-} // namespace style
-} // namespace mbgl
diff --git a/include/mbgl/style/conversion/property_value.hpp b/include/mbgl/style/conversion/property_value.hpp
index f8937da07d..97117de2ec 100644
--- a/include/mbgl/style/conversion/property_value.hpp
+++ b/include/mbgl/style/conversion/property_value.hpp
@@ -4,6 +4,11 @@
#include <mbgl/style/conversion.hpp>
#include <mbgl/style/conversion/constant.hpp>
#include <mbgl/style/conversion/function.hpp>
+#include <mbgl/style/conversion/expression.hpp>
+#include <mbgl/style/expression/value.hpp>
+#include <mbgl/style/expression/is_constant.hpp>
+#include <mbgl/style/expression/is_expression.hpp>
+#include <mbgl/style/expression/find_zoom_curve.hpp>
namespace mbgl {
namespace style {
@@ -11,10 +16,20 @@ namespace conversion {
template <class T>
struct Converter<PropertyValue<T>> {
- template <class V>
- optional<PropertyValue<T>> operator()(const V& value, Error& error) const {
+ optional<PropertyValue<T>> operator()(const Convertible& value, Error& error) const {
if (isUndefined(value)) {
return PropertyValue<T>();
+ } else if (isExpression(value)) {
+ optional<std::unique_ptr<Expression>> expression = convert<std::unique_ptr<Expression>>(value, error, valueTypeToExpressionType<T>());
+ if (!expression) {
+ return {};
+ }
+ if (isFeatureConstant(**expression)) {
+ return { CameraFunction<T>(std::move(*expression)) };
+ } else {
+ error = { "property expressions not supported" };
+ return {};
+ }
} else if (isObject(value)) {
optional<CameraFunction<T>> function = convert<CameraFunction<T>>(value, error);
if (!function) {
diff --git a/include/mbgl/style/conversion/source.hpp b/include/mbgl/style/conversion/source.hpp
index e0563ac10b..2cf2e36da4 100644
--- a/include/mbgl/style/conversion/source.hpp
+++ b/include/mbgl/style/conversion/source.hpp
@@ -1,16 +1,9 @@
#pragma once
#include <mbgl/style/conversion.hpp>
-#include <mbgl/style/conversion/coordinate.hpp>
-#include <mbgl/style/conversion/geojson.hpp>
-#include <mbgl/style/conversion/geojson_options.hpp>
-#include <mbgl/style/conversion/tileset.hpp>
#include <mbgl/style/source.hpp>
-#include <mbgl/style/sources/geojson_source.hpp>
-#include <mbgl/style/sources/raster_source.hpp>
-#include <mbgl/style/sources/vector_source.hpp>
-#include <mbgl/style/sources/image_source.hpp>
-#include <mbgl/util/geo.hpp>
+
+#include <memory>
namespace mbgl {
namespace style {
@@ -19,170 +12,7 @@ namespace conversion {
template <>
struct Converter<std::unique_ptr<Source>> {
public:
-
- template <class V>
- optional<std::unique_ptr<Source>> operator()(const V& value, Error& error, const std::string& id) const {
- if (!isObject(value)) {
- error = { "source must be an object" };
- return {};
- }
-
- auto typeValue = objectMember(value, "type");
- if (!typeValue) {
- error = { "source must have a type" };
- return {};
- }
-
- optional<std::string> type = toString(*typeValue);
- if (!type) {
- error = { "source type must be a string" };
- return {};
- }
-
- if (*type == "raster") {
- return convertRasterSource(id, value, error);
- } else if (*type == "vector") {
- return convertVectorSource(id, value, error);
- } else if (*type == "geojson") {
- return convertGeoJSONSource(id, value, error);
- } else if (*type == "image") {
- return convertImageSource(id, value, error);
- } else {
- error = { "invalid source type" };
- return {};
- }
- }
-
-private:
- // A tile source can either specify a URL to TileJSON, or inline TileJSON.
- template <class V>
- optional<variant<std::string, Tileset>> convertURLOrTileset(const V& value, Error& error) const {
- auto urlVal = objectMember(value, "url");
- if (!urlVal) {
- optional<Tileset> tileset = convert<Tileset>(value, error);
- if (!tileset) {
- return {};
- }
- return { *tileset };
- }
-
- optional<std::string> url = toString(*urlVal);
- if (!url) {
- error = { "source url must be a string" };
- return {};
- }
-
- return { *url };
- }
-
- template <class V>
- optional<std::unique_ptr<Source>> convertRasterSource(const std::string& id,
- const V& value,
- Error& error) const {
- optional<variant<std::string, Tileset>> urlOrTileset = convertURLOrTileset(value, error);
- if (!urlOrTileset) {
- return {};
- }
-
- uint16_t tileSize = util::tileSize;
- auto tileSizeValue = objectMember(value, "tileSize");
- if (tileSizeValue) {
- optional<float> size = toNumber(*tileSizeValue);
- if (!size || *size < 0 || *size > std::numeric_limits<uint16_t>::max()) {
- error = { "invalid tileSize" };
- return {};
- }
- tileSize = *size;
- }
-
- return { std::make_unique<RasterSource>(id, std::move(*urlOrTileset), tileSize) };
- }
-
- template <class V>
- optional<std::unique_ptr<Source>> convertVectorSource(const std::string& id,
- const V& value,
- Error& error) const {
- optional<variant<std::string, Tileset>> urlOrTileset = convertURLOrTileset(value, error);
- if (!urlOrTileset) {
- return {};
- }
-
- return { std::make_unique<VectorSource>(id, std::move(*urlOrTileset)) };
- }
-
- template <class V>
- optional<std::unique_ptr<Source>> convertGeoJSONSource(const std::string& id,
- const V& value,
- Error& error) const {
- auto dataValue = objectMember(value, "data");
- if (!dataValue) {
- error = { "GeoJSON source must have a data value" };
- return {};
- }
-
- optional<GeoJSONOptions> options = convert<GeoJSONOptions>(value, error);
- if (!options) {
- return {};
- }
-
- auto result = std::make_unique<GeoJSONSource>(id, *options);
-
- if (isObject(*dataValue)) {
- optional<GeoJSON> geoJSON = convert<GeoJSON>(*dataValue, error);
- if (!geoJSON) {
- return {};
- }
- result->setGeoJSON(std::move(*geoJSON));
- } else if (toString(*dataValue)) {
- result->setURL(*toString(*dataValue));
- } else {
- error = { "GeoJSON data must be a URL or an object" };
- return {};
- }
-
- return { std::move(result) };
- }
-
- template <class V>
- optional<std::unique_ptr<Source>> convertImageSource(const std::string& id,
- const V& value,
- Error& error) const {
- auto urlValue = objectMember(value, "url");
- if (!urlValue) {
- error = { "Image source must have a url value" };
- return {};
- }
-
- auto urlString = toString(*urlValue);
- if (!urlString) {
- error = { "Image url must be a URL string" };
- return {};
- }
-
- auto coordinatesValue = objectMember(value, "coordinates");
- if (!coordinatesValue) {
- error = { "Image source must have a coordinates values" };
- return {};
- }
-
- if (!isArray(*coordinatesValue) || arrayLength(*coordinatesValue) != 4) {
- error = { "Image coordinates must be an array of four longitude latitude pairs" };
- return {};
- }
-
- std::array<LatLng, 4> coordinates;
- for (std::size_t i=0; i < 4; i++) {
- auto latLng = conversion::convert<LatLng>(arrayMember(*coordinatesValue,i), error);
- if (!latLng) {
- return {};
- }
- coordinates[i] = *latLng;
- }
- auto result = std::make_unique<ImageSource>(id, coordinates);
- result->setURL(*urlString);
-
- return { std::move(result) };
- }
+ optional<std::unique_ptr<Source>> operator()(const Convertible& value, Error& error, const std::string& id) const;
};
} // namespace conversion
diff --git a/include/mbgl/style/conversion/tileset.hpp b/include/mbgl/style/conversion/tileset.hpp
index 6ec46aa7b6..1fb4acf70d 100644
--- a/include/mbgl/style/conversion/tileset.hpp
+++ b/include/mbgl/style/conversion/tileset.hpp
@@ -10,101 +10,7 @@ namespace conversion {
template <>
struct Converter<Tileset> {
public:
-
- bool validateLatitude(const double lat) const {
- return lat <= 90 && lat >= -90;
- }
-
- template <class V>
- optional<Tileset> operator()(const V& value, Error& error) const {
- Tileset result;
-
- auto tiles = objectMember(value, "tiles");
- if (!tiles) {
- error = { "source must have tiles" };
- return {};
- }
-
- if (!isArray(*tiles)) {
- error = { "source tiles must be an array" };
- return {};
- }
-
- for (std::size_t i = 0; i < arrayLength(*tiles); i++) {
- optional<std::string> urlTemplate = toString(arrayMember(*tiles, i));
- if (!urlTemplate) {
- error = { "source tiles member must be a string" };
- return {};
- }
- result.tiles.push_back(std::move(*urlTemplate));
- }
-
- auto schemeValue = objectMember(value, "scheme");
- if (schemeValue) {
- optional<std::string> scheme = toString(*schemeValue);
- if (scheme && *scheme == "tms") {
- result.scheme = Tileset::Scheme::TMS;
- }
- }
-
- auto minzoomValue = objectMember(value, "minzoom");
- if (minzoomValue) {
- optional<float> minzoom = toNumber(*minzoomValue);
- if (!minzoom || *minzoom < 0 || *minzoom > std::numeric_limits<uint8_t>::max()) {
- error = { "invalid minzoom" };
- return {};
- }
- result.zoomRange.min = *minzoom;
- }
-
- auto maxzoomValue = objectMember(value, "maxzoom");
- if (maxzoomValue) {
- optional<float> maxzoom = toNumber(*maxzoomValue);
- if (!maxzoom || *maxzoom < 0 || *maxzoom > std::numeric_limits<uint8_t>::max()) {
- error = { "invalid maxzoom" };
- return {};
- }
- result.zoomRange.max = *maxzoom;
- }
-
- auto attributionValue = objectMember(value, "attribution");
- if (attributionValue) {
- optional<std::string> attribution = toString(*attributionValue);
- if (!attribution) {
- error = { "source attribution must be a string" };
- return {};
- }
- result.attribution = std::move(*attribution);
- }
-
- auto boundsValue = objectMember(value, "bounds");
- if (boundsValue) {
- if (!isArray(*boundsValue) || arrayLength(*boundsValue) != 4) {
- error = { "bounds must be an array with left, bottom, top, and right values" };
- return {};
- }
- optional<double> left = toDouble(arrayMember(*boundsValue, 0));
- optional<double> bottom = toDouble(arrayMember(*boundsValue, 1));
- optional<double> right = toDouble(arrayMember(*boundsValue, 2));
- optional<double> top = toDouble(arrayMember(*boundsValue, 3));
-
- if (!left || !right || !bottom || !top) {
- error = { "bounds array must contain numeric longitude and latitude values" };
- return {};
- }
- if (!validateLatitude(*bottom) || !validateLatitude(*top) || top <= bottom){
- error = { "bounds latitude values must be between -90 and 90 with bottom less than top" };
- return {};
- }
- if(*left >= *right) {
- error = { "bounds left longitude should be less than right longitude" };
- return {};
- }
- result.bounds = LatLngBounds::hull({ *bottom, *left }, { *top, *right });
- }
-
- return result;
- }
+ optional<Tileset> operator()(const Convertible& value, Error& error) const;
};
} // namespace conversion
diff --git a/include/mbgl/style/conversion/transition_options.hpp b/include/mbgl/style/conversion/transition_options.hpp
index de8834d578..0563f39ac3 100644
--- a/include/mbgl/style/conversion/transition_options.hpp
+++ b/include/mbgl/style/conversion/transition_options.hpp
@@ -10,37 +10,7 @@ namespace conversion {
template <>
struct Converter<TransitionOptions> {
public:
- template <class V>
- optional<TransitionOptions> operator()(const V& value, Error& error) const {
- if (!isObject(value)) {
- error = { "transition must be an object" };
- return {};
- }
-
- TransitionOptions result;
-
- auto duration = objectMember(value, "duration");
- if (duration) {
- auto number = toNumber(*duration);
- if (!number) {
- error = { "duration must be a number" };
- return {};
- }
- result.duration = { std::chrono::milliseconds(int64_t(*number)) };
- }
-
- auto delay = objectMember(value, "delay");
- if (delay) {
- auto number = toNumber(*delay);
- if (!number) {
- error = { "delay must be a number" };
- return {};
- }
- result.delay = { std::chrono::milliseconds(int64_t(*number)) };
- }
-
- return result;
- }
+ optional<TransitionOptions> operator()(const Convertible& value, Error& error) const;
};
} // namespace conversion
diff --git a/include/mbgl/style/expression/array_assertion.hpp b/include/mbgl/style/expression/array_assertion.hpp
new file mode 100644
index 0000000000..7f36f8aac2
--- /dev/null
+++ b/include/mbgl/style/expression/array_assertion.hpp
@@ -0,0 +1,43 @@
+#pragma once
+
+#include <mbgl/style/expression/expression.hpp>
+#include <mbgl/style/expression/type.hpp>
+#include <mbgl/style/expression/parsing_context.hpp>
+#include <mbgl/style/conversion.hpp>
+
+#include <memory>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+class ArrayAssertion : public Expression {
+public:
+ ArrayAssertion(type::Array type_, std::unique_ptr<Expression> input_) :
+ Expression(type_),
+ input(std::move(input_))
+ {}
+
+ static ParseResult parse(const mbgl::style::conversion::Convertible& value, ParsingContext& ctx);
+
+ EvaluationResult evaluate(const EvaluationContext& params) const override;
+ void eachChild(const std::function<void(const Expression&)>& visit) const override;
+
+ bool operator==(const Expression& e) const override {
+ if (auto rhs = dynamic_cast<const ArrayAssertion*>(&e)) {
+ return getType() == rhs->getType() && *input == *(rhs->input);
+ }
+ return false;
+ }
+
+ std::vector<optional<Value>> possibleOutputs() const override {
+ return input->possibleOutputs();
+ }
+
+private:
+ std::unique_ptr<Expression> input;
+};
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/expression/assertion.hpp b/include/mbgl/style/expression/assertion.hpp
new file mode 100644
index 0000000000..43ea73f2ba
--- /dev/null
+++ b/include/mbgl/style/expression/assertion.hpp
@@ -0,0 +1,36 @@
+#pragma once
+
+#include <mbgl/style/expression/expression.hpp>
+#include <mbgl/style/conversion.hpp>
+#include <mbgl/style/expression/parsing_context.hpp>
+
+#include <memory>
+#include <vector>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+class Assertion : public Expression {
+public:
+ Assertion(type::Type type_, std::vector<std::unique_ptr<Expression>> inputs_) :
+ Expression(type_),
+ inputs(std::move(inputs_))
+ {}
+
+ static ParseResult parse(const mbgl::style::conversion::Convertible& value, ParsingContext& ctx);
+
+ EvaluationResult evaluate(const EvaluationContext& params) const override;
+ void eachChild(const std::function<void(const Expression&)>& visit) const override;
+
+ bool operator==(const Expression& e) const override;
+
+ std::vector<optional<Value>> possibleOutputs() const override;
+
+private:
+ std::vector<std::unique_ptr<Expression>> inputs;
+};
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/expression/at.hpp b/include/mbgl/style/expression/at.hpp
new file mode 100644
index 0000000000..27fccc761f
--- /dev/null
+++ b/include/mbgl/style/expression/at.hpp
@@ -0,0 +1,42 @@
+#pragma once
+
+#include <mbgl/style/expression/expression.hpp>
+#include <mbgl/style/conversion.hpp>
+#include <memory>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+class At : public Expression {
+public:
+ At(std::unique_ptr<Expression> index_, std::unique_ptr<Expression> input_) :
+ Expression(input_->getType().get<type::Array>().itemType),
+ index(std::move(index_)),
+ input(std::move(input_))
+ {}
+
+ static ParseResult parse(const mbgl::style::conversion::Convertible& value, ParsingContext& ctx);
+
+ EvaluationResult evaluate(const EvaluationContext& params) const override;
+ void eachChild(const std::function<void(const Expression&)>&) const override;
+
+ bool operator==(const Expression& e) const override {
+ if (auto rhs = dynamic_cast<const At*>(&e)) {
+ return *index == *(rhs->index) && *input == *(rhs->input);
+ }
+ return false;
+ }
+
+ std::vector<optional<Value>> possibleOutputs() const override {
+ return { nullopt };
+ }
+
+private:
+ std::unique_ptr<Expression> index;
+ std::unique_ptr<Expression> input;
+};
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/expression/boolean_operator.hpp b/include/mbgl/style/expression/boolean_operator.hpp
new file mode 100644
index 0000000000..115a096665
--- /dev/null
+++ b/include/mbgl/style/expression/boolean_operator.hpp
@@ -0,0 +1,50 @@
+#pragma once
+
+#include <mbgl/style/expression/expression.hpp>
+#include <mbgl/style/conversion.hpp>
+
+#include <memory>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+class Any : public Expression {
+public:
+ Any(std::vector<std::unique_ptr<Expression>> inputs_) :
+ Expression(type::Boolean),
+ inputs(std::move(inputs_))
+ {}
+
+ static ParseResult parse(const mbgl::style::conversion::Convertible& value, ParsingContext& ctx);
+
+ EvaluationResult evaluate(const EvaluationContext& params) const override;
+ void eachChild(const std::function<void(const Expression&)>& visit) const override;
+ bool operator==(const Expression& e) const override;
+ std::vector<optional<Value>> possibleOutputs() const override;
+
+private:
+ std::vector<std::unique_ptr<Expression>> inputs;
+};
+
+class All : public Expression {
+public:
+ All(std::vector<std::unique_ptr<Expression>> inputs_) :
+ Expression(type::Boolean),
+ inputs(std::move(inputs_))
+ {}
+
+ static ParseResult parse(const mbgl::style::conversion::Convertible& value, ParsingContext& ctx);
+
+ EvaluationResult evaluate(const EvaluationContext& params) const override;
+ void eachChild(const std::function<void(const Expression&)>& visit) const override;
+ bool operator==(const Expression& e) const override;
+ std::vector<optional<Value>> possibleOutputs() const override;
+
+private:
+ std::vector<std::unique_ptr<Expression>> inputs;
+};
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/expression/case.hpp b/include/mbgl/style/expression/case.hpp
new file mode 100644
index 0000000000..e61a55fc6d
--- /dev/null
+++ b/include/mbgl/style/expression/case.hpp
@@ -0,0 +1,38 @@
+#pragma once
+
+#include <mbgl/style/expression/expression.hpp>
+#include <mbgl/style/conversion.hpp>
+#include <mbgl/style/expression/parsing_context.hpp>
+
+#include <memory>
+#include <vector>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+class Case : public Expression {
+public:
+ using Branch = std::pair<std::unique_ptr<Expression>, std::unique_ptr<Expression>>;
+
+ Case(type::Type type_, std::vector<Branch> branches_, std::unique_ptr<Expression> otherwise_)
+ : Expression(type_), branches(std::move(branches_)), otherwise(std::move(otherwise_)) {
+ }
+
+ static ParseResult parse(const mbgl::style::conversion::Convertible& value, ParsingContext& ctx);
+
+ EvaluationResult evaluate(const EvaluationContext& params) const override;
+ void eachChild(const std::function<void(const Expression&)>& visit) const override;
+
+ bool operator==(const Expression& e) const override;
+
+ std::vector<optional<Value>> possibleOutputs() const override;
+
+private:
+ std::vector<Branch> branches;
+ std::unique_ptr<Expression> otherwise;
+};
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/expression/check_subtype.hpp b/include/mbgl/style/expression/check_subtype.hpp
new file mode 100644
index 0000000000..90e5169de7
--- /dev/null
+++ b/include/mbgl/style/expression/check_subtype.hpp
@@ -0,0 +1,17 @@
+#pragma once
+
+#include <mbgl/style/expression/type.hpp>
+#include <mbgl/util/optional.hpp>
+#include <memory>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+namespace type {
+
+optional<std::string> checkSubtype(const Type& expected, const Type& t);
+
+} // namespace type
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/expression/coalesce.hpp b/include/mbgl/style/expression/coalesce.hpp
new file mode 100644
index 0000000000..52d9498cbd
--- /dev/null
+++ b/include/mbgl/style/expression/coalesce.hpp
@@ -0,0 +1,47 @@
+#pragma once
+
+#include <mbgl/style/expression/expression.hpp>
+#include <mbgl/style/conversion.hpp>
+#include <mbgl/style/expression/parsing_context.hpp>
+
+#include <memory>
+#include <map>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+class Coalesce : public Expression {
+public:
+ using Args = std::vector<std::unique_ptr<Expression>>;
+ Coalesce(const type::Type& type_, Args args_) :
+ Expression(type_),
+ args(std::move(args_))
+ {}
+
+ static ParseResult parse(const mbgl::style::conversion::Convertible& value, ParsingContext& ctx);
+
+
+ EvaluationResult evaluate(const EvaluationContext& params) const override;
+
+ void eachChild(const std::function<void(const Expression&)>& visit) const override;
+
+ bool operator==(const Expression& e) const override;
+
+ std::vector<optional<Value>> possibleOutputs() const override;
+
+ std::size_t getLength() const {
+ return args.size();
+ }
+
+ Expression* getChild(std::size_t i) const {
+ return args.at(i).get();
+ }
+
+private:
+ Args args;
+};
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/expression/coercion.hpp b/include/mbgl/style/expression/coercion.hpp
new file mode 100644
index 0000000000..40d2490186
--- /dev/null
+++ b/include/mbgl/style/expression/coercion.hpp
@@ -0,0 +1,39 @@
+#pragma once
+
+#include <mbgl/style/expression/expression.hpp>
+#include <mbgl/style/conversion.hpp>
+
+#include <memory>
+#include <vector>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+/**
+ * Special form for error-coalescing coercion expressions "to-number",
+ * "to-color". Since these coercions can fail at runtime, they accept multiple
+ * arguments, only evaluating one at a time until one succeeds.
+ */
+class Coercion : public Expression {
+public:
+ Coercion(type::Type type_, std::vector<std::unique_ptr<Expression>> inputs_);
+
+ static ParseResult parse(const mbgl::style::conversion::Convertible& value, ParsingContext& ctx);
+
+ EvaluationResult evaluate(const EvaluationContext& params) const override;
+ void eachChild(const std::function<void(const Expression&)>& visit) const override;
+
+ bool operator==(const Expression& e) const override;
+
+ std::vector<optional<Value>> possibleOutputs() const override;
+
+private:
+ EvaluationResult (*coerceSingleValue) (const Value& v);
+ std::vector<std::unique_ptr<Expression>> inputs;
+};
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
+
diff --git a/include/mbgl/style/expression/compound_expression.hpp b/include/mbgl/style/expression/compound_expression.hpp
new file mode 100644
index 0000000000..8b74027578
--- /dev/null
+++ b/include/mbgl/style/expression/compound_expression.hpp
@@ -0,0 +1,142 @@
+#pragma once
+
+#include <mbgl/style/expression/expression.hpp>
+#include <mbgl/style/conversion.hpp>
+#include <mbgl/style/expression/parsing_context.hpp>
+#include <mbgl/style/expression/type.hpp>
+#include <mbgl/style/expression/value.hpp>
+
+#include <mbgl/util/optional.hpp>
+#include <mbgl/util/variant.hpp>
+
+#include <memory>
+#include <vector>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+/*
+ CompoundExpression provides a mechanism for implementing an expression
+ simply by providing a list of pure functions of the form
+ (const T0& arg0, const T1& arg1, ...) -> Result<U> where T0, T1, ..., U are
+ member types of mbgl::style::expression::Value.
+
+ The majority of expressions specified in the style-spec are implemented in
+ this fashion (see compound_expression.cpp).
+*/
+
+
+/*
+ Represents the parameter list for an expression that takes an arbitrary
+ number of arguments (of a specific type).
+*/
+struct VarargsType { type::Type type; };
+template <typename T>
+struct Varargs : std::vector<T> { using std::vector<T>::vector; };
+
+namespace detail {
+// Base class for the Signature<Fn> structs that are used to determine the
+// each CompoundExpression definition's type::Type data from the type of its
+// "evaluate" function.
+struct SignatureBase {
+ SignatureBase(type::Type result_, variant<std::vector<type::Type>, VarargsType> params_) :
+ result(std::move(result_)),
+ params(std::move(params_))
+ {}
+ virtual ~SignatureBase() = default;
+ virtual std::unique_ptr<Expression> makeExpression(const std::string& name, std::vector<std::unique_ptr<Expression>>) const = 0;
+ type::Type result;
+ variant<std::vector<type::Type>, VarargsType> params;
+};
+} // namespace detail
+
+
+/*
+ Common base class for CompoundExpression<Signature> instances. Used to
+ allow downcasting (and access to things like name & parameter list) during
+ an Expression tree traversal.
+*/
+class CompoundExpressionBase : public Expression {
+public:
+ CompoundExpressionBase(std::string name_, const detail::SignatureBase& signature) :
+ Expression(signature.result),
+ name(std::move(name_)),
+ params(signature.params)
+ {}
+
+ std::string getName() const { return name; }
+ optional<std::size_t> getParameterCount() const {
+ return params.match(
+ [&](const VarargsType&) { return optional<std::size_t>(); },
+ [&](const std::vector<type::Type>& p) -> optional<std::size_t> { return p.size(); }
+ );
+ }
+
+ std::vector<optional<Value>> possibleOutputs() const override {
+ return { nullopt };
+ }
+
+private:
+ std::string name;
+ variant<std::vector<type::Type>, VarargsType> params;
+};
+
+template <typename Signature>
+class CompoundExpression : public CompoundExpressionBase {
+public:
+ using Args = typename Signature::Args;
+
+ CompoundExpression(const std::string& name_,
+ Signature signature_,
+ typename Signature::Args args_) :
+ CompoundExpressionBase(name_, signature_),
+ signature(signature_),
+ args(std::move(args_))
+ {}
+
+ EvaluationResult evaluate(const EvaluationContext& evaluationParams) const override {
+ return signature.apply(evaluationParams, args);
+ }
+
+ void eachChild(const std::function<void(const Expression&)>& visit) const override {
+ for (const std::unique_ptr<Expression>& e : args) {
+ visit(*e);
+ }
+ }
+
+ bool operator==(const Expression& e) const override {
+ if (auto rhs = dynamic_cast<const CompoundExpression*>(&e)) {
+ return getName() == rhs->getName() && Expression::childrenEqual(args, rhs->args);
+ }
+ return false;
+ }
+
+private:
+ Signature signature;
+ typename Signature::Args args;
+};
+
+/*
+ Holds the map of expression name => implementation (which is just one or
+ more evaluation functions, each wrapped in a Signature struct).
+*/
+struct CompoundExpressionRegistry {
+ using Definition = std::vector<std::unique_ptr<detail::SignatureBase>>;
+ static std::unordered_map<std::string, Definition> definitions;
+};
+
+ParseResult parseCompoundExpression(const std::string name, const mbgl::style::conversion::Convertible& value, ParsingContext& ctx);
+
+ParseResult createCompoundExpression(const std::string& name,
+ const CompoundExpressionRegistry::Definition& definition,
+ std::vector<std::unique_ptr<Expression>> args,
+ ParsingContext& ctx);
+
+ParseResult createCompoundExpression(const std::string& name,
+ std::vector<std::unique_ptr<Expression>> args,
+ ParsingContext& ctx);
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/expression/equals.hpp b/include/mbgl/style/expression/equals.hpp
new file mode 100644
index 0000000000..80550bd59d
--- /dev/null
+++ b/include/mbgl/style/expression/equals.hpp
@@ -0,0 +1,32 @@
+#pragma once
+
+#include <mbgl/style/expression/expression.hpp>
+#include <mbgl/style/expression/parsing_context.hpp>
+#include <mbgl/style/conversion.hpp>
+
+#include <memory>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+class Equals : public Expression {
+public:
+ Equals(std::unique_ptr<Expression> lhs, std::unique_ptr<Expression> rhs, bool negate);
+
+ static ParseResult parse(const mbgl::style::conversion::Convertible&, ParsingContext&);
+
+ void eachChild(const std::function<void(const Expression&)>& visit) const override;
+ bool operator==(const Expression&) const override;
+ EvaluationResult evaluate(const EvaluationContext&) const override;
+ std::vector<optional<Value>> possibleOutputs() const override;
+
+private:
+ std::unique_ptr<Expression> lhs;
+ std::unique_ptr<Expression> rhs;
+ bool negate;
+};
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/expression/expression.hpp b/include/mbgl/style/expression/expression.hpp
new file mode 100644
index 0000000000..cf9fa0cb21
--- /dev/null
+++ b/include/mbgl/style/expression/expression.hpp
@@ -0,0 +1,179 @@
+#pragma once
+
+#include <mbgl/util/optional.hpp>
+#include <mbgl/util/variant.hpp>
+#include <mbgl/util/color.hpp>
+#include <mbgl/style/expression/type.hpp>
+#include <mbgl/style/expression/value.hpp>
+#include <mbgl/style/expression/parsing_context.hpp>
+
+#include <array>
+#include <vector>
+#include <memory>
+
+namespace mbgl {
+
+class GeometryTileFeature;
+
+namespace style {
+namespace expression {
+
+class EvaluationError {
+public:
+ std::string message;
+};
+
+class EvaluationContext {
+public:
+ EvaluationContext(float zoom_) : zoom(zoom_), feature(nullptr) {}
+ EvaluationContext(GeometryTileFeature const * feature_) : zoom(optional<float>()), feature(feature_) {}
+ EvaluationContext(float zoom_, GeometryTileFeature const * feature_) :
+ zoom(zoom_), feature(feature_)
+ {}
+ EvaluationContext(optional<float> zoom_, GeometryTileFeature const * feature_, optional<double> heatmapDensity_) :
+ zoom(std::move(zoom_)), feature(feature_), heatmapDensity(std::move(heatmapDensity_))
+ {}
+
+ optional<float> zoom;
+ GeometryTileFeature const * feature;
+ optional<double> heatmapDensity;
+};
+
+template <typename T>
+class Result : private variant<EvaluationError, T> {
+public:
+ using variant<EvaluationError, T>::variant;
+ using Value = T;
+
+ Result() = default;
+
+ explicit operator bool () const {
+ return this->template is<T>();
+ }
+
+ // optional does some type trait magic for this one, so this might
+ // be problematic as is.
+ const T* operator->() const {
+ assert(this->template is<T>());
+ return std::addressof(this->template get<T>());
+ }
+
+ T* operator->() {
+ assert(this->template is<T>());
+ return std::addressof(this->template get<T>());
+ }
+
+ T& operator*() {
+ assert(this->template is<T>());
+ return this->template get<T>();
+ }
+
+ const T& operator*() const {
+ assert(this->template is<T>());
+ return this->template get<T>();
+ }
+
+ const EvaluationError& error() const {
+ assert(this->template is<EvaluationError>());
+ return this->template get<EvaluationError>();
+ }
+};
+
+class EvaluationResult : public Result<Value> {
+public:
+ using Result::Result; // NOLINT
+
+ EvaluationResult() = default;
+
+ EvaluationResult(const std::array<double, 4>& arr) :
+ Result(toExpressionValue(arr))
+ {}
+
+ // used only for the special (private) "error" expression
+ EvaluationResult(const type::ErrorType&) {
+ assert(false);
+ }
+};
+
+/*
+ Expression is an abstract class that serves as an interface and base class
+ for particular expression implementations.
+
+ CompoundExpression implements the majority of expressions in the spec by
+ inferring the argument and output from a simple function (const T0& arg0,
+ const T1& arg1, ...) -> Result<U> where T0, T1, ..., U are member types of
+ mbgl::style::expression::Value.
+
+ The other Expression subclasses (Let, Curve, Match, etc.) exist in order to
+ implement expressions that need specialized parsing, type checking, or
+ evaluation logic that can't be handled by CompoundExpression's inference
+ mechanism.
+
+ Each Expression subclass also provides a static
+ ParseResult ExpressionClass::parse(const V&, ParsingContext),
+ which handles parsing a style-spec JSON representation of the expression.
+*/
+class Expression {
+public:
+ Expression(type::Type type_) : type(std::move(type_)) {}
+ virtual ~Expression() = default;
+
+ virtual EvaluationResult evaluate(const EvaluationContext& params) const = 0;
+ virtual void eachChild(const std::function<void(const Expression&)>&) const = 0;
+ virtual bool operator==(const Expression&) const = 0;
+ bool operator!=(const Expression& rhs) const {
+ return !operator==(rhs);
+ }
+
+ type::Type getType() const { return type; };
+
+ EvaluationResult evaluate(optional<float> zoom, const Feature& feature, optional<double> heatmapDensity) const;
+
+ /**
+ * Statically analyze the expression, attempting to enumerate possible outputs. Returns
+ * an array of values plus the sentinel null optional value, used to indicate that the
+ * complete set of outputs is statically undecidable.
+ */
+ virtual std::vector<optional<Value>> possibleOutputs() const = 0;
+
+protected:
+ template <typename T>
+ static bool childrenEqual(const T& lhs, const T& rhs) {
+ if (lhs.size() != rhs.size()) return false;
+ for (auto leftChild = lhs.begin(), rightChild = rhs.begin();
+ leftChild != lhs.end();
+ leftChild++, rightChild++)
+ {
+ if (!Expression::childEqual(*leftChild, *rightChild)) return false;
+ }
+ return true;
+ }
+
+ static bool childEqual(const std::unique_ptr<Expression>& lhs, const std::unique_ptr<Expression>& rhs) {
+ return *lhs == *rhs;
+ }
+
+ template <typename T>
+ static bool childEqual(const std::pair<T, std::unique_ptr<Expression>>& lhs,
+ const std::pair<T, std::unique_ptr<Expression>>& rhs) {
+ return lhs.first == rhs.first && *(lhs.second) == *(rhs.second);
+ }
+
+ template <typename T>
+ static bool childEqual(const std::pair<T, std::shared_ptr<Expression>>& lhs,
+ const std::pair<T, std::shared_ptr<Expression>>& rhs) {
+ return lhs.first == rhs.first && *(lhs.second) == *(rhs.second);
+ }
+
+ static bool childEqual(const std::pair<std::unique_ptr<Expression>, std::unique_ptr<Expression>>& lhs,
+ const std::pair<std::unique_ptr<Expression>, std::unique_ptr<Expression>>& rhs) {
+ return *(lhs.first) == *(rhs.first) && *(lhs.second) == *(rhs.second);
+ }
+
+private:
+ type::Type type;
+};
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/expression/find_zoom_curve.hpp b/include/mbgl/style/expression/find_zoom_curve.hpp
new file mode 100644
index 0000000000..6301938033
--- /dev/null
+++ b/include/mbgl/style/expression/find_zoom_curve.hpp
@@ -0,0 +1,20 @@
+#pragma once
+
+#include <mbgl/style/expression/parsing_context.hpp>
+#include <mbgl/style/expression/interpolate.hpp>
+#include <mbgl/style/expression/step.hpp>
+
+#include <mbgl/util/variant.hpp>
+#include <mbgl/util/optional.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+optional<variant<const InterpolateBase*, const Step*, ParsingError>> findZoomCurve(const expression::Expression* e);
+
+variant<const InterpolateBase*, const Step*> findZoomCurveChecked(const expression::Expression* e);
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/expression/get_covering_stops.hpp b/include/mbgl/style/expression/get_covering_stops.hpp
new file mode 100644
index 0000000000..157aefe7bc
--- /dev/null
+++ b/include/mbgl/style/expression/get_covering_stops.hpp
@@ -0,0 +1,18 @@
+#pragma once
+
+#include <mbgl/style/expression/expression.hpp>
+#include <mbgl/util/range.hpp>
+#include <memory>
+#include <map>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+// Return the smallest range of stops that covers the interval [lower, upper]
+Range<float> getCoveringStops(const std::map<double, std::unique_ptr<Expression>>& stops,
+ const double lower, const double upper);
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/expression/interpolate.hpp b/include/mbgl/style/expression/interpolate.hpp
new file mode 100644
index 0000000000..dbed74b4cd
--- /dev/null
+++ b/include/mbgl/style/expression/interpolate.hpp
@@ -0,0 +1,192 @@
+#pragma once
+
+#include <mbgl/style/expression/expression.hpp>
+#include <mbgl/style/expression/parsing_context.hpp>
+#include <mbgl/style/expression/get_covering_stops.hpp>
+#include <mbgl/style/conversion.hpp>
+
+#include <mbgl/util/interpolate.hpp>
+#include <mbgl/util/range.hpp>
+#include <mbgl/util/unitbezier.hpp>
+
+#include <memory>
+#include <map>
+#include <cmath>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+class ExponentialInterpolator {
+public:
+ ExponentialInterpolator(double base_) : base(base_) {}
+
+ double base;
+
+ double interpolationFactor(const Range<double>& inputLevels, const double input) const {
+ return util::interpolationFactor(base,
+ Range<float> {
+ static_cast<float>(inputLevels.min),
+ static_cast<float>(inputLevels.max)
+ },
+ input);
+ }
+
+ bool operator==(const ExponentialInterpolator& rhs) const {
+ return base == rhs.base;
+ }
+};
+
+class CubicBezierInterpolator {
+public:
+ CubicBezierInterpolator(double x1_, double y1_, double x2_, double y2_) : ub(x1_, y1_, x2_, y2_) {}
+
+ double interpolationFactor(const Range<double>& inputLevels, const double input) const {
+ return ub.solve(input / (inputLevels.max - inputLevels.min), 1e-6);
+ }
+
+ bool operator==(const CubicBezierInterpolator& rhs) const {
+ return ub == rhs.ub;
+ }
+
+ util::UnitBezier ub;
+};
+
+
+ParseResult parseInterpolate(const mbgl::style::conversion::Convertible& value, ParsingContext& ctx);
+
+class InterpolateBase : public Expression {
+public:
+ using Interpolator = variant<ExponentialInterpolator, CubicBezierInterpolator>;
+
+ InterpolateBase(const type::Type& type_,
+ Interpolator interpolator_,
+ std::unique_ptr<Expression> input_,
+ std::map<double, std::unique_ptr<Expression>> stops_
+ ) : Expression(type_),
+ interpolator(std::move(interpolator_)),
+ input(std::move(input_)),
+ stops(std::move(stops_))
+ {}
+
+ const std::unique_ptr<Expression>& getInput() const { return input; }
+ const Interpolator& getInterpolator() const { return interpolator; }
+
+ void eachChild(const std::function<void(const Expression&)>& visit) const override {
+ visit(*input);
+ for (const auto& stop : stops) {
+ visit(*stop.second);
+ }
+ }
+
+ void eachStop(const std::function<void(double, const Expression&)>& visit) const {
+ for (const auto& stop : stops) {
+ visit(stop.first, *stop.second);
+ }
+ }
+
+ // Return the smallest range of stops that covers the interval [lower, upper]
+ Range<float> getCoveringStops(const double lower, const double upper) const {
+ return ::mbgl::style::expression::getCoveringStops(stops, lower, upper);
+ }
+
+ double interpolationFactor(const Range<double>& inputLevels, const double inputValue) const {
+ return interpolator.match(
+ [&](const auto& interp) { return interp.interpolationFactor(inputLevels, inputValue); }
+ );
+ }
+
+ std::vector<optional<Value>> possibleOutputs() const override;
+
+protected:
+ const Interpolator interpolator;
+ const std::unique_ptr<Expression> input;
+ const std::map<double, std::unique_ptr<Expression>> stops;
+};
+
+template <typename T>
+class Interpolate : public InterpolateBase {
+public:
+ Interpolate(type::Type type_,
+ Interpolator interpolator_,
+ std::unique_ptr<Expression> input_,
+ std::map<double, std::unique_ptr<Expression>> stops_
+ ) : InterpolateBase(std::move(type_), std::move(interpolator_), std::move(input_), std::move(stops_))
+ {
+ static_assert(util::Interpolatable<T>::value, "Interpolate expression requires an interpolatable value type.");
+ }
+
+ EvaluationResult evaluate(const EvaluationContext& params) const override {
+ const EvaluationResult evaluatedInput = input->evaluate(params);
+ if (!evaluatedInput) {
+ return evaluatedInput.error();
+ }
+
+ float x = *fromExpressionValue<float>(*evaluatedInput);
+ if (std::isnan(x)) {
+ return EvaluationError { "Input is not a number." };
+ }
+
+ if (stops.empty()) {
+ return EvaluationError { "No stops in exponential curve." };
+ }
+
+ auto it = stops.upper_bound(x);
+ if (it == stops.end()) {
+ return stops.rbegin()->second->evaluate(params);
+ } else if (it == stops.begin()) {
+ return stops.begin()->second->evaluate(params);
+ } else {
+ float t = interpolationFactor({ std::prev(it)->first, it->first }, x);
+
+ if (t == 0.0f) {
+ return std::prev(it)->second->evaluate(params);
+ }
+ if (t == 1.0f) {
+ return it->second->evaluate(params);
+ }
+
+ EvaluationResult lower = std::prev(it)->second->evaluate(params);
+ if (!lower) {
+ return lower.error();
+ }
+ EvaluationResult upper = it->second->evaluate(params);
+ if (!upper) {
+ return upper.error();
+ }
+
+ if (!lower->is<T>()) {
+ return EvaluationError {
+ "Expected value to be of type " + toString(valueTypeToExpressionType<T>()) +
+ ", but found " + toString(typeOf(*lower)) + " instead."
+ };
+ }
+
+ if (!upper->is<T>()) {
+ return EvaluationError {
+ "Expected value to be of type " + toString(valueTypeToExpressionType<T>()) +
+ ", but found " + toString(typeOf(*upper)) + " instead."
+ };
+ }
+ return util::interpolate(lower->get<T>(), upper->get<T>(), t);
+ }
+ }
+
+ bool operator==(const Expression& e) const override {
+ if (auto rhs = dynamic_cast<const Interpolate*>(&e)) {
+ if (interpolator != rhs->interpolator ||
+ *input != *(rhs->input) ||
+ stops.size() != rhs->stops.size())
+ {
+ return false;
+ }
+
+ return Expression::childrenEqual(stops, rhs->stops);
+ }
+ return false;
+ }
+};
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/expression/is_constant.hpp b/include/mbgl/style/expression/is_constant.hpp
new file mode 100644
index 0000000000..29e03ccbc0
--- /dev/null
+++ b/include/mbgl/style/expression/is_constant.hpp
@@ -0,0 +1,35 @@
+#pragma once
+
+#include <mbgl/style/expression/expression.hpp>
+#include <mbgl/style/expression/compound_expression.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+template <typename T>
+bool isGlobalPropertyConstant(const Expression& expression, const T& properties) {
+ if (auto e = dynamic_cast<const CompoundExpressionBase*>(&expression)) {
+ for (const std::string& property : properties) {
+ if (e->getName() == property) {
+ return false;
+ }
+ }
+ }
+
+ bool isConstant = true;
+ expression.eachChild([&](const Expression& e) {
+ if (isConstant && !isGlobalPropertyConstant(e, properties)) {
+ isConstant = false;
+ }
+ });
+ return isConstant;
+};
+
+bool isFeatureConstant(const Expression& expression);
+bool isZoomConstant(const Expression& e);
+
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/expression/is_expression.hpp b/include/mbgl/style/expression/is_expression.hpp
new file mode 100644
index 0000000000..77c489619c
--- /dev/null
+++ b/include/mbgl/style/expression/is_expression.hpp
@@ -0,0 +1,13 @@
+#pragma once
+
+#include <mbgl/style/expression/expression.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+bool isExpression(const conversion::Convertible& value);
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/expression/let.hpp b/include/mbgl/style/expression/let.hpp
new file mode 100644
index 0000000000..6829ded9b8
--- /dev/null
+++ b/include/mbgl/style/expression/let.hpp
@@ -0,0 +1,76 @@
+#pragma once
+
+#include <mbgl/style/expression/expression.hpp>
+#include <mbgl/style/expression/parsing_context.hpp>
+#include <mbgl/style/conversion.hpp>
+
+#include <memory>
+#include <map>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+class Let : public Expression {
+public:
+ using Bindings = std::map<std::string, std::shared_ptr<Expression>>;
+
+ Let(Bindings bindings_, std::unique_ptr<Expression> result_) :
+ Expression(result_->getType()),
+ bindings(std::move(bindings_)),
+ result(std::move(result_))
+ {}
+
+ static ParseResult parse(const mbgl::style::conversion::Convertible&, ParsingContext&);
+
+ EvaluationResult evaluate(const EvaluationContext& params) const override;
+ void eachChild(const std::function<void(const Expression&)>&) const override;
+
+ bool operator==(const Expression& e) const override {
+ if (auto rhs = dynamic_cast<const Let*>(&e)) {
+ return *result == *(rhs->result);
+ }
+ return false;
+ }
+
+ std::vector<optional<Value>> possibleOutputs() const override;
+
+ Expression* getResult() const {
+ return result.get();
+ }
+
+private:
+ Bindings bindings;
+ std::unique_ptr<Expression> result;
+};
+
+class Var : public Expression {
+public:
+ Var(std::string name_, std::shared_ptr<Expression> value_) :
+ Expression(value_->getType()),
+ name(std::move(name_)),
+ value(value_)
+ {}
+
+ static ParseResult parse(const mbgl::style::conversion::Convertible&, ParsingContext&);
+
+ EvaluationResult evaluate(const EvaluationContext& params) const override;
+ void eachChild(const std::function<void(const Expression&)>&) const override;
+
+ bool operator==(const Expression& e) const override {
+ if (auto rhs = dynamic_cast<const Var*>(&e)) {
+ return *value == *(rhs->value);
+ }
+ return false;
+ }
+
+ std::vector<optional<Value>> possibleOutputs() const override;
+
+private:
+ std::string name;
+ std::shared_ptr<Expression> value;
+};
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/expression/literal.hpp b/include/mbgl/style/expression/literal.hpp
new file mode 100644
index 0000000000..82983d78af
--- /dev/null
+++ b/include/mbgl/style/expression/literal.hpp
@@ -0,0 +1,42 @@
+#pragma once
+
+#include <mbgl/style/expression/expression.hpp>
+#include <mbgl/style/expression/parsing_context.hpp>
+#include <mbgl/style/conversion.hpp>
+
+#include <memory>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+class Literal : public Expression {
+public:
+ Literal(Value value_) : Expression(typeOf(value_)), value(value_) {}
+ Literal(type::Array type_, std::vector<Value> value_) : Expression(type_), value(value_) {}
+ EvaluationResult evaluate(const EvaluationContext&) const override {
+ return value;
+ }
+
+ static ParseResult parse(const mbgl::style::conversion::Convertible&, ParsingContext&);
+
+ void eachChild(const std::function<void(const Expression&)>&) const override {}
+
+ bool operator==(const Expression& e) const override {
+ if (auto rhs = dynamic_cast<const Literal*>(&e)) {
+ return value == rhs->value;
+ }
+ return false;
+ }
+
+ std::vector<optional<Value>> possibleOutputs() const override {
+ return {{ value }};
+ }
+
+private:
+ Value value;
+};
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/expression/match.hpp b/include/mbgl/style/expression/match.hpp
new file mode 100644
index 0000000000..682d784b0f
--- /dev/null
+++ b/include/mbgl/style/expression/match.hpp
@@ -0,0 +1,46 @@
+#pragma once
+
+#include <mbgl/style/expression/expression.hpp>
+#include <mbgl/style/expression/parsing_context.hpp>
+#include <mbgl/style/conversion.hpp>
+
+#include <memory>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+template <typename T>
+class Match : public Expression {
+public:
+ using Branches = std::unordered_map<T, std::shared_ptr<Expression>>;
+
+ Match(type::Type type_,
+ std::unique_ptr<Expression> input_,
+ Branches branches_,
+ std::unique_ptr<Expression> otherwise_
+ ) : Expression(type_),
+ input(std::move(input_)),
+ branches(std::move(branches_)),
+ otherwise(std::move(otherwise_))
+ {}
+
+ EvaluationResult evaluate(const EvaluationContext& params) const override;
+
+ void eachChild(const std::function<void(const Expression&)>& visit) const override;
+
+ bool operator==(const Expression& e) const override;
+
+ std::vector<optional<Value>> possibleOutputs() const override;
+
+private:
+ std::unique_ptr<Expression> input;
+ Branches branches;
+ std::unique_ptr<Expression> otherwise;
+};
+
+ParseResult parseMatch(const mbgl::style::conversion::Convertible& value, ParsingContext& ctx);
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/expression/parsing_context.hpp b/include/mbgl/style/expression/parsing_context.hpp
new file mode 100644
index 0000000000..f92a4c95ea
--- /dev/null
+++ b/include/mbgl/style/expression/parsing_context.hpp
@@ -0,0 +1,156 @@
+#pragma once
+
+#include <mbgl/util/optional.hpp>
+#include <mbgl/util/string.hpp>
+#include <mbgl/style/expression/type.hpp>
+#include <mbgl/style/conversion.hpp>
+
+#include <iterator>
+#include <map>
+#include <string>
+#include <vector>
+#include <memory>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+class Expression;
+
+struct ParsingError {
+ std::string message;
+ std::string key;
+ bool operator==(const ParsingError& rhs) const { return message == rhs.message && key == rhs.key; }
+};
+
+using ParseResult = optional<std::unique_ptr<Expression>>;
+
+namespace detail {
+
+class Scope {
+public:
+ Scope(const std::map<std::string, std::shared_ptr<Expression>>& bindings_, std::shared_ptr<Scope> parent_ = nullptr) :
+ bindings(bindings_),
+ parent(std::move(parent_))
+ {}
+
+ const std::map<std::string, std::shared_ptr<Expression>>& bindings;
+ std::shared_ptr<Scope> parent;
+
+ optional<std::shared_ptr<Expression>> get(const std::string& name) {
+ auto it = bindings.find(name);
+ if (it != bindings.end()) {
+ return {it->second};
+ } else if (parent) {
+ return parent->get(name);
+ } else {
+ return optional<std::shared_ptr<Expression>>();
+ }
+ }
+};
+
+} // namespace detail
+
+class ParsingContext {
+public:
+ ParsingContext() : errors(std::make_shared<std::vector<ParsingError>>()) {}
+ ParsingContext(std::string key_) : key(std::move(key_)), errors(std::make_shared<std::vector<ParsingError>>()) {}
+ explicit ParsingContext(optional<type::Type> expected_)
+ : expected(std::move(expected_)),
+ errors(std::make_shared<std::vector<ParsingError>>())
+ {}
+ ParsingContext(ParsingContext&&) = default;
+
+ ParsingContext(const ParsingContext&) = delete;
+ ParsingContext& operator=(const ParsingContext&) = delete;
+
+ std::string getKey() const { return key; }
+ optional<type::Type> getExpected() const { return expected; }
+ const std::vector<ParsingError>& getErrors() const { return *errors; }
+
+ enum TypeAnnotationOption {
+ includeTypeAnnotations,
+ omitTypeAnnotations
+ };
+
+ /*
+ Parse the given style-spec JSON value into an Expression object.
+ Specifically, this function is responsible for determining the expression
+ type (either Literal, or the one named in value[0]) and dispatching to the
+ appropriate ParseXxxx::parse(const V&, ParsingContext) method.
+ */
+ ParseResult parse(const mbgl::style::conversion::Convertible& value,
+ TypeAnnotationOption typeAnnotationOption = includeTypeAnnotations);
+
+ /*
+ Parse a child expression.
+ */
+ ParseResult parse(const mbgl::style::conversion::Convertible&,
+ std::size_t,
+ optional<type::Type> = {},
+ TypeAnnotationOption typeAnnotationOption = includeTypeAnnotations);
+
+ /*
+ Parse a child expression.
+ */
+ ParseResult parse(const mbgl::style::conversion::Convertible&,
+ std::size_t index,
+ optional<type::Type>,
+ const std::map<std::string, std::shared_ptr<Expression>>&);
+
+ /*
+ Check whether `t` is a subtype of `expected`, collecting an error if not.
+ */
+ optional<std::string> checkType(const type::Type& t);
+
+ optional<std::shared_ptr<Expression>> getBinding(const std::string name) {
+ if (!scope) return optional<std::shared_ptr<Expression>>();
+ return scope->get(name);
+ }
+
+ void error(std::string message) {
+ errors->push_back({message, key});
+ }
+
+ void error(std::string message, std::size_t child) {
+ errors->push_back({message, key + "[" + util::toString(child) + "]"});
+ }
+
+ void error(std::string message, std::size_t child, std::size_t grandchild) {
+ errors->push_back({message, key + "[" + util::toString(child) + "][" + util::toString(grandchild) + "]"});
+ }
+
+ void appendErrors(ParsingContext&& ctx) {
+ errors->reserve(errors->size() + ctx.errors->size());
+ std::move(ctx.errors->begin(), ctx.errors->end(), std::inserter(*errors, errors->end()));
+ ctx.errors->clear();
+ }
+
+ void clearErrors() {
+ errors->clear();
+ }
+
+private:
+ ParsingContext(std::string key_,
+ std::shared_ptr<std::vector<ParsingError>> errors_,
+ optional<type::Type> expected_,
+ std::shared_ptr<detail::Scope> scope_)
+ : key(std::move(key_)),
+ expected(std::move(expected_)),
+ scope(std::move(scope_)),
+ errors(std::move(errors_))
+ {}
+
+ std::string key;
+ optional<type::Type> expected;
+ std::shared_ptr<detail::Scope> scope;
+ std::shared_ptr<std::vector<ParsingError>> errors;
+};
+
+using ParseFunction = ParseResult (*)(const conversion::Convertible&, ParsingContext&);
+using ExpressionRegistry = std::unordered_map<std::string, ParseFunction>;
+const ExpressionRegistry& getExpressionRegistry();
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/expression/step.hpp b/include/mbgl/style/expression/step.hpp
new file mode 100644
index 0000000000..6bf42e20f1
--- /dev/null
+++ b/include/mbgl/style/expression/step.hpp
@@ -0,0 +1,48 @@
+
+#pragma once
+
+#include <mbgl/style/expression/expression.hpp>
+#include <mbgl/style/expression/parsing_context.hpp>
+#include <mbgl/style/conversion.hpp>
+
+#include <mbgl/util/range.hpp>
+
+#include <memory>
+#include <map>
+
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+class Step : public Expression {
+public:
+ Step(const type::Type& type_,
+ std::unique_ptr<Expression> input_,
+ std::map<double, std::unique_ptr<Expression>> stops_
+ ) : Expression(type_),
+ input(std::move(input_)),
+ stops(std::move(stops_))
+ {}
+
+ EvaluationResult evaluate(const EvaluationContext& params) const override;
+ void eachChild(const std::function<void(const Expression&)>& visit) const override;
+ void eachStop(const std::function<void(double, const Expression&)>& visit) const;
+
+ const std::unique_ptr<Expression>& getInput() const { return input; }
+ Range<float> getCoveringStops(const double lower, const double upper) const;
+
+ bool operator==(const Expression& e) const override;
+
+ std::vector<optional<Value>> possibleOutputs() const override;
+
+ static ParseResult parse(const mbgl::style::conversion::Convertible& value, ParsingContext& ctx);
+
+private:
+ const std::unique_ptr<Expression> input;
+ const std::map<double, std::unique_ptr<Expression>> stops;
+};
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/expression/type.hpp b/include/mbgl/style/expression/type.hpp
new file mode 100644
index 0000000000..513c4bdc17
--- /dev/null
+++ b/include/mbgl/style/expression/type.hpp
@@ -0,0 +1,112 @@
+#pragma once
+
+#include <mbgl/util/optional.hpp>
+#include <mbgl/util/string.hpp>
+#include <mbgl/util/variant.hpp>
+#include <vector>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+namespace type {
+
+template <class T>
+std::string toString(const T& t);
+
+struct NullType {
+ constexpr NullType() {};
+ std::string getName() const { return "null"; }
+ bool operator==(const NullType&) const { return true; }
+};
+
+struct NumberType {
+ constexpr NumberType() {};
+ std::string getName() const { return "number"; }
+ bool operator==(const NumberType&) const { return true; }
+};
+
+struct BooleanType {
+ constexpr BooleanType() {};
+ std::string getName() const { return "boolean"; }
+ bool operator==(const BooleanType&) const { return true; }
+};
+
+struct StringType {
+ constexpr StringType() {};
+ std::string getName() const { return "string"; }
+ bool operator==(const StringType&) const { return true; }
+};
+
+struct ColorType {
+ constexpr ColorType() {};
+ std::string getName() const { return "color"; }
+ bool operator==(const ColorType&) const { return true; }
+};
+
+struct ObjectType {
+ constexpr ObjectType() {};
+ std::string getName() const { return "object"; }
+ bool operator==(const ObjectType&) const { return true; }
+};
+
+struct ErrorType {
+ constexpr ErrorType() {};
+ std::string getName() const { return "error"; }
+ bool operator==(const ErrorType&) const { return true; }
+};
+
+struct ValueType {
+ constexpr ValueType() {};
+ std::string getName() const { return "value"; }
+ bool operator==(const ValueType&) const { return true; }
+};
+
+constexpr NullType Null;
+constexpr NumberType Number;
+constexpr StringType String;
+constexpr BooleanType Boolean;
+constexpr ColorType Color;
+constexpr ValueType Value;
+constexpr ObjectType Object;
+constexpr ErrorType Error;
+
+struct Array;
+
+using Type = variant<
+ NullType,
+ NumberType,
+ BooleanType,
+ StringType,
+ ColorType,
+ ObjectType,
+ ValueType,
+ mapbox::util::recursive_wrapper<Array>,
+ ErrorType>;
+
+struct Array {
+ explicit Array(Type itemType_) : itemType(std::move(itemType_)) {}
+ Array(Type itemType_, std::size_t N_) : itemType(std::move(itemType_)), N(N_) {}
+ Array(Type itemType_, optional<std::size_t> N_) : itemType(std::move(itemType_)), N(std::move(N_)) {}
+ std::string getName() const {
+ if (N) {
+ return "array<" + toString(itemType) + ", " + util::toString(*N) + ">";
+ } else if (itemType == Value) {
+ return "array";
+ } else {
+ return "array<" + toString(itemType) + ">";
+ }
+ }
+
+ bool operator==(const Array& rhs) const { return itemType == rhs.itemType && N == rhs.N; }
+
+ Type itemType;
+ optional<std::size_t> N;
+};
+
+template <class T>
+std::string toString(const T& type) { return type.match([&] (const auto& t) { return t.getName(); }); }
+
+} // namespace type
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/expression/value.hpp b/include/mbgl/style/expression/value.hpp
new file mode 100644
index 0000000000..be5be64752
--- /dev/null
+++ b/include/mbgl/style/expression/value.hpp
@@ -0,0 +1,162 @@
+#pragma once
+
+#include <mbgl/style/expression/type.hpp>
+#include <mbgl/style/position.hpp>
+#include <mbgl/style/types.hpp>
+#include <mbgl/util/color.hpp>
+#include <mbgl/util/enum.hpp>
+#include <mbgl/util/feature.hpp>
+#include <mbgl/util/variant.hpp>
+
+#include <array>
+#include <vector>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+struct Value;
+
+using ValueBase = variant<
+ NullValue,
+ bool,
+ double,
+ std::string,
+ Color,
+ mapbox::util::recursive_wrapper<std::vector<Value>>,
+ mapbox::util::recursive_wrapper<std::unordered_map<std::string, Value>>>;
+struct Value : ValueBase {
+ using ValueBase::ValueBase;
+
+ // Javascript's Number.MAX_SAFE_INTEGER
+ static uint64_t maxSafeInteger() { return 9007199254740991ULL; }
+
+ static bool isSafeInteger(uint64_t x) { return x <= maxSafeInteger(); };
+ static bool isSafeInteger(int64_t x) {
+ return static_cast<uint64_t>(x > 0 ? x : -x) <= maxSafeInteger();
+ }
+ static bool isSafeInteger(double x) {
+ return static_cast<uint64_t>(x > 0 ? x : -x) <= maxSafeInteger();
+ }
+
+};
+
+constexpr NullValue Null = NullValue();
+
+type::Type typeOf(const Value& value);
+std::string stringify(const Value& value);
+
+/*
+ Returns a Type object representing the expression type that corresponds to
+ the value type T. (Specialized for primitives and specific array types in
+ the .cpp.)
+*/
+template <typename T>
+type::Type valueTypeToExpressionType();
+
+/*
+ Conversions between style value types and expression::Value
+*/
+
+// no-op overloads
+Value toExpressionValue(const Value&);
+
+// T = Value (just wrap in optional)
+template <typename T>
+std::enable_if_t<std::is_same<T, Value>::value,
+optional<T>> fromExpressionValue(const Value& v)
+{
+ return optional<T>(v);
+}
+
+// T = member type of Value
+template <typename T>
+std::enable_if_t< std::is_convertible<T, Value>::value && !std::is_same<T, Value>::value,
+optional<T>> fromExpressionValue(const Value& v)
+{
+ return v.template is<T>() ? v.template get<T>() : optional<T>();
+}
+
+// real conversions
+template <typename T, typename Enable = std::enable_if_t< !std::is_convertible<T, Value>::value >>
+Value toExpressionValue(const T& value);
+
+template <typename T>
+std::enable_if_t< !std::is_convertible<T, Value>::value,
+optional<T>> fromExpressionValue(const Value& v);
+
+
+
+template <class T, class Enable = void>
+struct ValueConverter {
+ using ExpressionType = T;
+
+ static Value toExpressionValue(const T& value) {
+ return Value(value);
+ }
+ static optional<T> fromExpressionValue(const Value& value) {
+ return value.template is<T>() ? value.template get<T>() : optional<T>();
+ }
+};
+
+template <>
+struct ValueConverter<float> {
+ using ExpressionType = double;
+ static type::Type expressionType() { return type::Number; }
+ static Value toExpressionValue(const float value);
+ static optional<float> fromExpressionValue(const Value& value);
+};
+
+template<>
+struct ValueConverter<mbgl::Value> {
+ static Value toExpressionValue(const mbgl::Value& value);
+};
+
+template <typename T, std::size_t N>
+struct ValueConverter<std::array<T, N>> {
+ using ExpressionType = std::vector<Value>;
+ static type::Type expressionType() {
+ return type::Array(valueTypeToExpressionType<T>(), N);
+ }
+ static Value toExpressionValue(const std::array<T, N>& value);
+ static optional<std::array<T, N>> fromExpressionValue(const Value& value);
+};
+
+template <typename T>
+struct ValueConverter<std::vector<T>> {
+ using ExpressionType = std::vector<Value>;
+ static type::Type expressionType() {
+ return type::Array(valueTypeToExpressionType<T>());
+ }
+ static Value toExpressionValue(const std::vector<T>& value);
+ static optional<std::vector<T>> fromExpressionValue(const Value& value);
+};
+
+template <>
+struct ValueConverter<Position> {
+ using ExpressionType = std::vector<Value>;
+ static type::Type expressionType() { return type::Array(type::Number, 3); }
+ static Value toExpressionValue(const mbgl::style::Position& value);
+ static optional<Position> fromExpressionValue(const Value& v);
+};
+
+template <typename T>
+struct ValueConverter<T, std::enable_if_t< std::is_enum<T>::value >> {
+ using ExpressionType = std::string;
+ static type::Type expressionType() { return type::String; }
+ static Value toExpressionValue(const T& value);
+ static optional<T> fromExpressionValue(const Value& value);
+};
+
+template <typename T>
+std::vector<optional<T>> fromExpressionValues(const std::vector<optional<Value>>& values) {
+ std::vector<optional<T>> result;
+ for (const auto& value : values) {
+ result.push_back(value ? fromExpressionValue<T>(*value) : nullopt);
+ }
+ return result;
+}
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/function/camera_function.hpp b/include/mbgl/style/function/camera_function.hpp
index 7fde365b3d..1da5d2c601 100644
--- a/include/mbgl/style/function/camera_function.hpp
+++ b/include/mbgl/style/function/camera_function.hpp
@@ -1,5 +1,12 @@
#pragma once
+#include <mbgl/style/expression/expression.hpp>
+#include <mbgl/style/expression/interpolate.hpp>
+#include <mbgl/style/expression/step.hpp>
+#include <mbgl/style/expression/find_zoom_curve.hpp>
+#include <mbgl/style/expression/value.hpp>
+#include <mbgl/style/expression/is_constant.hpp>
+#include <mbgl/style/function/convert.hpp>
#include <mbgl/style/function/exponential_stops.hpp>
#include <mbgl/style/function/interval_stops.hpp>
#include <mbgl/util/interpolate.hpp>
@@ -18,24 +25,66 @@ public:
IntervalStops<T>>,
variant<
IntervalStops<T>>>;
+
+ CameraFunction(std::unique_ptr<expression::Expression> expression_)
+ : expression(std::move(expression_)),
+ zoomCurve(expression::findZoomCurveChecked(expression.get()))
+ {
+ assert(!expression::isZoomConstant(*expression));
+ assert(expression::isFeatureConstant(*expression));
+ }
CameraFunction(Stops stops_)
- : stops(std::move(stops_)) {
- }
+ : stops(std::move(stops_)),
+ expression(stops.match([&] (const auto& s) {
+ return expression::Convert::toExpression(s);
+ })),
+ zoomCurve(expression::findZoomCurveChecked(expression.get()))
+ {}
T evaluate(float zoom) const {
- return stops.match([&] (const auto& s) {
- return s.evaluate(zoom).value_or(T());
- });
+ const expression::EvaluationResult result = expression->evaluate(expression::EvaluationContext(zoom, nullptr));
+ if (result) {
+ const optional<T> typed = expression::fromExpressionValue<T>(*result);
+ return typed ? *typed : T();
+ }
+ return T();
}
+ float interpolationFactor(const Range<float>& inputLevels, const float inputValue) const {
+ return zoomCurve.match(
+ [&](const expression::InterpolateBase* z) {
+ return z->interpolationFactor(Range<double> { inputLevels.min, inputLevels.max }, inputValue);
+ },
+ [&](const expression::Step*) { return 0.0f; }
+ );
+ }
+
+ Range<float> getCoveringStops(const float lower, const float upper) const {
+ return zoomCurve.match(
+ [&](auto z) { return z->getCoveringStops(lower, upper); }
+ );
+ }
+
+ std::vector<optional<T>> possibleOutputs() const {
+ return expression::fromExpressionValues<T>(expression->possibleOutputs());
+ }
+
friend bool operator==(const CameraFunction& lhs,
const CameraFunction& rhs) {
- return lhs.stops == rhs.stops;
+ return *lhs.expression == *rhs.expression;
}
- Stops stops;
bool useIntegerZoom = false;
+
+ const expression::Expression& getExpression() const { return *expression; }
+
+ // retained for compatibility with pre-expression function API
+ Stops stops;
+
+private:
+ std::shared_ptr<expression::Expression> expression;
+ const variant<const expression::InterpolateBase*, const expression::Step*> zoomCurve;
};
} // namespace style
diff --git a/include/mbgl/style/function/composite_function.hpp b/include/mbgl/style/function/composite_function.hpp
index 7b524b6021..f391b101ae 100644
--- a/include/mbgl/style/function/composite_function.hpp
+++ b/include/mbgl/style/function/composite_function.hpp
@@ -1,5 +1,12 @@
#pragma once
+#include <mbgl/style/expression/expression.hpp>
+#include <mbgl/style/expression/interpolate.hpp>
+#include <mbgl/style/expression/step.hpp>
+#include <mbgl/style/expression/find_zoom_curve.hpp>
+#include <mbgl/style/expression/value.hpp>
+#include <mbgl/style/expression/is_constant.hpp>
+#include <mbgl/style/function/convert.hpp>
#include <mbgl/style/function/composite_exponential_stops.hpp>
#include <mbgl/style/function/composite_interval_stops.hpp>
#include <mbgl/style/function/composite_categorical_stops.hpp>
@@ -43,110 +50,77 @@ public:
CompositeIntervalStops<T>,
CompositeCategoricalStops<T>>>;
- CompositeFunction(std::string property_, Stops stops_, optional<T> defaultValue_ = {})
- : property(std::move(property_)),
- stops(std::move(stops_)),
- defaultValue(std::move(defaultValue_)) {
- }
-
- struct CoveringRanges {
- float zoom;
- Range<float> coveringZoomRange;
- Range<InnerStops> coveringStopsRange;
- };
-
- // Return the relevant stop zoom values and inner stops that bracket a given zoom level. This
- // is the first step toward evaluating the function, and is used for in the course of both partial
- // evaluation of data-driven paint properties, and full evaluation of data-driven layout properties.
- CoveringRanges coveringRanges(float zoom) const {
- return stops.match(
- [&] (const auto& s) {
- assert(!s.stops.empty());
- auto minIt = s.stops.lower_bound(zoom);
- auto maxIt = s.stops.upper_bound(zoom);
-
- // lower_bound yields first element >= zoom, but we want the *last*
- // element <= zoom, so if we found a stop > zoom, back up by one.
- if (minIt != s.stops.begin() && minIt != s.stops.end() && minIt->first > zoom) {
- minIt--;
- }
-
- return CoveringRanges {
- zoom,
- Range<float> {
- minIt == s.stops.end() ? s.stops.rbegin()->first : minIt->first,
- maxIt == s.stops.end() ? s.stops.rbegin()->first : maxIt->first
- },
- Range<InnerStops> {
- s.innerStops(minIt == s.stops.end() ? s.stops.rbegin()->second : minIt->second),
- s.innerStops(maxIt == s.stops.end() ? s.stops.rbegin()->second : maxIt->second)
- }
- };
- }
- );
- }
-
- // Given a range of zoom values (typically two adjacent integer zoom levels, e.g. 5.0 and 6.0),
- // return the covering ranges for both. This is used in the course of partial evaluation for
- // data-driven paint properties.
- Range<CoveringRanges> rangeOfCoveringRanges(Range<float> zoomRange) {
- return Range<CoveringRanges> {
- coveringRanges(zoomRange.min),
- coveringRanges(zoomRange.max)
- };
+ CompositeFunction(std::unique_ptr<expression::Expression> expression_)
+ : expression(std::move(expression_)),
+ zoomCurve(expression::findZoomCurveChecked(expression.get()))
+ {
+ assert(!expression::isZoomConstant(*expression));
+ assert(!expression::isFeatureConstant(*expression));
}
- // Given the covering ranges for range of zoom values (typically two adjacent integer zoom levels,
- // e.g. 5.0 and 6.0), and a feature, return the results of fully evaluating the function for that
- // feature at each of the two zoom levels. These two results are what go into the paint vertex buffers
- // for vertices associated with this feature. The shader will interpolate between them at render time.
+ CompositeFunction(std::string property_, Stops stops_, optional<T> defaultValue_ = {})
+ : property(std::move(property_)),
+ stops(std::move(stops_)),
+ defaultValue(std::move(defaultValue_)),
+ expression(stops.match([&] (const auto& s) {
+ return expression::Convert::toExpression(property, s);
+ })),
+ zoomCurve(expression::findZoomCurveChecked(expression.get()))
+ {}
+
+ // Return the range obtained by evaluating the function at each of the zoom levels in zoomRange
template <class Feature>
- Range<T> evaluate(const Range<CoveringRanges>& ranges, const Feature& feature, T finalDefaultValue) {
- optional<Value> value = feature.getValue(property);
- if (!value) {
- return Range<T> {
- defaultValue.value_or(finalDefaultValue),
- defaultValue.value_or(finalDefaultValue)
- };
- }
+ Range<T> evaluate(const Range<float>& zoomRange, const Feature& feature, T finalDefaultValue) {
return Range<T> {
- evaluateFinal(ranges.min, *value, finalDefaultValue),
- evaluateFinal(ranges.max, *value, finalDefaultValue)
+ evaluate(zoomRange.min, feature, finalDefaultValue),
+ evaluate(zoomRange.max, feature, finalDefaultValue)
};
}
- // Fully evaluate the function for a zoom value and feature. This is used when evaluating data-driven
- // layout properties.
template <class Feature>
T evaluate(float zoom, const Feature& feature, T finalDefaultValue) const {
- optional<Value> value = feature.getValue(property);
- if (!value) {
- return defaultValue.value_or(finalDefaultValue);
+ const expression::EvaluationResult result = expression->evaluate(expression::EvaluationContext({zoom}, &feature));
+ if (result) {
+ const optional<T> typed = expression::fromExpressionValue<T>(*result);
+ return typed ? *typed : defaultValue ? *defaultValue : finalDefaultValue;
}
- return evaluateFinal(coveringRanges(zoom), *value, finalDefaultValue);
+ return defaultValue ? *defaultValue : finalDefaultValue;
+ }
+
+ float interpolationFactor(const Range<float>& inputLevels, const float inputValue) const {
+ return zoomCurve.match(
+ [&](const expression::InterpolateBase* z) {
+ return z->interpolationFactor(Range<double> { inputLevels.min, inputLevels.max }, inputValue);
+ },
+ [&](const expression::Step*) { return 0.0f; }
+ );
+ }
+
+ Range<float> getCoveringStops(const float lower, const float upper) const {
+ return zoomCurve.match(
+ [&](auto z) { return z->getCoveringStops(lower, upper); }
+ );
+ }
+
+ std::vector<optional<T>> possibleOutputs() const {
+ return expression::fromExpressionValues<T>(expression->possibleOutputs());
}
friend bool operator==(const CompositeFunction& lhs,
const CompositeFunction& rhs) {
- return std::tie(lhs.property, lhs.stops, lhs.defaultValue)
- == std::tie(rhs.property, rhs.stops, rhs.defaultValue);
+ return *lhs.expression == *rhs.expression;
}
+ const expression::Expression& getExpression() const { return *expression; }
+
std::string property;
Stops stops;
optional<T> defaultValue;
bool useIntegerZoom = false;
-
+
private:
- T evaluateFinal(const CoveringRanges& ranges, const Value& value, T finalDefaultValue) const {
- auto eval = [&] (const auto& s) {
- return s.evaluate(value).value_or(defaultValue.value_or(finalDefaultValue));
- };
- return util::interpolate(
- ranges.coveringStopsRange.min.match(eval),
- ranges.coveringStopsRange.max.match(eval),
- util::interpolationFactor(1.0f, ranges.coveringZoomRange, ranges.zoom));
- }
+ std::shared_ptr<expression::Expression> expression;
+ const variant<const expression::InterpolateBase*, const expression::Step*> zoomCurve;
};
} // namespace style
diff --git a/include/mbgl/style/function/convert.hpp b/include/mbgl/style/function/convert.hpp
new file mode 100644
index 0000000000..8e544d3ad5
--- /dev/null
+++ b/include/mbgl/style/function/convert.hpp
@@ -0,0 +1,355 @@
+#pragma once
+
+#include <mbgl/style/expression/array_assertion.hpp>
+#include <mbgl/style/expression/assertion.hpp>
+#include <mbgl/style/expression/case.hpp>
+#include <mbgl/style/expression/coalesce.hpp>
+#include <mbgl/style/expression/compound_expression.hpp>
+#include <mbgl/style/expression/coercion.hpp>
+#include <mbgl/style/expression/interpolate.hpp>
+#include <mbgl/style/expression/expression.hpp>
+#include <mbgl/style/expression/literal.hpp>
+#include <mbgl/style/expression/match.hpp>
+#include <mbgl/style/expression/step.hpp>
+
+#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/composite_exponential_stops.hpp>
+#include <mbgl/style/function/composite_interval_stops.hpp>
+#include <mbgl/style/function/composite_categorical_stops.hpp>
+#include <mbgl/style/function/identity_stops.hpp>
+
+#include <mbgl/util/enum.hpp>
+#include <mbgl/style/types.hpp>
+
+#include <string>
+
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+namespace detail {
+
+class ErrorExpression : public Expression {
+public:
+ ErrorExpression(std::string message_) : Expression(type::Error), message(std::move(message_)) {}
+ void eachChild(const std::function<void(const Expression&)>&) const override {}
+
+ bool operator==(const Expression& e) const override {
+ return dynamic_cast<const ErrorExpression*>(&e);
+ }
+
+ EvaluationResult evaluate(const EvaluationContext&) const override {
+ return EvaluationError{message};
+ }
+
+ std::vector<optional<Value>> possibleOutputs() const override {
+ return {};
+ }
+
+private:
+ std::string message;
+};
+
+} // namespace detail
+
+
+// Create expressions representing 'classic' (i.e. stop-based) style functions
+
+struct Convert {
+ template <typename T>
+ static std::unique_ptr<Literal> makeLiteral(const T& value) {
+ return std::make_unique<Literal>(Value(toExpressionValue(value)));
+ }
+
+ static std::unique_ptr<Expression> makeGet(type::Type type, const std::string& property) {
+ ParsingContext ctx;
+ std::vector<std::unique_ptr<Expression>> getArgs;
+ getArgs.push_back(makeLiteral(property));
+ ParseResult get = createCompoundExpression("get", std::move(getArgs), ctx);
+ assert(get);
+ assert(ctx.getErrors().size() == 0);
+
+ std::vector<std::unique_ptr<Expression>> assertionArgs;
+ assertionArgs.push_back(std::move(*get));
+
+ return std::make_unique<Assertion>(type, std::move(assertionArgs));
+ }
+
+ static std::unique_ptr<Expression> makeZoom() {
+ ParsingContext ctx;
+ ParseResult zoom = createCompoundExpression("zoom", std::vector<std::unique_ptr<Expression>>(), ctx);
+ assert(zoom);
+ assert(ctx.getErrors().size() == 0);
+ return std::move(*zoom);
+ }
+
+ static std::unique_ptr<Expression> makeError(std::string message) {
+ return std::make_unique<detail::ErrorExpression>(message);
+ }
+
+ template <typename OutputType>
+ static ParseResult makeInterpolate(type::Type type,
+ std::unique_ptr<Expression> input,
+ std::map<double, std::unique_ptr<Expression>> convertedStops,
+ typename Interpolate<OutputType>::Interpolator interpolator)
+ {
+ ParseResult curve = ParseResult(std::make_unique<Interpolate<OutputType>>(
+ std::move(type),
+ std::move(interpolator),
+ std::move(input),
+ std::move(convertedStops)
+ ));
+ assert(curve);
+ return std::move(*curve);
+ }
+
+ template <typename Key>
+ static ParseResult makeMatch(type::Type type,
+ std::unique_ptr<Expression> input,
+ std::map<CategoricalValue, std::unique_ptr<Expression>> stops) {
+ // match expression
+ typename Match<Key>::Branches branches;
+ for(auto it = stops.begin(); it != stops.end(); it++) {
+ assert(it->first.template is<Key>());
+ Key key = it->first.template get<Key>();
+ branches.emplace(
+ std::move(key),
+ std::move(it->second)
+ );
+ }
+
+ return ParseResult(std::make_unique<Match<Key>>(std::move(type),
+ std::move(input),
+ std::move(branches),
+ makeError("No matching label")));
+ }
+
+ static ParseResult makeCase(type::Type type,
+ std::unique_ptr<Expression> input,
+ std::map<CategoricalValue, std::unique_ptr<Expression>> stops) {
+ // case expression
+ std::vector<typename Case::Branch> branches;
+
+ auto it = stops.find(true);
+ std::unique_ptr<Expression> true_case = it == stops.end() ?
+ makeError("No matching label") :
+ std::move(it->second);
+
+ it = stops.find(false);
+ std::unique_ptr<Expression> false_case = it == stops.end() ?
+ makeError("No matching label") :
+ std::move(it->second);
+
+ branches.push_back(std::make_pair(std::move(input), std::move(true_case)));
+ return ParseResult(std::make_unique<Case>(std::move(type), std::move(branches), std::move(false_case)));
+ }
+
+ template <typename T>
+ static ParseResult fromCategoricalStops(std::map<CategoricalValue, T> stops, const std::string& property) {
+ assert(stops.size() > 0);
+
+ std::map<CategoricalValue, std::unique_ptr<Expression>> convertedStops;
+ for(const std::pair<CategoricalValue, T>& stop : stops) {
+ convertedStops.emplace(
+ stop.first,
+ makeLiteral(stop.second)
+ );
+ }
+
+ type::Type type = valueTypeToExpressionType<T>();
+
+ const CategoricalValue& firstKey = stops.begin()->first;
+ return firstKey.match(
+ [&](bool) {
+ return makeCase(type, makeGet(type::Boolean, property), std::move(convertedStops));
+ },
+ [&](const std::string&) {
+ return makeMatch<std::string>(type, makeGet(type::String, property), std::move(convertedStops));
+ },
+ [&](int64_t) {
+ return makeMatch<int64_t>(type, makeGet(type::Number, property), std::move(convertedStops));
+ }
+ );
+ }
+
+ template <typename T>
+ static std::map<double, std::unique_ptr<Expression>> convertStops(const std::map<float, T>& stops) {
+ std::map<double, std::unique_ptr<Expression>> convertedStops;
+ for(const auto& stop : stops) {
+ convertedStops.emplace(
+ stop.first,
+ makeLiteral(stop.second)
+ );
+ }
+ return convertedStops;
+ }
+
+ template <typename T>
+ static std::unique_ptr<Expression> toExpression(const ExponentialStops<T>& stops)
+ {
+ ParseResult e = makeInterpolate<typename ValueConverter<T>::ExpressionType>(
+ valueTypeToExpressionType<T>(),
+ makeZoom(),
+ convertStops(stops.stops),
+ ExponentialInterpolator(stops.base));
+ assert(e);
+ return std::move(*e);
+ }
+
+ template <typename T>
+ static std::unique_ptr<Expression> toExpression(const IntervalStops<T>& stops)
+ {
+ ParseResult e(std::make_unique<Step>(valueTypeToExpressionType<T>(),
+ makeZoom(),
+ convertStops(stops.stops)));
+ assert(e);
+ return std::move(*e);
+ }
+
+ template <typename T>
+ static std::unique_ptr<Expression> toExpression(const std::string& property,
+ const ExponentialStops<T>& stops)
+ {
+ ParseResult e = makeInterpolate<typename ValueConverter<T>::ExpressionType>(valueTypeToExpressionType<T>(),
+ makeGet(type::Number, property),
+ convertStops(stops.stops),
+ ExponentialInterpolator(stops.base));
+ assert(e);
+ return std::move(*e);
+ }
+
+ template <typename T>
+ static std::unique_ptr<Expression> toExpression(const std::string& property,
+ const IntervalStops<T>& stops)
+ {
+ std::unique_ptr<Expression> get = makeGet(type::Number, property);
+ ParseResult e(std::make_unique<Step>(valueTypeToExpressionType<T>(),
+ std::move(get),
+ convertStops(stops.stops)));
+ assert(e);
+ return std::move(*e);
+ }
+
+ template <typename T>
+ static std::unique_ptr<Expression> toExpression(const std::string& property,
+ const CategoricalStops<T>& stops)
+ {
+ ParseResult expr = fromCategoricalStops(stops.stops, property);
+ assert(expr);
+ return std::move(*expr);
+ }
+
+ // interpolatable zoom curve
+ template <typename T>
+ static typename std::enable_if_t<util::Interpolatable<T>::value,
+ ParseResult> makeZoomCurve(std::map<double, std::unique_ptr<Expression>> stops) {
+ return makeInterpolate<typename ValueConverter<T>::ExpressionType>(valueTypeToExpressionType<T>(),
+ makeZoom(),
+ std::move(stops),
+ ExponentialInterpolator(1.0));
+ }
+
+ // non-interpolatable zoom curve
+ template <typename T>
+ static typename std::enable_if_t<!util::Interpolatable<T>::value,
+ ParseResult> makeZoomCurve(std::map<double, std::unique_ptr<Expression>> stops) {
+ return ParseResult(std::make_unique<Step>(valueTypeToExpressionType<T>(), makeZoom(), std::move(stops)));
+ }
+
+ template <typename T>
+ static std::unique_ptr<Expression> toExpression(const std::string& property,
+ const CompositeExponentialStops<T>& stops)
+ {
+ std::map<double, std::unique_ptr<Expression>> outerStops;
+ for (const std::pair<float, std::map<float, T>>& stop : stops.stops) {
+ std::unique_ptr<Expression> get = makeGet(type::Number, property);
+ ParseResult innerInterpolate = makeInterpolate<typename ValueConverter<T>::ExpressionType>(valueTypeToExpressionType<T>(),
+ std::move(get),
+ convertStops(stop.second),
+ ExponentialInterpolator(stops.base));
+ assert(innerInterpolate);
+ outerStops.emplace(stop.first, std::move(*innerInterpolate));
+ }
+
+ ParseResult zoomCurve = makeZoomCurve<T>(std::move(outerStops));
+ assert(zoomCurve);
+ return std::move(*zoomCurve);
+ }
+
+ template <typename T>
+ static std::unique_ptr<Expression> toExpression(const std::string& property,
+ const CompositeIntervalStops<T>& stops)
+ {
+ std::map<double, std::unique_ptr<Expression>> outerStops;
+ for (const std::pair<float, std::map<float, T>>& stop : stops.stops) {
+ std::unique_ptr<Expression> get = makeGet(type::Number, property);
+ ParseResult innerInterpolate(std::make_unique<Step>(valueTypeToExpressionType<T>(),
+ std::move(get),
+ convertStops(stop.second)));
+ assert(innerInterpolate);
+ outerStops.emplace(stop.first, std::move(*innerInterpolate));
+ }
+
+ ParseResult zoomCurve = makeZoomCurve<T>(std::move(outerStops));
+ assert(zoomCurve);
+ return std::move(*zoomCurve);
+ }
+
+ template <typename T>
+ static std::unique_ptr<Expression> toExpression(const std::string& property,
+ const CompositeCategoricalStops<T>& stops)
+ {
+ std::map<double, std::unique_ptr<Expression>> outerStops;
+ for (const std::pair<float, std::map<CategoricalValue, T>>& stop : stops.stops) {
+ ParseResult innerInterpolate = fromCategoricalStops(stop.second, property);
+ assert(innerInterpolate);
+ outerStops.emplace(stop.first, std::move(*innerInterpolate));
+ }
+
+ ParseResult zoomCurve = makeZoomCurve<T>(std::move(outerStops));
+ assert(zoomCurve);
+ return std::move(*zoomCurve);
+ }
+
+
+ static std::unique_ptr<Expression> fromIdentityFunction(type::Type type, const std::string& property)
+ {
+ std::unique_ptr<Expression> input = type.match(
+ [&] (const type::StringType&) {
+ return makeGet(type::String, property);
+ },
+ [&] (const type::NumberType&) {
+ return makeGet(type::Number, property);
+ },
+ [&] (const type::BooleanType&) {
+ return makeGet(type::Boolean, property);
+ },
+ [&] (const type::ColorType&) {
+ std::vector<std::unique_ptr<Expression>> args;
+ args.push_back(makeGet(type::String, property));
+ return std::make_unique<Coercion>(type::Color, std::move(args));
+ },
+ [&] (const type::Array& arr) {
+ std::vector<std::unique_ptr<Expression>> getArgs;
+ getArgs.push_back(makeLiteral(property));
+ ParsingContext ctx;
+ ParseResult get = createCompoundExpression("get", std::move(getArgs), ctx);
+ assert(get);
+ assert(ctx.getErrors().size() == 0);
+ return std::make_unique<ArrayAssertion>(arr, std::move(*get));
+ },
+ [&] (const auto&) -> std::unique_ptr<Expression> {
+ return makeLiteral(Null);
+ }
+ );
+
+ return input;
+ }
+};
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/function/source_function.hpp b/include/mbgl/style/function/source_function.hpp
index 9c2ad101ec..d3caa90ee5 100644
--- a/include/mbgl/style/function/source_function.hpp
+++ b/include/mbgl/style/function/source_function.hpp
@@ -1,5 +1,7 @@
#pragma once
+#include <mbgl/style/expression/is_constant.hpp>
+#include <mbgl/style/function/convert.hpp>
#include <mbgl/style/function/exponential_stops.hpp>
#include <mbgl/style/function/interval_stops.hpp>
#include <mbgl/style/function/categorical_stops.hpp>
@@ -27,33 +29,54 @@ public:
CategoricalStops<T>,
IdentityStops<T>>>;
+ SourceFunction(std::unique_ptr<expression::Expression> expression_)
+ : expression(std::move(expression_))
+ {
+ assert(expression::isZoomConstant(*expression));
+ assert(!expression::isFeatureConstant(*expression));
+ }
+
SourceFunction(std::string property_, Stops stops_, optional<T> defaultValue_ = {})
: property(std::move(property_)),
stops(std::move(stops_)),
- defaultValue(std::move(defaultValue_)) {
- }
+ defaultValue(std::move(defaultValue_)),
+ expression(stops.match([&] (const IdentityStops<T>&) {
+ return expression::Convert::fromIdentityFunction(expression::valueTypeToExpressionType<T>(), property);
+ }, [&] (const auto& s) {
+ return expression::Convert::toExpression(property, s);
+ }))
+ {}
template <class Feature>
T evaluate(const Feature& feature, T finalDefaultValue) const {
- optional<Value> v = feature.getValue(property);
- if (!v) {
- return defaultValue.value_or(finalDefaultValue);
+ const expression::EvaluationResult result = expression->evaluate(expression::EvaluationContext(&feature));
+ if (result) {
+ const optional<T> typed = expression::fromExpressionValue<T>(*result);
+ return typed ? *typed : defaultValue ? *defaultValue : finalDefaultValue;
}
- return stops.match([&] (const auto& s) -> T {
- return s.evaluate(*v).value_or(defaultValue.value_or(finalDefaultValue));
- });
+ return defaultValue ? *defaultValue : finalDefaultValue;
+ }
+
+ std::vector<optional<T>> possibleOutputs() const {
+ return expression::fromExpressionValues<T>(expression->possibleOutputs());
}
friend bool operator==(const SourceFunction& lhs,
const SourceFunction& rhs) {
- return std::tie(lhs.property, lhs.stops, lhs.defaultValue)
- == std::tie(rhs.property, rhs.stops, rhs.defaultValue);
+ return *lhs.expression == *rhs.expression;
}
+ bool useIntegerZoom = false;
+
+ const expression::Expression& getExpression() const { return *expression; }
+
+ // retained for compatibility with pre-expression function API
std::string property;
Stops stops;
optional<T> defaultValue;
- bool useIntegerZoom = false;
+
+private:
+ std::shared_ptr<expression::Expression> expression;
};
} // namespace style
diff --git a/include/mbgl/style/layer.hpp b/include/mbgl/style/layer.hpp
index c6a3c0e735..8a5a4236b3 100644
--- a/include/mbgl/style/layer.hpp
+++ b/include/mbgl/style/layer.hpp
@@ -1,7 +1,7 @@
#pragma once
#include <mbgl/util/noncopyable.hpp>
-#include <mbgl/util/any.hpp>
+#include <mbgl/util/unique_any.hpp>
#include <mbgl/util/immutable.hpp>
#include <mbgl/style/layer_type.hpp>
#include <mbgl/style/types.hpp>
@@ -19,6 +19,7 @@ class LineLayer;
class CircleLayer;
class SymbolLayer;
class RasterLayer;
+class HillshadeLayer;
class BackgroundLayer;
class CustomLayer;
class FillExtrusionLayer;
@@ -86,6 +87,8 @@ public:
return std::forward<V>(visitor)(*as<RasterLayer>());
case LayerType::Background:
return std::forward<V>(visitor)(*as<BackgroundLayer>());
+ case LayerType::Hillshade:
+ return std::forward<V>(visitor)(*as<HillshadeLayer>());
case LayerType::Custom:
return std::forward<V>(visitor)(*as<CustomLayer>());
case LayerType::FillExtrusion:
@@ -126,7 +129,7 @@ public:
// For use in SDK bindings, which store a reference to a platform-native peer
// object here, so that separately-obtained references to this object share
// identical platform-native peers.
- any peer;
+ util::unique_any peer;
};
} // namespace style
diff --git a/include/mbgl/style/layer_type.hpp b/include/mbgl/style/layer_type.hpp
index 66ff834eee..951757134b 100644
--- a/include/mbgl/style/layer_type.hpp
+++ b/include/mbgl/style/layer_type.hpp
@@ -9,10 +9,11 @@ enum class LayerType {
Circle,
Symbol,
Raster,
+ Hillshade,
Background,
Custom,
FillExtrusion,
};
} // namespace style
-} // namespace mbgl \ No newline at end of file
+} // namespace mbgl
diff --git a/include/mbgl/style/layers/hillshade_layer.hpp b/include/mbgl/style/layers/hillshade_layer.hpp
new file mode 100644
index 0000000000..35664da46c
--- /dev/null
+++ b/include/mbgl/style/layers/hillshade_layer.hpp
@@ -0,0 +1,86 @@
+// This file is generated. Do not edit.
+
+#pragma once
+
+#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>
+
+namespace mbgl {
+namespace style {
+
+class TransitionOptions;
+
+class HillshadeLayer : public Layer {
+public:
+ HillshadeLayer(const std::string& layerID, const std::string& sourceID);
+ ~HillshadeLayer() final;
+
+ // Source
+ const std::string& getSourceID() const;
+
+ // Visibility
+ void setVisibility(VisibilityType) final;
+
+ // Zoom range
+ void setMinZoom(float) final;
+ void setMaxZoom(float) final;
+
+ // Paint properties
+
+ static PropertyValue<float> getDefaultHillshadeIlluminationDirection();
+ PropertyValue<float> getHillshadeIlluminationDirection() const;
+ void setHillshadeIlluminationDirection(PropertyValue<float>);
+ void setHillshadeIlluminationDirectionTransition(const TransitionOptions&);
+ TransitionOptions getHillshadeIlluminationDirectionTransition() const;
+
+ static PropertyValue<HillshadeIlluminationAnchorType> getDefaultHillshadeIlluminationAnchor();
+ PropertyValue<HillshadeIlluminationAnchorType> getHillshadeIlluminationAnchor() const;
+ void setHillshadeIlluminationAnchor(PropertyValue<HillshadeIlluminationAnchorType>);
+ void setHillshadeIlluminationAnchorTransition(const TransitionOptions&);
+ TransitionOptions getHillshadeIlluminationAnchorTransition() const;
+
+ static PropertyValue<float> getDefaultHillshadeExaggeration();
+ PropertyValue<float> getHillshadeExaggeration() const;
+ void setHillshadeExaggeration(PropertyValue<float>);
+ void setHillshadeExaggerationTransition(const TransitionOptions&);
+ TransitionOptions getHillshadeExaggerationTransition() const;
+
+ static PropertyValue<Color> getDefaultHillshadeShadowColor();
+ PropertyValue<Color> getHillshadeShadowColor() const;
+ void setHillshadeShadowColor(PropertyValue<Color>);
+ void setHillshadeShadowColorTransition(const TransitionOptions&);
+ TransitionOptions getHillshadeShadowColorTransition() const;
+
+ static PropertyValue<Color> getDefaultHillshadeHighlightColor();
+ PropertyValue<Color> getHillshadeHighlightColor() const;
+ void setHillshadeHighlightColor(PropertyValue<Color>);
+ void setHillshadeHighlightColorTransition(const TransitionOptions&);
+ TransitionOptions getHillshadeHighlightColorTransition() const;
+
+ static PropertyValue<Color> getDefaultHillshadeAccentColor();
+ PropertyValue<Color> getHillshadeAccentColor() const;
+ void setHillshadeAccentColor(PropertyValue<Color>);
+ void setHillshadeAccentColorTransition(const TransitionOptions&);
+ TransitionOptions getHillshadeAccentColorTransition() const;
+
+ // Private implementation
+
+ class Impl;
+ const Impl& impl() const;
+
+ Mutable<Impl> mutableImpl() const;
+ HillshadeLayer(Immutable<Impl>);
+ std::unique_ptr<Layer> cloneRef(const std::string& id) const final;
+};
+
+template <>
+inline bool Layer::is<HillshadeLayer>() const {
+ return getType() == LayerType::Hillshade;
+}
+
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/layers/layer.hpp.ejs b/include/mbgl/style/layers/layer.hpp.ejs
index 4ee5545247..265dd57e1f 100644
--- a/include/mbgl/style/layers/layer.hpp.ejs
+++ b/include/mbgl/style/layers/layer.hpp.ejs
@@ -35,7 +35,7 @@ public:
<% if (type !== 'background') { -%>
// Source
const std::string& getSourceID() const;
-<% if (type !== 'raster') { -%>
+<% if (type !== 'raster' && type !== 'hillshade') { -%>
const std::string& getSourceLayer() const;
void setSourceLayer(const std::string& sourceLayer);
diff --git a/include/mbgl/style/layers/symbol_layer.hpp b/include/mbgl/style/layers/symbol_layer.hpp
index a72baa0b4e..f068e2d060 100644
--- a/include/mbgl/style/layers/symbol_layer.hpp
+++ b/include/mbgl/style/layers/symbol_layer.hpp
@@ -118,9 +118,9 @@ public:
DataDrivenPropertyValue<std::string> getTextField() const;
void setTextField(DataDrivenPropertyValue<std::string>);
- static PropertyValue<std::vector<std::string>> getDefaultTextFont();
- PropertyValue<std::vector<std::string>> getTextFont() const;
- void setTextFont(PropertyValue<std::vector<std::string>>);
+ static DataDrivenPropertyValue<std::vector<std::string>> getDefaultTextFont();
+ DataDrivenPropertyValue<std::vector<std::string>> getTextFont() const;
+ void setTextFont(DataDrivenPropertyValue<std::vector<std::string>>);
static DataDrivenPropertyValue<float> getDefaultTextSize();
DataDrivenPropertyValue<float> getTextSize() const;
diff --git a/include/mbgl/style/source.hpp b/include/mbgl/style/source.hpp
index cec9619451..2f2838ade8 100644
--- a/include/mbgl/style/source.hpp
+++ b/include/mbgl/style/source.hpp
@@ -2,7 +2,7 @@
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/util/optional.hpp>
-#include <mbgl/util/any.hpp>
+#include <mbgl/util/unique_any.hpp>
#include <mbgl/util/immutable.hpp>
#include <mbgl/style/types.hpp>
@@ -17,6 +17,7 @@ namespace style {
class VectorSource;
class RasterSource;
+class RasterDEMSource;
class GeoJSONSource;
class SourceObserver;
@@ -76,7 +77,7 @@ public:
// For use in SDK bindings, which store a reference to a platform-native peer
// object here, so that separately-obtained references to this object share
// identical platform-native peers.
- any peer;
+ util::unique_any peer;
};
} // namespace style
diff --git a/include/mbgl/style/sources/custom_geometry_source.hpp b/include/mbgl/style/sources/custom_geometry_source.hpp
new file mode 100644
index 0000000000..9daeeb3819
--- /dev/null
+++ b/include/mbgl/style/sources/custom_geometry_source.hpp
@@ -0,0 +1,58 @@
+#pragma once
+
+#include <mbgl/style/source.hpp>
+#include <mbgl/util/geo.hpp>
+#include <mbgl/util/geojson.hpp>
+#include <mbgl/util/range.hpp>
+#include <mbgl/util/constants.hpp>
+
+namespace mbgl {
+
+class OverscaledTileID;
+class CanonicalTileID;
+template <class T>
+class Actor;
+
+namespace style {
+
+using TileFunction = std::function<void(const CanonicalTileID&)>;
+
+class CustomTileLoader;
+
+class CustomGeometrySource : public Source {
+public:
+ struct TileOptions {
+ double tolerance = 0.375;
+ uint16_t tileSize = util::tileSize;
+ uint16_t buffer = 128;
+ bool clip = false;
+ bool wrap = false;
+ };
+
+ struct Options {
+ TileFunction fetchTileFunction;
+ TileFunction cancelTileFunction;
+ Range<uint8_t> zoomRange = { 0, 18};
+ TileOptions tileOptions;
+ };
+public:
+ CustomGeometrySource(std::string id, CustomGeometrySource::Options options);
+ ~CustomGeometrySource() final;
+ void loadDescription(FileSource&) final;
+ void setTileData(const CanonicalTileID&, const GeoJSON&);
+ void invalidateTile(const CanonicalTileID&);
+ void invalidateRegion(const LatLngBounds&);
+ // Private implementation
+ class Impl;
+ const Impl& impl() const;
+private:
+ std::unique_ptr<Actor<CustomTileLoader>> loader;
+};
+
+template <>
+inline bool Source::is<CustomGeometrySource>() const {
+ return getType() == SourceType::CustomVector;
+}
+
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/sources/geojson_source.hpp b/include/mbgl/style/sources/geojson_source.hpp
index 5bdf1ef957..372e7c7a78 100644
--- a/include/mbgl/style/sources/geojson_source.hpp
+++ b/include/mbgl/style/sources/geojson_source.hpp
@@ -3,6 +3,7 @@
#include <mbgl/style/source.hpp>
#include <mbgl/util/geojson.hpp>
#include <mbgl/util/optional.hpp>
+#include <mbgl/util/constants.hpp>
namespace mbgl {
@@ -14,6 +15,7 @@ struct GeoJSONOptions {
// GeoJSON-VT options
uint8_t minzoom = 0;
uint8_t maxzoom = 18;
+ uint16_t tileSize = util::tileSize;
uint16_t buffer = 128;
double tolerance = 0.375;
diff --git a/include/mbgl/style/sources/raster_dem_source.hpp b/include/mbgl/style/sources/raster_dem_source.hpp
new file mode 100644
index 0000000000..82588613bc
--- /dev/null
+++ b/include/mbgl/style/sources/raster_dem_source.hpp
@@ -0,0 +1,25 @@
+#pragma once
+
+#include <mbgl/style/sources/raster_source.hpp>
+#include <mbgl/util/tileset.hpp>
+#include <mbgl/util/variant.hpp>
+
+namespace mbgl {
+
+class AsyncRequest;
+
+namespace style {
+
+class RasterDEMSource : public RasterSource {
+public:
+ RasterDEMSource(std::string id, variant<std::string, Tileset> urlOrTileset, uint16_t tileSize);
+
+};
+
+template <>
+inline bool Source::is<RasterDEMSource>() const {
+ return getType() == SourceType::RasterDEM;
+}
+
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/sources/raster_source.hpp b/include/mbgl/style/sources/raster_source.hpp
index 7f23a7ca4b..5aa81aa979 100644
--- a/include/mbgl/style/sources/raster_source.hpp
+++ b/include/mbgl/style/sources/raster_source.hpp
@@ -12,8 +12,8 @@ namespace style {
class RasterSource : public Source {
public:
- RasterSource(std::string id, variant<std::string, Tileset> urlOrTileset, uint16_t tileSize);
- ~RasterSource() final;
+ RasterSource(std::string id, variant<std::string, Tileset> urlOrTileset, uint16_t tileSize, SourceType sourceType = SourceType::Raster);
+ ~RasterSource() override;
const variant<std::string, Tileset>& getURLOrTileset() const;
optional<std::string> getURL() const;
diff --git a/include/mbgl/style/types.hpp b/include/mbgl/style/types.hpp
index ec7358de8c..693972a72f 100644
--- a/include/mbgl/style/types.hpp
+++ b/include/mbgl/style/types.hpp
@@ -4,18 +4,20 @@
namespace mbgl {
-// TODO: should be in public source.hpp header and style namespace
+namespace style {
+
+// TODO: should be in public source.hpp header
enum class SourceType : uint8_t {
Vector,
Raster,
+ RasterDEM,
GeoJSON,
Video,
Annotations,
- Image
+ Image,
+ CustomVector
};
-namespace style {
-
enum class VisibilityType : bool {
Visible,
None,
@@ -36,6 +38,11 @@ enum class LineJoinType : uint8_t {
FlipBevel
};
+enum class HillshadeIlluminationAnchorType : bool {
+ Map,
+ Viewport
+};
+
enum class TranslateAnchorType : bool {
Map,
Viewport
diff --git a/include/mbgl/tile/tile_id.hpp b/include/mbgl/tile/tile_id.hpp
index 0457dd3a07..11fb5ce537 100644
--- a/include/mbgl/tile/tile_id.hpp
+++ b/include/mbgl/tile/tile_id.hpp
@@ -30,9 +30,9 @@ public:
CanonicalTileID scaledTo(uint8_t z) const;
std::array<CanonicalTileID, 4> children() const;
- const uint8_t z;
- const uint32_t x;
- const uint32_t y;
+ uint8_t z;
+ uint32_t x;
+ uint32_t y;
};
::std::ostream& operator<<(::std::ostream& os, const CanonicalTileID& rhs);
diff --git a/include/mbgl/util/any.hpp b/include/mbgl/util/any.hpp
deleted file mode 100644
index eea64b188a..0000000000
--- a/include/mbgl/util/any.hpp
+++ /dev/null
@@ -1,10 +0,0 @@
-#pragma once
-
-#include <linb/any.hpp>
-
-namespace mbgl {
-
-using linb::any;
-using linb::any_cast;
-
-} // namespace mbgl
diff --git a/include/mbgl/util/enum.hpp b/include/mbgl/util/enum.hpp
index 369ca86bfd..608befd3c4 100644
--- a/include/mbgl/util/enum.hpp
+++ b/include/mbgl/util/enum.hpp
@@ -11,6 +11,7 @@ namespace mbgl {
template <typename T>
class Enum {
public:
+ using Type = T;
static const char * toString(T);
static optional<T> toEnum(const std::string&);
};
diff --git a/include/mbgl/util/indexed_tuple.hpp b/include/mbgl/util/indexed_tuple.hpp
index fd0b931d36..ea4fe74624 100644
--- a/include/mbgl/util/indexed_tuple.hpp
+++ b/include/mbgl/util/indexed_tuple.hpp
@@ -1,9 +1,9 @@
#pragma once
#include <mbgl/util/type_list.hpp>
+#include <mbgl/util/tuple.hpp>
#include <type_traits>
-#include <tuple>
namespace mbgl {
@@ -24,20 +24,20 @@ template <class...> class IndexedTuple;
// for motivation.
//
template <class... Is, class... Ts>
-class IndexedTuple<TypeList<Is...>, TypeList<Ts...>> : public std::tuple<Ts...> {
+class IndexedTuple<TypeList<Is...>, TypeList<Ts...>> : public tuple_polyfill<Ts...> {
public:
static_assert(sizeof...(Is) == sizeof...(Ts), "IndexedTuple size mismatch");
- using std::tuple<Ts...>::tuple;
+ using tuple_polyfill<Ts...>::tuple;
template <class I>
auto& get() {
- return std::get<TypeIndex<I, Is...>::value>(*this);
+ return get_polyfill<TypeIndex<I, Is...>::value>(*this);
}
template <class I>
const auto& get() const {
- return std::get<TypeIndex<I, Is...>::value>(*this);
+ return get_polyfill<TypeIndex<I, Is...>::value>(*this);
}
template <class... Js, class... Us>
diff --git a/include/mbgl/util/interpolate.hpp b/include/mbgl/util/interpolate.hpp
index 6738987598..aff730a0a2 100644
--- a/include/mbgl/util/interpolate.hpp
+++ b/include/mbgl/util/interpolate.hpp
@@ -3,6 +3,7 @@
#include <mbgl/util/color.hpp>
#include <mbgl/util/range.hpp>
#include <mbgl/style/position.hpp>
+#include <mbgl/style/expression/value.hpp>
#include <array>
#include <vector>
@@ -47,6 +48,36 @@ public:
}
};
+
+// In order to accept Array<Number, N> as an output value for Curve
+// expressions, we need to have an interpolatable std::vector type.
+// However, style properties like line-dasharray are represented using
+// std::vector<float>, and should NOT be considered interpolatable.
+// So, we use std::vector<Value> to represent expression array values,
+// asserting that (a) the vectors are the same size, and (b) they contain
+// only numeric values. (These invariants should be relatively safe,
+// being enforced by the expression type system.)
+template<>
+struct Interpolator<std::vector<style::expression::Value>> {
+ std::vector<style::expression::Value> operator()(const std::vector<style::expression::Value>& a,
+ const std::vector<style::expression::Value>& b,
+ const double t) const {
+ assert(a.size() == b.size());
+ if (a.size() == 0) return {};
+ std::vector<style::expression::Value> result;
+ for (std::size_t i = 0; i < a.size(); i++) {
+ assert(a[i].template is<double>());
+ assert(b[i].template is<double>());
+ style::expression::Value item = interpolate(
+ a[i].template get<double>(),
+ b[i].template get<double>(),
+ t);
+ result.push_back(item);
+ }
+ return result;
+ }
+};
+
template <>
struct Interpolator<style::Position> {
public:
@@ -101,5 +132,7 @@ struct Interpolatable
std::true_type,
std::false_type> {};
+
+
} // namespace util
} // namespace mbgl
diff --git a/include/mbgl/util/optional.hpp b/include/mbgl/util/optional.hpp
index a9374a1b53..abec02dca9 100644
--- a/include/mbgl/util/optional.hpp
+++ b/include/mbgl/util/optional.hpp
@@ -7,4 +7,7 @@ namespace mbgl {
template <typename T>
using optional = std::experimental::optional<T>;
+using nullopt_t = std::experimental::nullopt_t;
+constexpr nullopt_t nullopt = std::experimental::nullopt;
+
} // namespace mbgl
diff --git a/include/mbgl/util/projection.hpp b/include/mbgl/util/projection.hpp
index a9c12e595b..b4a34521a4 100644
--- a/include/mbgl/util/projection.hpp
+++ b/include/mbgl/util/projection.hpp
@@ -96,8 +96,8 @@ public:
const double t2z = tileSize * std::pow(2, zoom);
Point<double> pt = project_(point, t2z);
// Flip y coordinate
- auto x = std::round(std::min(pt.x, t2z));
- auto y = std::round(std::min(t2z - pt.y, t2z));
+ auto x = ::round(std::min(pt.x, t2z));
+ auto y = ::round(std::min(t2z - pt.y, t2z));
return { x, y };
}
private:
diff --git a/include/mbgl/util/run_loop.hpp b/include/mbgl/util/run_loop.hpp
index acbea80273..381e3ae213 100644
--- a/include/mbgl/util/run_loop.hpp
+++ b/include/mbgl/util/run_loop.hpp
@@ -26,6 +26,11 @@ public:
New,
};
+ enum class Priority : bool {
+ Default = false,
+ High = true,
+ };
+
enum class Event : uint8_t {
None = 0,
Read = 1,
@@ -49,9 +54,14 @@ public:
// Invoke fn(args...) on this RunLoop.
template <class Fn, class... Args>
+ void invoke(Priority priority, Fn&& fn, Args&&... args) {
+ push(priority, WorkTask::make(std::forward<Fn>(fn), std::forward<Args>(args)...));
+ }
+
+ // Invoke fn(args...) on this RunLoop.
+ template <class Fn, class... Args>
void invoke(Fn&& fn, Args&&... args) {
- std::shared_ptr<WorkTask> task = WorkTask::make(std::forward<Fn>(fn), std::forward<Args>(args)...);
- push(task);
+ invoke(Priority::Default, std::forward<Fn>(fn), std::forward<Args>(args)...);
}
// Post the cancellable work fn(args...) to this RunLoop.
@@ -59,7 +69,7 @@ public:
std::unique_ptr<AsyncRequest>
invokeCancellable(Fn&& fn, Args&&... args) {
std::shared_ptr<WorkTask> task = WorkTask::make(std::forward<Fn>(fn), std::forward<Args>(args)...);
- push(task);
+ push(Priority::Default, task);
return std::make_unique<WorkRequest>(task);
}
@@ -76,24 +86,42 @@ private:
using Queue = std::queue<std::shared_ptr<WorkTask>>;
- void push(std::shared_ptr<WorkTask>);
+ // Wakes up the RunLoop so that it starts processing items in the queue.
+ void wake();
- void withMutex(std::function<void()>&& fn) {
+ // Adds a WorkTask to the queue, and wakes it up.
+ void push(Priority priority, std::shared_ptr<WorkTask> task) {
std::lock_guard<std::mutex> lock(mutex);
- fn();
+ if (priority == Priority::High) {
+ highPriorityQueue.emplace(std::move(task));
+ } else {
+ defaultQueue.emplace(std::move(task));
+ }
+ wake();
}
void process() {
- Queue queue_;
- withMutex([&] { queue_.swap(queue); });
-
- while (!queue_.empty()) {
- (*(queue_.front()))();
- queue_.pop();
+ std::shared_ptr<WorkTask> task;
+ std::unique_lock<std::mutex> lock(mutex);
+ while (true) {
+ if (!highPriorityQueue.empty()) {
+ task = std::move(highPriorityQueue.front());
+ highPriorityQueue.pop();
+ } else if (!defaultQueue.empty()) {
+ task = std::move(defaultQueue.front());
+ defaultQueue.pop();
+ } else {
+ break;
+ }
+ lock.unlock();
+ (*task)();
+ task.reset();
+ lock.lock();
}
}
- Queue queue;
+ Queue defaultQueue;
+ Queue highPriorityQueue;
std::mutex mutex;
std::unique_ptr<Impl> impl;
diff --git a/include/mbgl/util/string.hpp b/include/mbgl/util/string.hpp
index 82d317c620..13498ccb92 100644
--- a/include/mbgl/util/string.hpp
+++ b/include/mbgl/util/string.hpp
@@ -25,6 +25,10 @@ inline int stoi(const std::string &str)
return atoi(str.c_str());
}
+inline float stof(const std::string &str) {
+ return static_cast<float>(atof(str.c_str()));
+}
+
} // namespace std
#endif
@@ -65,5 +69,9 @@ inline std::string toString(std::exception_ptr error) {
}
}
+inline float stof(const std::string& str) {
+ return std::stof(str);
+}
+
} // namespace util
} // namespace mbgl
diff --git a/include/mbgl/util/thread.hpp b/include/mbgl/util/thread.hpp
index 672eebf6db..74e722b02d 100644
--- a/include/mbgl/util/thread.hpp
+++ b/include/mbgl/util/thread.hpp
@@ -103,7 +103,7 @@ public:
auto pausing = paused->get_future();
- loop->invoke([this] {
+ loop->invoke(RunLoop::Priority::High, [this] {
auto resuming = resumed->get_future();
paused->set_value();
resuming.get();
@@ -128,26 +128,9 @@ private:
MBGL_STORE_THREAD(tid);
void schedule(std::weak_ptr<Mailbox> mailbox) override {
- {
- std::lock_guard<std::mutex> lock(mutex);
- queue.push(mailbox);
- }
-
- loop->invoke([this] { receive(); });
- }
-
- void receive() {
- std::unique_lock<std::mutex> lock(mutex);
-
- auto mailbox = queue.front();
- queue.pop();
- lock.unlock();
-
- Mailbox::maybeReceive(mailbox);
+ loop->schedule(mailbox);
}
- std::mutex mutex;
- std::queue<std::weak_ptr<Mailbox>> queue;
std::thread thread;
std::unique_ptr<Actor<Object>> object;
diff --git a/include/mbgl/util/tileset.hpp b/include/mbgl/util/tileset.hpp
index 7bef0e89ed..1b7b8f0f75 100644
--- a/include/mbgl/util/tileset.hpp
+++ b/include/mbgl/util/tileset.hpp
@@ -37,6 +37,10 @@ public:
return std::tie(lhs.tiles, lhs.zoomRange, lhs.attribution, lhs.scheme, lhs.bounds)
== std::tie(rhs.tiles, rhs.zoomRange, rhs.attribution, rhs.scheme, rhs.bounds);
}
+
+ friend bool operator!=(const Tileset& lhs, const Tileset& rhs) {
+ return !(lhs == rhs);
+ }
};
} // namespace mbgl
diff --git a/include/mbgl/util/tuple.hpp b/include/mbgl/util/tuple.hpp
new file mode 100644
index 0000000000..3f6e80a8c7
--- /dev/null
+++ b/include/mbgl/util/tuple.hpp
@@ -0,0 +1,19 @@
+#pragma once
+
+// Polyfill needed by Windows because MSVC STL
+// is not compatible with our IndexedTuple code
+#if defined(_WINDOWS)
+
+#include <tao/tuple/tuple.hpp>
+
+#define get_polyfill tao::get
+#define tuple_polyfill tao::tuple
+
+#else
+
+#include <tuple>
+
+#define get_polyfill std::get
+#define tuple_polyfill std::tuple
+
+#endif
diff --git a/include/mbgl/util/unique_any.hpp b/include/mbgl/util/unique_any.hpp
new file mode 100644
index 0000000000..d488930a03
--- /dev/null
+++ b/include/mbgl/util/unique_any.hpp
@@ -0,0 +1,275 @@
+#pragma once
+
+#include <typeinfo>
+#include <type_traits>
+#include <stdexcept>
+namespace mbgl {
+namespace util {
+
+class bad_any_cast : public std::bad_cast {
+public:
+ const char* what() const noexcept override {
+ return "bad any_cast<>()";
+ }
+};
+/**
+ * A variant of `std::any` for non-copyable types.
+ *
+ * Use `unique_any` for non-copyable types (e.g. `std::unique_ptr<T>`)
+ * or to ensure that no copies are made of copyable types that are
+ * moved in.
+ *
+ * `uniqe_any` differs from `std::any` in that it does not support copy construction
+ * or copy assignment. It also does not require the contained type to be copy
+ * constructible.
+ *
+ * The `any_cast<T>()` methods work similar to `std::any_cast<T>()` except that
+ * non-copyable types may only be cast to references.
+ *
+ * Example usage:
+ * unique_any u1(3);
+ * auto u2 = unique_any(std::move(u1)); // u1 is moved from
+ * int i = any_cast<int>(u2);
+ *
+ * unique_any u2;
+ * u2 = std::unique_ptr<int>(new int);
+ * std::unique_ptr<int> iPtr = any_cast<std::unique_ptr<int>>(std::move(u2));
+ *
+ * Inspired by linb::any (https://github.com/thelink2012/any) and the
+ * libc++ implementation (https://github.com/llvm-mirror/libcxx).
+ */
+class unique_any final
+{
+public:
+ unique_any() = default;
+
+ //Copy constructor (deleted)
+ unique_any(const unique_any& rhs) = delete;
+
+ unique_any(unique_any&& rhs) : vtable(rhs.vtable) {
+ if (vtable) {
+ vtable->move(std::move(rhs.storage), storage);
+ }
+ rhs.vtable = nullptr;
+ }
+
+ // Constructs with a direct-initilizated object of type ValueType
+ template <typename ValueType,
+ typename _Vt = std::decay_t<ValueType>,
+ typename = std::enable_if_t<!std::is_same<_Vt, unique_any>::value> >
+ unique_any(ValueType&& value) {
+ create(std::forward<ValueType>(value));
+ }
+
+ ~unique_any() {
+ reset();
+ }
+
+ unique_any& operator=(unique_any&& rhs) {
+ unique_any(std::move(rhs)).swap(*this);
+ return *this;
+ }
+
+ template <class ValueType,
+ typename = std::enable_if_t<!std::is_same<std::decay_t<ValueType>, unique_any>::value> >
+ unique_any& operator=(ValueType&& rhs) {
+ unique_any(std::forward<ValueType>(rhs)).swap(*this);
+ return *this;
+ }
+
+ void reset() {
+ if (vtable) {
+ vtable->destroy(storage);
+ vtable = nullptr;
+ }
+ }
+
+ void swap(unique_any& rhs) {
+ if (this == &rhs) {
+ return;
+ } else {
+ unique_any tmp(std::move(rhs));
+ rhs.vtable = vtable;
+ if (rhs.vtable) {
+ rhs.vtable->move(std::move(storage), rhs.storage);
+ }
+ vtable = tmp.vtable;
+ if (vtable) {
+ vtable->move(std::move(tmp.storage), storage);
+ }
+ }
+ }
+
+ const std::type_info& type() const {
+ return !has_value()? typeid(void) : vtable->type();
+ }
+
+ bool has_value() const {
+ return vtable != nullptr;
+ }
+
+private:
+
+ union Storage {
+ using StackStorage = std::aligned_storage_t<3*sizeof(void*), std::alignment_of<void*>::value>;
+ Storage() = default;
+
+ void * dynamic { nullptr };
+ StackStorage stack;
+ };
+
+ template<typename T>
+ struct AllocateOnStack : std::integral_constant<bool,
+ sizeof(T) <= sizeof(Storage::stack)
+ && std::alignment_of<T>::value <= std::alignment_of<Storage::StackStorage>::value
+ && std::is_nothrow_move_constructible<T>::value> {
+ };
+
+ struct VTable {
+ virtual ~VTable() = default;
+ virtual void move(Storage&& src, Storage& dest) = 0;
+ virtual void destroy(Storage&) = 0;
+ virtual const std::type_info& type() = 0;
+ };
+
+ template <typename ValueType>
+ struct VTableHeap : public VTable {
+ void move(Storage&& src, Storage& dest) override {
+ destroy(dest);
+ dest.dynamic = src.dynamic;
+ }
+
+ void destroy(Storage& s) override {
+ if (s.dynamic) {
+ delete reinterpret_cast<ValueType*>(s.dynamic);
+ }
+ s.dynamic = nullptr;
+ }
+
+ const std::type_info& type() override {
+ return typeid(ValueType);
+ }
+ };
+
+ template <typename ValueType>
+ struct VTableStack : public VTable {
+ void move(Storage&& src, Storage& dest) override {
+ auto srcValue = reinterpret_cast<ValueType&&>(src.stack);
+ new (static_cast<void*>(&dest.stack)) ValueType(std::move(srcValue));
+ srcValue.~ValueType();
+ }
+
+ void destroy(Storage& s) override {
+ reinterpret_cast<ValueType&>(s.stack).~ValueType();
+ }
+
+ const std::type_info& type() override {
+ return typeid(ValueType);
+ }
+ };
+
+ template <typename ValueType>
+ static VTable* vtableForType() {
+ using VTableType = std::conditional_t<AllocateOnStack<ValueType>::value, VTableStack<ValueType>, VTableHeap<ValueType> >;
+ static VTableType vtable;
+ return &vtable;
+ }
+
+ template <typename ValueType, typename _Vt>
+ std::enable_if_t<AllocateOnStack<_Vt>::value>
+ createStorage(ValueType&& value) {
+ new (static_cast<void*>(&storage.stack)) _Vt(std::forward<ValueType>(value));
+ }
+
+ template <typename ValueType, typename _Vt>
+ std::enable_if_t<!AllocateOnStack<_Vt>::value>
+ createStorage(ValueType&& value) {
+ storage.dynamic = static_cast<void*>(new _Vt(std::forward<ValueType>(value)));
+ }
+
+ template <typename ValueType>
+ void create(ValueType&& value) {
+ using _Vt = std::decay_t<ValueType>;
+ vtable = vtableForType<_Vt>();
+ createStorage<ValueType, _Vt>(std::forward<ValueType>(value));
+ }
+
+ VTable* vtable { nullptr };
+ Storage storage;
+
+protected:
+ template<class ValueType>
+ friend const ValueType* any_cast(const unique_any* operand) ;
+
+ template<class ValueType>
+ friend ValueType* any_cast(unique_any* operand) ;
+
+ template<typename ValueType, typename _Vt = std::decay_t<ValueType> >
+ ValueType* cast()
+ {
+ return reinterpret_cast<ValueType *>(
+ AllocateOnStack<_Vt>::value ? &storage.stack : storage.dynamic);
+ }
+};
+
+template<typename ValueType>
+inline const ValueType* any_cast(const unique_any* any)
+{
+ return any_cast<ValueType>(const_cast<unique_any *>(any));
+}
+
+template<typename ValueType>
+inline ValueType* any_cast(unique_any* any)
+{
+ if(any == nullptr || any->type() != typeid(ValueType))
+ return nullptr;
+ else
+ return any->cast<ValueType>();
+}
+
+template<typename ValueType, typename _Vt = std::decay_t<ValueType> >
+inline ValueType any_cast(const unique_any& any)
+{
+ static_assert(std::is_constructible<ValueType, const _Vt&>::value,
+ "any_cast type can't construct copy of contained object");
+ auto temp = any_cast<_Vt>(&any);
+ if (temp == nullptr) {
+ throw bad_any_cast();
+ }
+ return static_cast<ValueType>(*temp);
+}
+
+template<typename ValueType, typename _Vt = std::decay_t<ValueType> >
+inline ValueType any_cast(unique_any& any)
+{
+ static_assert(std::is_constructible<ValueType, const _Vt&>::value,
+ "any_cast type can't construct copy of contained object");
+ auto temp = any_cast<_Vt>(&any);
+ if (temp == nullptr) {
+ throw bad_any_cast();
+ }
+ return static_cast<ValueType>(*temp);
+}
+
+template<typename ValueType, typename _Vt = std::remove_cv_t<ValueType> >
+inline ValueType any_cast(unique_any&& any)
+{
+ auto temp = any_cast<_Vt>(&any);
+ if (temp == nullptr) {
+ throw bad_any_cast();
+ }
+ auto retValue = static_cast<ValueType>(std::move(*temp));
+ any.reset();
+ return std::move(retValue);
+}
+
+} // namespace util
+} // namespace mbgl
+
+namespace std {
+
+inline void swap(mbgl::util::unique_any& lhs, mbgl::util::unique_any& rhs) {
+ lhs.swap(rhs);
+}
+
+} // namespace std
diff --git a/include/mbgl/util/unitbezier.hpp b/include/mbgl/util/unitbezier.hpp
index 6e644e2d1f..56d2ab6ead 100644
--- a/include/mbgl/util/unitbezier.hpp
+++ b/include/mbgl/util/unitbezier.hpp
@@ -26,6 +26,7 @@
#pragma once
#include <cmath>
+#include <tuple>
namespace mbgl {
namespace util {
@@ -41,6 +42,17 @@ struct UnitBezier {
, ay(1.0 - (3.0 * p1y) - (3.0 * (p2y - p1y) - (3.0 * p1y))) {
}
+ std::pair<double, double> getP1() const {
+ return { cx / 3.0, cy / 3.0 };
+ }
+
+ std::pair<double, double> getP2() const {
+ return {
+ (bx + (3.0 * cx / 3.0) + cx) / 3.0,
+ (by + (3.0 * cy / 3.0) + cy) / 3.0,
+ };
+ }
+
double sampleCurveX(double t) const {
// `ax t^3 + bx t^2 + cx t' expanded using Horner's rule.
return ((ax * t + bx) * t + cx) * t;
@@ -102,6 +114,11 @@ struct UnitBezier {
double solve(double x, double epsilon) const {
return sampleCurveY(solveCurveX(x, epsilon));
}
+
+ bool operator==(const UnitBezier& rhs) const {
+ return std::tie(cx, bx, ax, cy, by, ay) ==
+ std::tie(rhs.cx, rhs.bx, rhs.ax, rhs.cy, rhs.by, rhs.ay);
+ }
private:
const double cx;