summaryrefslogtreecommitdiff
path: root/include
diff options
context:
space:
mode:
authorLangston Smith <langston.smith@mapbox.com>2018-01-04 11:15:50 -0800
committerGitHub <noreply@github.com>2018-01-04 11:15:50 -0800
commit2ea955d2751ba6459f99a0695e53505c0a11702b (patch)
treef54450918b634a2eea1bd2c4ebc671bf1bb06106 /include
parentf2ec6ae326bad79fea2b06a21151a2835522572a (diff)
parentc62b0af24fc76b4bb2eb34100611dd3ee9ee5536 (diff)
downloadqtlocation-mapboxgl-2ea955d2751ba6459f99a0695e53505c0a11702b.tar.gz
Merge branch 'master' into ls-android-readme-tweaksupstream/ls-android-readme-tweaks
Diffstat (limited to 'include')
-rw-r--r--include/mbgl/actor/actor.hpp27
-rw-r--r--include/mbgl/actor/actor_ref.hpp21
-rw-r--r--include/mbgl/actor/message.hpp60
-rw-r--r--include/mbgl/actor/scheduler.hpp15
-rw-r--r--include/mbgl/annotation/annotation.hpp34
-rw-r--r--include/mbgl/map/map.hpp53
-rw-r--r--include/mbgl/map/mode.hpp23
-rw-r--r--include/mbgl/map/view.hpp18
-rw-r--r--include/mbgl/math/log2.hpp4
-rw-r--r--include/mbgl/renderer/backend_scope.hpp (renamed from include/mbgl/map/backend_scope.hpp)10
-rw-r--r--include/mbgl/renderer/mode.hpp18
-rw-r--r--include/mbgl/renderer/query.hpp (renamed from include/mbgl/map/query.hpp)10
-rw-r--r--include/mbgl/renderer/renderer.hpp58
-rw-r--r--include/mbgl/renderer/renderer_backend.hpp (renamed from include/mbgl/map/backend.hpp)45
-rw-r--r--include/mbgl/renderer/renderer_frontend.hpp31
-rw-r--r--include/mbgl/storage/default_file_source.hpp3
-rw-r--r--include/mbgl/storage/file_source.hpp6
-rw-r--r--include/mbgl/storage/offline.hpp6
-rw-r--r--include/mbgl/storage/online_file_source.hpp3
-rw-r--r--include/mbgl/storage/resource.hpp44
-rw-r--r--include/mbgl/storage/response.hpp11
-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.hpp64
-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.hpp49
-rw-r--r--include/mbgl/style/conversion/geojson.hpp10
-rw-r--r--include/mbgl/style/conversion/geojson_options.hpp69
-rw-r--r--include/mbgl/style/conversion/get_json_type.hpp14
-rw-r--r--include/mbgl/style/conversion/layer.hpp210
-rw-r--r--include/mbgl/style/conversion/light.hpp106
-rw-r--r--include/mbgl/style/conversion/make_property_setters.hpp207
-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.hpp65
-rw-r--r--include/mbgl/style/conversion/transition_options.hpp32
-rw-r--r--include/mbgl/style/expression/array_assertion.hpp39
-rw-r--r--include/mbgl/style/expression/assertion.hpp33
-rw-r--r--include/mbgl/style/expression/at.hpp38
-rw-r--r--include/mbgl/style/expression/boolean_operator.hpp49
-rw-r--r--include/mbgl/style/expression/case.hpp36
-rw-r--r--include/mbgl/style/expression/check_subtype.hpp17
-rw-r--r--include/mbgl/style/expression/coalesce.hpp45
-rw-r--r--include/mbgl/style/expression/coercion.hpp34
-rw-r--r--include/mbgl/style/expression/compound_expression.hpp138
-rw-r--r--include/mbgl/style/expression/expression.hpp173
-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.hpp177
-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.hpp72
-rw-r--r--include/mbgl/style/expression/literal.hpp38
-rw-r--r--include/mbgl/style/expression/match.hpp45
-rw-r--r--include/mbgl/style/expression/parsing_context.hpp148
-rw-r--r--include/mbgl/style/expression/step.hpp45
-rw-r--r--include/mbgl/style/expression/type.hpp112
-rw-r--r--include/mbgl/style/expression/value.hpp153
-rw-r--r--include/mbgl/style/function/camera_function.hpp60
-rw-r--r--include/mbgl/style/function/composite_function.hpp138
-rw-r--r--include/mbgl/style/function/convert.hpp351
-rw-r--r--include/mbgl/style/function/source_function.hpp41
-rw-r--r--include/mbgl/style/layer.hpp4
-rw-r--r--include/mbgl/style/layers/circle_layer.hpp6
-rw-r--r--include/mbgl/style/layers/custom_layer.hpp16
-rw-r--r--include/mbgl/style/layers/line_layer.hpp6
-rw-r--r--include/mbgl/style/layers/symbol_layer.hpp32
-rw-r--r--include/mbgl/style/source.hpp4
-rw-r--r--include/mbgl/style/sources/custom_geometry_source.hpp56
-rw-r--r--include/mbgl/style/sources/geojson_source.hpp3
-rw-r--r--include/mbgl/style/sources/image_source.hpp2
-rw-r--r--include/mbgl/style/style.hpp6
-rw-r--r--include/mbgl/style/transition_options.hpp9
-rw-r--r--include/mbgl/style/types.hpp11
-rw-r--r--include/mbgl/tile/tile_id.hpp263
-rw-r--r--include/mbgl/tile/tile_necessity.hpp15
-rw-r--r--include/mbgl/util/any.hpp10
-rw-r--r--include/mbgl/util/constants.hpp7
-rw-r--r--include/mbgl/util/convert.hpp6
-rw-r--r--include/mbgl/util/enum.hpp1
-rw-r--r--include/mbgl/util/geo.hpp90
-rw-r--r--include/mbgl/util/geometry.hpp6
-rw-r--r--include/mbgl/util/image.hpp32
-rw-r--r--include/mbgl/util/indexed_tuple.hpp7
-rw-r--r--include/mbgl/util/interpolate.hpp39
-rw-r--r--include/mbgl/util/projection.hpp22
-rw-r--r--include/mbgl/util/run_loop.hpp12
-rw-r--r--include/mbgl/util/size.hpp4
-rw-r--r--include/mbgl/util/string.hpp33
-rw-r--r--include/mbgl/util/thread.hpp161
-rw-r--r--include/mbgl/util/tileset.hpp15
-rw-r--r--include/mbgl/util/unique_any.hpp275
-rw-r--r--include/mbgl/util/unitbezier.hpp14
-rw-r--r--include/mbgl/util/util.hpp7
100 files changed, 3897 insertions, 1688 deletions
diff --git a/include/mbgl/actor/actor.hpp b/include/mbgl/actor/actor.hpp
index 810114c513..a0df19208e 100644
--- a/include/mbgl/actor/actor.hpp
+++ b/include/mbgl/actor/actor.hpp
@@ -6,6 +6,8 @@
#include <mbgl/util/noncopyable.hpp>
#include <memory>
+#include <future>
+#include <type_traits>
namespace mbgl {
@@ -46,10 +48,18 @@ namespace mbgl {
template <class Object>
class Actor : public util::noncopyable {
public:
- template <class... Args>
+
+ // Enabled for Objects with a constructor taking ActorRef<Object> as the first parameter
+ template <typename U = Object, class... Args, typename std::enable_if<std::is_constructible<U, ActorRef<U>, Args...>::value>::type * = nullptr>
Actor(Scheduler& scheduler, Args&&... args_)
- : mailbox(std::make_shared<Mailbox>(scheduler)),
- object(self(), std::forward<Args>(args_)...) {
+ : mailbox(std::make_shared<Mailbox>(scheduler)),
+ object(self(), std::forward<Args>(args_)...) {
+ }
+
+ // Enabled for plain Objects
+ template<typename U = Object, class... Args, typename std::enable_if<!std::is_constructible<U, ActorRef<U>, Args...>::value>::type * = nullptr>
+ Actor(Scheduler& scheduler, Args&& ... args_)
+ : mailbox(std::make_shared<Mailbox>(scheduler)), object(std::forward<Args>(args_)...) {
}
~Actor() {
@@ -61,6 +71,17 @@ public:
mailbox->push(actor::makeMessage(object, fn, std::forward<Args>(args)...));
}
+ template <typename Fn, class... Args>
+ auto ask(Fn fn, Args&&... args) {
+ // Result type is deduced from the function's return type
+ using ResultType = typename std::result_of<decltype(fn)(Object, Args...)>::type;
+
+ std::promise<ResultType> promise;
+ auto future = promise.get_future();
+ mailbox->push(actor::makeMessage(std::move(promise), object, fn, std::forward<Args>(args)...));
+ return future;
+ }
+
ActorRef<std::decay_t<Object>> self() {
return ActorRef<std::decay_t<Object>>(object, mailbox);
}
diff --git a/include/mbgl/actor/actor_ref.hpp b/include/mbgl/actor/actor_ref.hpp
index aeb5bb4507..958ee3777c 100644
--- a/include/mbgl/actor/actor_ref.hpp
+++ b/include/mbgl/actor/actor_ref.hpp
@@ -35,6 +35,27 @@ public:
}
}
+ template <typename Fn, class... Args>
+ auto ask(Fn fn, Args&&... args) {
+ // Result type is deduced from the function's return type
+ using ResultType = typename std::result_of<decltype(fn)(Object, Args...)>::type;
+
+ std::promise<ResultType> promise;
+ auto future = promise.get_future();
+
+ if (auto mailbox = weakMailbox.lock()) {
+ mailbox->push(
+ actor::makeMessage(
+ std::move(promise), *object, fn, std::forward<Args>(args)...
+ )
+ );
+ } else {
+ promise.set_exception(std::make_exception_ptr(std::runtime_error("Actor has gone away")));
+ }
+
+ return future;
+ }
+
private:
Object* object;
std::weak_ptr<Mailbox> weakMailbox;
diff --git a/include/mbgl/actor/message.hpp b/include/mbgl/actor/message.hpp
index cf071d4933..0a20993352 100644
--- a/include/mbgl/actor/message.hpp
+++ b/include/mbgl/actor/message.hpp
@@ -1,5 +1,8 @@
#pragma once
+#include <mbgl/util/optional.hpp>
+
+#include <future>
#include <utility>
namespace mbgl {
@@ -36,6 +39,57 @@ public:
ArgsTuple argsTuple;
};
+template <class ResultType, class Object, class MemberFn, class ArgsTuple>
+class AskMessageImpl : public Message {
+public:
+ AskMessageImpl(std::promise<ResultType> promise_, Object& object_, MemberFn memberFn_, ArgsTuple argsTuple_)
+ : object(object_),
+ memberFn(memberFn_),
+ argsTuple(std::move(argsTuple_)),
+ promise(std::move(promise_)) {
+ }
+
+ void operator()() override {
+ promise.set_value(ask(std::make_index_sequence<std::tuple_size<ArgsTuple>::value>()));
+ }
+
+ template <std::size_t... I>
+ ResultType ask(std::index_sequence<I...>) {
+ return (object.*memberFn)(std::move(std::get<I>(argsTuple))...);
+ }
+
+ Object& object;
+ MemberFn memberFn;
+ ArgsTuple argsTuple;
+ std::promise<ResultType> promise;
+};
+
+template <class Object, class MemberFn, class ArgsTuple>
+class AskMessageImpl<void, Object, MemberFn, ArgsTuple> : public Message {
+public:
+ AskMessageImpl(std::promise<void> promise_, Object& object_, MemberFn memberFn_, ArgsTuple argsTuple_)
+ : object(object_),
+ memberFn(memberFn_),
+ argsTuple(std::move(argsTuple_)),
+ promise(std::move(promise_)) {
+ }
+
+ void operator()() override {
+ ask(std::make_index_sequence<std::tuple_size<ArgsTuple>::value>());
+ promise.set_value();
+ }
+
+ template <std::size_t... I>
+ void ask(std::index_sequence<I...>) {
+ (object.*memberFn)(std::move(std::get<I>(argsTuple))...);
+ }
+
+ Object& object;
+ MemberFn memberFn;
+ ArgsTuple argsTuple;
+ std::promise<void> promise;
+};
+
namespace actor {
template <class Object, class MemberFn, class... Args>
@@ -44,5 +98,11 @@ std::unique_ptr<Message> makeMessage(Object& object, MemberFn memberFn, Args&&..
return std::make_unique<MessageImpl<Object, MemberFn, decltype(tuple)>>(object, memberFn, std::move(tuple));
}
+template <class ResultType, class Object, class MemberFn, class... Args>
+std::unique_ptr<Message> makeMessage(std::promise<ResultType>&& promise, Object& object, MemberFn memberFn, Args&&... args) {
+ auto tuple = std::make_tuple(std::forward<Args>(args)...);
+ return std::make_unique<AskMessageImpl<ResultType, Object, MemberFn, decltype(tuple)>>(std::move(promise), object, memberFn, std::move(tuple));
+}
+
} // namespace actor
} // namespace mbgl
diff --git a/include/mbgl/actor/scheduler.hpp b/include/mbgl/actor/scheduler.hpp
index 83689c3348..d8a26ebeab 100644
--- a/include/mbgl/actor/scheduler.hpp
+++ b/include/mbgl/actor/scheduler.hpp
@@ -21,18 +21,21 @@ class Mailbox;
Subject to these constraints, processing can happen on whatever thread in the
pool is available.
- * `RunLoop` is a `Scheduler` that is typically used to create a mailbox and
- `ActorRef` for an object that lives on the main thread and is not itself wrapped
- as an `Actor`:
-
- auto mailbox = std::make_shared<Mailbox>(*util::RunLoop::Get());
+ * `Scheduler::GetCurrent()` is typically used to create a mailbox and `ActorRef`
+ for an object that lives on the main thread and is not itself wrapped an
+ `Actor`. The underlying implementation of this Scheduler should usually be
+ a `RunLoop`
+ auto mailbox = std::make_shared<Mailbox>(*Scheduler::Get());
Actor<Worker> worker(threadPool, ActorRef<Foo>(*this, mailbox));
*/
-
class Scheduler {
public:
virtual ~Scheduler() = default;
virtual void schedule(std::weak_ptr<Mailbox>) = 0;
+
+ // Set/Get the current Scheduler for this thread
+ static Scheduler* GetCurrent();
+ static void SetCurrent(Scheduler*);
};
} // namespace mbgl
diff --git a/include/mbgl/annotation/annotation.hpp b/include/mbgl/annotation/annotation.hpp
index 96e06ca222..bbe479b5ba 100644
--- a/include/mbgl/annotation/annotation.hpp
+++ b/include/mbgl/annotation/annotation.hpp
@@ -17,6 +17,10 @@ using AnnotationIDs = std::vector<AnnotationID>;
class SymbolAnnotation {
public:
+ SymbolAnnotation(Point<double> geometry_, std::string icon_ = {})
+ : geometry(std::move(geometry_)),
+ icon(std::move(icon_)) {}
+
Point<double> geometry;
std::string icon;
};
@@ -29,18 +33,36 @@ using ShapeAnnotationGeometry = variant<
class LineAnnotation {
public:
+ LineAnnotation(ShapeAnnotationGeometry geometry_,
+ style::DataDrivenPropertyValue<float> opacity_ = 1.0f,
+ style::DataDrivenPropertyValue<float> width_ = 1.0f,
+ style::DataDrivenPropertyValue<Color> color_ = Color::black())
+ : geometry(std::move(geometry_)),
+ opacity(std::move(opacity_)),
+ width(std::move(width_)),
+ color(std::move(color_)) {}
+
ShapeAnnotationGeometry geometry;
- style::DataDrivenPropertyValue<float> opacity { 1.0f };
- style::DataDrivenPropertyValue<float> width { 1.0f };
- style::DataDrivenPropertyValue<Color> color { Color::black() };
+ style::DataDrivenPropertyValue<float> opacity;
+ style::DataDrivenPropertyValue<float> width;
+ style::DataDrivenPropertyValue<Color> color;
};
class FillAnnotation {
public:
+ FillAnnotation(ShapeAnnotationGeometry geometry_,
+ style::DataDrivenPropertyValue<float> opacity_ = 1.0f,
+ style::DataDrivenPropertyValue<Color> color_ = Color::black(),
+ style::DataDrivenPropertyValue<Color> outlineColor_ = {})
+ : geometry(std::move(geometry_)),
+ opacity(std::move(opacity_)),
+ color(std::move(color_)),
+ outlineColor(std::move(outlineColor_)) {}
+
ShapeAnnotationGeometry geometry;
- style::DataDrivenPropertyValue<float> opacity { 1.0f };
- style::DataDrivenPropertyValue<Color> color { Color::black() };
- style::DataDrivenPropertyValue<Color> outlineColor {};
+ style::DataDrivenPropertyValue<float> opacity;
+ style::DataDrivenPropertyValue<Color> color;
+ style::DataDrivenPropertyValue<Color> outlineColor;
};
using Annotation = variant<
diff --git a/include/mbgl/map/map.hpp b/include/mbgl/map/map.hpp
index dd29d444bd..5ba23a76dd 100644
--- a/include/mbgl/map/map.hpp
+++ b/include/mbgl/map/map.hpp
@@ -4,13 +4,11 @@
#include <mbgl/util/chrono.hpp>
#include <mbgl/map/map_observer.hpp>
#include <mbgl/map/mode.hpp>
-#include <mbgl/util/geo.hpp>
-#include <mbgl/util/feature.hpp>
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/util/size.hpp>
#include <mbgl/annotation/annotation.hpp>
#include <mbgl/map/camera.hpp>
-#include <mbgl/map/query.hpp>
+#include <mbgl/util/geometry.hpp>
#include <cstdint>
#include <string>
@@ -20,10 +18,9 @@
namespace mbgl {
-class Backend;
-class View;
class FileSource;
class Scheduler;
+class RendererFrontend;
namespace style {
class Image;
@@ -32,29 +29,26 @@ class Style;
class Map : private util::noncopyable {
public:
- explicit Map(Backend&,
+ explicit Map(RendererFrontend&,
+ MapObserver&,
Size size,
float pixelRatio,
FileSource&,
Scheduler&,
MapMode mapMode = MapMode::Continuous,
- GLContextMode contextMode = GLContextMode::Unique,
ConstrainMode constrainMode = ConstrainMode::HeightOnly,
- ViewportMode viewportMode = ViewportMode::Default,
- const optional<std::string>& programCacheDir = {});
+ ViewportMode viewportMode = ViewportMode::Default);
~Map();
// Register a callback that will get called (on the render thread) when all resources have
// been loaded and a complete render occurs.
using StillImageCallback = std::function<void (std::exception_ptr)>;
- void renderStill(View&, StillImageCallback callback);
+ void renderStill(StillImageCallback);
+ void renderStill(const CameraOptions&, MapDebugOptions, StillImageCallback);
// Triggers a repaint.
void triggerRepaint();
- // Main render function.
- void render(View&);
-
style::Style& getStyle();
const style::Style& getStyle() const;
@@ -73,6 +67,10 @@ public:
void jumpTo(const CameraOptions&);
void easeTo(const CameraOptions&, const AnimationOptions&);
void flyTo(const CameraOptions&, const AnimationOptions&);
+ CameraOptions cameraForLatLngBounds(const LatLngBounds&, const EdgeInsets&, optional<double> bearing = {}) const;
+ CameraOptions cameraForLatLngs(const std::vector<LatLng>&, const EdgeInsets&, optional<double> bearing = {}) const;
+ CameraOptions cameraForGeometry(const Geometry<double>&, const EdgeInsets&, optional<double> bearing = {}) const;
+ LatLngBounds latLngBoundsForCamera(const CameraOptions&) const;
// Position
void moveBy(const ScreenCoordinate&, const AnimationOptions& = {});
@@ -89,9 +87,6 @@ public:
double getZoom() const;
void setLatLngZoom(const LatLng&, double zoom, const AnimationOptions& = {});
void setLatLngZoom(const LatLng&, double zoom, const EdgeInsets&, const AnimationOptions& = {});
- CameraOptions cameraForLatLngBounds(const LatLngBounds&, const EdgeInsets&) const;
- CameraOptions cameraForLatLngs(const std::vector<LatLng>&, const EdgeInsets&) const;
- LatLngBounds latLngBoundsForCamera(const CameraOptions&) const;
void resetZoom();
// Bounds
@@ -132,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;
@@ -149,16 +152,14 @@ public:
void updateAnnotation(AnnotationID, const Annotation&);
void removeAnnotation(AnnotationID);
- // Feature queries
- std::vector<Feature> queryRenderedFeatures(const ScreenCoordinate&, const RenderedQueryOptions& options = {});
- std::vector<Feature> queryRenderedFeatures(const ScreenBox&, const RenderedQueryOptions& options = {});
- std::vector<Feature> querySourceFeatures(const std::string& sourceID, const SourceQueryOptions& options = {});
-
- AnnotationIDs queryPointAnnotations(const ScreenBox&);
-
- // Memory
- void setSourceTileCacheSize(size_t);
- void onLowMemory();
+ // Tile prefetching
+ //
+ // When loading a map, if `PrefetchZoomDelta` is set to any number greater than 0, the map will
+ // first request a tile for `zoom = getZoom() - delta` in a attempt to display a full map at
+ // lower resolution as quick as possible. It will get clamped at the tile source minimum zoom.
+ // The default `delta` is 4.
+ void setPrefetchZoomDelta(uint8_t delta);
+ uint8_t getPrefetchZoomDelta() const;
// Debug
void setDebug(MapDebugOptions);
diff --git a/include/mbgl/map/mode.hpp b/include/mbgl/map/mode.hpp
index afec5c0a08..4ee289d855 100644
--- a/include/mbgl/map/mode.hpp
+++ b/include/mbgl/map/mode.hpp
@@ -1,5 +1,6 @@
#pragma once
+#include <mbgl/util/util.hpp>
#include <mbgl/util/traits.hpp>
#include <cstdint>
@@ -10,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
@@ -51,23 +44,23 @@ enum class MapDebugOptions : EnumType {
#endif // MBGL_USE_GLES2
};
-constexpr MapDebugOptions operator|(MapDebugOptions lhs, MapDebugOptions rhs) {
+MBGL_CONSTEXPR MapDebugOptions operator|(MapDebugOptions lhs, MapDebugOptions rhs) {
return MapDebugOptions(mbgl::underlying_type(lhs) | mbgl::underlying_type(rhs));
}
-constexpr MapDebugOptions& operator|=(MapDebugOptions& lhs, MapDebugOptions rhs) {
+MBGL_CONSTEXPR MapDebugOptions& operator|=(MapDebugOptions& lhs, MapDebugOptions rhs) {
return (lhs = MapDebugOptions(mbgl::underlying_type(lhs) | mbgl::underlying_type(rhs)));
}
-constexpr bool operator&(MapDebugOptions lhs, MapDebugOptions rhs) {
+MBGL_CONSTEXPR bool operator&(MapDebugOptions lhs, MapDebugOptions rhs) {
return mbgl::underlying_type(lhs) & mbgl::underlying_type(rhs);
}
-constexpr MapDebugOptions& operator&=(MapDebugOptions& lhs, MapDebugOptions rhs) {
+MBGL_CONSTEXPR MapDebugOptions& operator&=(MapDebugOptions& lhs, MapDebugOptions rhs) {
return (lhs = MapDebugOptions(mbgl::underlying_type(lhs) & mbgl::underlying_type(rhs)));
}
-constexpr MapDebugOptions operator~(MapDebugOptions value) {
+MBGL_CONSTEXPR MapDebugOptions operator~(MapDebugOptions value) {
return MapDebugOptions(~mbgl::underlying_type(value));
}
diff --git a/include/mbgl/map/view.hpp b/include/mbgl/map/view.hpp
deleted file mode 100644
index 295779fe51..0000000000
--- a/include/mbgl/map/view.hpp
+++ /dev/null
@@ -1,18 +0,0 @@
-#pragma once
-
-namespace mbgl {
-
-class Map;
-
-class View {
-public:
- virtual ~View() = default;
-
- // Called when this View is used for rendering. Implementations should ensure that a renderable
- // object is bound and glClear/glDraw* calls can be done. They should also make sure that
- // calling .bind() repeatedly is a no-op and that the appropriate gl::Context values are
- // set to the current state.
- virtual void bind() = 0;
-};
-
-} // namespace mbgl
diff --git a/include/mbgl/math/log2.hpp b/include/mbgl/math/log2.hpp
index 8a3bc7f1c0..53d5e45545 100644
--- a/include/mbgl/math/log2.hpp
+++ b/include/mbgl/math/log2.hpp
@@ -17,9 +17,9 @@ typename std::enable_if_t<std::is_floating_point<T>::value, T> log2(T x)
// log2() is producing wrong results on ARMv5 binaries
// running on ARMv7+ CPUs.
#if defined(__ANDROID__)
- return std::log(x) / M_LN2;
+ return ::log(x) / M_LN2;
#else
- return std::log2(x);
+ return ::log2(x);
#endif
}
diff --git a/include/mbgl/map/backend_scope.hpp b/include/mbgl/renderer/backend_scope.hpp
index 4985cd197f..73bafc84c7 100644
--- a/include/mbgl/map/backend_scope.hpp
+++ b/include/mbgl/renderer/backend_scope.hpp
@@ -2,7 +2,7 @@
namespace mbgl {
-class Backend;
+class RendererBackend;
class BackendScope {
public:
@@ -15,17 +15,21 @@ public:
Explicit,
};
- BackendScope(Backend&, ScopeType = ScopeType::Explicit);
+ BackendScope(RendererBackend&, ScopeType = ScopeType::Explicit);
~BackendScope();
// Returns true when there is currently a BackendScope active in this thread.
static bool exists();
private:
+ void activate();
+ void deactivate();
+
BackendScope* priorScope;
BackendScope* nextScope;
- Backend& backend;
+ RendererBackend& backend;
const ScopeType scopeType;
+ bool activated = false;
};
} // namespace mbgl
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/map/query.hpp b/include/mbgl/renderer/query.hpp
index e114fa2ebd..4cadf4f017 100644
--- a/include/mbgl/map/query.hpp
+++ b/include/mbgl/renderer/query.hpp
@@ -13,6 +13,11 @@ namespace mbgl {
*/
class RenderedQueryOptions {
public:
+ RenderedQueryOptions(optional<std::vector<std::string>> layerIDs_ = {},
+ optional<style::Filter> filter_ = {})
+ : layerIDs(std::move(layerIDs_)),
+ filter(std::move(filter_)) {}
+
/** layerIDs to include in the query */
optional<std::vector<std::string>> layerIDs;
@@ -24,6 +29,11 @@ public:
*/
class SourceQueryOptions {
public:
+ SourceQueryOptions(optional<std::vector<std::string>> sourceLayers_ = {},
+ optional<style::Filter> filter_ = {})
+ : sourceLayers(std::move(sourceLayers_)),
+ filter(std::move(filter_)) {}
+
// Required for VectorSource, ignored for GeoJSONSource
optional<std::vector<std::string>> sourceLayers;
diff --git a/include/mbgl/renderer/renderer.hpp b/include/mbgl/renderer/renderer.hpp
new file mode 100644
index 0000000000..db28ee92fc
--- /dev/null
+++ b/include/mbgl/renderer/renderer.hpp
@@ -0,0 +1,58 @@
+#pragma once
+
+#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>
+
+#include <functional>
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace mbgl {
+
+class FileSource;
+class RendererBackend;
+class RendererObserver;
+class RenderedQueryOptions;
+class Scheduler;
+class SourceQueryOptions;
+class UpdateParameters;
+
+class Renderer {
+public:
+ Renderer(RendererBackend&, float pixelRatio_, FileSource&, Scheduler&,
+ GLContextMode = GLContextMode::Unique,
+ const optional<std::string> programCacheDir = {},
+ const optional<std::string> localFontFamily = {});
+ ~Renderer();
+
+ void markContextLost();
+
+ void setObserver(RendererObserver*);
+
+ void render(const UpdateParameters&);
+
+ // Feature queries
+ std::vector<Feature> queryRenderedFeatures(const ScreenLineString&, const RenderedQueryOptions& options = {}) const;
+ std::vector<Feature> queryRenderedFeatures(const ScreenCoordinate& point, const RenderedQueryOptions& options = {}) const;
+ std::vector<Feature> queryRenderedFeatures(const ScreenBox& box, const RenderedQueryOptions& options = {}) const;
+ std::vector<Feature> querySourceFeatures(const std::string& sourceID, const SourceQueryOptions& options = {}) const;
+ AnnotationIDs queryPointAnnotations(const ScreenBox& box) const;
+ AnnotationIDs queryShapeAnnotations(const ScreenBox& box) const;
+ AnnotationIDs getAnnotationIDs(const std::vector<Feature>&) const;
+
+ // Debug
+ void dumpDebugLogs();
+
+ // Memory
+ void onLowMemory();
+
+private:
+ class Impl;
+ std::unique_ptr<Impl> impl;
+};
+
+} // namespace mbgl
diff --git a/include/mbgl/map/backend.hpp b/include/mbgl/renderer/renderer_backend.hpp
index 2e73ad994c..b83c128169 100644
--- a/include/mbgl/map/backend.hpp
+++ b/include/mbgl/renderer/renderer_backend.hpp
@@ -1,8 +1,9 @@
#pragma once
-#include <mbgl/map/map_observer.hpp>
+#include <mbgl/renderer/backend_scope.hpp>
#include <mbgl/util/image.hpp>
#include <mbgl/util/size.hpp>
+#include <mbgl/util/util.hpp>
#include <memory>
#include <mutex>
@@ -15,12 +16,12 @@ using ProcAddress = void (*)();
using FramebufferID = uint32_t;
} // namespace gl
-class BackendScope;
-
-class Backend : public MapObserver {
+// The RendererBackend is used by the Renderer to facilitate
+// the actual rendering.
+class RendererBackend {
public:
- Backend();
- virtual ~Backend();
+ RendererBackend();
+ virtual ~RendererBackend();
// Returns the backend's context which manages OpenGL state.
gl::Context& getContext();
@@ -28,26 +29,28 @@ public:
// Called prior to rendering to update the internally assumed OpenGL state.
virtual void updateAssumedState() = 0;
- // Called when the map needs to be rendered; the backend should call Map::render() at some point
- // in the near future. (Not called for Map::renderStill() mode.)
- virtual void invalidate() = 0;
+ // Called when this backend is used for rendering. Implementations should ensure that a renderable
+ // object is bound and glClear/glDraw* calls can be done. They should also make sure that
+ // calling .bind() repeatedly is a no-op and that the appropriate gl::Context values are
+ // set to the current state.
+ virtual void bind() = 0;
+
+ virtual Size getFramebufferSize() const = 0;
protected:
- // Called with the name of an OpenGL extension that should be loaded. Backend implementations
+ // 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, in four situations:
+ // as a matched pair, exclusively through BackendScope, in two situations:
//
- // 1. When releasing GL resources during Map destruction
- // 2. When calling a CustomLayerInitializeFunction, during Map::addLayer
- // 3. When calling a CustomLayerDeinitializeFunction, during Map::removeLayer
- // 4. When rendering for Map::renderStill
- //
- // They are *not* called for Map::render; it is assumed that the correct context is already
- // activated prior to calling Map::render.
+ // 1. When releasing GL resources during Renderer destruction
+ // (Including calling CustomLayerDeinitializeFunction during RenderCustomLayer destruction)
+ // 2. When renderering through Renderer::render()
+ // (Including calling CustomLayerDeinitializeFunction for newly added custom layers and
+ // CustomLayerDeinitializeFunction on layer removal)
virtual void activate() = 0;
virtual void deactivate() = 0;
@@ -82,4 +85,8 @@ private:
friend class BackendScope;
};
+MBGL_CONSTEXPR bool operator==(const RendererBackend& a, const RendererBackend& b) {
+ return &a == &b;
+}
+
} // namespace mbgl
diff --git a/include/mbgl/renderer/renderer_frontend.hpp b/include/mbgl/renderer/renderer_frontend.hpp
new file mode 100644
index 0000000000..f72b0ccdde
--- /dev/null
+++ b/include/mbgl/renderer/renderer_frontend.hpp
@@ -0,0 +1,31 @@
+#pragma once
+
+#include <memory>
+
+namespace mbgl {
+
+class RendererObserver;
+class UpdateParameters;
+
+// The RenderFrontend is the bridge between the Map and
+// platform used to update and observer the Renderer
+//
+// It hides any threading specifics and always replies on
+// the original thread.
+class RendererFrontend {
+public:
+
+ virtual ~RendererFrontend() = default;
+
+ // Must synchronously clean up the Renderer if set
+ virtual void reset() = 0;
+
+ // Implementer must bind the renderer observer to the renderer in a
+ // appropriate manner so that the callbacks occur on the main thread
+ virtual void setObserver(RendererObserver&) = 0;
+
+ // Coalescing updates is up to the implementer
+ virtual void update(std::shared_ptr<UpdateParameters>) = 0;
+};
+
+} // namespace mbgl
diff --git a/include/mbgl/storage/default_file_source.hpp b/include/mbgl/storage/default_file_source.hpp
index 9911e0ce67..b9c8de5052 100644
--- a/include/mbgl/storage/default_file_source.hpp
+++ b/include/mbgl/storage/default_file_source.hpp
@@ -34,7 +34,7 @@ public:
uint64_t maximumCacheSize = util::DEFAULT_MAX_CACHE_SIZE);
~DefaultFileSource() override;
- bool supportsOptionalRequests() const override {
+ bool supportsCacheOnlyRequests() const override {
return true;
}
@@ -140,6 +140,7 @@ public:
void resume();
// For testing only.
+ void setOnlineStatus(bool);
void put(const Resource&, const Response&);
class Impl;
diff --git a/include/mbgl/storage/file_source.hpp b/include/mbgl/storage/file_source.hpp
index 404c683fdb..0709a1c245 100644
--- a/include/mbgl/storage/file_source.hpp
+++ b/include/mbgl/storage/file_source.hpp
@@ -24,11 +24,11 @@ public:
// not be executed.
virtual std::unique_ptr<AsyncRequest> request(const Resource&, Callback) = 0;
- // When a file source supports optional requests, it must return true.
- // Optional requests are requests that aren't as urgent, but could be useful, e.g.
+ // When a file source supports consulting a local cache only, it must return true.
+ // Cache-only requests are requests that aren't as urgent, but could be useful, e.g.
// to cover part of the map while loading. The FileSource should only do cheap actions to
// retrieve the data, e.g. load it from a cache, but not from the internet.
- virtual bool supportsOptionalRequests() const {
+ virtual bool supportsCacheOnlyRequests() const {
return false;
}
};
diff --git a/include/mbgl/storage/offline.hpp b/include/mbgl/storage/offline.hpp
index 818cfe2ba5..ef4a499e83 100644
--- a/include/mbgl/storage/offline.hpp
+++ b/include/mbgl/storage/offline.hpp
@@ -30,13 +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;
-
+ 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(style::SourceType, uint16_t tileSize, const Range<uint8_t>& zoomRange) const;
};
/*
diff --git a/include/mbgl/storage/online_file_source.hpp b/include/mbgl/storage/online_file_source.hpp
index ffd75662e6..28d70ce544 100644
--- a/include/mbgl/storage/online_file_source.hpp
+++ b/include/mbgl/storage/online_file_source.hpp
@@ -24,6 +24,9 @@ public:
std::unique_ptr<AsyncRequest> request(const Resource&, Callback) override;
+ // For testing only.
+ void setOnlineStatus(bool);
+
private:
friend class OnlineFileRequest;
diff --git a/include/mbgl/storage/resource.hpp b/include/mbgl/storage/resource.hpp
index 7e9ced8049..318fa389f4 100644
--- a/include/mbgl/storage/resource.hpp
+++ b/include/mbgl/storage/resource.hpp
@@ -4,6 +4,8 @@
#include <mbgl/util/optional.hpp>
#include <mbgl/util/font_stack.hpp>
#include <mbgl/util/tileset.hpp>
+#include <mbgl/util/util.hpp>
+#include <mbgl/util/traits.hpp>
#include <string>
@@ -30,18 +32,28 @@ public:
int8_t z;
};
- enum Necessity : bool {
- Optional = false,
- Required = true,
+ enum class LoadingMethod : uint8_t {
+ None = 0b00,
+ Cache = 0b01,
+ Network = 0b10,
+
+ CacheOnly = Cache,
+ NetworkOnly = Network,
+ All = Cache | Network,
};
- Resource(Kind kind_, std::string url_, optional<TileData> tileData_ = {}, Necessity necessity_ = Required)
+ Resource(Kind kind_,
+ std::string url_,
+ optional<TileData> tileData_ = {},
+ LoadingMethod loadingMethod_ = LoadingMethod::All)
: kind(kind_),
- necessity(necessity_),
+ loadingMethod(loadingMethod_),
url(std::move(url_)),
tileData(std::move(tileData_)) {
}
+ bool hasLoadingMethod(LoadingMethod method);
+
static Resource style(const std::string& url);
static Resource source(const std::string& url);
static Resource tile(const std::string& urlTemplate,
@@ -50,7 +62,7 @@ public:
int32_t y,
int8_t z,
Tileset::Scheme scheme,
- Necessity = Required);
+ LoadingMethod = LoadingMethod::All);
static Resource glyphs(const std::string& urlTemplate,
const FontStack& fontStack,
const std::pair<uint16_t, uint16_t>& glyphRange);
@@ -59,7 +71,7 @@ public:
static Resource image(const std::string& url);
Kind kind;
- Necessity necessity;
+ LoadingMethod loadingMethod;
std::string url;
// Includes auxiliary data if this is a tile request.
@@ -68,6 +80,24 @@ public:
optional<Timestamp> priorModified = {};
optional<Timestamp> priorExpires = {};
optional<std::string> priorEtag = {};
+ std::shared_ptr<const std::string> priorData;
};
+
+MBGL_CONSTEXPR Resource::LoadingMethod operator|(Resource::LoadingMethod a, Resource::LoadingMethod b) {
+ return Resource::LoadingMethod(mbgl::underlying_type(a) | mbgl::underlying_type(b));
+}
+
+MBGL_CONSTEXPR Resource::LoadingMethod& operator|=(Resource::LoadingMethod& a, Resource::LoadingMethod b) {
+ return (a = a | b);
+}
+
+MBGL_CONSTEXPR Resource::LoadingMethod operator&(Resource::LoadingMethod a, Resource::LoadingMethod b) {
+ return Resource::LoadingMethod(mbgl::underlying_type(a) & mbgl::underlying_type(b));
+}
+
+inline bool Resource::hasLoadingMethod(Resource::LoadingMethod method) {
+ return (loadingMethod & method) != Resource::LoadingMethod::None;
+}
+
} // namespace mbgl
diff --git a/include/mbgl/storage/response.hpp b/include/mbgl/storage/response.hpp
index 32fe4e0c8a..508400141b 100644
--- a/include/mbgl/storage/response.hpp
+++ b/include/mbgl/storage/response.hpp
@@ -2,7 +2,6 @@
#include <mbgl/util/chrono.hpp>
#include <mbgl/util/optional.hpp>
-#include <mbgl/util/variant.hpp>
#include <string>
#include <memory>
@@ -27,6 +26,10 @@ public:
// This is set to true for 304 Not Modified responses.
bool notModified = false;
+ // This is set to true when the server requested that no expired resources be used by
+ // specifying "Cache-Control: must-revalidate".
+ bool mustRevalidate = false;
+
// The actual data of the response. Present only for non-error, non-notModified responses.
std::shared_ptr<const std::string> data;
@@ -37,6 +40,12 @@ public:
bool isFresh() const {
return expires ? *expires > util::now() : !error;
}
+
+ // Indicates whether we are allowed to use this response according to HTTP caching rules.
+ // It may or may not be stale.
+ bool isUsable() const {
+ return !mustRevalidate || (expires && *expires > util::now());
+ }
};
class Response::Error {
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..73b141e799
--- /dev/null
+++ b/include/mbgl/style/conversion/custom_geometry_source_options.hpp
@@ -0,0 +1,64 @@
+#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 {};
+ }
+ }
+
+ 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 bf5b27a9a6..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,9 +149,8 @@ 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 {
- std::string type = util::Interpolatable<T> ? "exponential" : "interval";
+ optional<variant<Ts...>> operator()(const Convertible& value, Error& error) const {
+ std::string type = util::Interpolatable<T>::value ? "exponential" : "interval";
auto typeValue = objectMember(value, "type");
if (typeValue && toString(*typeValue)) {
@@ -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 19383d90ce..3f625babb6 100644
--- a/include/mbgl/style/conversion/geojson_options.hpp
+++ b/include/mbgl/style/conversion/geojson_options.hpp
@@ -9,74 +9,7 @@ namespace conversion {
template <>
struct Converter<GeoJSONOptions> {
-
- template <class V>
- optional<GeoJSONOptions> operator()(const V& value, Error& error) const {
- GeoJSONOptions options;
-
- 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 1fe467165d..1c0e2e2f07 100644
--- a/include/mbgl/style/conversion/layer.hpp
+++ b/include/mbgl/style/conversion/layer.hpp
@@ -1,220 +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 {};
- }
- 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 eaea2e89dc..0000000000
--- a/include/mbgl/style/conversion/make_property_setters.hpp
+++ /dev/null
@@ -1,207 +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, PropertyValue<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["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, PropertyValue<float>, &SymbolLayer::setTextMaxWidth>;
- result["text-line-height"] = &setProperty<V, SymbolLayer, PropertyValue<float>, &SymbolLayer::setTextLineHeight>;
- result["text-letter-spacing"] = &setProperty<V, SymbolLayer, PropertyValue<float>, &SymbolLayer::setTextLetterSpacing>;
- result["text-justify"] = &setProperty<V, SymbolLayer, PropertyValue<TextJustifyType>, &SymbolLayer::setTextJustify>;
- result["text-anchor"] = &setProperty<V, SymbolLayer, PropertyValue<TextAnchorType>, &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-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 377170aa6a..1fb4acf70d 100644
--- a/include/mbgl/style/conversion/tileset.hpp
+++ b/include/mbgl/style/conversion/tileset.hpp
@@ -10,70 +10,7 @@ namespace conversion {
template <>
struct Converter<Tileset> {
public:
- 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);
- }
-
- 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..2516eea024
--- /dev/null
+++ b/include/mbgl/style/expression/array_assertion.hpp
@@ -0,0 +1,39 @@
+#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;
+ }
+
+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..504d49f4e5
--- /dev/null
+++ b/include/mbgl/style/expression/assertion.hpp
@@ -0,0 +1,33 @@
+#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;
+
+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..e3eefa4fe8
--- /dev/null
+++ b/include/mbgl/style/expression/at.hpp
@@ -0,0 +1,38 @@
+#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;
+ }
+
+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..01231d706b
--- /dev/null
+++ b/include/mbgl/style/expression/boolean_operator.hpp
@@ -0,0 +1,49 @@
+#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;
+
+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;
+
+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..ece2fe0329
--- /dev/null
+++ b/include/mbgl/style/expression/case.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 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;
+
+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..4e6a9b3793
--- /dev/null
+++ b/include/mbgl/style/expression/coalesce.hpp
@@ -0,0 +1,45 @@
+#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::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..665bb7ce7c
--- /dev/null
+++ b/include/mbgl/style/expression/coercion.hpp
@@ -0,0 +1,34 @@
+#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;
+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..fc3edbfd4a
--- /dev/null
+++ b/include/mbgl/style/expression/compound_expression.hpp
@@ -0,0 +1,138 @@
+#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(); }
+ );
+ }
+
+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/expression.hpp b/include/mbgl/style/expression/expression.hpp
new file mode 100644
index 0000000000..a22fc28724
--- /dev/null
+++ b/include/mbgl/style/expression/expression.hpp
@@ -0,0 +1,173 @@
+#pragma once
+
+#include <array>
+#include <vector>
+#include <memory>
+#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>
+
+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;
+
+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..439122f91c
--- /dev/null
+++ b/include/mbgl/style/expression/interpolate.hpp
@@ -0,0 +1,177 @@
+#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>
+
+
+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; }
+
+ void eachChild(const std::function<void(const Expression&)>& visit) const override {
+ visit(*input);
+ for (const auto& stop : stops) {
+ visit(*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); }
+ );
+ }
+
+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 (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..aaa16ca0c2
--- /dev/null
+++ b/include/mbgl/style/expression/let.hpp
@@ -0,0 +1,72 @@
+#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;
+ }
+
+ 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;
+ }
+
+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..a0819c7e73
--- /dev/null
+++ b/include/mbgl/style/expression/literal.hpp
@@ -0,0 +1,38 @@
+#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;
+ }
+
+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..e17fe96bfe
--- /dev/null
+++ b/include/mbgl/style/expression/match.hpp
@@ -0,0 +1,45 @@
+#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_))
+ {}
+
+ void eachChild(const std::function<void(const Expression&)>& visit) const override;
+
+ bool operator==(const Expression& e) const override;
+
+ EvaluationResult evaluate(const EvaluationContext& params) 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..a983789cbd
--- /dev/null
+++ b/include/mbgl/style/expression/parsing_context.hpp
@@ -0,0 +1,148 @@
+#pragma once
+
+#include <mbgl/util/optional.hpp>
+#include <mbgl/util/string.hpp>
+#include <mbgl/style/expression/type.hpp>
+#include <mbgl/style/conversion.hpp>
+
+#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; }
+
+ /*
+ 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);
+
+ /*
+ Parse a child expression.
+ */
+ ParseResult parse(const mbgl::style::conversion::Convertible&,
+ std::size_t,
+ optional<type::Type> = {});
+
+ /*
+ 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..e3c49bc609
--- /dev/null
+++ b/include/mbgl/style/expression/step.hpp
@@ -0,0 +1,45 @@
+
+#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;
+
+ 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;
+
+ 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..8baa9d2dba
--- /dev/null
+++ b/include/mbgl/style/expression/value.hpp
@@ -0,0 +1,153 @@
+#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);
+};
+
+} // 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 0fd5bcb078..25b38e3616 100644
--- a/include/mbgl/style/function/camera_function.hpp
+++ b/include/mbgl/style/function/camera_function.hpp
@@ -1,10 +1,18 @@
#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>
#include <mbgl/util/variant.hpp>
+
namespace mbgl {
namespace style {
@@ -12,30 +20,66 @@ template <class T>
class CameraFunction {
public:
using Stops = std::conditional_t<
- util::Interpolatable<T>,
+ util::Interpolatable<T>::value,
variant<
ExponentialStops<T>,
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); }
+ );
}
friend bool operator==(const CameraFunction& lhs,
const CameraFunction& rhs) {
- return lhs.stops == rhs.stops;
+ return *lhs.expression == *rhs.expression;
}
- Stops stops;
bool useIntegerZoom = false;
+
+ // 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 43599cd333..b44bf8e6fe 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>
@@ -24,7 +31,7 @@ template <class T>
class CompositeFunction {
public:
using InnerStops = std::conditional_t<
- util::Interpolatable<T>,
+ util::Interpolatable<T>::value,
variant<
ExponentialStops<T>,
IntervalStops<T>,
@@ -34,7 +41,7 @@ public:
CategoricalStops<T>>>;
using Stops = std::conditional_t<
- util::Interpolatable<T>,
+ util::Interpolatable<T>::value,
variant<
CompositeExponentialStops<T>,
CompositeIntervalStops<T>,
@@ -43,110 +50,71 @@ 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)
- }
- };
- }
- );
+ 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 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)
- };
- }
-
- // 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); }
+ );
}
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;
}
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..9f7b7ed1f8
--- /dev/null
+++ b/include/mbgl/style/function/convert.hpp
@@ -0,0 +1,351 @@
+#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};
+ }
+
+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 2872c63a64..02e4b604e2 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>
@@ -16,7 +18,7 @@ template <class T>
class SourceFunction {
public:
using Stops = std::conditional_t<
- util::Interpolatable<T>,
+ util::Interpolatable<T>::value,
variant<
ExponentialStops<T>,
IntervalStops<T>,
@@ -27,33 +29,48 @@ 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;
}
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;
+
+ // 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..eb2dbf830b 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>
@@ -126,7 +126,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/layers/circle_layer.hpp b/include/mbgl/style/layers/circle_layer.hpp
index 275a2e67a5..942dd67503 100644
--- a/include/mbgl/style/layers/circle_layer.hpp
+++ b/include/mbgl/style/layers/circle_layer.hpp
@@ -78,6 +78,12 @@ public:
void setCirclePitchScaleTransition(const TransitionOptions&);
TransitionOptions getCirclePitchScaleTransition() const;
+ static PropertyValue<AlignmentType> getDefaultCirclePitchAlignment();
+ PropertyValue<AlignmentType> getCirclePitchAlignment() const;
+ void setCirclePitchAlignment(PropertyValue<AlignmentType>);
+ void setCirclePitchAlignmentTransition(const TransitionOptions&);
+ TransitionOptions getCirclePitchAlignmentTransition() const;
+
static DataDrivenPropertyValue<float> getDefaultCircleStrokeWidth();
DataDrivenPropertyValue<float> getCircleStrokeWidth() const;
void setCircleStrokeWidth(DataDrivenPropertyValue<float>);
diff --git a/include/mbgl/style/layers/custom_layer.hpp b/include/mbgl/style/layers/custom_layer.hpp
index 79a353b047..bf3387f95b 100644
--- a/include/mbgl/style/layers/custom_layer.hpp
+++ b/include/mbgl/style/layers/custom_layer.hpp
@@ -39,6 +39,14 @@ struct CustomLayerRenderParameters {
using CustomLayerRenderFunction = void (*)(void* context, const CustomLayerRenderParameters&);
/**
+ * Called when the system has destroyed the underlying GL context. The
+ * `CustomLayerDeinitializeFunction` will not be called in this case, however
+ * `CustomLayerInitializeFunction` will be called instead to prepare for a new render.
+ *
+ */
+using CustomLayerContextLostFunction = void (*)(void* context);
+
+/**
* Destroy any GL state needed by the custom layer, and deallocate context, if necessary. This
* method is called once, from the main thread, at a point when the GL context is active.
*
@@ -51,8 +59,16 @@ public:
CustomLayer(const std::string& id,
CustomLayerInitializeFunction,
CustomLayerRenderFunction,
+ CustomLayerContextLostFunction,
CustomLayerDeinitializeFunction,
void* context);
+
+ CustomLayer(const std::string& id,
+ CustomLayerInitializeFunction,
+ CustomLayerRenderFunction,
+ CustomLayerDeinitializeFunction,
+ void* context);
+
~CustomLayer() final;
// Visibility
diff --git a/include/mbgl/style/layers/line_layer.hpp b/include/mbgl/style/layers/line_layer.hpp
index 0b49690fd8..4519296323 100644
--- a/include/mbgl/style/layers/line_layer.hpp
+++ b/include/mbgl/style/layers/line_layer.hpp
@@ -42,9 +42,9 @@ public:
PropertyValue<LineCapType> getLineCap() const;
void setLineCap(PropertyValue<LineCapType>);
- static PropertyValue<LineJoinType> getDefaultLineJoin();
- PropertyValue<LineJoinType> getLineJoin() const;
- void setLineJoin(PropertyValue<LineJoinType>);
+ static DataDrivenPropertyValue<LineJoinType> getDefaultLineJoin();
+ DataDrivenPropertyValue<LineJoinType> getLineJoin() const;
+ void setLineJoin(DataDrivenPropertyValue<LineJoinType>);
static PropertyValue<float> getDefaultLineMiterLimit();
PropertyValue<float> getLineMiterLimit() const;
diff --git a/include/mbgl/style/layers/symbol_layer.hpp b/include/mbgl/style/layers/symbol_layer.hpp
index 8158f267c9..a72baa0b4e 100644
--- a/include/mbgl/style/layers/symbol_layer.hpp
+++ b/include/mbgl/style/layers/symbol_layer.hpp
@@ -98,6 +98,14 @@ public:
DataDrivenPropertyValue<std::array<float, 2>> getIconOffset() const;
void setIconOffset(DataDrivenPropertyValue<std::array<float, 2>>);
+ static DataDrivenPropertyValue<SymbolAnchorType> getDefaultIconAnchor();
+ DataDrivenPropertyValue<SymbolAnchorType> getIconAnchor() const;
+ void setIconAnchor(DataDrivenPropertyValue<SymbolAnchorType>);
+
+ static PropertyValue<AlignmentType> getDefaultIconPitchAlignment();
+ PropertyValue<AlignmentType> getIconPitchAlignment() const;
+ void setIconPitchAlignment(PropertyValue<AlignmentType>);
+
static PropertyValue<AlignmentType> getDefaultTextPitchAlignment();
PropertyValue<AlignmentType> getTextPitchAlignment() const;
void setTextPitchAlignment(PropertyValue<AlignmentType>);
@@ -118,25 +126,25 @@ public:
DataDrivenPropertyValue<float> getTextSize() const;
void setTextSize(DataDrivenPropertyValue<float>);
- static PropertyValue<float> getDefaultTextMaxWidth();
- PropertyValue<float> getTextMaxWidth() const;
- void setTextMaxWidth(PropertyValue<float>);
+ static DataDrivenPropertyValue<float> getDefaultTextMaxWidth();
+ DataDrivenPropertyValue<float> getTextMaxWidth() const;
+ void setTextMaxWidth(DataDrivenPropertyValue<float>);
static PropertyValue<float> getDefaultTextLineHeight();
PropertyValue<float> getTextLineHeight() const;
void setTextLineHeight(PropertyValue<float>);
- static PropertyValue<float> getDefaultTextLetterSpacing();
- PropertyValue<float> getTextLetterSpacing() const;
- void setTextLetterSpacing(PropertyValue<float>);
+ static DataDrivenPropertyValue<float> getDefaultTextLetterSpacing();
+ DataDrivenPropertyValue<float> getTextLetterSpacing() const;
+ void setTextLetterSpacing(DataDrivenPropertyValue<float>);
- static PropertyValue<TextJustifyType> getDefaultTextJustify();
- PropertyValue<TextJustifyType> getTextJustify() const;
- void setTextJustify(PropertyValue<TextJustifyType>);
+ static DataDrivenPropertyValue<TextJustifyType> getDefaultTextJustify();
+ DataDrivenPropertyValue<TextJustifyType> getTextJustify() const;
+ void setTextJustify(DataDrivenPropertyValue<TextJustifyType>);
- static PropertyValue<TextAnchorType> getDefaultTextAnchor();
- PropertyValue<TextAnchorType> getTextAnchor() const;
- void setTextAnchor(PropertyValue<TextAnchorType>);
+ static DataDrivenPropertyValue<SymbolAnchorType> getDefaultTextAnchor();
+ DataDrivenPropertyValue<SymbolAnchorType> getTextAnchor() const;
+ void setTextAnchor(DataDrivenPropertyValue<SymbolAnchorType>);
static PropertyValue<float> getDefaultTextMaxAngle();
PropertyValue<float> getTextMaxAngle() const;
diff --git a/include/mbgl/style/source.hpp b/include/mbgl/style/source.hpp
index cec9619451..0b6a6c72d9 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>
@@ -76,7 +76,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..a0b990b44b
--- /dev/null
+++ b/include/mbgl/style/sources/custom_geometry_source.hpp
@@ -0,0 +1,56 @@
+#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;
+ };
+
+ 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 2dcfec51aa..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 {
@@ -12,7 +13,9 @@ namespace style {
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/image_source.hpp b/include/mbgl/style/sources/image_source.hpp
index d8a2c45bd8..009764291f 100644
--- a/include/mbgl/style/sources/image_source.hpp
+++ b/include/mbgl/style/sources/image_source.hpp
@@ -18,7 +18,7 @@ public:
optional<std::string> getURL() const;
void setURL(const std::string& url);
- void setImage(UnassociatedImage&&);
+ void setImage(PremultipliedImage&&);
void setCoordinates(const std::array<LatLng, 4>&);
std::array<LatLng, 4> getCoordinates() const;
diff --git a/include/mbgl/style/style.hpp b/include/mbgl/style/style.hpp
index cb84922b4d..d6fdbd8f2c 100644
--- a/include/mbgl/style/style.hpp
+++ b/include/mbgl/style/style.hpp
@@ -1,6 +1,7 @@
#pragma once
#include <mbgl/style/transition_options.hpp>
+#include <mbgl/map/camera.hpp>
#include <mbgl/util/geo.hpp>
#include <string>
@@ -32,10 +33,7 @@ public:
// Defaults
std::string getName() const;
- LatLng getDefaultLatLng() const;
- double getDefaultZoom() const;
- double getDefaultBearing() const;
- double getDefaultPitch() const;
+ CameraOptions getDefaultCamera() const;
// TransitionOptions
TransitionOptions getTransitionOptions() const;
diff --git a/include/mbgl/style/transition_options.hpp b/include/mbgl/style/transition_options.hpp
index 1583667025..87a81717a0 100644
--- a/include/mbgl/style/transition_options.hpp
+++ b/include/mbgl/style/transition_options.hpp
@@ -8,8 +8,13 @@ namespace style {
class TransitionOptions {
public:
- optional<Duration> duration = {};
- optional<Duration> delay = {};
+ optional<Duration> duration;
+ optional<Duration> delay;
+
+ TransitionOptions(optional<Duration> duration_ = {},
+ optional<Duration> delay_ = {})
+ : duration(std::move(duration_)),
+ delay(std::move(delay_)) {}
TransitionOptions reverseMerge(const TransitionOptions& defaults) const {
return {
diff --git a/include/mbgl/style/types.hpp b/include/mbgl/style/types.hpp
index 44b16f16e7..6fe457e181 100644
--- a/include/mbgl/style/types.hpp
+++ b/include/mbgl/style/types.hpp
@@ -4,18 +4,19 @@
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,
GeoJSON,
Video,
Annotations,
- Image
+ Image,
+ CustomVector
};
-namespace style {
-
enum class VisibilityType : bool {
Visible,
None,
@@ -68,7 +69,7 @@ enum class TextJustifyType : uint8_t {
Right
};
-enum class TextAnchorType : uint8_t {
+enum class SymbolAnchorType : uint8_t {
Center,
Left,
Right,
diff --git a/include/mbgl/tile/tile_id.hpp b/include/mbgl/tile/tile_id.hpp
new file mode 100644
index 0000000000..11fb5ce537
--- /dev/null
+++ b/include/mbgl/tile/tile_id.hpp
@@ -0,0 +1,263 @@
+#pragma once
+
+#include <mbgl/util/constants.hpp>
+
+#include <cstdint>
+#include <array>
+#include <tuple>
+#include <forward_list>
+#include <algorithm>
+#include <iosfwd>
+#include <cassert>
+
+namespace mbgl {
+
+class OverscaledTileID;
+class CanonicalTileID;
+class UnwrappedTileID;
+
+// Has integer z/x/y coordinates
+// All tiles must be derived from 0/0/0 (=no tiles outside of the main tile pyramid)
+// Used for requesting data; represents data tiles that exist out there.
+// z is never larger than the source's maxzoom
+class CanonicalTileID {
+public:
+ CanonicalTileID(uint8_t z, uint32_t x, uint32_t y);
+ bool operator==(const CanonicalTileID&) const;
+ bool operator!=(const CanonicalTileID&) const;
+ bool operator<(const CanonicalTileID&) const;
+ bool isChildOf(const CanonicalTileID&) const;
+ CanonicalTileID scaledTo(uint8_t z) const;
+ std::array<CanonicalTileID, 4> children() const;
+
+ uint8_t z;
+ uint32_t x;
+ uint32_t y;
+};
+
+::std::ostream& operator<<(::std::ostream& os, const CanonicalTileID& rhs);
+namespace util {
+std::string toString(const CanonicalTileID&);
+} // namespace util
+
+// Has integer z/x/y coordinates
+// overscaledZ describes the zoom level this tile is intented to represent, e.g. when parsing data
+// z is never larger than the source's maxzoom
+// z/x/y describe the
+class OverscaledTileID {
+public:
+ OverscaledTileID(uint8_t overscaledZ, int16_t wrap, CanonicalTileID);
+ OverscaledTileID(uint8_t overscaledZ, int16_t wrap, uint8_t z, uint32_t x, uint32_t y);
+ OverscaledTileID(uint8_t z, uint32_t x, uint32_t y);
+ explicit OverscaledTileID(const CanonicalTileID&);
+ explicit OverscaledTileID(CanonicalTileID&&);
+ bool operator==(const OverscaledTileID&) const;
+ bool operator!=(const OverscaledTileID&) const;
+ bool operator<(const OverscaledTileID&) const;
+ bool isChildOf(const OverscaledTileID&) const;
+ uint32_t overscaleFactor() const;
+ OverscaledTileID scaledTo(uint8_t z) const;
+ UnwrappedTileID toUnwrapped() const;
+
+ const uint8_t overscaledZ;
+ const int16_t wrap;
+ const CanonicalTileID canonical;
+};
+
+::std::ostream& operator<<(::std::ostream& os, const OverscaledTileID& rhs);
+namespace util {
+std::string toString(const OverscaledTileID&);
+} // namespace util
+
+// Has integer z/x/y coordinates
+// wrap describes tiles that are left/right of the main tile pyramid, e.g. when wrapping the world
+// Used for describing what position tiles are getting rendered at (= calc the matrix)
+// z is never larger than the source's maxzoom
+class UnwrappedTileID {
+public:
+ UnwrappedTileID(uint8_t z, int64_t x, int64_t y);
+ UnwrappedTileID(int16_t wrap, CanonicalTileID);
+ bool operator==(const UnwrappedTileID&) const;
+ bool operator!=(const UnwrappedTileID&) const;
+ bool operator<(const UnwrappedTileID&) const;
+ bool isChildOf(const UnwrappedTileID&) const;
+ std::array<UnwrappedTileID, 4> children() const;
+ OverscaledTileID overscaleTo(uint8_t z) const;
+ float pixelsToTileUnits(float pixelValue, float zoom) const;
+
+ const int16_t wrap;
+ const CanonicalTileID canonical;
+};
+
+::std::ostream& operator<<(::std::ostream& os, const UnwrappedTileID& rhs);
+namespace util {
+std::string toString(const UnwrappedTileID&);
+} // namespace util
+
+inline CanonicalTileID::CanonicalTileID(uint8_t z_, uint32_t x_, uint32_t y_) : z(z_), x(x_), y(y_) {
+ assert(z <= 32);
+ assert(x < (1ull << z));
+ assert(y < (1ull << z));
+}
+
+inline bool CanonicalTileID::operator==(const CanonicalTileID& rhs) const {
+ return z == rhs.z && x == rhs.x && y == rhs.y;
+}
+
+inline bool CanonicalTileID::operator!=(const CanonicalTileID& rhs) const {
+ return z != rhs.z || x != rhs.x || y != rhs.y;
+}
+
+inline bool CanonicalTileID::operator<(const CanonicalTileID& rhs) const {
+ return std::tie(z, x, y) < std::tie(rhs.z, rhs.x, rhs.y);
+}
+
+inline bool CanonicalTileID::isChildOf(const CanonicalTileID& parent) const {
+ // We're first testing for z == 0, to avoid a 32 bit shift, which is undefined.
+ return parent.z == 0 ||
+ (parent.z < z && parent.x == (x >> (z - parent.z)) && parent.y == (y >> (z - parent.z)));
+}
+
+inline CanonicalTileID CanonicalTileID::scaledTo(uint8_t targetZ) const {
+ if (targetZ <= z) {
+ return { targetZ, x >> (z - targetZ), y >> (z - targetZ) }; // parent or same
+ } else {
+ return { targetZ, x << (targetZ - z), y << (targetZ - z) }; // child
+ }
+}
+
+inline std::array<CanonicalTileID, 4> CanonicalTileID::children() const {
+ const uint8_t childZ = z + 1;
+ const uint32_t childX = x * 2;
+ const uint32_t childY = y * 2;
+ return { {
+ { childZ, childX, childY },
+ { childZ, childX, childY + 1 },
+ { childZ, childX + 1, childY },
+ { childZ, childX + 1, childY + 1 },
+ } };
+}
+
+inline OverscaledTileID::OverscaledTileID(uint8_t overscaledZ_, int16_t wrap_, CanonicalTileID canonical_)
+ : overscaledZ(overscaledZ_), wrap(wrap_), canonical(std::move(canonical_)) {
+ assert(overscaledZ >= canonical.z);
+}
+
+inline OverscaledTileID::OverscaledTileID(uint8_t overscaledZ_, int16_t wrap_, uint8_t z, uint32_t x, uint32_t y)
+ : overscaledZ(overscaledZ_), wrap(wrap_), canonical(z, x, y) {
+ assert(overscaledZ >= canonical.z);
+}
+
+inline OverscaledTileID::OverscaledTileID(uint8_t z, uint32_t x, uint32_t y)
+ : overscaledZ(z), wrap(0), canonical(z, x, y) {
+}
+
+inline OverscaledTileID::OverscaledTileID(const CanonicalTileID& canonical_)
+ : overscaledZ(canonical_.z), wrap(0), canonical(canonical_) {
+ assert(overscaledZ >= canonical.z);
+}
+
+inline OverscaledTileID::OverscaledTileID(CanonicalTileID&& canonical_)
+ : overscaledZ(canonical_.z), wrap(0), canonical(std::forward<CanonicalTileID>(canonical_)) {
+ assert(overscaledZ >= canonical.z);
+}
+
+inline bool OverscaledTileID::operator==(const OverscaledTileID& rhs) const {
+ return overscaledZ == rhs.overscaledZ && wrap == rhs.wrap &&canonical == rhs.canonical;
+}
+
+inline bool OverscaledTileID::operator!=(const OverscaledTileID& rhs) const {
+ return overscaledZ != rhs.overscaledZ || wrap != rhs.wrap || canonical != rhs.canonical;
+}
+
+inline bool OverscaledTileID::operator<(const OverscaledTileID& rhs) const {
+ return std::tie(overscaledZ, wrap, canonical) < std::tie(rhs.overscaledZ, rhs.wrap, rhs.canonical);
+}
+
+inline uint32_t OverscaledTileID::overscaleFactor() const {
+ return 1u << (overscaledZ - canonical.z);
+}
+
+inline bool OverscaledTileID::isChildOf(const OverscaledTileID& rhs) const {
+ return overscaledZ > rhs.overscaledZ &&
+ (canonical == rhs.canonical || canonical.isChildOf(rhs.canonical));
+}
+
+inline OverscaledTileID OverscaledTileID::scaledTo(uint8_t z) const {
+ return { z, wrap, z >= canonical.z ? canonical : canonical.scaledTo(z) };
+}
+
+inline UnwrappedTileID OverscaledTileID::toUnwrapped() const {
+ return { wrap, canonical };
+}
+
+inline UnwrappedTileID::UnwrappedTileID(uint8_t z_, int64_t x_, int64_t y_)
+ : wrap((x_ < 0 ? x_ - (1ll << z_) + 1 : x_) / (1ll << z_)),
+ canonical(
+ z_,
+ static_cast<uint32_t>(x_ - wrap * (1ll << z_)),
+ y_ < 0 ? 0 : std::min(static_cast<uint32_t>(y_), static_cast<uint32_t>(1ull << z_) - 1)) {
+}
+
+inline UnwrappedTileID::UnwrappedTileID(int16_t wrap_, CanonicalTileID canonical_)
+ : wrap(wrap_), canonical(std::move(canonical_)) {
+}
+
+inline bool UnwrappedTileID::operator==(const UnwrappedTileID& rhs) const {
+ return wrap == rhs.wrap && canonical == rhs.canonical;
+}
+
+inline bool UnwrappedTileID::operator!=(const UnwrappedTileID& rhs) const {
+ return wrap != rhs.wrap || canonical != rhs.canonical;
+}
+
+inline bool UnwrappedTileID::operator<(const UnwrappedTileID& rhs) const {
+ return std::tie(wrap, canonical) < std::tie(rhs.wrap, rhs.canonical);
+}
+
+inline bool UnwrappedTileID::isChildOf(const UnwrappedTileID& parent) const {
+ return wrap == parent.wrap && canonical.isChildOf(parent.canonical);
+}
+
+inline std::array<UnwrappedTileID, 4> UnwrappedTileID::children() const {
+ const uint8_t childZ = canonical.z + 1;
+ const uint32_t childX = canonical.x * 2;
+ const uint32_t childY = canonical.y * 2;
+ return { {
+ { wrap, { childZ, childX, childY } },
+ { wrap, { childZ, childX, childY + 1 } },
+ { wrap, { childZ, childX + 1, childY } },
+ { wrap, { childZ, childX + 1, childY + 1 } },
+ } };
+}
+
+inline OverscaledTileID UnwrappedTileID::overscaleTo(const uint8_t overscaledZ) const {
+ assert(overscaledZ >= canonical.z);
+ return { overscaledZ, wrap, canonical };
+}
+
+inline float UnwrappedTileID::pixelsToTileUnits(const float pixelValue, const float zoom) const {
+ return pixelValue * (util::EXTENT / (util::tileSize * std::pow(2, zoom - canonical.z)));
+}
+
+} // namespace mbgl
+
+namespace std {
+
+template <>
+struct hash<mbgl::CanonicalTileID> {
+ size_t operator()(const mbgl::CanonicalTileID& id) const;
+};
+
+template <>
+struct hash<mbgl::UnwrappedTileID> {
+ size_t operator()(const mbgl::UnwrappedTileID& id) const;
+};
+
+template <>
+struct hash<mbgl::OverscaledTileID> {
+ size_t operator()(const mbgl::OverscaledTileID& id) const;
+};
+
+} // namespace std
+
diff --git a/include/mbgl/tile/tile_necessity.hpp b/include/mbgl/tile/tile_necessity.hpp
new file mode 100644
index 0000000000..e51bf51d10
--- /dev/null
+++ b/include/mbgl/tile/tile_necessity.hpp
@@ -0,0 +1,15 @@
+#pragma once
+
+namespace mbgl {
+
+// Tiles can have two states: optional or required.
+// - optional means that only low-cost actions should be taken to obtain the data
+// (e.g. load from cache, but accept stale data)
+// - required means that every effort should be taken to obtain the data (e.g. load
+// from internet and keep the data fresh if it expires)
+enum class TileNecessity : bool {
+ Optional = false,
+ Required = true,
+};
+
+} // namespace mbgl
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/constants.hpp b/include/mbgl/util/constants.hpp
index 14aaa752bc..d5e55065c4 100644
--- a/include/mbgl/util/constants.hpp
+++ b/include/mbgl/util/constants.hpp
@@ -19,7 +19,8 @@ constexpr float tileSize = 512;
*
* Positions are stored as signed 16bit integers.
* One bit is lost for signedness to support features extending past the left edge of the tile.
- * One bit is lost because the line vertex buffer packs 1 bit of other data into the int.
+ * One bit is lost because the line vertex buffer used to pack 1 bit of other data into the int.
+ * This is no longer the case but we're reserving this bit anyway.
* One bit is lost to support features extending past the extent on the right edge of the tile.
* This leaves us with 2^13 = 8192
*/
@@ -37,6 +38,9 @@ constexpr double MIN_ZOOM = 0.0;
constexpr double MAX_ZOOM = 25.5;
constexpr float MIN_ZOOM_F = MIN_ZOOM;
constexpr float MAX_ZOOM_F = MAX_ZOOM;
+constexpr uint8_t DEFAULT_MAX_ZOOM = 22;
+
+constexpr uint8_t DEFAULT_PREFETCH_ZOOM_DELTA = 4;
constexpr uint64_t DEFAULT_MAX_CACHE_SIZE = 50 * 1024 * 1024;
@@ -57,7 +61,6 @@ extern const bool tileParseWarnings;
extern const bool styleParseWarnings;
extern const bool spriteWarnings;
extern const bool renderWarnings;
-extern const bool renderTree;
extern const bool labelTextMissingWarning;
extern const bool missingFontStackWarning;
extern const bool missingFontFaceWarning;
diff --git a/include/mbgl/util/convert.hpp b/include/mbgl/util/convert.hpp
index c2b3d9950d..02ec7feef9 100644
--- a/include/mbgl/util/convert.hpp
+++ b/include/mbgl/util/convert.hpp
@@ -1,3 +1,5 @@
+#include <mbgl/util/util.hpp>
+
#include <array>
#include <type_traits>
#include <utility>
@@ -7,8 +9,8 @@ namespace util {
template<typename To, typename From, std::size_t Size,
typename = std::enable_if_t<std::is_convertible<From, To>::value>>
-constexpr std::array<To, Size> convert(const std::array<From, Size>&from) {
- std::array<To, Size> to {};
+MBGL_CONSTEXPR std::array<To, Size> convert(const std::array<From, Size>&from) {
+ std::array<To, Size> to {{}};
std::copy(std::begin(from), std::end(from), std::begin(to));
return to;
}
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/geo.hpp b/include/mbgl/util/geo.hpp
index 6d725b102b..60043ee156 100644
--- a/include/mbgl/util/geo.hpp
+++ b/include/mbgl/util/geo.hpp
@@ -154,18 +154,84 @@ public:
sw.longitude() > ne.longitude();
}
- bool contains(const LatLng& point) const {
- return (point.latitude() >= sw.latitude() &&
- point.latitude() <= ne.latitude() &&
- point.longitude() >= sw.longitude() &&
- point.longitude() <= ne.longitude());
- }
-
- bool intersects(const LatLngBounds area) const {
- return (area.ne.latitude() > sw.latitude() &&
- area.sw.latitude() < ne.latitude() &&
- area.ne.longitude() > sw.longitude() &&
- area.sw.longitude() < ne.longitude());
+ bool crossesAntimeridian() const {
+ return (sw.wrapped().longitude() > ne.wrapped().longitude());
+ }
+
+ bool contains(const LatLng& point, LatLng::WrapMode wrap = LatLng::Unwrapped) const {
+ bool containsLatitude = point.latitude() >= sw.latitude() &&
+ point.latitude() <= ne.latitude();
+ if (!containsLatitude) {
+ return false;
+ }
+
+ bool containsUnwrappedLongitude = point.longitude() >= sw.longitude() &&
+ point.longitude() <= ne.longitude();
+ if (containsUnwrappedLongitude) {
+ return true;
+ } else if (wrap == LatLng::Wrapped) {
+ LatLngBounds wrapped(sw.wrapped(), ne.wrapped());
+ auto ptLon = point.wrapped().longitude();
+ if (crossesAntimeridian()) {
+ return (ptLon >= wrapped.sw.longitude() &&
+ ptLon <= util::LONGITUDE_MAX) ||
+ (ptLon <= wrapped.ne.longitude() &&
+ ptLon >= -util::LONGITUDE_MAX);
+ } else {
+ return (ptLon >= wrapped.sw.longitude() &&
+ ptLon <= wrapped.ne.longitude());
+ }
+ }
+ return false;
+ }
+
+ bool contains(const LatLngBounds& area, LatLng::WrapMode wrap = LatLng::Unwrapped) const {
+ bool containsLatitude = area.north() <= north() && area.south() >= south();
+ if (!containsLatitude) {
+ return false;
+ }
+
+ bool containsUnwrapped = area.east() <= east() && area.west() >= west();
+ if(containsUnwrapped) {
+ return true;
+ } else if (wrap == LatLng::Wrapped) {
+ LatLngBounds wrapped(sw.wrapped(), ne.wrapped());
+ LatLngBounds other(area.sw.wrapped(), area.ne.wrapped());
+ if (crossesAntimeridian() & !area.crossesAntimeridian()) {
+ return (other.east() <= util::LONGITUDE_MAX && other.west() >= wrapped.west()) ||
+ (other.east() <= wrapped.east() && other.west() >= -util::LONGITUDE_MAX);
+ } else {
+ return other.east() <= wrapped.east() && other.west() >= wrapped.west();
+ }
+ }
+ return false;
+ }
+
+ bool intersects(const LatLngBounds area, LatLng::WrapMode wrap = LatLng::Unwrapped) const {
+ bool latitudeIntersects = area.north() > south() && area.south() < north();
+ if (!latitudeIntersects) {
+ return false;
+ }
+
+ bool longitudeIntersects = area.east() > west() && area.west() < east();
+ if (longitudeIntersects) {
+ return true;
+ } else if (wrap == LatLng::Wrapped) {
+ LatLngBounds wrapped(sw.wrapped(), ne.wrapped());
+ LatLngBounds other(area.sw.wrapped(), area.ne.wrapped());
+ if (crossesAntimeridian()) {
+ return area.crossesAntimeridian() ||
+ other.east() > wrapped.west() ||
+ other.west() < wrapped.east();
+ } else if (other.crossesAntimeridian()){
+ return other.east() > wrapped.west() ||
+ other.west() < wrapped.east();
+ } else {
+ return other.east() > wrapped.west() &&
+ other.west() < wrapped.east();
+ }
+ }
+ return false;
}
private:
diff --git a/include/mbgl/util/geometry.hpp b/include/mbgl/util/geometry.hpp
index 6dc16bc514..a28c59a47d 100644
--- a/include/mbgl/util/geometry.hpp
+++ b/include/mbgl/util/geometry.hpp
@@ -2,6 +2,7 @@
#include <mapbox/geometry/geometry.hpp>
#include <mapbox/geometry/point_arithmetic.hpp>
+#include <mapbox/geometry/for_each_point.hpp>
namespace mbgl {
@@ -58,4 +59,9 @@ struct ToFeatureType {
FeatureType operator()(const mapbox::geometry::geometry_collection<T> &) const { return FeatureType::Unknown; }
};
+template <class T, typename F>
+auto forEachPoint(const Geometry<T>& g, F f) {
+ mapbox::geometry::for_each_point(g, f);
+}
+
} // namespace mbgl
diff --git a/include/mbgl/util/image.hpp b/include/mbgl/util/image.hpp
index 91bf06d727..4887058f79 100644
--- a/include/mbgl/util/image.hpp
+++ b/include/mbgl/util/image.hpp
@@ -5,6 +5,7 @@
#include <mbgl/util/size.hpp>
#include <string>
+#include <cstring>
#include <memory>
#include <algorithm>
@@ -66,9 +67,9 @@ public:
template <typename T = Image>
T clone() const {
- T copy(size);
- std::copy(data.get(), data.get() + bytes(), copy.data.get());
- return copy;
+ T copy_(size);
+ std::copy(data.get(), data.get() + bytes(), copy_.data.get());
+ return copy_;
}
size_t stride() const { return channels * size.width; }
@@ -91,6 +92,31 @@ public:
operator=(std::move(newImage));
}
+ // Clears the rect area specified by `pt` and `size` from `dstImage`.
+ static void clear(Image& dstImg, const Point<uint32_t>& pt, const Size& size) {
+ if (size.isEmpty()) {
+ return;
+ }
+
+ if (!dstImg.valid()) {
+ throw std::invalid_argument("invalid destination for image clear");
+ }
+
+ if (size.width > dstImg.size.width ||
+ size.height > dstImg.size.height ||
+ pt.x > dstImg.size.width - size.width ||
+ pt.y > dstImg.size.height - size.height) {
+ throw std::out_of_range("out of range destination coordinates for image clear");
+ }
+
+ uint8_t* dstData = dstImg.data.get();
+
+ for (uint32_t y = 0; y < size.height; y++) {
+ const std::size_t dstOffset = (pt.y + y) * dstImg.stride() + pt.x * channels;
+ std::memset(dstData + dstOffset, 0, size.width * channels);
+ }
+ }
+
// Copy image data within `rect` from `src` to the rectangle of the same size at `pt`
// in `dst`. If the specified bounds exceed the bounds of the source or destination,
// throw `std::out_of_range`. Must not be used to move data within a single Image.
diff --git a/include/mbgl/util/indexed_tuple.hpp b/include/mbgl/util/indexed_tuple.hpp
index a414639530..fd0b931d36 100644
--- a/include/mbgl/util/indexed_tuple.hpp
+++ b/include/mbgl/util/indexed_tuple.hpp
@@ -31,16 +31,13 @@ public:
using std::tuple<Ts...>::tuple;
template <class I>
- static constexpr std::size_t Index = TypeIndex<I, Is...>::value;
-
- template <class I>
auto& get() {
- return std::get<Index<I>>(*this);
+ return std::get<TypeIndex<I, Is...>::value>(*this);
}
template <class I>
const auto& get() const {
- return std::get<Index<I>>(*this);
+ return std::get<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 a2103f18b2..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:
@@ -95,7 +126,13 @@ struct Interpolator<std::vector<T>>
: Uninterpolated {};
template <class T>
-constexpr bool Interpolatable = !std::is_base_of<Uninterpolated, Interpolator<T>>::value;
+struct Interpolatable
+ : std::conditional_t<
+ !std::is_base_of<Uninterpolated, Interpolator<T>>::value,
+ std::true_type,
+ std::false_type> {};
+
+
} // namespace util
} // namespace mbgl
diff --git a/include/mbgl/util/projection.hpp b/include/mbgl/util/projection.hpp
index 3cc1146513..1613af3b36 100644
--- a/include/mbgl/util/projection.hpp
+++ b/include/mbgl/util/projection.hpp
@@ -75,10 +75,7 @@ public:
}
static Point<double> project(const LatLng& latLng, double scale) {
- return Point<double> {
- util::LONGITUDE_MAX + latLng.longitude(),
- util::LONGITUDE_MAX - util::RAD2DEG * std::log(std::tan(M_PI / 4 + latLng.latitude() * M_PI / util::DEGREES_MAX))
- } * worldSize(scale) / util::DEGREES_MAX;
+ return project_(latLng, worldSize(scale));
}
static LatLng unproject(const Point<double>& p, double scale, LatLng::WrapMode wrapMode = LatLng::Unwrapped) {
@@ -89,6 +86,23 @@ public:
wrapMode
};
}
+
+ // Project lat, lon to point in a zoom-dependent world size
+ static Point<double> project(const LatLng& point, uint8_t zoom, uint16_t tileSize) {
+ const double t2z = tileSize * std::pow(2, zoom);
+ Point<double> pt = project_(point, t2z);
+ // Flip y coordinate
+ auto x = ::round(std::min(pt.x, t2z));
+ auto y = ::round(std::min(t2z - pt.y, t2z));
+ return { x, y };
+ }
+private:
+ static Point<double> project_(const LatLng& latLng, double worldSize) {
+ return Point<double> {
+ util::LONGITUDE_MAX + latLng.longitude(),
+ util::LONGITUDE_MAX - util::RAD2DEG * std::log(std::tan(M_PI / 4 + latLng.latitude() * M_PI / util::DEGREES_MAX))
+ } * worldSize / util::DEGREES_MAX;
+ }
};
} // namespace mbgl
diff --git a/include/mbgl/util/run_loop.hpp b/include/mbgl/util/run_loop.hpp
index 14352ca823..acbea80273 100644
--- a/include/mbgl/util/run_loop.hpp
+++ b/include/mbgl/util/run_loop.hpp
@@ -62,6 +62,12 @@ public:
push(task);
return std::make_unique<WorkRequest>(task);
}
+
+ void schedule(std::weak_ptr<Mailbox> mailbox) override {
+ invoke([mailbox] () {
+ Mailbox::maybeReceive(mailbox);
+ });
+ }
class Impl;
@@ -72,12 +78,6 @@ private:
void push(std::shared_ptr<WorkTask>);
- void schedule(std::weak_ptr<Mailbox> mailbox) override {
- invoke([mailbox] () {
- Mailbox::maybeReceive(mailbox);
- });
- }
-
void withMutex(std::function<void()>&& fn) {
std::lock_guard<std::mutex> lock(mutex);
fn();
diff --git a/include/mbgl/util/size.hpp b/include/mbgl/util/size.hpp
index 45c303969c..12c0ad056b 100644
--- a/include/mbgl/util/size.hpp
+++ b/include/mbgl/util/size.hpp
@@ -15,6 +15,10 @@ public:
constexpr uint32_t area() const {
return width * height;
}
+
+ constexpr float aspectRatio() const {
+ return static_cast<float>(width) / static_cast<float>(height);
+ }
constexpr bool isEmpty() const {
return width == 0 || height == 0;
diff --git a/include/mbgl/util/string.hpp b/include/mbgl/util/string.hpp
index de061647b5..13498ccb92 100644
--- a/include/mbgl/util/string.hpp
+++ b/include/mbgl/util/string.hpp
@@ -1,9 +1,38 @@
#pragma once
+#include <sstream>
#include <string>
#include <cassert>
+#include <cstdlib>
#include <exception>
+// Polyfill needed by Qt when building for Android with GCC
+#if defined(__ANDROID__) && defined(__GLIBCXX__)
+
+namespace std {
+
+template <typename T>
+std::string to_string(T value)
+{
+ std::ostringstream oss;
+ oss << value;
+
+ return oss.str();
+}
+
+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
+
namespace mbgl {
namespace util {
@@ -40,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
new file mode 100644
index 0000000000..672eebf6db
--- /dev/null
+++ b/include/mbgl/util/thread.hpp
@@ -0,0 +1,161 @@
+#pragma once
+
+#include <mbgl/actor/actor.hpp>
+#include <mbgl/actor/mailbox.hpp>
+#include <mbgl/actor/scheduler.hpp>
+#include <mbgl/util/platform.hpp>
+#include <mbgl/util/run_loop.hpp>
+#include <mbgl/util/util.hpp>
+
+#include <cassert>
+#include <future>
+#include <memory>
+#include <mutex>
+#include <queue>
+#include <string>
+#include <thread>
+#include <utility>
+
+namespace mbgl {
+namespace util {
+
+// Manages a thread with `Object`.
+
+// Upon creation of this object, it launches a thread and creates an object of type `Object`
+// in that thread. When the `Thread<>` object is destructed, the destructor waits
+// for thread termination. The `Thread<>` constructor blocks until the thread and
+// the `Object` are fully created, so after the object creation, it's safe to obtain the
+// `Object` stored in this thread. The thread created will always have low priority on
+// the platforms that support setting thread priority.
+//
+// The following properties make this class different from `ThreadPool`:
+//
+// - Only one thread is created.
+// - `Object` will live in a single thread, providing thread affinity.
+// - It is safe to use `ThreadLocal` in an `Object` managed by `Thread<>`
+// - A `RunLoop` is created for the `Object` thread.
+// - `Object` can use `Timer` and do asynchronous I/O, like wait for sockets events.
+//
+template<class Object>
+class Thread : public Scheduler {
+public:
+ template <class... Args>
+ Thread(const std::string& name, Args&&... args) {
+ std::promise<void> running;
+
+ thread = std::thread([&] {
+ platform::setCurrentThreadName(name);
+ platform::makeThreadLowPriority();
+
+ util::RunLoop loop_(util::RunLoop::Type::New);
+ loop = &loop_;
+
+ object = std::make_unique<Actor<Object>>(*this, std::forward<Args>(args)...);
+ running.set_value();
+
+ loop->run();
+ loop = nullptr;
+ });
+
+ running.get_future().get();
+ }
+
+ ~Thread() override {
+ if (paused) {
+ resume();
+ }
+
+ std::promise<void> joinable;
+
+ // Kill the actor, so we don't get more
+ // messages posted on this scheduler after
+ // we delete the RunLoop.
+ loop->invoke([&] {
+ object.reset();
+ joinable.set_value();
+ });
+
+ joinable.get_future().get();
+
+ loop->stop();
+ thread.join();
+ }
+
+ // Returns a non-owning reference to `Object` that
+ // can be used to send messages to `Object`. It is safe
+ // to the non-owning reference to outlive this object
+ // and be used after the `Thread<>` gets destroyed.
+ ActorRef<std::decay_t<Object>> actor() const {
+ return object->self();
+ }
+
+ // Pauses the `Object` thread. It will prevent the object to wake
+ // up from events such as timers and file descriptor I/O. Messages
+ // sent to a paused `Object` will be queued and only processed after
+ // `resume()` is called.
+ void pause() {
+ MBGL_VERIFY_THREAD(tid);
+
+ assert(!paused);
+
+ paused = std::make_unique<std::promise<void>>();
+ resumed = std::make_unique<std::promise<void>>();
+
+ auto pausing = paused->get_future();
+
+ loop->invoke([this] {
+ auto resuming = resumed->get_future();
+ paused->set_value();
+ resuming.get();
+ });
+
+ pausing.get();
+ }
+
+ // Resumes the `Object` thread previously paused by `pause()`.
+ void resume() {
+ MBGL_VERIFY_THREAD(tid);
+
+ assert(paused);
+
+ resumed->set_value();
+
+ resumed.reset();
+ paused.reset();
+ }
+
+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);
+ }
+
+ std::mutex mutex;
+ std::queue<std::weak_ptr<Mailbox>> queue;
+ std::thread thread;
+ std::unique_ptr<Actor<Object>> object;
+
+ std::unique_ptr<std::promise<void>> paused;
+ std::unique_ptr<std::promise<void>> resumed;
+
+ util::RunLoop* loop = nullptr;
+};
+
+} // namespace util
+} // namespace mbgl
diff --git a/include/mbgl/util/tileset.hpp b/include/mbgl/util/tileset.hpp
index 1256e9fe96..5a03e1a9da 100644
--- a/include/mbgl/util/tileset.hpp
+++ b/include/mbgl/util/tileset.hpp
@@ -1,7 +1,9 @@
#pragma once
#include <mbgl/util/range.hpp>
+#include <mbgl/util/constants.hpp>
+#include <tuple>
#include <vector>
#include <string>
#include <cstdint>
@@ -13,9 +15,18 @@ public:
enum class Scheme : bool { XYZ, TMS };
std::vector<std::string> tiles;
- Range<uint8_t> zoomRange { 0, 22 };
+ Range<uint8_t> zoomRange;
std::string attribution;
- Scheme scheme = Scheme::XYZ;
+ Scheme scheme;
+
+ Tileset(std::vector<std::string> tiles_ = std::vector<std::string>(),
+ Range<uint8_t> zoomRange_ = { 0, util::DEFAULT_MAX_ZOOM },
+ std::string attribution_ = {},
+ Scheme scheme_ = Scheme::XYZ)
+ : tiles(std::move(tiles_)),
+ zoomRange(std::move(zoomRange_)),
+ attribution(std::move(attribution_)),
+ scheme(scheme_) {}
// TileJSON also includes center, zoom, and bounds, but they are not used by mbgl.
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 3a4994917b..92f23d6718 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 {
@@ -34,11 +35,11 @@ struct UnitBezier {
// Calculate the polynomial coefficients, implicit first and last control points are (0,0) and (1,1).
constexpr UnitBezier(double p1x, double p1y, double p2x, double p2y)
: cx(3.0 * p1x)
- , bx(3.0 * (p2x - p1x) - cx)
- , ax(1.0 - cx - bx)
+ , bx(3.0 * (p2x - p1x) - (3.0 * p1x))
+ , ax(1.0 - (3.0 * p1x) - (3.0 * (p2x - p1x) - (3.0 * p1x)))
, cy(3.0 * p1y)
- , by(3.0 * (p2y - p1y) - cy)
- , ay(1.0 - cy - by) {
+ , by(3.0 * (p2y - p1y) - (3.0 * p1y))
+ , ay(1.0 - (3.0 * p1y) - (3.0 * (p2y - p1y) - (3.0 * p1y))) {
}
double sampleCurveX(double t) const {
@@ -102,6 +103,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;
diff --git a/include/mbgl/util/util.hpp b/include/mbgl/util/util.hpp
index c5a7cb3780..7960b40299 100644
--- a/include/mbgl/util/util.hpp
+++ b/include/mbgl/util/util.hpp
@@ -12,3 +12,10 @@
#define MBGL_VERIFY_THREAD(tid)
#endif
+
+// GCC 4.9 compatibility
+#if !defined(__GNUC__) || __GNUC__ >= 5
+#define MBGL_CONSTEXPR constexpr
+#else
+#define MBGL_CONSTEXPR inline
+#endif