diff options
author | Thiago Marcos P. Santos <tmpsantos@gmail.com> | 2017-08-07 19:19:32 +0300 |
---|---|---|
committer | Thiago Marcos P. Santos <tmpsantos@gmail.com> | 2017-08-09 18:02:46 +0300 |
commit | c53896caefc96a8c18ab746026330ddc4fc0338e (patch) | |
tree | 8f562b0c416d6c99f7b565e58b758701f6081677 /include | |
parent | 9ecbe3642fb4a53b558598239b59bf1d0224c25b (diff) | |
download | qtlocation-mapboxgl-c53896caefc96a8c18ab746026330ddc4fc0338e.tar.gz |
Bump Mapbox GL Nativeqt-v1.1.0
mapbox-gl-native @ edd7948893fcd40a24d96b790e21d3dd028cecbe
Diffstat (limited to 'include')
111 files changed, 4523 insertions, 1484 deletions
diff --git a/include/mbgl/actor/actor.hpp b/include/mbgl/actor/actor.hpp new file mode 100644 index 0000000000..a0df19208e --- /dev/null +++ b/include/mbgl/actor/actor.hpp @@ -0,0 +1,98 @@ +#pragma once + +#include <mbgl/actor/mailbox.hpp> +#include <mbgl/actor/message.hpp> +#include <mbgl/actor/actor_ref.hpp> +#include <mbgl/util/noncopyable.hpp> + +#include <memory> +#include <future> +#include <type_traits> + +namespace mbgl { + +/* + An `Actor<O>` is an owning reference to an asynchronous object of type `O`: an "actor". + Communication with an actor happens via message passing: you send a message to the object + (using `invoke`), passing a pointer to the member function to call and arguments which + are then forwarded to the actor. + + The actor receives messages sent to it asynchronously, in a manner defined its `Scheduler`. + To store incoming messages before their receipt, each actor has a `Mailbox`, which acts as + a FIFO queue. Messages sent from actor S to actor R are guaranteed to be processed in the + order sent. However, relative order of messages sent by two *different* actors S1 and S2 + to R is *not* guaranteed (and can't be: S1 and S2 may be acting asynchronously with respect + to each other). + + An `Actor<O>` can be converted to an `ActorRef<O>`, a non-owning value object representing + a (weak) reference to the actor. Messages can be sent via the `Ref` as well. + + It's safe -- and encouraged -- to pass `Ref`s between actors via messages. This is how two-way + communication and other forms of collaboration between multiple actors is accomplished. + + It's safe for a `Ref` to outlive its `Actor` -- the reference is "weak", and does not extend + the lifetime of the owning Actor, and sending a message to a `Ref` whose `Actor` has died is + a no-op. (In the future, a dead-letters queue or log may be implemented.) + + Construction and destruction of an actor is currently synchronous: the corresponding `O` + object is constructed synchronously by the `Actor` constructor, and destructed synchronously + by the `~Actor` destructor, after ensuring that the `O` is not currently receiving an + asynchronous message. (Construction and destruction may change to be asynchronous in the + future.) The constructor of `O` is passed an `ActorRef<O>` referring to itself (which it + can use to self-send messages), followed by the forwarded arguments passed to `Actor<O>`. + + Please don't send messages that contain shared pointers or references. That subverts the + purpose of the actor model: prohibiting direct concurrent access to shared state. +*/ + +template <class Object> +class Actor : public util::noncopyable { +public: + + // 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_)...) { + } + + // 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() { + mailbox->close(); + } + + template <typename Fn, class... Args> + void invoke(Fn fn, Args&&... args) { + 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); + } + + operator ActorRef<std::decay_t<Object>>() { + return self(); + } + +private: + std::shared_ptr<Mailbox> mailbox; + Object object; +}; + +} // namespace mbgl diff --git a/include/mbgl/actor/actor_ref.hpp b/include/mbgl/actor/actor_ref.hpp new file mode 100644 index 0000000000..958ee3777c --- /dev/null +++ b/include/mbgl/actor/actor_ref.hpp @@ -0,0 +1,64 @@ +#pragma once + +#include <mbgl/actor/mailbox.hpp> +#include <mbgl/actor/message.hpp> + +#include <memory> + +namespace mbgl { + +/* + An `ActorRef<O>` is a *non*-owning, weak reference to an actor of type `O`. You can send it + messages just like an `Actor<O>`. It's a value object: safe to copy and pass between actors + via messages. + + An `ActorRef<O>` does not extend the lifetime of the corresponding `Actor<O>`. That's determined + entirely by whichever object owns the `Actor<O>` -- the actor's "supervisor". + + It's safe for a `Ref` to outlive its `Actor` -- the reference is "weak", and does not extend + the lifetime of the owning Actor, and sending a message to a `Ref` whose `Actor` has died is + a no-op. (In the future, a dead-letters queue or log may be implemented.) +*/ + +template <class Object> +class ActorRef { +public: + ActorRef(Object& object_, std::weak_ptr<Mailbox> weakMailbox_) + : object(&object_), + weakMailbox(std::move(weakMailbox_)) { + } + + template <typename Fn, class... Args> + void invoke(Fn fn, Args&&... args) { + if (auto mailbox = weakMailbox.lock()) { + 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(); + + 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; +}; + +} // namespace mbgl diff --git a/include/mbgl/actor/mailbox.hpp b/include/mbgl/actor/mailbox.hpp new file mode 100644 index 0000000000..8ecf91701a --- /dev/null +++ b/include/mbgl/actor/mailbox.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include <memory> +#include <mutex> +#include <queue> + +namespace mbgl { + +class Scheduler; +class Message; + +class Mailbox : public std::enable_shared_from_this<Mailbox> { +public: + Mailbox(Scheduler&); + + void push(std::unique_ptr<Message>); + + void close(); + void receive(); + + static void maybeReceive(std::weak_ptr<Mailbox>); + +private: + Scheduler& scheduler; + + std::recursive_mutex receivingMutex; + std::mutex pushingMutex; + + bool closed { false }; + + std::mutex queueMutex; + std::queue<std::unique_ptr<Message>> queue; +}; + +} // namespace mbgl diff --git a/include/mbgl/actor/message.hpp b/include/mbgl/actor/message.hpp new file mode 100644 index 0000000000..406de425d4 --- /dev/null +++ b/include/mbgl/actor/message.hpp @@ -0,0 +1,82 @@ +#pragma once + +#include <mbgl/util/optional.hpp> + +#include <future> +#include <utility> + +namespace mbgl { + +// A movable type-erasing function wrapper. This allows to store arbitrary invokable +// things (like std::function<>, or the result of a movable-only std::bind()) in the queue. +// Source: http://stackoverflow.com/a/29642072/331379 +class Message { +public: + virtual ~Message() = default; + virtual void operator()() = 0; +}; + +template <class Object, class MemberFn, class ArgsTuple> +class MessageImpl : public Message { +public: + MessageImpl(Object& object_, MemberFn memberFn_, ArgsTuple argsTuple_) + : object(object_), + memberFn(memberFn_), + argsTuple(std::move(argsTuple_)) { + } + + void operator()() override { + invoke(std::make_index_sequence<std::tuple_size<ArgsTuple>::value>()); + } + + template <std::size_t... I> + void invoke(std::index_sequence<I...>) { + (object.*memberFn)(std::move(std::get<I>(argsTuple))...); + } + + Object& object; + MemberFn memberFn; + 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; +}; + +namespace actor { + +template <class Object, class MemberFn, class... Args> +std::unique_ptr<Message> makeMessage(Object& object, MemberFn memberFn, Args&&... args) { + auto tuple = std::make_tuple(std::forward<Args>(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 new file mode 100644 index 0000000000..83689c3348 --- /dev/null +++ b/include/mbgl/actor/scheduler.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include <memory> + +namespace mbgl { + +class Mailbox; + +/* + A `Scheduler` is responsible for coordinating the processing of messages by + one or more actors via their mailboxes. It's an abstract interface. Currently, + the following concrete implementations exist: + + * `ThreadPool` can coordinate an unlimited number of actors over any number of + threads via a pool, preserving the following behaviors: + + - Messages from each individual mailbox are processed in order + - Only a single message from a mailbox is processed at a time; there is no + concurrency within a 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()); + Actor<Worker> worker(threadPool, ActorRef<Foo>(*this, mailbox)); +*/ + +class Scheduler { +public: + virtual ~Scheduler() = default; + virtual void schedule(std::weak_ptr<Mailbox>) = 0; +}; + +} // namespace mbgl diff --git a/include/mbgl/annotation/annotation.hpp b/include/mbgl/annotation/annotation.hpp index 1a107080de..bbe479b5ba 100644 --- a/include/mbgl/annotation/annotation.hpp +++ b/include/mbgl/annotation/annotation.hpp @@ -4,6 +4,7 @@ #include <mbgl/util/variant.hpp> #include <mbgl/util/color.hpp> #include <mbgl/style/property_value.hpp> +#include <mbgl/style/data_driven_property_value.hpp> #include <cstdint> #include <vector> @@ -16,10 +17,9 @@ using AnnotationIDs = std::vector<AnnotationID>; class SymbolAnnotation { public: - SymbolAnnotation() = default; - - SymbolAnnotation(Point<double> geometry_, std::string icon_) - : geometry(std::move(geometry_)), icon(std::move(icon_)) {} + SymbolAnnotation(Point<double> geometry_, std::string icon_ = {}) + : geometry(std::move(geometry_)), + icon(std::move(icon_)) {} Point<double> geometry; std::string icon; @@ -33,42 +33,41 @@ using ShapeAnnotationGeometry = variant< class LineAnnotation { public: - LineAnnotation(ShapeAnnotationGeometry geometry_, style::PropertyValue<float> opacity_, - style::PropertyValue<float> width_, style::PropertyValue<Color> color_) - : geometry(std::move(geometry_)), opacity(std::move(opacity_)), width(std::move(width_)), color(std::move(color_)) {} + 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::PropertyValue<float> opacity { 1.0f }; - style::PropertyValue<float> width { 1.0f }; - style::PropertyValue<Color> color { Color::black() }; + style::DataDrivenPropertyValue<float> opacity; + style::DataDrivenPropertyValue<float> width; + style::DataDrivenPropertyValue<Color> color; }; class FillAnnotation { public: - FillAnnotation(ShapeAnnotationGeometry geometry_, style::PropertyValue<float> opacity_, - style::PropertyValue<Color> color_, style::PropertyValue<Color> outlineColor_) - : geometry(std::move(geometry_)), opacity(std::move(opacity_)), color(std::move(color_)), outlineColor(std::move(outlineColor_)) {} - - ShapeAnnotationGeometry geometry; - style::PropertyValue<float> opacity { 1.0f }; - style::PropertyValue<Color> color { Color::black() }; - style::PropertyValue<Color> outlineColor {}; -}; - -// An annotation whose type and properties are sourced from a style layer. -class StyleSourcedAnnotation { -public: - StyleSourcedAnnotation(ShapeAnnotationGeometry geometry_, std::string layerID_) - : geometry(std::move(geometry_)), layerID(std::move(layerID_)) {} + 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; - std::string layerID; + style::DataDrivenPropertyValue<float> opacity; + style::DataDrivenPropertyValue<Color> color; + style::DataDrivenPropertyValue<Color> outlineColor; }; using Annotation = variant< SymbolAnnotation, LineAnnotation, - FillAnnotation, - StyleSourcedAnnotation>; + FillAnnotation>; } // namespace mbgl diff --git a/include/mbgl/gl/gl.hpp b/include/mbgl/gl/gl.hpp deleted file mode 100644 index 7985097c8c..0000000000 --- a/include/mbgl/gl/gl.hpp +++ /dev/null @@ -1,50 +0,0 @@ -#pragma once - -#include <stdexcept> -#include <limits> - -#if __APPLE__ - #include "TargetConditionals.h" - #if TARGET_OS_IPHONE - #include <OpenGLES/ES2/gl.h> - #include <OpenGLES/ES2/glext.h> - #elif TARGET_IPHONE_SIMULATOR - #include <OpenGLES/ES2/gl.h> - #include <OpenGLES/ES2/glext.h> - #elif TARGET_OS_MAC - #include <OpenGL/OpenGL.h> - #include <OpenGL/gl.h> - #include <OpenGL/glext.h> - #else - #error Unsupported Apple platform - #endif -#elif __ANDROID__ || MBGL_USE_GLES2 - #define GL_GLEXT_PROTOTYPES - #include <GLES2/gl2.h> - #include <GLES2/gl2ext.h> -#elif __QT__ && QT_VERSION >= 0x050000 - #define GL_GLEXT_PROTOTYPES - #include <QtGui/qopengl.h> -#else - #define GL_GLEXT_PROTOTYPES - #include <GL/gl.h> - #include <GL/glext.h> -#endif - -namespace mbgl { -namespace gl { - -struct Error : std::runtime_error { - using std::runtime_error::runtime_error; -}; - -void checkError(const char *cmd, const char *file, int line); - -#ifndef NDEBUG -#define MBGL_CHECK_ERROR(cmd) ([&]() { struct __MBGL_C_E { ~__MBGL_C_E() { ::mbgl::gl::checkError(#cmd, __FILE__, __LINE__); } } __MBGL_C_E; return cmd; }()) -#else -#define MBGL_CHECK_ERROR(cmd) (cmd) -#endif - -} // namespace gl -} // namespace mbgl diff --git a/include/mbgl/map/backend.hpp b/include/mbgl/map/backend.hpp deleted file mode 100644 index 0468449155..0000000000 --- a/include/mbgl/map/backend.hpp +++ /dev/null @@ -1,64 +0,0 @@ -#pragma once - -#include <mbgl/map/change.hpp> - -#include <memory> - -namespace mbgl { - -namespace gl { -class Context; -} // namespace gl - -class BackendScope; - -class Backend { -public: - Backend(); - virtual ~Backend(); - - // Returns the backend's context which manages OpenGL state. - gl::Context& getContext(); - - // 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; - - // Notifies a watcher of map x/y/scale/rotation changes. - virtual void notifyMapChange(MapChange change); - -protected: - // Called when the backend's GL context needs to be made active or inactive. These are called, - // as a matched pair, in four 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. - virtual void activate() = 0; - virtual void deactivate() = 0; - -private: - const std::unique_ptr<gl::Context> context; - - friend class BackendScope; -}; - -class BackendScope { -public: - BackendScope(Backend& backend_) : backend(backend_) { - backend.activate(); - } - - ~BackendScope() { - backend.deactivate(); - } - -private: - Backend& backend; -}; - -} // namespace mbgl diff --git a/include/mbgl/map/camera.hpp b/include/mbgl/map/camera.hpp index 1c389577f0..53b5b590b1 100644 --- a/include/mbgl/map/camera.hpp +++ b/include/mbgl/map/camera.hpp @@ -17,7 +17,7 @@ struct CameraOptions { /** Padding around the interior of the view that affects the frame of reference for `center`. */ - optional<EdgeInsets> padding; + EdgeInsets padding; /** Point of reference for `zoom` and `angle`, assuming an origin at the top-left corner of the view. */ @@ -36,6 +36,19 @@ struct CameraOptions { optional<double> pitch; }; +constexpr bool operator==(const CameraOptions& a, const CameraOptions& b) { + return a.center == b.center + && a.padding == b.padding + && a.anchor == b.anchor + && a.zoom == b.zoom + && a.angle == b.angle + && a.pitch == b.pitch; +} + +constexpr bool operator!=(const CameraOptions& a, const CameraOptions& b) { + return !(a == b); +} + /** Various options for describing a transition between viewpoints with animation. All fields are optional; the default values depend on how this struct is used. */ diff --git a/include/mbgl/map/change.hpp b/include/mbgl/map/change.hpp new file mode 100644 index 0000000000..3925d655a4 --- /dev/null +++ b/include/mbgl/map/change.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include <cstdint> + +namespace mbgl { + +enum MapChange : uint8_t { + MapChangeRegionWillChange = 0, + MapChangeRegionWillChangeAnimated = 1, + MapChangeRegionIsChanging = 2, + MapChangeRegionDidChange = 3, + MapChangeRegionDidChangeAnimated = 4, + MapChangeWillStartLoadingMap = 5, + MapChangeDidFinishLoadingMap = 6, + MapChangeDidFailLoadingMap = 7, + MapChangeWillStartRenderingFrame = 8, + MapChangeDidFinishRenderingFrame = 9, + MapChangeDidFinishRenderingFrameFullyRendered = 10, + MapChangeWillStartRenderingMap = 11, + MapChangeDidFinishRenderingMap = 12, + MapChangeDidFinishRenderingMapFullyRendered = 13, + MapChangeDidFinishLoadingStyle = 14, + MapChangeSourceDidChange = 15 +}; + +} // namespace mbgl diff --git a/include/mbgl/map/map.hpp b/include/mbgl/map/map.hpp index c80420371d..7d6678dc93 100644 --- a/include/mbgl/map/map.hpp +++ b/include/mbgl/map/map.hpp @@ -2,13 +2,12 @@ #include <mbgl/util/optional.hpp> #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/style/transition_options.hpp> +#include <mbgl/map/camera.hpp> #include <cstdint> #include <string> @@ -18,28 +17,24 @@ namespace mbgl { -class Backend; -class View; class FileSource; class Scheduler; -class SpriteImage; -struct CameraOptions; -struct AnimationOptions; +class RendererFrontend; namespace style { -class Source; -class Layer; +class Image; +class Style; } // namespace 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); ~Map(); @@ -47,29 +42,15 @@ public: // 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 callback); // Triggers a repaint. void triggerRepaint(); - // Main render function. - void render(View&); + style::Style& getStyle(); + const style::Style& getStyle() const; - // Styling - void addClass(const std::string&); - void removeClass(const std::string&); - void setClasses(const std::vector<std::string>&); - - style::TransitionOptions getTransitionOptions() const; - void setTransitionOptions(const style::TransitionOptions&); - - bool hasClass(const std::string&) const; - std::vector<std::string> getClasses() const; - - void setStyleURL(const std::string&); - void setStyleJSON(const std::string&); - std::string getStyleURL() const; - std::string getStyleJSON() const; + void setStyle(std::unique_ptr<style::Style>); // Transition void cancelTransitions(); @@ -80,48 +61,55 @@ public: bool isPanning() const; // Camera - CameraOptions getCameraOptions(optional<EdgeInsets>) const; + CameraOptions getCameraOptions(const EdgeInsets&) const; void jumpTo(const CameraOptions&); void easeTo(const CameraOptions&, const AnimationOptions&); void flyTo(const CameraOptions&, const AnimationOptions&); // Position - void moveBy(const ScreenCoordinate&, const Duration& = Duration::zero()); - void setLatLng(const LatLng&, optional<ScreenCoordinate>, const Duration& = Duration::zero()); - void setLatLng(const LatLng&, optional<EdgeInsets>, const Duration& = Duration::zero()); - void setLatLng(const LatLng&, const Duration& = Duration::zero()); - LatLng getLatLng(optional<EdgeInsets> = {}) const; - void resetPosition(optional<EdgeInsets> = {}); - - // Scale - void scaleBy(double ds, optional<ScreenCoordinate> = {}, const Duration& = Duration::zero()); - void setScale(double scale, optional<ScreenCoordinate> = {}, const Duration& = Duration::zero()); - double getScale() const; - void setZoom(double zoom, const Duration& = Duration::zero()); - void setZoom(double zoom, optional<EdgeInsets>, const Duration& = Duration::zero()); + void moveBy(const ScreenCoordinate&, const AnimationOptions& = {}); + void setLatLng(const LatLng&, optional<ScreenCoordinate>, const AnimationOptions& = {}); + void setLatLng(const LatLng&, const EdgeInsets&, const AnimationOptions& = {}); + void setLatLng(const LatLng&, const AnimationOptions& = {}); + LatLng getLatLng(const EdgeInsets& = {}) const; + void resetPosition(const EdgeInsets& = {}); + + // Zoom + void setZoom(double zoom, const AnimationOptions& = {}); + void setZoom(double zoom, optional<ScreenCoordinate>, const AnimationOptions& = {}); + void setZoom(double zoom, const EdgeInsets&, const AnimationOptions& = {}); double getZoom() const; - void setLatLngZoom(const LatLng&, double zoom, const Duration& = Duration::zero()); - void setLatLngZoom(const LatLng&, double zoom, optional<EdgeInsets>, const Duration& = Duration::zero()); - CameraOptions cameraForLatLngBounds(const LatLngBounds&, optional<EdgeInsets>) const; - CameraOptions cameraForLatLngs(const std::vector<LatLng>&, optional<EdgeInsets>) 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(); - void setMinZoom(const double minZoom); + + // Bounds + void setLatLngBounds(optional<LatLngBounds>); + optional<LatLngBounds> getLatLngBounds() const; + void setMinZoom(double); double getMinZoom() const; - void setMaxZoom(const double maxZoom); + void setMaxZoom(double); double getMaxZoom() const; + void setMinPitch(double); + double getMinPitch() const; + void setMaxPitch(double); + double getMaxPitch() const; // Rotation - void rotateBy(const ScreenCoordinate& first, const ScreenCoordinate& second, const Duration& = Duration::zero()); - void setBearing(double degrees, const Duration& = Duration::zero()); - void setBearing(double degrees, optional<ScreenCoordinate>, const Duration& = Duration::zero()); - void setBearing(double degrees, optional<EdgeInsets>, const Duration& = Duration::zero()); + void rotateBy(const ScreenCoordinate& first, const ScreenCoordinate& second, const AnimationOptions& = {}); + void setBearing(double degrees, const AnimationOptions& = {}); + void setBearing(double degrees, optional<ScreenCoordinate>, const AnimationOptions& = {}); + void setBearing(double degrees, const EdgeInsets&, const AnimationOptions& = {}); double getBearing() const; - void resetNorth(const Duration& = Milliseconds(500)); - void resetNorth(optional<EdgeInsets>, const Duration& = Milliseconds(500)); + void resetNorth(const AnimationOptions& = {{mbgl::Milliseconds(500)}}); + void resetNorth(const EdgeInsets&, const AnimationOptions& = {{mbgl::Milliseconds(500)}}); // Pitch - void setPitch(double pitch, const Duration& = Duration::zero()); - void setPitch(double pitch, optional<ScreenCoordinate>, const Duration& = Duration::zero()); + void setPitch(double pitch, const AnimationOptions& = {}); + void setPitch(double pitch, optional<ScreenCoordinate>, const AnimationOptions& = {}); double getPitch() const; // North Orientation @@ -141,53 +129,26 @@ public: Size getSize() const; // Projection - double getMetersPerPixelAtLatitude(double lat, double zoom) const; - ProjectedMeters projectedMetersForLatLng(const LatLng&) const; - LatLng latLngForProjectedMeters(const ProjectedMeters&) const; ScreenCoordinate pixelForLatLng(const LatLng&) const; LatLng latLngForPixel(const ScreenCoordinate&) const; // Annotations - void addAnnotationIcon(const std::string&, std::shared_ptr<const SpriteImage>); - void removeAnnotationIcon(const std::string&); - double getTopOffsetPixelsForAnnotationIcon(const std::string&); + void addAnnotationImage(std::unique_ptr<style::Image>); + void removeAnnotationImage(const std::string&); + double getTopOffsetPixelsForAnnotationImage(const std::string&); AnnotationID addAnnotation(const Annotation&); void updateAnnotation(AnnotationID, const Annotation&); void removeAnnotation(AnnotationID); - // Sources - std::vector<style::Source*> getSources(); - style::Source* getSource(const std::string& sourceID); - void addSource(std::unique_ptr<style::Source>); - std::unique_ptr<style::Source> removeSource(const std::string& sourceID); - - // Layers - std::vector<style::Layer*> getLayers(); - style::Layer* getLayer(const std::string& layerID); - void addLayer(std::unique_ptr<style::Layer>, const optional<std::string>& beforeLayerID = {}); - std::unique_ptr<style::Layer> removeLayer(const std::string& layerID); - - // Add image, bound to the style - void addImage(const std::string&, std::unique_ptr<const SpriteImage>); - void removeImage(const std::string&); - const SpriteImage* getImage(const std::string&); - - // Defaults - std::string getStyleName() const; - LatLng getDefaultLatLng() const; - double getDefaultZoom() const; - double getDefaultBearing() const; - double getDefaultPitch() const; - - // Feature queries - std::vector<Feature> queryRenderedFeatures(const ScreenCoordinate&, const optional<std::vector<std::string>>& layerIDs = {}); - std::vector<Feature> queryRenderedFeatures(const ScreenBox&, const optional<std::vector<std::string>>& layerIDs = {}); - 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/map_observer.hpp b/include/mbgl/map/map_observer.hpp new file mode 100644 index 0000000000..f63e5f2af3 --- /dev/null +++ b/include/mbgl/map/map_observer.hpp @@ -0,0 +1,44 @@ +#pragma once + +#include <mbgl/style/source.hpp> + +#include <cstdint> +#include <exception> +#include <string> + +namespace mbgl { + +class MapObserver { +public: + virtual ~MapObserver() = default; + + static MapObserver& nullObserver() { + static MapObserver mapObserver; + return mapObserver; + } + + enum class CameraChangeMode : uint32_t { + Immediate, + Animated + }; + + enum class RenderMode : uint32_t { + Partial, + Full + }; + + virtual void onCameraWillChange(CameraChangeMode) {} + virtual void onCameraIsChanging() {} + virtual void onCameraDidChange(CameraChangeMode) {} + virtual void onWillStartLoadingMap() {} + virtual void onDidFinishLoadingMap() {} + virtual void onDidFailLoadingMap(std::exception_ptr) {} + virtual void onWillStartRenderingFrame() {} + virtual void onDidFinishRenderingFrame(RenderMode) {} + virtual void onWillStartRenderingMap() {} + virtual void onDidFinishRenderingMap(RenderMode) {} + virtual void onDidFinishLoadingStyle() {} + virtual void onSourceChanged(style::Source&) {} +}; + +} // namespace mbgl diff --git a/include/mbgl/map/view.hpp b/include/mbgl/map/view.hpp deleted file mode 100644 index 6517c6b220..0000000000 --- a/include/mbgl/map/view.hpp +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include <mbgl/util/noncopyable.hpp> - -namespace mbgl { - -class Map; - -class View : private util::noncopyable { -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/renderer/backend_scope.hpp b/include/mbgl/renderer/backend_scope.hpp new file mode 100644 index 0000000000..73bafc84c7 --- /dev/null +++ b/include/mbgl/renderer/backend_scope.hpp @@ -0,0 +1,35 @@ +#pragma once + +namespace mbgl { + +class RendererBackend; + +class BackendScope { +public: + // There are two types of scopes: Creating an "Implicit" scope tells Mapbox GL that the + // supporting windowing system has already activated the GL Backend and that no further actions + // are required. Creating an "Explicit" scope actually enables the GL Backend, and disables it + // when the BackendScope is destroyed. + enum class ScopeType : bool { + Implicit, + 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; + RendererBackend& backend; + const ScopeType scopeType; + bool activated = false; +}; + +} // namespace mbgl diff --git a/include/mbgl/renderer/query.hpp b/include/mbgl/renderer/query.hpp new file mode 100644 index 0000000000..4cadf4f017 --- /dev/null +++ b/include/mbgl/renderer/query.hpp @@ -0,0 +1,43 @@ +#pragma once + +#include <mbgl/util/optional.hpp> +#include <mbgl/style/filter.hpp> + +#include <string> +#include <vector> + +namespace mbgl { + +/** + * Options for query rendered features. + */ +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; + + optional<style::Filter> filter; +}; + +/** + * Options for query source features + */ +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; + + optional<style::Filter> filter; +}; + +} // namespace mbgl diff --git a/include/mbgl/renderer/renderer.hpp b/include/mbgl/renderer/renderer.hpp new file mode 100644 index 0000000000..95828a1b79 --- /dev/null +++ b/include/mbgl/renderer/renderer.hpp @@ -0,0 +1,53 @@ +#pragma once + +#include <mbgl/map/mode.hpp> +#include <mbgl/renderer/query.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 = {}); + ~Renderer(); + + 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; + + // Debug + void dumpDebugLogs(); + + // Memory + void onLowMemory(); + +private: + class Impl; + std::unique_ptr<Impl> impl; +}; + +} // namespace mbgl diff --git a/include/mbgl/renderer/renderer_backend.hpp b/include/mbgl/renderer/renderer_backend.hpp new file mode 100644 index 0000000000..f7d19a1791 --- /dev/null +++ b/include/mbgl/renderer/renderer_backend.hpp @@ -0,0 +1,90 @@ +#pragma once + +#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> + +namespace mbgl { + +namespace gl { +class Context; +using ProcAddress = void (*)(); +using FramebufferID = uint32_t; +} // namespace gl + +// The RendererBackend is used by the Renderer to facilitate +// the actual rendering. +class RendererBackend { +public: + RendererBackend(); + virtual ~RendererBackend(); + + // Returns the backend's context which manages OpenGL state. + gl::Context& getContext(); + + // Called prior to rendering to update the internally assumed OpenGL state. + virtual void updateAssumedState() = 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; + +protected: + // Called with the name of an OpenGL extension that should be loaded. RendererBackend implementations + // must call the API-specific version that obtains the function pointer for this function, + // or a null pointer if unsupported/unavailable. + virtual gl::ProcAddress initializeExtension(const char*) = 0; + + // Called when the backend's GL context needs to be made active or inactive. These are called, + // as a matched pair, exclusively through BackendScope, in two situations: + // + // 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; + + // Reads the color pixel data from the currently bound framebuffer. + PremultipliedImage readFramebuffer(const Size&) const; + + // A constant to signal that a framebuffer is bound, but with an unknown ID. + static constexpr const gl::FramebufferID ImplicitFramebufferBinding = + std::numeric_limits<gl::FramebufferID>::max(); + + // Tells the renderer that OpenGL state has already been set by the windowing toolkit. + // It sets the internal assumed state to the supplied values. + void assumeFramebufferBinding(gl::FramebufferID fbo); + void assumeViewport(int32_t x, int32_t y, const Size&); + void assumeScissorTest(bool); + + // Returns true when assumed framebuffer binding hasn't changed from the implicit binding. + bool implicitFramebufferBound(); + + // Triggers an OpenGL state update if the internal assumed state doesn't match the + // supplied values. + void setFramebufferBinding(gl::FramebufferID fbo); + void setViewport(int32_t x, int32_t y, const Size&); + void setScissorTest(bool); + +protected: + std::unique_ptr<gl::Context> context; + +private: + std::once_flag initialized; + + 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/sprite/sprite_image.hpp b/include/mbgl/sprite/sprite_image.hpp deleted file mode 100644 index 05d9871bf9..0000000000 --- a/include/mbgl/sprite/sprite_image.hpp +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include <mbgl/util/noncopyable.hpp> -#include <mbgl/util/geo.hpp> -#include <mbgl/util/image.hpp> - -#include <string> -#include <memory> -#include <cstdint> - -namespace mbgl { - -class SpriteImage : private util::noncopyable { -public: - SpriteImage(PremultipliedImage&&, float pixelRatio, bool sdf = false); - - PremultipliedImage image; - - // Pixel ratio of the sprite image. - const float pixelRatio; - - // Whether this image should be interpreted as a signed distance field icon. - const bool sdf; - - float getWidth() const { return image.size.width / pixelRatio; } - float getHeight() const { return image.size.height / pixelRatio; } -}; - -} // namespace mbgl diff --git a/include/mbgl/storage/default_file_source.hpp b/include/mbgl/storage/default_file_source.hpp index b8f5e1167e..9911e0ce67 100644 --- a/include/mbgl/storage/default_file_source.hpp +++ b/include/mbgl/storage/default_file_source.hpp @@ -1,10 +1,13 @@ #pragma once +#include <mbgl/actor/actor_ref.hpp> #include <mbgl/storage/file_source.hpp> #include <mbgl/storage/offline.hpp> #include <mbgl/util/constants.hpp> +#include <mbgl/util/optional.hpp> #include <vector> +#include <mutex> namespace mbgl { @@ -12,6 +15,8 @@ namespace util { template <typename T> class Thread; } // namespace util +class ResourceTransform; + class DefaultFileSource : public FileSource { public: /* @@ -24,17 +29,22 @@ public: DefaultFileSource(const std::string& cachePath, const std::string& assetRoot, uint64_t maximumCacheSize = util::DEFAULT_MAX_CACHE_SIZE); + DefaultFileSource(const std::string& cachePath, + std::unique_ptr<FileSource>&& assetFileSource, + uint64_t maximumCacheSize = util::DEFAULT_MAX_CACHE_SIZE); ~DefaultFileSource() override; bool supportsOptionalRequests() const override { return true; } - + void setAPIBaseURL(const std::string&); - std::string getAPIBaseURL() const; + std::string getAPIBaseURL(); void setAccessToken(const std::string&); - std::string getAccessToken() const; + std::string getAccessToken(); + + void setResourceTransform(optional<ActorRef<ResourceTransform>>&&); std::unique_ptr<AsyncRequest> request(const Resource&, Callback) override; @@ -113,15 +123,37 @@ public: */ void setOfflineMapboxTileCountLimit(uint64_t) const; + /* + * Pause file request activity. + * + * If pause is called then no revalidation or network request activity + * will occur. + */ + void pause(); + + /* + * Resume file request activity. + * + * Calling resume will unpause the file source and process any tasks that + * expired while the file source was paused. + */ + void resume(); + // For testing only. void put(const Resource&, const Response&); class Impl; private: - const std::unique_ptr<util::Thread<Impl>> thread; - const std::unique_ptr<FileSource> assetFileSource; - const std::unique_ptr<FileSource> localFileSource; + // Shared so destruction is done on this thread + const std::shared_ptr<FileSource> assetFileSource; + const std::unique_ptr<util::Thread<Impl>> impl; + + std::mutex cachedBaseURLMutex; + std::string cachedBaseURL = mbgl::util::API_BASE_URL; + + std::mutex cachedAccessTokenMutex; + std::string cachedAccessToken; }; } // namespace mbgl diff --git a/include/mbgl/storage/online_file_source.hpp b/include/mbgl/storage/online_file_source.hpp index 9c7feceb47..ffd75662e6 100644 --- a/include/mbgl/storage/online_file_source.hpp +++ b/include/mbgl/storage/online_file_source.hpp @@ -1,10 +1,14 @@ #pragma once +#include <mbgl/actor/actor_ref.hpp> #include <mbgl/storage/file_source.hpp> #include <mbgl/util/constants.hpp> +#include <mbgl/util/optional.hpp> namespace mbgl { +class ResourceTransform; + class OnlineFileSource : public FileSource { public: OnlineFileSource(); @@ -12,10 +16,12 @@ public: void setAPIBaseURL(const std::string& t) { apiBaseURL = t; } std::string getAPIBaseURL() const { return apiBaseURL; } - + void setAccessToken(const std::string& t) { accessToken = t; } std::string getAccessToken() const { return accessToken; } + void setResourceTransform(optional<ActorRef<ResourceTransform>>&&); + std::unique_ptr<AsyncRequest> request(const Resource&, Callback) override; private: diff --git a/include/mbgl/storage/resource.hpp b/include/mbgl/storage/resource.hpp index c05f40b65c..5d44f4869f 100644 --- a/include/mbgl/storage/resource.hpp +++ b/include/mbgl/storage/resource.hpp @@ -18,7 +18,8 @@ public: Tile, Glyphs, SpriteImage, - SpriteJSON + SpriteJSON, + Image }; struct TileData { @@ -55,7 +56,8 @@ public: const std::pair<uint16_t, uint16_t>& glyphRange); static Resource spriteImage(const std::string& base, float pixelRatio); static Resource spriteJSON(const std::string& base, float pixelRatio); - + static Resource image(const std::string& url); + Kind kind; Necessity necessity; std::string url; @@ -66,6 +68,7 @@ public: optional<Timestamp> priorModified = {}; optional<Timestamp> priorExpires = {}; optional<std::string> priorEtag = {}; + std::shared_ptr<const std::string> priorData; }; } // namespace mbgl diff --git a/include/mbgl/storage/resource_transform.hpp b/include/mbgl/storage/resource_transform.hpp new file mode 100644 index 0000000000..738c497176 --- /dev/null +++ b/include/mbgl/storage/resource_transform.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include <mbgl/actor/actor_ref.hpp> +#include <mbgl/storage/resource.hpp> + +#include <functional> +#include <string> + +namespace mbgl { + +class Mailbox; + +class ResourceTransform { +public: + using TransformCallback = std::function<std::string(Resource::Kind kind, const std::string&& url)>; + using FinishedCallback = std::function<void(const std::string&&)>; + + ResourceTransform(ActorRef<ResourceTransform>, TransformCallback&&); + + void transform(Resource::Kind, const std::string&& url, FinishedCallback&&); + +private: + TransformCallback transformCallback; +}; + +} // namespace mbgl diff --git a/include/mbgl/storage/response.hpp b/include/mbgl/storage/response.hpp index 448423c1cb..711f008e83 100644 --- a/include/mbgl/storage/response.hpp +++ b/include/mbgl/storage/response.hpp @@ -27,6 +27,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; @@ -35,7 +39,13 @@ public: optional<std::string> etag; bool isFresh() const { - return !expires || *expires > util::now(); + 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()); } }; @@ -53,7 +63,7 @@ public: // An error message from the request handler, e.g. a server message or a system message // informing the user about the reason for the failure. std::string message; - + optional<Timestamp> retryAfter; public: diff --git a/include/mbgl/style/conversion.hpp b/include/mbgl/style/conversion.hpp index e53adcb942..27504a89b1 100644 --- a/include/mbgl/style/conversion.hpp +++ b/include/mbgl/style/conversion.hpp @@ -1,6 +1,6 @@ #pragma once -#include <mbgl/util/variant.hpp> +#include <mbgl/util/optional.hpp> #include <string> @@ -21,11 +21,11 @@ namespace conversion { A single template function serves as the public interface: template <class T, class V> - Result<T> convert(const V& value); + optional<T> convert(const V& value, Error& error); - Where `T` is one of the above types. If the conversion fails, the `Error` variant of `Result` is - returned, which includes diagnostic text suitable for presentation to a library user. Otherwise, - the `T` variant of `Result` is returned. + 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&`: @@ -46,6 +46,7 @@ namespace conversion { * `toBool(v)` -- returns `optional<bool>`, absence indicating `v` is not a JSON boolean * `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, absence indicating `v` is not a boolean, number, or string. Numbers should be converted to @@ -57,37 +58,12 @@ namespace conversion { struct Error { std::string message; }; -template <class T> -class Result : private variant<T, Error> { -public: - using variant<T, Error>::variant; - - explicit operator bool() const { - return this->template is<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 Error& error() const { - assert(this->template is<Error>()); - return this->template get<Error>(); - } -}; - template <class T, class Enable = void> struct Converter; template <class T, class V, class...Args> -Result<T> convert(const V& value, Args&&...args) { - return Converter<T>()(value, std::forward<Args>(args)...); +optional<T> convert(const V& value, Error& error, Args&&...args) { + return Converter<T>()(value, error, std::forward<Args>(args)...); } } // namespace conversion diff --git a/include/mbgl/style/conversion/constant.hpp b/include/mbgl/style/conversion/constant.hpp index 05bf968f4d..07c0a35fae 100644 --- a/include/mbgl/style/conversion/constant.hpp +++ b/include/mbgl/style/conversion/constant.hpp @@ -4,6 +4,7 @@ #include <mbgl/util/optional.hpp> #include <mbgl/util/color.hpp> #include <mbgl/util/enum.hpp> +#include <mbgl/util/string.hpp> #include <array> #include <string> @@ -16,10 +17,11 @@ namespace conversion { template <> struct Converter<bool> { template <class V> - Result<bool> operator()(const V& value) const { + optional<bool> operator()(const V& value, Error& error) const { optional<bool> converted = toBool(value); if (!converted) { - return Error { "value must be a boolean" }; + error = { "value must be a boolean" }; + return {}; } return *converted; } @@ -28,10 +30,11 @@ struct Converter<bool> { template <> struct Converter<float> { template <class V> - Result<float> operator()(const V& value) const { + optional<float> operator()(const V& value, Error& error) const { optional<float> converted = toNumber(value); if (!converted) { - return Error { "value must be a number" }; + error = { "value must be a number" }; + return {}; } return *converted; } @@ -40,10 +43,11 @@ struct Converter<float> { template <> struct Converter<std::string> { template <class V> - Result<std::string> operator()(const V& value) const { + optional<std::string> operator()(const V& value, Error& error) const { optional<std::string> converted = toString(value); if (!converted) { - return Error { "value must be a string" }; + error = { "value must be a string" }; + return {}; } return *converted; } @@ -52,15 +56,17 @@ struct Converter<std::string> { template <class T> struct Converter<T, typename std::enable_if_t<std::is_enum<T>::value>> { template <class V> - Result<T> operator()(const V& value) const { + optional<T> operator()(const V& value, Error& error) const { optional<std::string> string = toString(value); if (!string) { - return Error { "value must be a string" }; + error = { "value must be a string" }; + return {}; } const auto result = Enum<T>::toEnum(*string); if (!result) { - return Error { "value must be a valid enumeration value" }; + error = { "value must be a valid enumeration value" }; + return {}; } return *result; @@ -70,65 +76,52 @@ struct Converter<T, typename std::enable_if_t<std::is_enum<T>::value>> { template <> struct Converter<Color> { template <class V> - Result<Color> operator()(const V& value) const { + optional<Color> operator()(const V& value, Error& error) const { optional<std::string> string = toString(value); if (!string) { - return Error { "value must be a string" }; + error = { "value must be a string" }; + return {}; } optional<Color> color = Color::parse(*string); if (!color) { - return Error { "value must be a valid color" }; + error = { "value must be a valid color" }; + return {}; } return *color; } }; -template <> -struct Converter<std::array<float, 2>> { +template <size_t N> +struct Converter<std::array<float, N>> { template <class V> - Result<std::array<float, 2>> operator()(const V& value) const { - if (!isArray(value) || arrayLength(value) != 2) { - return Error { "value must be an array of two numbers" }; - } - - optional<float> first = toNumber(arrayMember(value, 0)); - optional<float> second = toNumber(arrayMember(value, 1)); - if (!first || !second) { - return Error { "value must be an array of two numbers" }; + optional<std::array<float, N>> operator()(const V& value, Error& error) const { + if (!isArray(value) || arrayLength(value) != N) { + error = { "value must be an array of " + util::toString(N) + " numbers" }; + return {}; } - return std::array<float, 2> {{ *first, *second }}; - } -}; - -template <> -struct Converter<std::array<float, 4>> { - template <class V> - Result<std::array<float, 4>> operator()(const V& value) const { - if (!isArray(value) || arrayLength(value) != 4) { - return Error { "value must be an array of four numbers" }; - } - - optional<float> first = toNumber(arrayMember(value, 0)); - optional<float> second = toNumber(arrayMember(value, 1)); - optional<float> third = toNumber(arrayMember(value, 2)); - optional<float> fourth = toNumber(arrayMember(value, 3)); - if (!first || !second) { - return Error { "value must be an array of four numbers" }; + std::array<float, N> result; + for (size_t i = 0; i < N; i++) { + optional<float> n = toNumber(arrayMember(value, i)); + if (!n) { + error = { "value must be an array of " + util::toString(N) + " numbers" }; + return {}; + } + result[i] = *n; } - - return std::array<float, 4> {{ *first, *second, *third, *fourth }}; + return result; } }; template <> struct Converter<std::vector<float>> { template <class V> - Result<std::vector<float>> operator()(const V& value) const { + optional<std::vector<float>> operator()(const V& value, Error& error) const { if (!isArray(value)) { - return Error { "value must be an array" }; + error = { "value must be an array" }; + return {}; } std::vector<float> result; @@ -137,7 +130,8 @@ struct Converter<std::vector<float>> { for (std::size_t i = 0; i < arrayLength(value); ++i) { optional<float> number = toNumber(arrayMember(value, i)); if (!number) { - return Error { "value must be an array of numbers" }; + error = { "value must be an array of numbers" }; + return {}; } result.push_back(*number); } @@ -149,9 +143,10 @@ struct Converter<std::vector<float>> { template <> struct Converter<std::vector<std::string>> { template <class V> - Result<std::vector<std::string>> operator()(const V& value) const { + optional<std::vector<std::string>> operator()(const V& value, Error& error) const { if (!isArray(value)) { - return Error { "value must be an array" }; + error = { "value must be an array" }; + return {}; } std::vector<std::string> result; @@ -160,7 +155,8 @@ struct Converter<std::vector<std::string>> { for (std::size_t i = 0; i < arrayLength(value); ++i) { optional<std::string> string = toString(arrayMember(value, i)); if (!string) { - return Error { "value must be an array of strings" }; + error = { "value must be an array of strings" }; + return {}; } result.push_back(*string); } diff --git a/include/mbgl/style/conversion/coordinate.hpp b/include/mbgl/style/conversion/coordinate.hpp new file mode 100644 index 0000000000..732624e77f --- /dev/null +++ b/include/mbgl/style/conversion/coordinate.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include <mbgl/style/conversion.hpp> +#include <mbgl/util/geo.hpp> + +namespace mbgl { +namespace style { +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); + } +}; + +} // 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 new file mode 100644 index 0000000000..79b15dcfb0 --- /dev/null +++ b/include/mbgl/style/conversion/data_driven_property_value.hpp @@ -0,0 +1,46 @@ +#pragma once + +#include <mbgl/style/data_driven_property_value.hpp> +#include <mbgl/style/conversion.hpp> +#include <mbgl/style/conversion/constant.hpp> +#include <mbgl/style/conversion/function.hpp> + +namespace mbgl { +namespace style { +namespace conversion { + +template <class T> +struct Converter<DataDrivenPropertyValue<T>> { + template <class V> + optional<DataDrivenPropertyValue<T>> operator()(const V& value, Error& error) const { + if (isUndefined(value)) { + return DataDrivenPropertyValue<T>(); + } else if (!isObject(value)) { + optional<T> constant = convert<T>(value, error); + if (!constant) { + return {}; + } + return DataDrivenPropertyValue<T>(*constant); + } else if (!objectMember(value, "property")) { + optional<CameraFunction<T>> function = convert<CameraFunction<T>>(value, error); + if (!function) { + return {}; + } + return DataDrivenPropertyValue<T>(*function); + } else { + optional<CompositeFunction<T>> composite = convert<CompositeFunction<T>>(value, error); + if (composite) { + return DataDrivenPropertyValue<T>(*composite); + } + optional<SourceFunction<T>> source = convert<SourceFunction<T>>(value, error); + if (!source) { + return {}; + } + return DataDrivenPropertyValue<T>(*source); + } + } +}; + +} // namespace conversion +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/style/conversion/filter.hpp b/include/mbgl/style/conversion/filter.hpp index 3ab91e5bbc..986d1bf80d 100644 --- a/include/mbgl/style/conversion/filter.hpp +++ b/include/mbgl/style/conversion/filter.hpp @@ -12,136 +12,245 @@ template <> struct Converter<Filter> { public: template <class V> - Result<Filter> operator()(const V& value) const { + optional<Filter> operator()(const V& value, Error& error) const { if (!isArray(value)) { - return Error { "filter expression must be an array" }; + error = { "filter expression must be an array" }; + return {}; } if (arrayLength(value) < 1) { - return Error { "filter expression must have at least 1 element" }; + error = { "filter expression must have at least 1 element" }; + return {}; } optional<std::string> op = toString(arrayMember(value, 0)); if (!op) { - return Error { "filter operator must be a string" }; + error = { "filter operator must be a string" }; + return {}; } if (*op == "==") { - return convertBinaryFilter<EqualsFilter>(value); + return convertEqualityFilter<EqualsFilter, TypeEqualsFilter, IdentifierEqualsFilter>(value, error); } else if (*op == "!=") { - return convertBinaryFilter<NotEqualsFilter>(value); + return convertEqualityFilter<NotEqualsFilter, TypeNotEqualsFilter, IdentifierNotEqualsFilter>(value, error); } else if (*op == ">") { - return convertBinaryFilter<GreaterThanFilter>(value); + return convertBinaryFilter<GreaterThanFilter>(value, error); } else if (*op == ">=") { - return convertBinaryFilter<GreaterThanEqualsFilter>(value); + return convertBinaryFilter<GreaterThanEqualsFilter>(value, error); } else if (*op == "<") { - return convertBinaryFilter<LessThanFilter>(value); + return convertBinaryFilter<LessThanFilter>(value, error); } else if (*op == "<=") { - return convertBinaryFilter<LessThanEqualsFilter>(value); + return convertBinaryFilter<LessThanEqualsFilter>(value, error); } else if (*op == "in") { - return convertSetFilter<InFilter>(value); + return convertSetFilter<InFilter, TypeInFilter, IdentifierInFilter>(value, error); } else if (*op == "!in") { - return convertSetFilter<NotInFilter>(value); + return convertSetFilter<NotInFilter, TypeNotInFilter, IdentifierNotInFilter>(value, error); } else if (*op == "all") { - return convertCompoundFilter<AllFilter>(value); + return convertCompoundFilter<AllFilter>(value, error); } else if (*op == "any") { - return convertCompoundFilter<AnyFilter>(value); + return convertCompoundFilter<AnyFilter>(value, error); } else if (*op == "none") { - return convertCompoundFilter<NoneFilter>(value); + return convertCompoundFilter<NoneFilter>(value, error); } else if (*op == "has") { - return convertUnaryFilter<HasFilter>(value); + return convertUnaryFilter<HasFilter, HasIdentifierFilter>(value, error); } else if (*op == "!has") { - return convertUnaryFilter<NotHasFilter>(value); + return convertUnaryFilter<NotHasFilter, NotHasIdentifierFilter>(value, error); } - return Error { "filter operator must be one of \"==\", \"!=\", \">\", \">=\", \"<\", \"<=\", \"in\", \"!in\", \"all\", \"any\", \"none\", \"has\", or \"!has\"" }; + error = { R"(filter operator must be one of "==", "!=", ">", ">=", "<", "<=", "in", "!in", "all", "any", "none", "has", or "!has")" }; + return {}; } private: - Result<Value> normalizeValue(const std::string& key, const optional<Value>& value) const { + optional<Value> normalizeValue(const optional<Value>& value, Error& error) const { if (!value) { - return Error { "filter expression value must be a boolean, number, or string" }; - } else if (key != "$type") { + error = { "filter expression value must be a boolean, number, or string" }; + return {}; + } else { return *value; - } else if (*value == std::string("Point")) { - return Value(uint64_t(FeatureType::Point)); - } else if (*value == std::string("LineString")) { - return Value(uint64_t(FeatureType::LineString)); - } else if (*value == std::string("Polygon")) { - return Value(uint64_t(FeatureType::Polygon)); + } + } + + 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 { - return Error { "value for $type filter must be Point, LineString, or Polygon" }; + error = { "value for $type filter must be Point, LineString, or Polygon" }; + return {}; } } - template <class FilterType, class V> - Result<Filter> convertUnaryFilter(const V& value) const { + 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) { - return Error { "filter expression must have 2 elements" }; + error = { "filter expression must have 2 elements" }; + return {}; } optional<std::string> key = toString(arrayMember(value, 1)); if (!key) { - return Error { "filter expression key must be a string" }; + error = { "filter expression key must be a string" }; + return {}; } - return FilterType { *key }; + 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> - Result<Filter> convertBinaryFilter(const V& value) const { + optional<Filter> convertBinaryFilter(const V& value, Error& error) const { if (arrayLength(value) < 3) { - return Error { "filter expression must have 3 elements" }; + error = { "filter expression must have 3 elements" }; + return {}; } optional<std::string> key = toString(arrayMember(value, 1)); if (!key) { - return Error { "filter expression key must be a string" }; + error = { "filter expression key must be a string" }; + return {}; } - Result<Value> filterValue = normalizeValue(*key, toValue(arrayMember(value, 2))); + optional<Value> filterValue = normalizeValue(toValue(arrayMember(value, 2)), error); if (!filterValue) { - return filterValue.error(); + return {}; } - return FilterType { *key, *filterValue }; + return { FilterType { *key, *filterValue } }; } - template <class FilterType, class V> - Result<Filter> convertSetFilter(const V& value) const { + template <class FilterType, class TypeFilterType, class IdentifierFilterType, class V> + optional<Filter> convertSetFilter(const V& value, Error& error) const { if (arrayLength(value) < 2) { - return Error { "filter expression must at least 2 elements" }; + error = { "filter expression must at least 2 elements" }; + return {}; } optional<std::string> key = toString(arrayMember(value, 1)); if (!key) { - return Error { "filter expression key must be a string" }; + error = { "filter expression key must be a string" }; + return {}; } - std::vector<Value> values; - for (std::size_t i = 2; i < arrayLength(value); ++i) { - Result<Value> filterValue = normalizeValue(*key, toValue(arrayMember(value, i))); - if (!filterValue) { - return filterValue.error(); + 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); } - values.push_back(*filterValue); - } - return FilterType { *key, std::move(values) }; + 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> - Result<Filter> convertCompoundFilter(const V& value) const { + optional<Filter> convertCompoundFilter(const V& value, Error& error) const { std::vector<Filter> filters; for (std::size_t i = 1; i < arrayLength(value); ++i) { - Result<Filter> element = operator()(arrayMember(value, i)); + optional<Filter> element = operator()(arrayMember(value, i), error); if (!element) { - return element.error(); + return {}; } filters.push_back(*element); } - return FilterType { std::move(filters) }; + return { FilterType { std::move(filters) } }; } }; diff --git a/include/mbgl/style/conversion/function.hpp b/include/mbgl/style/conversion/function.hpp index 6a0b67618f..752b6dd045 100644 --- a/include/mbgl/style/conversion/function.hpp +++ b/include/mbgl/style/conversion/function.hpp @@ -1,70 +1,403 @@ #pragma once -#include <mbgl/style/function.hpp> +#include <mbgl/style/function/camera_function.hpp> +#include <mbgl/style/function/source_function.hpp> +#include <mbgl/style/function/composite_function.hpp> #include <mbgl/style/conversion.hpp> #include <mbgl/style/conversion/constant.hpp> +#include <mbgl/util/ignore.hpp> namespace mbgl { namespace style { namespace conversion { +template <class D, class R, class V> +optional<std::map<D, R>> convertStops(const V& value, Error& error) { + auto stopsValue = objectMember(value, "stops"); + if (!stopsValue) { + error = { "function value must specify stops" }; + return {}; + } + + if (!isArray(*stopsValue)) { + error = { "function stops must be an array" }; + return {}; + } + + if (arrayLength(*stopsValue) == 0) { + error = { "function must have at least one stop" }; + return {}; + } + + std::map<D, R> stops; + for (std::size_t i = 0; i < arrayLength(*stopsValue); ++i) { + const auto& stopValue = arrayMember(*stopsValue, i); + + if (!isArray(stopValue)) { + error = { "function stop must be an array" }; + return {}; + } + + if (arrayLength(stopValue) != 2) { + error = { "function stop must have two elements" }; + return {}; + } + + optional<D> d = convert<D>(arrayMember(stopValue, 0), error); + if (!d) { + return {}; + } + + optional<R> r = convert<R>(arrayMember(stopValue, 1), error); + if (!r) { + return {}; + } + + stops.emplace(*d, *r); + } + + return stops; +} + template <class T> -struct Converter<Function<T>> { +struct Converter<ExponentialStops<T>> { + static constexpr const char * type = "exponential"; + template <class V> - Result<Function<T>> operator()(const V& value) const { - if (!isObject(value)) { - return Error { "function must be an object" }; + optional<ExponentialStops<T>> operator()(const V& value, Error& error) const { + auto stops = convertStops<float, T>(value, error); + if (!stops) { + return {}; } - auto stopsValue = objectMember(value, "stops"); - if (!stopsValue) { - return Error { "function value must specify stops" }; + auto baseValue = objectMember(value, "base"); + if (!baseValue) { + return ExponentialStops<T>(*stops); } - if (!isArray(*stopsValue)) { - return Error { "function stops must be an array" }; + optional<float> base = toNumber(*baseValue); + if (!base) { + error = { "function base must be a number"}; + return {}; } - if (arrayLength(*stopsValue) == 0) { - return Error { "function must have at least one stop" }; + return ExponentialStops<T>(*stops, *base); + } +}; + +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 { + auto stops = convertStops<float, T>(value, error); + if (!stops) { + return {}; } + return IntervalStops<T>(*stops); + } +}; - std::vector<std::pair<float, T>> stops; - for (std::size_t i = 0; i < arrayLength(*stopsValue); ++i) { - const auto& stopValue = arrayMember(*stopsValue, i); +template <> +struct Converter<CategoricalValue> { + template <class V> + optional<CategoricalValue> operator()(const V& value, Error& error) const { + auto b = toBool(value); + if (b) { + return { *b }; + } - if (!isArray(stopValue)) { - return Error { "function stop must be an array" }; - } + auto n = toNumber(value); + if (n) { + return { int64_t(*n) }; + } - if (arrayLength(stopValue) != 2) { - return Error { "function stop must have two elements" }; - } + auto s = toString(value); + if (s) { + return { *s }; + } - optional<float> z = toNumber(arrayMember(stopValue, 0)); - if (!z) { - return Error { "function stop zoom level must be a number" }; - } + error = { "stop domain value must be a number, string, or boolean" }; + return {}; + } +}; + +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 { + auto stops = convertStops<CategoricalValue, T>(value, error); + if (!stops) { + return {}; + } + return CategoricalStops<T>( + std::map<CategoricalValue, T>((*stops).begin(), (*stops).end())); + } +}; + +template <class T> +struct Converter<IdentityStops<T>> { + static constexpr const char * type = "identity"; + + template <class V> + optional<IdentityStops<T>> operator()(const V&, Error&) const { + return IdentityStops<T>(); + } +}; + +template <class, class> +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>::value ? "exponential" : "interval"; + + auto typeValue = objectMember(value, "type"); + if (typeValue && toString(*typeValue)) { + type = *toString(*typeValue); + } - Result<T> v = convert<T>(arrayMember(stopValue, 1)); - if (!v) { - return v.error(); + bool matched = false; + optional<variant<Ts...>> result; + + // Workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47226 + auto tryConvert = [&] (auto* tp) { + using Stops = std::decay_t<decltype(*tp)>; + if (type == Converter<Stops>::type) { + matched = true; + optional<Stops> stops = convert<Stops>(value, error); + if (stops) { + result = variant<Ts...>(*stops); + } } + }; + + util::ignore({ + (tryConvert((Ts*)nullptr), 0)... + }); + + if (!matched) { + error = { "unsupported function type" }; + return {}; + } + + return result; + } +}; + +template <class T> +struct Converter<CameraFunction<T>> { + template <class V> + optional<CameraFunction<T>> operator()(const V& value, Error& error) const { + if (!isObject(value)) { + error = { "function must be an object" }; + return {}; + } + + auto stops = StopsConverter<T, typename CameraFunction<T>::Stops>()(value, error); + if (!stops) { + return {}; + } + + return CameraFunction<T>(*stops); + } +}; + +template <class T, class V> +optional<optional<T>> convertDefaultValue(const V& value, Error& error) { + auto defaultValueValue = objectMember(value, "default"); + if (!defaultValueValue) { + return optional<T>(); + } - stops.emplace_back(*z, *v); + auto defaultValue = convert<T>(*defaultValueValue, error); + if (!defaultValue) { + error = { R"(wrong type for "default": )" + error.message }; + return {}; + } + + return { *defaultValue }; +} + +template <class T> +struct Converter<SourceFunction<T>> { + template <class V> + optional<SourceFunction<T>> operator()(const V& value, Error& error) const { + if (!isObject(value)) { + error = { "function must be an object" }; + return {}; + } + + auto propertyValue = objectMember(value, "property"); + if (!propertyValue) { + error = { "function must specify property" }; + return {}; + } + + auto propertyString = toString(*propertyValue); + if (!propertyString) { + error = { "function property must be a string" }; + return {}; + } + + auto stops = StopsConverter<T, typename SourceFunction<T>::Stops>()(value, error); + if (!stops) { + return {}; + } + + auto defaultValue = convertDefaultValue<T>(value, error); + if (!defaultValue) { + return {}; + } + + return SourceFunction<T>(*propertyString, *stops, *defaultValue); + } +}; + +template <class S> +struct CompositeValue : std::pair<float, S> { + using std::pair<float, S>::pair; +}; + +template <class S> +struct Converter<CompositeValue<S>> { + template <class V> + optional<CompositeValue<S>> operator()(const V& value, Error& error) const { + if (!isObject(value)) { + error = { "stop must be an object" }; + return {}; + } + + auto zoomValue = objectMember(value, "zoom"); + if (!zoomValue) { + error = { "stop must specify zoom" }; + return {}; + } + + auto propertyValue = objectMember(value, "value"); + if (!propertyValue) { + error = { "stop must specify value" }; + return {}; + } + + optional<float> z = convert<float>(*zoomValue, error); + if (!z) { + return {}; + } + + optional<S> s = convert<S>(*propertyValue, error); + if (!s) { + return {}; + } + + return CompositeValue<S> { *z, *s }; + } +}; + +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 { + auto stops = convertStops<CompositeValue<float>, T>(value, error); + if (!stops) { + return {}; } + auto base = 1.0f; auto baseValue = objectMember(value, "base"); - if (!baseValue) { - return Function<T>(stops, 1.0f); + if (baseValue && toNumber(*baseValue)) { + base = *toNumber(*baseValue); } - optional<float> base = toNumber(*baseValue); - if (!base) { - return Error { "function base must be a number"}; + std::map<float, std::map<float, T>> convertedStops; + for (const auto& stop : *stops) { + convertedStops[stop.first.first].emplace(stop.first.second, stop.second); + } + + return CompositeExponentialStops<T>(convertedStops, base); + } +}; + +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 { + auto stops = convertStops<CompositeValue<float>, T>(value, error); + if (!stops) { + return {}; + } + + std::map<float, std::map<float, T>> convertedStops; + for (const auto& stop : *stops) { + convertedStops[stop.first.first].emplace(stop.first.second, stop.second); + } + + return CompositeIntervalStops<T>(convertedStops); + } +}; + +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 { + auto stops = convertStops<CompositeValue<CategoricalValue>, T>(value, error); + if (!stops) { + return {}; + } + + std::map<float, std::map<CategoricalValue, T>> convertedStops; + for (const auto& stop : *stops) { + convertedStops[stop.first.first].emplace(stop.first.second, stop.second); + } + + return CompositeCategoricalStops<T>(convertedStops); + } +}; + +template <class T> +struct Converter<CompositeFunction<T>> { + template <class V> + optional<CompositeFunction<T>> operator()(const V& value, Error& error) const { + if (!isObject(value)) { + error = { "function must be an object" }; + return {}; + } + + auto propertyValue = objectMember(value, "property"); + if (!propertyValue) { + error = { "function must specify property" }; + return {}; + } + + auto propertyString = toString(*propertyValue); + if (!propertyString) { + error = { "function property must be a string" }; + return {}; + } + + auto stops = StopsConverter<T, typename CompositeFunction<T>::Stops>()(value, error); + if (!stops) { + return {}; + } + + auto defaultValue = convertDefaultValue<T>(value, error); + if (!defaultValue) { + return {}; } - return Function<T>(stops, *base); + return CompositeFunction<T>(*propertyString, *stops, *defaultValue); } }; diff --git a/include/mbgl/style/conversion/geojson.hpp b/include/mbgl/style/conversion/geojson.hpp index ba10b3ecc8..0b594f066c 100644 --- a/include/mbgl/style/conversion/geojson.hpp +++ b/include/mbgl/style/conversion/geojson.hpp @@ -1,14 +1,22 @@ #pragma once #include <mbgl/style/conversion.hpp> -#include <mbgl/style/sources/geojson_source.hpp> +#include <mbgl/util/geojson.hpp> namespace mbgl { namespace style { namespace conversion { -template <class V> -Result<GeoJSON> convertGeoJSON(const V& value); +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; +}; } // namespace conversion } // namespace style diff --git a/include/mbgl/style/conversion/geojson_options.hpp b/include/mbgl/style/conversion/geojson_options.hpp index 880090b402..19383d90ce 100644 --- a/include/mbgl/style/conversion/geojson_options.hpp +++ b/include/mbgl/style/conversion/geojson_options.hpp @@ -11,7 +11,7 @@ template <> struct Converter<GeoJSONOptions> { template <class V> - Result<GeoJSONOptions> operator()(const V& value) const { + optional<GeoJSONOptions> operator()(const V& value, Error& error) const { GeoJSONOptions options; const auto maxzoomValue = objectMember(value, "maxzoom"); @@ -19,7 +19,8 @@ struct Converter<GeoJSONOptions> { if (toNumber(*maxzoomValue)) { options.maxzoom = static_cast<uint8_t>(*toNumber(*maxzoomValue)); } else { - return Error{ "GeoJSON source maxzoom value must be a number" }; + error = { "GeoJSON source maxzoom value must be a number" }; + return {}; } } @@ -28,7 +29,8 @@ struct Converter<GeoJSONOptions> { if (toNumber(*bufferValue)) { options.buffer = static_cast<uint16_t>(*toNumber(*bufferValue)); } else { - return Error{ "GeoJSON source buffer value must be a number" }; + error = { "GeoJSON source buffer value must be a number" }; + return {}; } } @@ -37,7 +39,8 @@ struct Converter<GeoJSONOptions> { if (toNumber(*toleranceValue)) { options.tolerance = static_cast<double>(*toNumber(*toleranceValue)); } else { - return Error{ "GeoJSON source tolerance value must be a number" }; + error = { "GeoJSON source tolerance value must be a number" }; + return {}; } } @@ -46,7 +49,8 @@ struct Converter<GeoJSONOptions> { if (toBool(*clusterValue)) { options.cluster = *toBool(*clusterValue); } else { - return Error{ "GeoJSON source cluster value must be a boolean" }; + error = { "GeoJSON source cluster value must be a boolean" }; + return {}; } } @@ -55,7 +59,8 @@ struct Converter<GeoJSONOptions> { if (toNumber(*clusterMaxZoomValue)) { options.clusterMaxZoom = static_cast<uint8_t>(*toNumber(*clusterMaxZoomValue)); } else { - return Error{ "GeoJSON source clusterMaxZoom value must be a number" }; + error = { "GeoJSON source clusterMaxZoom value must be a number" }; + return {}; } } @@ -64,7 +69,8 @@ struct Converter<GeoJSONOptions> { if (toNumber(*clusterRadiusValue)) { options.clusterRadius = static_cast<double>(*toNumber(*clusterRadiusValue)); } else { - return Error{ "GeoJSON source clusterRadius value must be a number" }; + error = { "GeoJSON source clusterRadius value must be a number" }; + return {}; } } diff --git a/include/mbgl/style/conversion/layer.hpp b/include/mbgl/style/conversion/layer.hpp index 0539dcf9ad..1fe467165d 100644 --- a/include/mbgl/style/conversion/layer.hpp +++ b/include/mbgl/style/conversion/layer.hpp @@ -4,6 +4,7 @@ #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> @@ -27,30 +28,23 @@ optional<Error> setLayoutProperty(Layer& layer, const std::string& name, const V } template <class V> -optional<Error> setPaintProperty(Layer& layer, const std::string& name, const V& value, const optional<std::string>& klass) { +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, klass); + return it->second(layer, value); } template <class V> optional<Error> setPaintProperties(Layer& layer, const V& value) { - return eachMember(value, [&] (const std::string& paintName, const V& paintValue) -> optional<Error> { - if (paintName.compare(0, 5, "paint") != 0) { - return {}; - } - - optional<std::string> klass; - if (paintName.compare(0, 6, "paint.") == 0) { - klass = paintName.substr(6); - } - - return eachMember(paintValue, [&] (const std::string& k, const V& v) { - return setPaintProperty(layer, k, v, klass); - }); + auto paintValue = objectMember(value, "paint"); + if (!paintValue) { + return {}; + } + return eachMember(*paintValue, [&] (const std::string& k, const V& v) { + return setPaintProperty(layer, k, v); }); } @@ -58,47 +52,55 @@ template <> struct Converter<std::unique_ptr<Layer>> { public: template <class V> - Result<std::unique_ptr<Layer>> operator()(const V& value) const { + optional<std::unique_ptr<Layer>> operator()(const V& value, Error& error) const { if (!isObject(value)) { - return Error { "layer must be an object" }; + error = { "layer must be an object" }; + return {}; } auto idValue = objectMember(value, "id"); if (!idValue) { - return Error { "layer must have an id" }; + error = { "layer must have an id" }; + return {}; } optional<std::string> id = toString(*idValue); if (!id) { - return Error { "layer id must be a string" }; + error = { "layer id must be a string" }; + return {}; } auto typeValue = objectMember(value, "type"); if (!typeValue) { - return Error { "layer must have a type" }; + error = { "layer must have a type" }; + return {}; } optional<std::string> type = toString(*typeValue); if (!type) { - return Error { "layer type must be a string" }; + error = { "layer type must be a string" }; + return {}; } - Result<std::unique_ptr<Layer>> converted; + optional<std::unique_ptr<Layer>> converted; if (*type == "fill") { - converted = convertVectorLayer<FillLayer>(*id, value); + 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); + converted = convertVectorLayer<LineLayer>(*id, value, error); } else if (*type == "circle") { - converted = convertVectorLayer<CircleLayer>(*id, value); + converted = convertVectorLayer<CircleLayer>(*id, value, error); } else if (*type == "symbol") { - converted = convertVectorLayer<SymbolLayer>(*id, value); + converted = convertVectorLayer<SymbolLayer>(*id, value, error); } else if (*type == "raster") { - converted = convertRasterLayer(*id, value); + converted = convertRasterLayer(*id, value, error); } else if (*type == "background") { - converted = convertBackgroundLayer(*id, value); + converted = convertBackgroundLayer(*id, value, error); } else { - return Error { "invalid layer type" }; + error = { "invalid layer type" }; + return {}; } if (!converted) { @@ -111,7 +113,8 @@ public: if (minzoomValue) { optional<float> minzoom = toNumber(*minzoomValue); if (!minzoom) { - return Error { "minzoom must be numeric" }; + error = { "minzoom must be numeric" }; + return {}; } layer->setMinZoom(*minzoom); } @@ -120,7 +123,8 @@ public: if (maxzoomValue) { optional<float> maxzoom = toNumber(*maxzoomValue); if (!maxzoom) { - return Error { "maxzoom must be numeric" }; + error = { "maxzoom must be numeric" }; + return {}; } layer->setMaxZoom(*maxzoom); } @@ -128,19 +132,22 @@ public: auto layoutValue = objectMember(value, "layout"); if (layoutValue) { if (!isObject(*layoutValue)) { - return Error { "layout must be an object" }; + error = { "layout must be an object" }; + return {}; } - optional<Error> error = eachMember(*layoutValue, [&] (const std::string& k, const V& v) { + optional<Error> error_ = eachMember(*layoutValue, [&] (const std::string& k, const V& v) { return setLayoutProperty(*layer, k, v); }); - if (error) { - return *error; + if (error_) { + error = *error_; + return {}; } } - optional<Error> error = setPaintProperties(*layer, value); - if (error) { - return *error; + optional<Error> error_ = setPaintProperties(*layer, value); + if (error_) { + error = *error_; + return {}; } return std::move(layer); @@ -148,15 +155,17 @@ public: private: template <class LayerType, class V> - Result<std::unique_ptr<Layer>> convertVectorLayer(const std::string& id, const V& value) const { + optional<std::unique_ptr<Layer>> convertVectorLayer(const std::string& id, const V& value, Error& error) const { auto sourceValue = objectMember(value, "source"); if (!sourceValue) { - return Error { "layer must have a source" }; + error = { "layer must have a source" }; + return {}; } optional<std::string> source = toString(*sourceValue); if (!source) { - return Error { "layer source must be a string" }; + error = { "layer source must be a string" }; + return {}; } std::unique_ptr<LayerType> layer = std::make_unique<LayerType>(id, *source); @@ -165,41 +174,44 @@ private: if (sourceLayerValue) { optional<std::string> sourceLayer = toString(*sourceLayerValue); if (!sourceLayer) { - return Error { "layer source-layer must be a string" }; + error = { "layer source-layer must be a string" }; + return {}; } layer->setSourceLayer(*sourceLayer); } auto filterValue = objectMember(value, "filter"); if (filterValue) { - Result<Filter> filter = convert<Filter>(*filterValue); + optional<Filter> filter = convert<Filter>(*filterValue, error); if (!filter) { - return filter.error(); + return {}; } layer->setFilter(*filter); } - return std::move(layer); + return { std::move(layer) }; } template <class V> - Result<std::unique_ptr<Layer>> convertRasterLayer(const std::string& id, const V& value) const { + optional<std::unique_ptr<Layer>> convertRasterLayer(const std::string& id, const V& value, Error& error) const { auto sourceValue = objectMember(value, "source"); if (!sourceValue) { - return Error { "layer must have a source" }; + error = { "layer must have a source" }; + return {}; } optional<std::string> source = toString(*sourceValue); if (!source) { - return Error { "layer source must be a string" }; + error = { "layer source must be a string" }; + return {}; } - return std::make_unique<RasterLayer>(id, *source); + return { std::make_unique<RasterLayer>(id, *source) }; } template <class V> - Result<std::unique_ptr<Layer>> convertBackgroundLayer(const std::string& id, const V&) const { - return std::make_unique<BackgroundLayer>(id); + optional<std::unique_ptr<Layer>> convertBackgroundLayer(const std::string& id, const V&, Error&) const { + return { std::make_unique<BackgroundLayer>(id) }; } }; diff --git a/include/mbgl/style/conversion/light.hpp b/include/mbgl/style/conversion/light.hpp new file mode 100644 index 0000000000..ba162516c0 --- /dev/null +++ b/include/mbgl/style/conversion/light.hpp @@ -0,0 +1,122 @@ +#pragma once + +#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 { +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) }; + }; +}; + +} // namespace conversion +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/style/conversion/make_property_setters.hpp b/include/mbgl/style/conversion/make_property_setters.hpp index e30359937e..9252297d75 100644 --- a/include/mbgl/style/conversion/make_property_setters.hpp +++ b/include/mbgl/style/conversion/make_property_setters.hpp @@ -20,50 +20,51 @@ namespace conversion { template <class V> auto makeLayoutPropertySetters() { - std::unordered_map<std::string, LayoutPropertySetter<V>> result; + std::unordered_map<std::string, PropertySetter<V>> result; result["visibility"] = &setVisibility<V>; - result["line-cap"] = makePropertySetter<V>(&LineLayer::setLineCap); - result["line-join"] = makePropertySetter<V>(&LineLayer::setLineJoin); - result["line-miter-limit"] = makePropertySetter<V>(&LineLayer::setLineMiterLimit); - result["line-round-limit"] = makePropertySetter<V>(&LineLayer::setLineRoundLimit); - - result["symbol-placement"] = makePropertySetter<V>(&SymbolLayer::setSymbolPlacement); - result["symbol-spacing"] = makePropertySetter<V>(&SymbolLayer::setSymbolSpacing); - result["symbol-avoid-edges"] = makePropertySetter<V>(&SymbolLayer::setSymbolAvoidEdges); - result["icon-allow-overlap"] = makePropertySetter<V>(&SymbolLayer::setIconAllowOverlap); - result["icon-ignore-placement"] = makePropertySetter<V>(&SymbolLayer::setIconIgnorePlacement); - result["icon-optional"] = makePropertySetter<V>(&SymbolLayer::setIconOptional); - result["icon-rotation-alignment"] = makePropertySetter<V>(&SymbolLayer::setIconRotationAlignment); - result["icon-size"] = makePropertySetter<V>(&SymbolLayer::setIconSize); - result["icon-text-fit"] = makePropertySetter<V>(&SymbolLayer::setIconTextFit); - result["icon-text-fit-padding"] = makePropertySetter<V>(&SymbolLayer::setIconTextFitPadding); - result["icon-image"] = makePropertySetter<V>(&SymbolLayer::setIconImage); - result["icon-rotate"] = makePropertySetter<V>(&SymbolLayer::setIconRotate); - result["icon-padding"] = makePropertySetter<V>(&SymbolLayer::setIconPadding); - result["icon-keep-upright"] = makePropertySetter<V>(&SymbolLayer::setIconKeepUpright); - result["icon-offset"] = makePropertySetter<V>(&SymbolLayer::setIconOffset); - result["text-pitch-alignment"] = makePropertySetter<V>(&SymbolLayer::setTextPitchAlignment); - result["text-rotation-alignment"] = makePropertySetter<V>(&SymbolLayer::setTextRotationAlignment); - result["text-field"] = makePropertySetter<V>(&SymbolLayer::setTextField); - result["text-font"] = makePropertySetter<V>(&SymbolLayer::setTextFont); - result["text-size"] = makePropertySetter<V>(&SymbolLayer::setTextSize); - result["text-max-width"] = makePropertySetter<V>(&SymbolLayer::setTextMaxWidth); - result["text-line-height"] = makePropertySetter<V>(&SymbolLayer::setTextLineHeight); - result["text-letter-spacing"] = makePropertySetter<V>(&SymbolLayer::setTextLetterSpacing); - result["text-justify"] = makePropertySetter<V>(&SymbolLayer::setTextJustify); - result["text-anchor"] = makePropertySetter<V>(&SymbolLayer::setTextAnchor); - result["text-max-angle"] = makePropertySetter<V>(&SymbolLayer::setTextMaxAngle); - result["text-rotate"] = makePropertySetter<V>(&SymbolLayer::setTextRotate); - result["text-padding"] = makePropertySetter<V>(&SymbolLayer::setTextPadding); - result["text-keep-upright"] = makePropertySetter<V>(&SymbolLayer::setTextKeepUpright); - result["text-transform"] = makePropertySetter<V>(&SymbolLayer::setTextTransform); - result["text-offset"] = makePropertySetter<V>(&SymbolLayer::setTextOffset); - result["text-allow-overlap"] = makePropertySetter<V>(&SymbolLayer::setTextAllowOverlap); - result["text-ignore-placement"] = makePropertySetter<V>(&SymbolLayer::setTextIgnorePlacement); - result["text-optional"] = makePropertySetter<V>(&SymbolLayer::setTextOptional); + result["line-cap"] = &setProperty<V, LineLayer, PropertyValue<LineCapType>, &LineLayer::setLineCap>; + result["line-join"] = &setProperty<V, LineLayer, DataDrivenPropertyValue<LineJoinType>, &LineLayer::setLineJoin>; + result["line-miter-limit"] = &setProperty<V, LineLayer, PropertyValue<float>, &LineLayer::setLineMiterLimit>; + result["line-round-limit"] = &setProperty<V, LineLayer, PropertyValue<float>, &LineLayer::setLineRoundLimit>; + + result["symbol-placement"] = &setProperty<V, SymbolLayer, PropertyValue<SymbolPlacementType>, &SymbolLayer::setSymbolPlacement>; + result["symbol-spacing"] = &setProperty<V, SymbolLayer, PropertyValue<float>, &SymbolLayer::setSymbolSpacing>; + result["symbol-avoid-edges"] = &setProperty<V, SymbolLayer, PropertyValue<bool>, &SymbolLayer::setSymbolAvoidEdges>; + result["icon-allow-overlap"] = &setProperty<V, SymbolLayer, PropertyValue<bool>, &SymbolLayer::setIconAllowOverlap>; + result["icon-ignore-placement"] = &setProperty<V, SymbolLayer, PropertyValue<bool>, &SymbolLayer::setIconIgnorePlacement>; + result["icon-optional"] = &setProperty<V, SymbolLayer, PropertyValue<bool>, &SymbolLayer::setIconOptional>; + result["icon-rotation-alignment"] = &setProperty<V, SymbolLayer, PropertyValue<AlignmentType>, &SymbolLayer::setIconRotationAlignment>; + result["icon-size"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setIconSize>; + result["icon-text-fit"] = &setProperty<V, SymbolLayer, PropertyValue<IconTextFitType>, &SymbolLayer::setIconTextFit>; + result["icon-text-fit-padding"] = &setProperty<V, SymbolLayer, PropertyValue<std::array<float, 4>>, &SymbolLayer::setIconTextFitPadding>; + result["icon-image"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<std::string>, &SymbolLayer::setIconImage>; + result["icon-rotate"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setIconRotate>; + result["icon-padding"] = &setProperty<V, SymbolLayer, PropertyValue<float>, &SymbolLayer::setIconPadding>; + result["icon-keep-upright"] = &setProperty<V, SymbolLayer, PropertyValue<bool>, &SymbolLayer::setIconKeepUpright>; + result["icon-offset"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<std::array<float, 2>>, &SymbolLayer::setIconOffset>; + result["icon-pitch-alignment"] = &setProperty<V, SymbolLayer, PropertyValue<AlignmentType>, &SymbolLayer::setIconPitchAlignment>; + result["text-pitch-alignment"] = &setProperty<V, SymbolLayer, PropertyValue<AlignmentType>, &SymbolLayer::setTextPitchAlignment>; + result["text-rotation-alignment"] = &setProperty<V, SymbolLayer, PropertyValue<AlignmentType>, &SymbolLayer::setTextRotationAlignment>; + result["text-field"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<std::string>, &SymbolLayer::setTextField>; + result["text-font"] = &setProperty<V, SymbolLayer, PropertyValue<std::vector<std::string>>, &SymbolLayer::setTextFont>; + result["text-size"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextSize>; + result["text-max-width"] = &setProperty<V, SymbolLayer, 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, DataDrivenPropertyValue<TextJustifyType>, &SymbolLayer::setTextJustify>; + result["text-anchor"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<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>; @@ -74,72 +75,132 @@ auto makeLayoutPropertySetters() { template <class V> auto makePaintPropertySetters() { - std::unordered_map<std::string, PaintPropertySetter<V>> result; - - result["fill-antialias"] = makePropertySetter<V>(&FillLayer::setFillAntialias); - result["fill-opacity"] = makePropertySetter<V>(&FillLayer::setFillOpacity); - result["fill-color"] = makePropertySetter<V>(&FillLayer::setFillColor); - result["fill-outline-color"] = makePropertySetter<V>(&FillLayer::setFillOutlineColor); - result["fill-translate"] = makePropertySetter<V>(&FillLayer::setFillTranslate); - result["fill-translate-anchor"] = makePropertySetter<V>(&FillLayer::setFillTranslateAnchor); - result["fill-pattern"] = makePropertySetter<V>(&FillLayer::setFillPattern); - - result["line-opacity"] = makePropertySetter<V>(&LineLayer::setLineOpacity); - result["line-color"] = makePropertySetter<V>(&LineLayer::setLineColor); - result["line-translate"] = makePropertySetter<V>(&LineLayer::setLineTranslate); - result["line-translate-anchor"] = makePropertySetter<V>(&LineLayer::setLineTranslateAnchor); - result["line-width"] = makePropertySetter<V>(&LineLayer::setLineWidth); - result["line-gap-width"] = makePropertySetter<V>(&LineLayer::setLineGapWidth); - result["line-offset"] = makePropertySetter<V>(&LineLayer::setLineOffset); - result["line-blur"] = makePropertySetter<V>(&LineLayer::setLineBlur); - result["line-dasharray"] = makePropertySetter<V>(&LineLayer::setLineDasharray); - result["line-pattern"] = makePropertySetter<V>(&LineLayer::setLinePattern); - - result["icon-opacity"] = makePropertySetter<V>(&SymbolLayer::setIconOpacity); - result["icon-color"] = makePropertySetter<V>(&SymbolLayer::setIconColor); - result["icon-halo-color"] = makePropertySetter<V>(&SymbolLayer::setIconHaloColor); - result["icon-halo-width"] = makePropertySetter<V>(&SymbolLayer::setIconHaloWidth); - result["icon-halo-blur"] = makePropertySetter<V>(&SymbolLayer::setIconHaloBlur); - result["icon-translate"] = makePropertySetter<V>(&SymbolLayer::setIconTranslate); - result["icon-translate-anchor"] = makePropertySetter<V>(&SymbolLayer::setIconTranslateAnchor); - result["text-opacity"] = makePropertySetter<V>(&SymbolLayer::setTextOpacity); - result["text-color"] = makePropertySetter<V>(&SymbolLayer::setTextColor); - result["text-halo-color"] = makePropertySetter<V>(&SymbolLayer::setTextHaloColor); - result["text-halo-width"] = makePropertySetter<V>(&SymbolLayer::setTextHaloWidth); - result["text-halo-blur"] = makePropertySetter<V>(&SymbolLayer::setTextHaloBlur); - result["text-translate"] = makePropertySetter<V>(&SymbolLayer::setTextTranslate); - result["text-translate-anchor"] = makePropertySetter<V>(&SymbolLayer::setTextTranslateAnchor); - - result["circle-radius"] = makePropertySetter<V>(&CircleLayer::setCircleRadius); - result["circle-color"] = makePropertySetter<V>(&CircleLayer::setCircleColor); - result["circle-blur"] = makePropertySetter<V>(&CircleLayer::setCircleBlur); - result["circle-opacity"] = makePropertySetter<V>(&CircleLayer::setCircleOpacity); - result["circle-translate"] = makePropertySetter<V>(&CircleLayer::setCircleTranslate); - result["circle-translate-anchor"] = makePropertySetter<V>(&CircleLayer::setCircleTranslateAnchor); - result["circle-pitch-scale"] = makePropertySetter<V>(&CircleLayer::setCirclePitchScale); - result["circle-stroke-width"] = makePropertySetter<V>(&CircleLayer::setCircleStrokeWidth); - result["circle-stroke-color"] = makePropertySetter<V>(&CircleLayer::setCircleStrokeColor); - result["circle-stroke-opacity"] = makePropertySetter<V>(&CircleLayer::setCircleStrokeOpacity); - - result["fill-extrusion-opacity"] = makePropertySetter<V>(&FillExtrusionLayer::setFillExtrusionOpacity); - result["fill-extrusion-color"] = makePropertySetter<V>(&FillExtrusionLayer::setFillExtrusionColor); - result["fill-extrusion-translate"] = makePropertySetter<V>(&FillExtrusionLayer::setFillExtrusionTranslate); - result["fill-extrusion-translate-anchor"] = makePropertySetter<V>(&FillExtrusionLayer::setFillExtrusionTranslateAnchor); - result["fill-extrusion-pattern"] = makePropertySetter<V>(&FillExtrusionLayer::setFillExtrusionPattern); - result["fill-extrusion-height"] = makePropertySetter<V>(&FillExtrusionLayer::setFillExtrusionHeight); - result["fill-extrusion-base"] = makePropertySetter<V>(&FillExtrusionLayer::setFillExtrusionBase); - - result["raster-opacity"] = makePropertySetter<V>(&RasterLayer::setRasterOpacity); - result["raster-hue-rotate"] = makePropertySetter<V>(&RasterLayer::setRasterHueRotate); - result["raster-brightness-min"] = makePropertySetter<V>(&RasterLayer::setRasterBrightnessMin); - result["raster-brightness-max"] = makePropertySetter<V>(&RasterLayer::setRasterBrightnessMax); - result["raster-saturation"] = makePropertySetter<V>(&RasterLayer::setRasterSaturation); - result["raster-contrast"] = makePropertySetter<V>(&RasterLayer::setRasterContrast); - result["raster-fade-duration"] = makePropertySetter<V>(&RasterLayer::setRasterFadeDuration); - - result["background-color"] = makePropertySetter<V>(&BackgroundLayer::setBackgroundColor); - result["background-pattern"] = makePropertySetter<V>(&BackgroundLayer::setBackgroundPattern); - result["background-opacity"] = makePropertySetter<V>(&BackgroundLayer::setBackgroundOpacity); + std::unordered_map<std::string, PropertySetter<V>> result; + + result["fill-antialias"] = &setProperty<V, FillLayer, PropertyValue<bool>, &FillLayer::setFillAntialias>; + result["fill-antialias-transition"] = &setTransition<V, FillLayer, &FillLayer::setFillAntialiasTransition>; + result["fill-opacity"] = &setProperty<V, FillLayer, DataDrivenPropertyValue<float>, &FillLayer::setFillOpacity>; + result["fill-opacity-transition"] = &setTransition<V, FillLayer, &FillLayer::setFillOpacityTransition>; + result["fill-color"] = &setProperty<V, FillLayer, DataDrivenPropertyValue<Color>, &FillLayer::setFillColor>; + result["fill-color-transition"] = &setTransition<V, FillLayer, &FillLayer::setFillColorTransition>; + result["fill-outline-color"] = &setProperty<V, FillLayer, DataDrivenPropertyValue<Color>, &FillLayer::setFillOutlineColor>; + result["fill-outline-color-transition"] = &setTransition<V, FillLayer, &FillLayer::setFillOutlineColorTransition>; + result["fill-translate"] = &setProperty<V, FillLayer, PropertyValue<std::array<float, 2>>, &FillLayer::setFillTranslate>; + result["fill-translate-transition"] = &setTransition<V, FillLayer, &FillLayer::setFillTranslateTransition>; + result["fill-translate-anchor"] = &setProperty<V, FillLayer, PropertyValue<TranslateAnchorType>, &FillLayer::setFillTranslateAnchor>; + result["fill-translate-anchor-transition"] = &setTransition<V, FillLayer, &FillLayer::setFillTranslateAnchorTransition>; + result["fill-pattern"] = &setProperty<V, FillLayer, PropertyValue<std::string>, &FillLayer::setFillPattern>; + result["fill-pattern-transition"] = &setTransition<V, FillLayer, &FillLayer::setFillPatternTransition>; + + result["line-opacity"] = &setProperty<V, LineLayer, DataDrivenPropertyValue<float>, &LineLayer::setLineOpacity>; + result["line-opacity-transition"] = &setTransition<V, LineLayer, &LineLayer::setLineOpacityTransition>; + result["line-color"] = &setProperty<V, LineLayer, DataDrivenPropertyValue<Color>, &LineLayer::setLineColor>; + result["line-color-transition"] = &setTransition<V, LineLayer, &LineLayer::setLineColorTransition>; + result["line-translate"] = &setProperty<V, LineLayer, PropertyValue<std::array<float, 2>>, &LineLayer::setLineTranslate>; + result["line-translate-transition"] = &setTransition<V, LineLayer, &LineLayer::setLineTranslateTransition>; + result["line-translate-anchor"] = &setProperty<V, LineLayer, PropertyValue<TranslateAnchorType>, &LineLayer::setLineTranslateAnchor>; + result["line-translate-anchor-transition"] = &setTransition<V, LineLayer, &LineLayer::setLineTranslateAnchorTransition>; + result["line-width"] = &setProperty<V, LineLayer, DataDrivenPropertyValue<float>, &LineLayer::setLineWidth>; + result["line-width-transition"] = &setTransition<V, LineLayer, &LineLayer::setLineWidthTransition>; + result["line-gap-width"] = &setProperty<V, LineLayer, DataDrivenPropertyValue<float>, &LineLayer::setLineGapWidth>; + result["line-gap-width-transition"] = &setTransition<V, LineLayer, &LineLayer::setLineGapWidthTransition>; + result["line-offset"] = &setProperty<V, LineLayer, DataDrivenPropertyValue<float>, &LineLayer::setLineOffset>; + result["line-offset-transition"] = &setTransition<V, LineLayer, &LineLayer::setLineOffsetTransition>; + result["line-blur"] = &setProperty<V, LineLayer, DataDrivenPropertyValue<float>, &LineLayer::setLineBlur>; + result["line-blur-transition"] = &setTransition<V, LineLayer, &LineLayer::setLineBlurTransition>; + result["line-dasharray"] = &setProperty<V, LineLayer, PropertyValue<std::vector<float>>, &LineLayer::setLineDasharray>; + result["line-dasharray-transition"] = &setTransition<V, LineLayer, &LineLayer::setLineDasharrayTransition>; + result["line-pattern"] = &setProperty<V, LineLayer, PropertyValue<std::string>, &LineLayer::setLinePattern>; + result["line-pattern-transition"] = &setTransition<V, LineLayer, &LineLayer::setLinePatternTransition>; + + result["icon-opacity"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setIconOpacity>; + result["icon-opacity-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setIconOpacityTransition>; + result["icon-color"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<Color>, &SymbolLayer::setIconColor>; + result["icon-color-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setIconColorTransition>; + result["icon-halo-color"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<Color>, &SymbolLayer::setIconHaloColor>; + result["icon-halo-color-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setIconHaloColorTransition>; + result["icon-halo-width"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setIconHaloWidth>; + result["icon-halo-width-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setIconHaloWidthTransition>; + result["icon-halo-blur"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setIconHaloBlur>; + result["icon-halo-blur-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setIconHaloBlurTransition>; + result["icon-translate"] = &setProperty<V, SymbolLayer, PropertyValue<std::array<float, 2>>, &SymbolLayer::setIconTranslate>; + result["icon-translate-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setIconTranslateTransition>; + result["icon-translate-anchor"] = &setProperty<V, SymbolLayer, PropertyValue<TranslateAnchorType>, &SymbolLayer::setIconTranslateAnchor>; + result["icon-translate-anchor-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setIconTranslateAnchorTransition>; + result["text-opacity"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextOpacity>; + result["text-opacity-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setTextOpacityTransition>; + result["text-color"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<Color>, &SymbolLayer::setTextColor>; + result["text-color-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setTextColorTransition>; + result["text-halo-color"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<Color>, &SymbolLayer::setTextHaloColor>; + result["text-halo-color-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setTextHaloColorTransition>; + result["text-halo-width"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextHaloWidth>; + result["text-halo-width-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setTextHaloWidthTransition>; + result["text-halo-blur"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextHaloBlur>; + result["text-halo-blur-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setTextHaloBlurTransition>; + result["text-translate"] = &setProperty<V, SymbolLayer, PropertyValue<std::array<float, 2>>, &SymbolLayer::setTextTranslate>; + result["text-translate-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setTextTranslateTransition>; + result["text-translate-anchor"] = &setProperty<V, SymbolLayer, PropertyValue<TranslateAnchorType>, &SymbolLayer::setTextTranslateAnchor>; + result["text-translate-anchor-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setTextTranslateAnchorTransition>; + + result["circle-radius"] = &setProperty<V, CircleLayer, DataDrivenPropertyValue<float>, &CircleLayer::setCircleRadius>; + result["circle-radius-transition"] = &setTransition<V, CircleLayer, &CircleLayer::setCircleRadiusTransition>; + result["circle-color"] = &setProperty<V, CircleLayer, DataDrivenPropertyValue<Color>, &CircleLayer::setCircleColor>; + result["circle-color-transition"] = &setTransition<V, CircleLayer, &CircleLayer::setCircleColorTransition>; + result["circle-blur"] = &setProperty<V, CircleLayer, DataDrivenPropertyValue<float>, &CircleLayer::setCircleBlur>; + result["circle-blur-transition"] = &setTransition<V, CircleLayer, &CircleLayer::setCircleBlurTransition>; + result["circle-opacity"] = &setProperty<V, CircleLayer, DataDrivenPropertyValue<float>, &CircleLayer::setCircleOpacity>; + result["circle-opacity-transition"] = &setTransition<V, CircleLayer, &CircleLayer::setCircleOpacityTransition>; + result["circle-translate"] = &setProperty<V, CircleLayer, PropertyValue<std::array<float, 2>>, &CircleLayer::setCircleTranslate>; + result["circle-translate-transition"] = &setTransition<V, CircleLayer, &CircleLayer::setCircleTranslateTransition>; + result["circle-translate-anchor"] = &setProperty<V, CircleLayer, PropertyValue<TranslateAnchorType>, &CircleLayer::setCircleTranslateAnchor>; + result["circle-translate-anchor-transition"] = &setTransition<V, CircleLayer, &CircleLayer::setCircleTranslateAnchorTransition>; + result["circle-pitch-scale"] = &setProperty<V, CircleLayer, PropertyValue<CirclePitchScaleType>, &CircleLayer::setCirclePitchScale>; + result["circle-pitch-scale-transition"] = &setTransition<V, CircleLayer, &CircleLayer::setCirclePitchScaleTransition>; + result["circle-pitch-alignment"] = &setProperty<V, CircleLayer, PropertyValue<AlignmentType>, &CircleLayer::setCirclePitchAlignment>; + result["circle-pitch-alignment-transition"] = &setTransition<V, CircleLayer, &CircleLayer::setCirclePitchAlignmentTransition>; + result["circle-stroke-width"] = &setProperty<V, CircleLayer, DataDrivenPropertyValue<float>, &CircleLayer::setCircleStrokeWidth>; + result["circle-stroke-width-transition"] = &setTransition<V, CircleLayer, &CircleLayer::setCircleStrokeWidthTransition>; + result["circle-stroke-color"] = &setProperty<V, CircleLayer, DataDrivenPropertyValue<Color>, &CircleLayer::setCircleStrokeColor>; + result["circle-stroke-color-transition"] = &setTransition<V, CircleLayer, &CircleLayer::setCircleStrokeColorTransition>; + result["circle-stroke-opacity"] = &setProperty<V, CircleLayer, DataDrivenPropertyValue<float>, &CircleLayer::setCircleStrokeOpacity>; + result["circle-stroke-opacity-transition"] = &setTransition<V, CircleLayer, &CircleLayer::setCircleStrokeOpacityTransition>; + + result["fill-extrusion-opacity"] = &setProperty<V, FillExtrusionLayer, PropertyValue<float>, &FillExtrusionLayer::setFillExtrusionOpacity>; + result["fill-extrusion-opacity-transition"] = &setTransition<V, FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionOpacityTransition>; + result["fill-extrusion-color"] = &setProperty<V, FillExtrusionLayer, DataDrivenPropertyValue<Color>, &FillExtrusionLayer::setFillExtrusionColor>; + result["fill-extrusion-color-transition"] = &setTransition<V, FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionColorTransition>; + result["fill-extrusion-translate"] = &setProperty<V, FillExtrusionLayer, PropertyValue<std::array<float, 2>>, &FillExtrusionLayer::setFillExtrusionTranslate>; + result["fill-extrusion-translate-transition"] = &setTransition<V, FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionTranslateTransition>; + result["fill-extrusion-translate-anchor"] = &setProperty<V, FillExtrusionLayer, PropertyValue<TranslateAnchorType>, &FillExtrusionLayer::setFillExtrusionTranslateAnchor>; + result["fill-extrusion-translate-anchor-transition"] = &setTransition<V, FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionTranslateAnchorTransition>; + result["fill-extrusion-pattern"] = &setProperty<V, FillExtrusionLayer, PropertyValue<std::string>, &FillExtrusionLayer::setFillExtrusionPattern>; + result["fill-extrusion-pattern-transition"] = &setTransition<V, FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionPatternTransition>; + result["fill-extrusion-height"] = &setProperty<V, FillExtrusionLayer, DataDrivenPropertyValue<float>, &FillExtrusionLayer::setFillExtrusionHeight>; + result["fill-extrusion-height-transition"] = &setTransition<V, FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionHeightTransition>; + result["fill-extrusion-base"] = &setProperty<V, FillExtrusionLayer, DataDrivenPropertyValue<float>, &FillExtrusionLayer::setFillExtrusionBase>; + result["fill-extrusion-base-transition"] = &setTransition<V, FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionBaseTransition>; + + result["raster-opacity"] = &setProperty<V, RasterLayer, PropertyValue<float>, &RasterLayer::setRasterOpacity>; + result["raster-opacity-transition"] = &setTransition<V, RasterLayer, &RasterLayer::setRasterOpacityTransition>; + result["raster-hue-rotate"] = &setProperty<V, RasterLayer, PropertyValue<float>, &RasterLayer::setRasterHueRotate>; + result["raster-hue-rotate-transition"] = &setTransition<V, RasterLayer, &RasterLayer::setRasterHueRotateTransition>; + result["raster-brightness-min"] = &setProperty<V, RasterLayer, PropertyValue<float>, &RasterLayer::setRasterBrightnessMin>; + result["raster-brightness-min-transition"] = &setTransition<V, RasterLayer, &RasterLayer::setRasterBrightnessMinTransition>; + result["raster-brightness-max"] = &setProperty<V, RasterLayer, PropertyValue<float>, &RasterLayer::setRasterBrightnessMax>; + result["raster-brightness-max-transition"] = &setTransition<V, RasterLayer, &RasterLayer::setRasterBrightnessMaxTransition>; + result["raster-saturation"] = &setProperty<V, RasterLayer, PropertyValue<float>, &RasterLayer::setRasterSaturation>; + result["raster-saturation-transition"] = &setTransition<V, RasterLayer, &RasterLayer::setRasterSaturationTransition>; + result["raster-contrast"] = &setProperty<V, RasterLayer, PropertyValue<float>, &RasterLayer::setRasterContrast>; + result["raster-contrast-transition"] = &setTransition<V, RasterLayer, &RasterLayer::setRasterContrastTransition>; + result["raster-fade-duration"] = &setProperty<V, RasterLayer, PropertyValue<float>, &RasterLayer::setRasterFadeDuration>; + result["raster-fade-duration-transition"] = &setTransition<V, RasterLayer, &RasterLayer::setRasterFadeDurationTransition>; + + result["background-color"] = &setProperty<V, BackgroundLayer, PropertyValue<Color>, &BackgroundLayer::setBackgroundColor>; + result["background-color-transition"] = &setTransition<V, BackgroundLayer, &BackgroundLayer::setBackgroundColorTransition>; + result["background-pattern"] = &setProperty<V, BackgroundLayer, PropertyValue<std::string>, &BackgroundLayer::setBackgroundPattern>; + result["background-pattern-transition"] = &setTransition<V, BackgroundLayer, &BackgroundLayer::setBackgroundPatternTransition>; + result["background-opacity"] = &setProperty<V, BackgroundLayer, PropertyValue<float>, &BackgroundLayer::setBackgroundOpacity>; + result["background-opacity-transition"] = &setTransition<V, BackgroundLayer, &BackgroundLayer::setBackgroundOpacityTransition>; return result; } 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 ed8f6e891c..0000000000 --- a/include/mbgl/style/conversion/make_property_setters.hpp.ejs +++ /dev/null @@ -1,47 +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, LayoutPropertySetter<V>> result; - - result["visibility"] = &setVisibility<V>; - -<% for (const layer of locals.layers) { -%> -<% for (const property of layer.layoutProperties) { -%> - result["<%- property.name %>"] = makePropertySetter<V>(&<%- camelize(layer.type) %>Layer::set<%- camelize(property.name) %>); -<% } -%> - -<% } -%> - return result; -} - -template <class V> -auto makePaintPropertySetters() { - std::unordered_map<std::string, PaintPropertySetter<V>> result; - -<% for (const layer of locals.layers) { -%> -<% for (const property of layer.paintProperties) { -%> - result["<%- property.name %>"] = makePropertySetter<V>(&<%- camelize(layer.type) %>Layer::set<%- camelize(property.name) %>); -<% } -%> - -<% } -%> - return result; -} - -} // namespace conversion -} // namespace style -} // namespace mbgl diff --git a/include/mbgl/style/conversion/position.hpp b/include/mbgl/style/conversion/position.hpp new file mode 100644 index 0000000000..7036b03822 --- /dev/null +++ b/include/mbgl/style/conversion/position.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include <mbgl/style/conversion.hpp> +#include <mbgl/style/position.hpp> +#include <mbgl/util/optional.hpp> + +#include <array> + +namespace mbgl { +namespace style { +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); + } +}; + +} // namespace conversion +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/style/conversion/property_setter.hpp b/include/mbgl/style/conversion/property_setter.hpp index 1a601c7c1b..759c4512cc 100644 --- a/include/mbgl/style/conversion/property_setter.hpp +++ b/include/mbgl/style/conversion/property_setter.hpp @@ -4,8 +4,9 @@ #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 <functional> #include <string> namespace mbgl { @@ -13,27 +14,40 @@ namespace style { namespace conversion { template <class V> -using LayoutPropertySetter = std::function<optional<Error> (Layer&, const V&)>; +using PropertySetter = optional<Error> (*) (Layer&, const V&); -template <class V> -using PaintPropertySetter = std::function<optional<Error> (Layer&, const V&, const optional<std::string>&)>; - -template <class V, class L, class T, class...Args> -auto makePropertySetter(void (L::*setter)(PropertyValue<T>, const Args&...args)) { - return [setter] (Layer& layer, const V& value, const Args&...args) -> optional<Error> { - L* typedLayer = layer.as<L>(); - if (!typedLayer) { - return Error { "layer doesn't support this property" }; - } - - Result<PropertyValue<T>> typedValue = convert<PropertyValue<T>>(value); - if (!typedValue) { - return typedValue.error(); - } - - (typedLayer->*setter)(*typedValue, args...); - return {}; - }; +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> @@ -43,9 +57,10 @@ optional<Error> setVisibility(Layer& layer, const V& value) { return {}; } - Result<VisibilityType> visibility = convert<VisibilityType>(value); + Error error; + optional<VisibilityType> visibility = convert<VisibilityType>(value, error); if (!visibility) { - return visibility.error(); + return error; } layer.setVisibility(*visibility); diff --git a/include/mbgl/style/conversion/property_value.hpp b/include/mbgl/style/conversion/property_value.hpp index de95b56155..f8937da07d 100644 --- a/include/mbgl/style/conversion/property_value.hpp +++ b/include/mbgl/style/conversion/property_value.hpp @@ -12,21 +12,21 @@ namespace conversion { template <class T> struct Converter<PropertyValue<T>> { template <class V> - Result<PropertyValue<T>> operator()(const V& value) const { + optional<PropertyValue<T>> operator()(const V& value, Error& error) const { if (isUndefined(value)) { - return {}; + return PropertyValue<T>(); } else if (isObject(value)) { - Result<Function<T>> function = convert<Function<T>>(value); + optional<CameraFunction<T>> function = convert<CameraFunction<T>>(value, error); if (!function) { - return function.error(); + return {}; } - return *function; + return { *function }; } else { - Result<T> constant = convert<T>(value); + optional<T> constant = convert<T>(value, error); if (!constant) { - return constant.error(); + return {}; } - return *constant; + return { *constant }; } } }; diff --git a/include/mbgl/style/conversion/source.hpp b/include/mbgl/style/conversion/source.hpp index 6e1b4347c3..e0563ac10b 100644 --- a/include/mbgl/style/conversion/source.hpp +++ b/include/mbgl/style/conversion/source.hpp @@ -1,6 +1,7 @@ #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> @@ -8,6 +9,8 @@ #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> namespace mbgl { namespace style { @@ -16,60 +19,69 @@ namespace conversion { template <> struct Converter<std::unique_ptr<Source>> { public: + template <class V> - Result<std::unique_ptr<Source>> operator()(const V& value, const std::string& id) const { + optional<std::unique_ptr<Source>> operator()(const V& value, Error& error, const std::string& id) const { if (!isObject(value)) { - return Error{ "source must be an object" }; + error = { "source must be an object" }; + return {}; } auto typeValue = objectMember(value, "type"); if (!typeValue) { - return Error{ "source must have a type" }; + error = { "source must have a type" }; + return {}; } optional<std::string> type = toString(*typeValue); if (!type) { - return Error{ "source type must be a string" }; + error = { "source type must be a string" }; + return {}; } if (*type == "raster") { - return convertRasterSource(id, value); + return convertRasterSource(id, value, error); } else if (*type == "vector") { - return convertVectorSource(id, value); + return convertVectorSource(id, value, error); } else if (*type == "geojson") { - return convertGeoJSONSource(id, value); + return convertGeoJSONSource(id, value, error); + } else if (*type == "image") { + return convertImageSource(id, value, error); } else { - return Error{ "invalid source type" }; + error = { "invalid source type" }; + return {}; } } private: // A tile source can either specify a URL to TileJSON, or inline TileJSON. template <class V> - Result<variant<std::string, Tileset>> convertURLOrTileset(const V& value) const { + optional<variant<std::string, Tileset>> convertURLOrTileset(const V& value, Error& error) const { auto urlVal = objectMember(value, "url"); if (!urlVal) { - Result<Tileset> tileset = convert<Tileset>(value); + optional<Tileset> tileset = convert<Tileset>(value, error); if (!tileset) { - return tileset.error(); + return {}; } - return *tileset; + return { *tileset }; } optional<std::string> url = toString(*urlVal); if (!url) { - return Error{ "source url must be a string" }; + error = { "source url must be a string" }; + return {}; } - return *url; + return { *url }; } template <class V> - Result<std::unique_ptr<Source>> convertRasterSource(const std::string& id, - const V& value) const { - Result<variant<std::string, Tileset>> urlOrTileset = convertURLOrTileset(value); + 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 urlOrTileset.error(); + return {}; } uint16_t tileSize = util::tileSize; @@ -77,53 +89,99 @@ private: if (tileSizeValue) { optional<float> size = toNumber(*tileSizeValue); if (!size || *size < 0 || *size > std::numeric_limits<uint16_t>::max()) { - return Error{ "invalid tileSize" }; + error = { "invalid tileSize" }; + return {}; } tileSize = *size; } - return std::make_unique<RasterSource>(id, std::move(*urlOrTileset), tileSize); + return { std::make_unique<RasterSource>(id, std::move(*urlOrTileset), tileSize) }; } template <class V> - Result<std::unique_ptr<Source>> convertVectorSource(const std::string& id, - const V& value) const { - Result<variant<std::string, Tileset>> urlOrTileset = convertURLOrTileset(value); + 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 urlOrTileset.error(); + return {}; } - return std::make_unique<VectorSource>(id, std::move(*urlOrTileset)); + return { std::make_unique<VectorSource>(id, std::move(*urlOrTileset)) }; } template <class V> - Result<std::unique_ptr<Source>> convertGeoJSONSource(const std::string& id, - const V& value) const { + optional<std::unique_ptr<Source>> convertGeoJSONSource(const std::string& id, + const V& value, + Error& error) const { auto dataValue = objectMember(value, "data"); if (!dataValue) { - return Error{ "GeoJSON source must have a data value" }; + error = { "GeoJSON source must have a data value" }; + return {}; } - Result<GeoJSONOptions> options = convert<GeoJSONOptions>(value); + optional<GeoJSONOptions> options = convert<GeoJSONOptions>(value, error); if (!options) { - return options.error(); + return {}; } auto result = std::make_unique<GeoJSONSource>(id, *options); if (isObject(*dataValue)) { - Result<GeoJSON> geoJSON = convertGeoJSON(*dataValue); + optional<GeoJSON> geoJSON = convert<GeoJSON>(*dataValue, error); if (!geoJSON) { - return geoJSON.error(); + return {}; } result->setGeoJSON(std::move(*geoJSON)); } else if (toString(*dataValue)) { result->setURL(*toString(*dataValue)); } else { - return Error{ "GeoJSON data must be a URL or an object" }; + error = { "GeoJSON data must be a URL or an object" }; + return {}; } - return std::move(result); + 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) }; } }; diff --git a/include/mbgl/style/conversion/tileset.hpp b/include/mbgl/style/conversion/tileset.hpp index 1955cc16cf..377170aa6a 100644 --- a/include/mbgl/style/conversion/tileset.hpp +++ b/include/mbgl/style/conversion/tileset.hpp @@ -11,22 +11,25 @@ template <> struct Converter<Tileset> { public: template <class V> - Result<Tileset> operator()(const V& value) const { + optional<Tileset> operator()(const V& value, Error& error) const { Tileset result; auto tiles = objectMember(value, "tiles"); if (!tiles) { - return Error { "source must have tiles" }; + error = { "source must have tiles" }; + return {}; } if (!isArray(*tiles)) { - return Error { "source tiles must be an array" }; + 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) { - return Error { "source tiles member must be a string" }; + error = { "source tiles member must be a string" }; + return {}; } result.tiles.push_back(std::move(*urlTemplate)); } @@ -43,7 +46,8 @@ public: if (minzoomValue) { optional<float> minzoom = toNumber(*minzoomValue); if (!minzoom || *minzoom < 0 || *minzoom > std::numeric_limits<uint8_t>::max()) { - return Error { "invalid minzoom" }; + error = { "invalid minzoom" }; + return {}; } result.zoomRange.min = *minzoom; } @@ -52,7 +56,8 @@ public: if (maxzoomValue) { optional<float> maxzoom = toNumber(*maxzoomValue); if (!maxzoom || *maxzoom < 0 || *maxzoom > std::numeric_limits<uint8_t>::max()) { - return Error { "invalid maxzoom" }; + error = { "invalid maxzoom" }; + return {}; } result.zoomRange.max = *maxzoom; } @@ -61,7 +66,8 @@ public: if (attributionValue) { optional<std::string> attribution = toString(*attributionValue); if (!attribution) { - return Error { "source attribution must be a string" }; + error = { "source attribution must be a string" }; + return {}; } result.attribution = std::move(*attribution); } diff --git a/include/mbgl/style/conversion/transition_options.hpp b/include/mbgl/style/conversion/transition_options.hpp new file mode 100644 index 0000000000..de8834d578 --- /dev/null +++ b/include/mbgl/style/conversion/transition_options.hpp @@ -0,0 +1,48 @@ +#pragma once + +#include <mbgl/style/transition_options.hpp> +#include <mbgl/style/conversion.hpp> + +namespace mbgl { +namespace style { +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; + } +}; + +} // namespace conversion +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/style/data_driven_property_value.hpp b/include/mbgl/style/data_driven_property_value.hpp new file mode 100644 index 0000000000..5d7c596363 --- /dev/null +++ b/include/mbgl/style/data_driven_property_value.hpp @@ -0,0 +1,69 @@ +#pragma once + +#include <mbgl/util/variant.hpp> +#include <mbgl/style/undefined.hpp> +#include <mbgl/style/function/camera_function.hpp> +#include <mbgl/style/function/source_function.hpp> +#include <mbgl/style/function/composite_function.hpp> + +namespace mbgl { +namespace style { + +template <class T> +class DataDrivenPropertyValue { +private: + using Value = variant< + Undefined, + T, + CameraFunction<T>, + SourceFunction<T>, + CompositeFunction<T>>; + + Value value; + + friend bool operator==(const DataDrivenPropertyValue& lhs, + const DataDrivenPropertyValue& rhs) { + return lhs.value == rhs.value; + } + + friend bool operator!=(const DataDrivenPropertyValue& lhs, + const DataDrivenPropertyValue& rhs) { + return !(lhs == rhs); + } + +public: + DataDrivenPropertyValue() = default; + DataDrivenPropertyValue( T v) : value(std::move(v)) {} + DataDrivenPropertyValue( CameraFunction<T> v) : value(std::move(v)) {} + DataDrivenPropertyValue( SourceFunction<T> v) : value(std::move(v)) {} + DataDrivenPropertyValue(CompositeFunction<T> v) : value(std::move(v)) {} + + bool isUndefined() const { + return value.template is<Undefined>(); + } + + bool isDataDriven() const { + return value.template is<SourceFunction<T>>() || value.template is<CompositeFunction<T>>(); + } + + bool isZoomConstant() const { + return !value.template is<CameraFunction<T>>() && !value.template is<CompositeFunction<T>>(); + } + + template <class... Ts> + auto match(Ts&&... ts) const { + return value.match(std::forward<Ts>(ts)...); + } + + template <typename Evaluator> + auto evaluate(const Evaluator& evaluator, TimePoint = {}) const { + return Value::visit(value, evaluator); + } + + bool hasDataDrivenPropertyDifference(const DataDrivenPropertyValue<T>& other) const { + return *this != other && (isDataDriven() || other.isDataDriven()); + } +}; + +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/style/filter.hpp b/include/mbgl/style/filter.hpp index e5d8081d82..a204a2b17a 100644 --- a/include/mbgl/style/filter.hpp +++ b/include/mbgl/style/filter.hpp @@ -145,6 +145,95 @@ public: } }; + +class TypeEqualsFilter { +public: + FeatureType value; + + friend bool operator==(const TypeEqualsFilter& lhs, const TypeEqualsFilter& rhs) { + return lhs.value == rhs.value; + } +}; + +class TypeNotEqualsFilter { +public: + FeatureType value; + + friend bool operator==(const TypeNotEqualsFilter& lhs, const TypeNotEqualsFilter& rhs) { + return lhs.value == rhs.value; + } +}; + +class TypeInFilter { +public: + std::vector<FeatureType> values; + + friend bool operator==(const TypeInFilter& lhs, const TypeInFilter& rhs) { + return lhs.values == rhs.values; + } +}; + +class TypeNotInFilter { +public: + std::vector<FeatureType> values; + + friend bool operator==(const TypeNotInFilter& lhs, const TypeNotInFilter& rhs) { + return lhs.values == rhs.values; + } +}; + + +class IdentifierEqualsFilter { +public: + FeatureIdentifier value; + + friend bool operator==(const IdentifierEqualsFilter& lhs, const IdentifierEqualsFilter& rhs) { + return lhs.value == rhs.value; + } +}; + +class IdentifierNotEqualsFilter { +public: + FeatureIdentifier value; + + friend bool operator==(const IdentifierNotEqualsFilter& lhs, const IdentifierNotEqualsFilter& rhs) { + return lhs.value == rhs.value; + } +}; + +class IdentifierInFilter { +public: + std::vector<FeatureIdentifier> values; + + friend bool operator==(const IdentifierInFilter& lhs, const IdentifierInFilter& rhs) { + return lhs.values == rhs.values; + } +}; + +class IdentifierNotInFilter { +public: + std::vector<FeatureIdentifier> values; + + friend bool operator==(const IdentifierNotInFilter& lhs, const IdentifierNotInFilter& rhs) { + return lhs.values == rhs.values; + } +}; + +class HasIdentifierFilter { +public: + friend bool operator==(const HasIdentifierFilter&, const HasIdentifierFilter&) { + return true; + } +}; + +class NotHasIdentifierFilter { +public: + friend bool operator==(const NotHasIdentifierFilter&, const NotHasIdentifierFilter&) { + return true; + } +}; + + using FilterBase = variant< class NullFilter, class EqualsFilter, @@ -159,7 +248,17 @@ using FilterBase = variant< class AllFilter, class NoneFilter, class HasFilter, - class NotHasFilter>; + class NotHasFilter, + class TypeEqualsFilter, + class TypeNotEqualsFilter, + class TypeInFilter, + class TypeNotInFilter, + class IdentifierEqualsFilter, + class IdentifierNotEqualsFilter, + class IdentifierInFilter, + class IdentifierNotInFilter, + class HasIdentifierFilter, + class NotHasIdentifierFilter>; class Filter : public FilterBase { public: @@ -167,6 +266,9 @@ public: bool operator()(const Feature&) const; + template <class GeometryTileFeature> + bool operator()(const GeometryTileFeature&) const; + template <class PropertyAccessor> bool operator()(FeatureType type, optional<FeatureIdentifier> id, PropertyAccessor accessor) const; }; diff --git a/include/mbgl/style/filter_evaluator.hpp b/include/mbgl/style/filter_evaluator.hpp index 659f554bba..66223d7282 100644 --- a/include/mbgl/style/filter_evaluator.hpp +++ b/include/mbgl/style/filter_evaluator.hpp @@ -31,37 +31,37 @@ public: } bool operator()(const EqualsFilter& filter) const { - optional<Value> actual = getValue(filter.key); + optional<Value> actual = propertyAccessor(filter.key); return actual && equal(*actual, filter.value); } bool operator()(const NotEqualsFilter& filter) const { - optional<Value> actual = getValue(filter.key); + optional<Value> actual = propertyAccessor(filter.key); return !actual || !equal(*actual, filter.value); } bool operator()(const LessThanFilter& filter) const { - optional<Value> actual = getValue(filter.key); + optional<Value> actual = propertyAccessor(filter.key); return actual && compare(*actual, filter.value, [] (const auto& lhs_, const auto& rhs_) { return lhs_ < rhs_; }); } bool operator()(const LessThanEqualsFilter& filter) const { - optional<Value> actual = getValue(filter.key); + optional<Value> actual = propertyAccessor(filter.key); return actual && compare(*actual, filter.value, [] (const auto& lhs_, const auto& rhs_) { return lhs_ <= rhs_; }); } bool operator()(const GreaterThanFilter& filter) const { - optional<Value> actual = getValue(filter.key); + optional<Value> actual = propertyAccessor(filter.key); return actual && compare(*actual, filter.value, [] (const auto& lhs_, const auto& rhs_) { return lhs_ > rhs_; }); } bool operator()(const GreaterThanEqualsFilter& filter) const { - optional<Value> actual = getValue(filter.key); + optional<Value> actual = propertyAccessor(filter.key); return actual && compare(*actual, filter.value, [] (const auto& lhs_, const auto& rhs_) { return lhs_ >= rhs_; }); } bool operator()(const InFilter& filter) const { - optional<Value> actual = getValue(filter.key); + optional<Value> actual = propertyAccessor(filter.key); if (!actual) return false; for (const auto& v: filter.values) { @@ -73,7 +73,7 @@ public: } bool operator()(const NotInFilter& filter) const { - optional<Value> actual = getValue(filter.key); + optional<Value> actual = propertyAccessor(filter.key); if (!actual) return true; for (const auto& v: filter.values) { @@ -112,30 +112,76 @@ public: } bool operator()(const HasFilter& filter) const { - return bool(getValue(filter.key)); + return bool(propertyAccessor(filter.key)); } bool operator()(const NotHasFilter& filter) const { - return !getValue(filter.key); + return !propertyAccessor(filter.key); } -private: - optional<Value> getValue(const std::string& key_) const { - if (key_ == "$type") { - return optional<Value>(uint64_t(featureType)); - } else if (key_ == "$id") { - if (featureIdentifier) { - return FeatureIdentifier::visit(*featureIdentifier, [] (auto id) { - return Value(std::move(id)); - }); - } else { - return optional<Value>(); + + bool operator()(const TypeEqualsFilter& filter) const { + return featureType == filter.value; + } + + bool operator()(const TypeNotEqualsFilter& filter) const { + return featureType != filter.value; + } + + bool operator()(const TypeInFilter& filter) const { + for (const auto& v: filter.values) { + if (featureType == v) { + return true; + } + } + return false; + } + + bool operator()(const TypeNotInFilter& filter) const { + for (const auto& v: filter.values) { + if (featureType == v) { + return false; + } + } + return true; + } + + + bool operator()(const IdentifierEqualsFilter& filter) const { + return featureIdentifier == filter.value; + } + + bool operator()(const IdentifierNotEqualsFilter& filter) const { + return featureIdentifier != filter.value; + } + + bool operator()(const IdentifierInFilter& filter) const { + for (const auto& v: filter.values) { + if (featureIdentifier == v) { + return true; } - } else { - return propertyAccessor(key_); } + return false; } + bool operator()(const IdentifierNotInFilter& filter) const { + for (const auto& v: filter.values) { + if (featureIdentifier == v) { + return false; + } + } + return true; + } + + bool operator()(const HasIdentifierFilter&) const { + return bool(featureIdentifier); + } + + bool operator()(const NotHasIdentifierFilter&) const { + return !featureIdentifier; + } + +private: template <class Op> struct Comparator { const Op& op; @@ -200,6 +246,11 @@ inline bool Filter::operator()(const Feature& feature) const { }); } +template <class GeometryTileFeature> +bool Filter::operator()(const GeometryTileFeature& feature) const { + return operator()(feature.getType(), feature.getID(), [&] (const auto& key) { return feature.getValue(key); }); +} + template <class PropertyAccessor> bool Filter::operator()(FeatureType type, optional<FeatureIdentifier> id, PropertyAccessor accessor) const { return FilterBase::visit(*this, FilterEvaluator<PropertyAccessor> { type, id, accessor }); diff --git a/include/mbgl/style/function.hpp b/include/mbgl/style/function.hpp deleted file mode 100644 index b023229e4f..0000000000 --- a/include/mbgl/style/function.hpp +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once - -#include <cassert> -#include <utility> -#include <vector> - -namespace mbgl { -namespace style { - -template <typename T> -class Function { -public: - using Stop = std::pair<float, T>; - using Stops = std::vector<Stop>; - - Function(Stops stops_, float base_) - : base(base_), stops(std::move(stops_)) { - assert(stops.size() > 0); - } - - float getBase() const { return base; } - const std::vector<std::pair<float, T>>& getStops() const { return stops; } - - T evaluate(float z) const; - - friend bool operator==(const Function& lhs, const Function& rhs) { - return lhs.base == rhs.base && lhs.stops == rhs.stops; - } - - friend bool operator!=(const Function& lhs, const Function& rhs) { - return !(lhs == rhs); - } - -private: - float base = 1; - std::vector<std::pair<float, T>> stops; -}; - -} // namespace style -} // namespace mbgl diff --git a/include/mbgl/style/function/camera_function.hpp b/include/mbgl/style/function/camera_function.hpp new file mode 100644 index 0000000000..7fde365b3d --- /dev/null +++ b/include/mbgl/style/function/camera_function.hpp @@ -0,0 +1,42 @@ +#pragma once + +#include <mbgl/style/function/exponential_stops.hpp> +#include <mbgl/style/function/interval_stops.hpp> +#include <mbgl/util/interpolate.hpp> +#include <mbgl/util/variant.hpp> + +namespace mbgl { +namespace style { + +template <class T> +class CameraFunction { +public: + using Stops = std::conditional_t< + util::Interpolatable<T>::value, + variant< + ExponentialStops<T>, + IntervalStops<T>>, + variant< + IntervalStops<T>>>; + + CameraFunction(Stops stops_) + : stops(std::move(stops_)) { + } + + T evaluate(float zoom) const { + return stops.match([&] (const auto& s) { + return s.evaluate(zoom).value_or(T()); + }); + } + + friend bool operator==(const CameraFunction& lhs, + const CameraFunction& rhs) { + return lhs.stops == rhs.stops; + } + + Stops stops; + bool useIntegerZoom = false; +}; + +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/style/function/categorical_stops.hpp b/include/mbgl/style/function/categorical_stops.hpp new file mode 100644 index 0000000000..c8505115ab --- /dev/null +++ b/include/mbgl/style/function/categorical_stops.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include <mbgl/util/feature.hpp> +#include <mbgl/util/variant.hpp> + +#include <cassert> +#include <utility> +#include <map> + +namespace mbgl { +namespace style { + +class CategoricalValue : public variant<bool, int64_t, std::string> { +public: + using variant<bool, int64_t, std::string>::variant; +}; + +template <class T> +class CategoricalStops { +public: + using Stops = std::map<CategoricalValue, T>; + + Stops stops; + + CategoricalStops() = default; + CategoricalStops(Stops stops_) + : stops(std::move(stops_)) { + assert(stops.size() > 0); + } + + optional<T> evaluate(const Value&) const; + + friend bool operator==(const CategoricalStops& lhs, + const CategoricalStops& rhs) { + return lhs.stops == rhs.stops; + } +}; + +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/style/function/composite_categorical_stops.hpp b/include/mbgl/style/function/composite_categorical_stops.hpp new file mode 100644 index 0000000000..b796621d1a --- /dev/null +++ b/include/mbgl/style/function/composite_categorical_stops.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include <mbgl/style/function/categorical_stops.hpp> + +namespace mbgl { +namespace style { + +template <class T> +class CompositeCategoricalStops { +public: + using Stops = std::map<float, std::map<CategoricalValue, T>>; + Stops stops; + + CompositeCategoricalStops() = default; + CompositeCategoricalStops(Stops stops_) + : stops(std::move(stops_)) { + } + + CategoricalStops<T> innerStops(const std::map<CategoricalValue, T>& stops_) const { + return CategoricalStops<T>(stops_); + } + + friend bool operator==(const CompositeCategoricalStops& lhs, + const CompositeCategoricalStops& rhs) { + return lhs.stops == rhs.stops; + } +}; + +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/style/function/composite_exponential_stops.hpp b/include/mbgl/style/function/composite_exponential_stops.hpp new file mode 100644 index 0000000000..f1ad32a04d --- /dev/null +++ b/include/mbgl/style/function/composite_exponential_stops.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include <mbgl/style/function/exponential_stops.hpp> + +#include <map> + +namespace mbgl { +namespace style { + +template <class T> +class CompositeExponentialStops { +public: + using Stops = std::map<float, std::map<float, T>>; + + Stops stops; + float base = 1.0f; + + CompositeExponentialStops() = default; + CompositeExponentialStops(Stops stops_, float base_ = 1.0f) + : stops(std::move(stops_)), + base(base_) { + } + + ExponentialStops<T> innerStops(const std::map<float, T>& stops_) const { + return ExponentialStops<T>(stops_, base); + } + + friend bool operator==(const CompositeExponentialStops& lhs, + const CompositeExponentialStops& rhs) { + return lhs.stops == rhs.stops && lhs.base == rhs.base; + } +}; + +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/style/function/composite_function.hpp b/include/mbgl/style/function/composite_function.hpp new file mode 100644 index 0000000000..7b524b6021 --- /dev/null +++ b/include/mbgl/style/function/composite_function.hpp @@ -0,0 +1,153 @@ +#pragma once + +#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/util/interpolate.hpp> +#include <mbgl/util/range.hpp> +#include <mbgl/util/variant.hpp> + +#include <string> +#include <tuple> + +namespace mbgl { + +class GeometryTileFeature; + +namespace style { + +// A CompositeFunction consists of an outer zoom function whose stop range values are +// "inner" source functions. It provides the GL Native implementation of +// "zoom-and-property" functions from the style spec. + +template <class T> +class CompositeFunction { +public: + using InnerStops = std::conditional_t< + util::Interpolatable<T>::value, + variant< + ExponentialStops<T>, + IntervalStops<T>, + CategoricalStops<T>>, + variant< + IntervalStops<T>, + CategoricalStops<T>>>; + + using Stops = std::conditional_t< + util::Interpolatable<T>::value, + variant< + CompositeExponentialStops<T>, + CompositeIntervalStops<T>, + CompositeCategoricalStops<T>>, + variant< + CompositeIntervalStops<T>, + CompositeCategoricalStops<T>>>; + + CompositeFunction(std::string property_, Stops stops_, optional<T> defaultValue_ = {}) + : property(std::move(property_)), + stops(std::move(stops_)), + defaultValue(std::move(defaultValue_)) { + } + + struct CoveringRanges { + float zoom; + Range<float> coveringZoomRange; + Range<InnerStops> coveringStopsRange; + }; + + // Return the relevant stop zoom values and inner stops that bracket a given zoom level. This + // is the first step toward evaluating the function, and is used for in the course of both partial + // evaluation of data-driven paint properties, and full evaluation of data-driven layout properties. + CoveringRanges coveringRanges(float zoom) const { + return stops.match( + [&] (const auto& s) { + assert(!s.stops.empty()); + auto minIt = s.stops.lower_bound(zoom); + auto maxIt = s.stops.upper_bound(zoom); + + // lower_bound yields first element >= zoom, but we want the *last* + // element <= zoom, so if we found a stop > zoom, back up by one. + if (minIt != s.stops.begin() && minIt != s.stops.end() && minIt->first > zoom) { + minIt--; + } + + return CoveringRanges { + zoom, + Range<float> { + minIt == s.stops.end() ? s.stops.rbegin()->first : minIt->first, + maxIt == s.stops.end() ? s.stops.rbegin()->first : maxIt->first + }, + Range<InnerStops> { + s.innerStops(minIt == s.stops.end() ? s.stops.rbegin()->second : minIt->second), + s.innerStops(maxIt == s.stops.end() ? s.stops.rbegin()->second : maxIt->second) + } + }; + } + ); + } + + // Given a range of zoom values (typically two adjacent integer zoom levels, e.g. 5.0 and 6.0), + // return the covering ranges for both. This is used in the course of partial evaluation for + // data-driven paint properties. + Range<CoveringRanges> rangeOfCoveringRanges(Range<float> zoomRange) { + return Range<CoveringRanges> { + coveringRanges(zoomRange.min), + coveringRanges(zoomRange.max) + }; + } + + // 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. + 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) + }; + } + return Range<T> { + evaluateFinal(ranges.min, *value, finalDefaultValue), + evaluateFinal(ranges.max, *value, 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); + } + return evaluateFinal(coveringRanges(zoom), *value, finalDefaultValue); + } + + 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); + } + + 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)); + } +}; + +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/style/function/composite_interval_stops.hpp b/include/mbgl/style/function/composite_interval_stops.hpp new file mode 100644 index 0000000000..3c495f2a7f --- /dev/null +++ b/include/mbgl/style/function/composite_interval_stops.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include <mbgl/style/function/interval_stops.hpp> + +#include <map> + +namespace mbgl { +namespace style { + +template <class T> +class CompositeIntervalStops { +public: + using Stops = std::map<float, std::map<float, T>>; + Stops stops; + + CompositeIntervalStops() = default; + CompositeIntervalStops(Stops stops_) + : stops(std::move(stops_)) { + } + + IntervalStops<T> innerStops(const std::map<float, T>& stops_) const { + return IntervalStops<T>(stops_); + } + + friend bool operator==(const CompositeIntervalStops& lhs, + const CompositeIntervalStops& rhs) { + return lhs.stops == rhs.stops; + } +}; + +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/style/function/exponential_stops.hpp b/include/mbgl/style/function/exponential_stops.hpp new file mode 100644 index 0000000000..b3866c4059 --- /dev/null +++ b/include/mbgl/style/function/exponential_stops.hpp @@ -0,0 +1,56 @@ +#pragma once + +#include <mbgl/util/feature.hpp> +#include <mbgl/util/interpolate.hpp> + +#include <map> + +namespace mbgl { +namespace style { + +template <class T> +class ExponentialStops { +public: + using Stops = std::map<float, T>; + + Stops stops; + float base = 1.0f; + + ExponentialStops() = default; + ExponentialStops(Stops stops_, float base_ = 1.0f) + : stops(std::move(stops_)), + base(base_) { + } + + optional<T> evaluate(float z) const { + if (stops.empty()) { + return {}; + } + + auto it = stops.upper_bound(z); + if (it == stops.end()) { + return stops.rbegin()->second; + } else if (it == stops.begin()) { + return stops.begin()->second; + } else { + return util::interpolate(std::prev(it)->second, it->second, + util::interpolationFactor(base, { std::prev(it)->first, it->first }, z)); + } + } + + optional<T> evaluate(const Value& value) const { + optional<float> z = numericValue<float>(value); + if (!z) { + return {}; + } + return evaluate(*z); + } + + friend bool operator==(const ExponentialStops& lhs, + const ExponentialStops& rhs) { + return lhs.stops == rhs.stops && lhs.base == rhs.base; + } +}; + +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/style/function/identity_stops.hpp b/include/mbgl/style/function/identity_stops.hpp new file mode 100644 index 0000000000..741ebbbe0c --- /dev/null +++ b/include/mbgl/style/function/identity_stops.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include <mbgl/util/feature.hpp> + +namespace mbgl { +namespace style { + +template <class T> +class IdentityStops { +public: + optional<T> evaluate(const Value&) const; + + friend bool operator==(const IdentityStops&, + const IdentityStops&) { + return true; + } +}; + +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/style/function/interval_stops.hpp b/include/mbgl/style/function/interval_stops.hpp new file mode 100644 index 0000000000..45e2dc6f2e --- /dev/null +++ b/include/mbgl/style/function/interval_stops.hpp @@ -0,0 +1,51 @@ +#pragma once + +#include <mbgl/util/feature.hpp> + +#include <map> + +namespace mbgl { +namespace style { + +template <class T> +class IntervalStops { +public: + using Stops = std::map<float, T>; + Stops stops; + + IntervalStops() = default; + IntervalStops(Stops stops_) + : stops(std::move(stops_)) { + } + + optional<T> evaluate(float z) const { + if (stops.empty()) { + return {}; + } + + auto it = stops.upper_bound(z); + if (it == stops.end()) { + return stops.rbegin()->second; + } else if (it == stops.begin()) { + return stops.begin()->second; + } else { + return std::prev(it)->second; + } + } + + optional<T> evaluate(const Value& value) const { + optional<float> z = numericValue<float>(value); + if (!z) { + return {}; + } + return evaluate(*z); + } + + friend bool operator==(const IntervalStops& lhs, + const IntervalStops& rhs) { + return lhs.stops == rhs.stops; + } +}; + +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/style/function/source_function.hpp b/include/mbgl/style/function/source_function.hpp new file mode 100644 index 0000000000..9c2ad101ec --- /dev/null +++ b/include/mbgl/style/function/source_function.hpp @@ -0,0 +1,60 @@ +#pragma once + +#include <mbgl/style/function/exponential_stops.hpp> +#include <mbgl/style/function/interval_stops.hpp> +#include <mbgl/style/function/categorical_stops.hpp> +#include <mbgl/style/function/identity_stops.hpp> +#include <mbgl/util/interpolate.hpp> +#include <mbgl/util/variant.hpp> + +#include <string> + +namespace mbgl { +namespace style { + +template <class T> +class SourceFunction { +public: + using Stops = std::conditional_t< + util::Interpolatable<T>::value, + variant< + ExponentialStops<T>, + IntervalStops<T>, + CategoricalStops<T>, + IdentityStops<T>>, + variant< + IntervalStops<T>, + CategoricalStops<T>, + IdentityStops<T>>>; + + SourceFunction(std::string property_, Stops stops_, optional<T> defaultValue_ = {}) + : property(std::move(property_)), + stops(std::move(stops_)), + defaultValue(std::move(defaultValue_)) { + } + + template <class Feature> + T evaluate(const Feature& feature, T finalDefaultValue) const { + optional<Value> v = feature.getValue(property); + if (!v) { + return defaultValue.value_or(finalDefaultValue); + } + return stops.match([&] (const auto& s) -> T { + return s.evaluate(*v).value_or(defaultValue.value_or(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); + } + + std::string property; + Stops stops; + optional<T> defaultValue; + bool useIntegerZoom = false; +}; + +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/style/image.hpp b/include/mbgl/style/image.hpp new file mode 100644 index 0000000000..ff3bfedf46 --- /dev/null +++ b/include/mbgl/style/image.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include <mbgl/util/image.hpp> +#include <mbgl/util/immutable.hpp> + +#include <string> + +namespace mbgl { +namespace style { + +class Image { +public: + Image(std::string id, PremultipliedImage&&, float pixelRatio, bool sdf = false); + Image(const Image&); + + std::string getID() const; + + const PremultipliedImage& getImage() const; + + // Pixel ratio of the sprite image. + float getPixelRatio() const; + + // Whether this image should be interpreted as a signed distance field icon. + bool isSdf() const; + + class Impl; + Immutable<Impl> baseImpl; +}; + +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/style/layer.hpp b/include/mbgl/style/layer.hpp index 670faa8254..c6a3c0e735 100644 --- a/include/mbgl/style/layer.hpp +++ b/include/mbgl/style/layer.hpp @@ -1,10 +1,15 @@ #pragma once #include <mbgl/util/noncopyable.hpp> +#include <mbgl/util/any.hpp> +#include <mbgl/util/immutable.hpp> +#include <mbgl/style/layer_type.hpp> #include <mbgl/style/types.hpp> +#include <cassert> #include <memory> #include <string> +#include <stdexcept> namespace mbgl { namespace style { @@ -17,6 +22,7 @@ class RasterLayer; class BackgroundLayer; class CustomLayer; class FillExtrusionLayer; +class LayerObserver; /** * The runtime representation of a [layer](https://www.mapbox.com/mapbox-gl-style-spec/#layers) from the Mapbox Style @@ -35,22 +41,6 @@ class FillExtrusionLayer; * auto circleLayer = std::make_unique<CircleLayer>("my-circle-layer"); */ class Layer : public mbgl::util::noncopyable { -protected: - enum class Type { - Fill, - Line, - Circle, - Symbol, - Raster, - Background, - Custom, - FillExtrusion, - }; - - class Impl; - const Type type; - Layer(Type, std::unique_ptr<Impl>); - public: virtual ~Layer(); @@ -71,7 +61,7 @@ public: // Convenience method for dynamic dispatch on the concrete layer type. Using // method overloading, this allows consolidation of logic common to vector-based - // layers (Fill, Line, Circle, or Symbol). For example: + // layers (Fill, FillExtrusion, Line, Circle, or Symbol). For example: // // struct Visitor { // void operator()(CustomLayer&) { ... } @@ -83,42 +73,60 @@ public: // template <class V> auto accept(V&& visitor) { - switch (type) { - case Type::Fill: - return visitor(*as<FillLayer>()); - case Type::Line: - return visitor(*as<LineLayer>()); - case Type::Circle: - return visitor(*as<CircleLayer>()); - case Type::Symbol: - return visitor(*as<SymbolLayer>()); - case Type::Raster: - return visitor(*as<RasterLayer>()); - case Type::Background: - return visitor(*as<BackgroundLayer>()); - case Type::Custom: - return visitor(*as<CustomLayer>()); - case Type::FillExtrusion: - return visitor(*as<FillExtrusionLayer>()); + switch (getType()) { + case LayerType::Fill: + return std::forward<V>(visitor)(*as<FillLayer>()); + case LayerType::Line: + return std::forward<V>(visitor)(*as<LineLayer>()); + case LayerType::Circle: + return std::forward<V>(visitor)(*as<CircleLayer>()); + case LayerType::Symbol: + return std::forward<V>(visitor)(*as<SymbolLayer>()); + case LayerType::Raster: + return std::forward<V>(visitor)(*as<RasterLayer>()); + case LayerType::Background: + return std::forward<V>(visitor)(*as<BackgroundLayer>()); + case LayerType::Custom: + return std::forward<V>(visitor)(*as<CustomLayer>()); + case LayerType::FillExtrusion: + return std::forward<V>(visitor)(*as<FillExtrusionLayer>()); } + + + // Not reachable, but placate GCC. + assert(false); + throw new std::runtime_error("unknown layer type"); } - const std::string& getID() const; + LayerType getType() const; + std::string getID() const; // Visibility VisibilityType getVisibility() const; - void setVisibility(VisibilityType); + virtual void setVisibility(VisibilityType) = 0; // Zoom range float getMinZoom() const; - void setMinZoom(float) const; float getMaxZoom() const; - void setMaxZoom(float) const; + virtual void setMinZoom(float) = 0; + virtual void setMaxZoom(float) = 0; // Private implementation - const std::unique_ptr<Impl> baseImpl; + class Impl; + Immutable<Impl> baseImpl; + + Layer(Immutable<Impl>); + + // Create a layer, copying all properties except id and paint properties from this layer. + virtual std::unique_ptr<Layer> cloneRef(const std::string& id) const = 0; + + LayerObserver* observer = nullptr; + void setObserver(LayerObserver*); - friend std::string layoutKey(const Layer&); + // 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; }; } // namespace style diff --git a/include/mbgl/style/layer_type.hpp b/include/mbgl/style/layer_type.hpp new file mode 100644 index 0000000000..66ff834eee --- /dev/null +++ b/include/mbgl/style/layer_type.hpp @@ -0,0 +1,18 @@ +#pragma once + +namespace mbgl { +namespace style { + +enum class LayerType { + Fill, + Line, + Circle, + Symbol, + Raster, + Background, + Custom, + FillExtrusion, +}; + +} // namespace style +} // namespace mbgl
\ No newline at end of file diff --git a/include/mbgl/style/layers/background_layer.hpp b/include/mbgl/style/layers/background_layer.hpp index c120b7f493..903983844f 100644 --- a/include/mbgl/style/layers/background_layer.hpp +++ b/include/mbgl/style/layers/background_layer.hpp @@ -5,43 +5,60 @@ #include <mbgl/style/layer.hpp> #include <mbgl/style/filter.hpp> #include <mbgl/style/property_value.hpp> +#include <mbgl/style/data_driven_property_value.hpp> #include <mbgl/util/color.hpp> namespace mbgl { namespace style { +class TransitionOptions; + class BackgroundLayer : public Layer { public: BackgroundLayer(const std::string& layerID); ~BackgroundLayer() final; + // Visibility + void setVisibility(VisibilityType) final; + + // Zoom range + void setMinZoom(float) final; + void setMaxZoom(float) final; + // Paint properties static PropertyValue<Color> getDefaultBackgroundColor(); - PropertyValue<Color> getBackgroundColor(const optional<std::string>& klass = {}) const; - void setBackgroundColor(PropertyValue<Color>, const optional<std::string>& klass = {}); + PropertyValue<Color> getBackgroundColor() const; + void setBackgroundColor(PropertyValue<Color>); + void setBackgroundColorTransition(const TransitionOptions&); + TransitionOptions getBackgroundColorTransition() const; static PropertyValue<std::string> getDefaultBackgroundPattern(); - PropertyValue<std::string> getBackgroundPattern(const optional<std::string>& klass = {}) const; - void setBackgroundPattern(PropertyValue<std::string>, const optional<std::string>& klass = {}); + PropertyValue<std::string> getBackgroundPattern() const; + void setBackgroundPattern(PropertyValue<std::string>); + void setBackgroundPatternTransition(const TransitionOptions&); + TransitionOptions getBackgroundPatternTransition() const; static PropertyValue<float> getDefaultBackgroundOpacity(); - PropertyValue<float> getBackgroundOpacity(const optional<std::string>& klass = {}) const; - void setBackgroundOpacity(PropertyValue<float>, const optional<std::string>& klass = {}); + PropertyValue<float> getBackgroundOpacity() const; + void setBackgroundOpacity(PropertyValue<float>); + void setBackgroundOpacityTransition(const TransitionOptions&); + TransitionOptions getBackgroundOpacityTransition() const; // Private implementation class Impl; - Impl* const impl; + const Impl& impl() const; - BackgroundLayer(const Impl&); - BackgroundLayer(const BackgroundLayer&) = delete; + Mutable<Impl> mutableImpl() const; + BackgroundLayer(Immutable<Impl>); + std::unique_ptr<Layer> cloneRef(const std::string& id) const final; }; template <> inline bool Layer::is<BackgroundLayer>() const { - return type == Type::Background; + return getType() == LayerType::Background; } } // namespace style diff --git a/include/mbgl/style/layers/circle_layer.hpp b/include/mbgl/style/layers/circle_layer.hpp index 5562126c2f..942dd67503 100644 --- a/include/mbgl/style/layers/circle_layer.hpp +++ b/include/mbgl/style/layers/circle_layer.hpp @@ -5,12 +5,15 @@ #include <mbgl/style/layer.hpp> #include <mbgl/style/filter.hpp> #include <mbgl/style/property_value.hpp> +#include <mbgl/style/data_driven_property_value.hpp> #include <mbgl/util/color.hpp> namespace mbgl { namespace style { +class TransitionOptions; + class CircleLayer : public Layer { public: CircleLayer(const std::string& layerID, const std::string& sourceID); @@ -24,60 +27,94 @@ public: void setFilter(const Filter&); const Filter& getFilter() const; - // Paint properties - - static PropertyValue<float> getDefaultCircleRadius(); - PropertyValue<float> getCircleRadius(const optional<std::string>& klass = {}) const; - void setCircleRadius(PropertyValue<float>, const optional<std::string>& klass = {}); + // Visibility + void setVisibility(VisibilityType) final; - static PropertyValue<Color> getDefaultCircleColor(); - PropertyValue<Color> getCircleColor(const optional<std::string>& klass = {}) const; - void setCircleColor(PropertyValue<Color>, const optional<std::string>& klass = {}); + // Zoom range + void setMinZoom(float) final; + void setMaxZoom(float) final; - static PropertyValue<float> getDefaultCircleBlur(); - PropertyValue<float> getCircleBlur(const optional<std::string>& klass = {}) const; - void setCircleBlur(PropertyValue<float>, const optional<std::string>& klass = {}); + // Paint properties - static PropertyValue<float> getDefaultCircleOpacity(); - PropertyValue<float> getCircleOpacity(const optional<std::string>& klass = {}) const; - void setCircleOpacity(PropertyValue<float>, const optional<std::string>& klass = {}); + static DataDrivenPropertyValue<float> getDefaultCircleRadius(); + DataDrivenPropertyValue<float> getCircleRadius() const; + void setCircleRadius(DataDrivenPropertyValue<float>); + void setCircleRadiusTransition(const TransitionOptions&); + TransitionOptions getCircleRadiusTransition() const; + + static DataDrivenPropertyValue<Color> getDefaultCircleColor(); + DataDrivenPropertyValue<Color> getCircleColor() const; + void setCircleColor(DataDrivenPropertyValue<Color>); + void setCircleColorTransition(const TransitionOptions&); + TransitionOptions getCircleColorTransition() const; + + static DataDrivenPropertyValue<float> getDefaultCircleBlur(); + DataDrivenPropertyValue<float> getCircleBlur() const; + void setCircleBlur(DataDrivenPropertyValue<float>); + void setCircleBlurTransition(const TransitionOptions&); + TransitionOptions getCircleBlurTransition() const; + + static DataDrivenPropertyValue<float> getDefaultCircleOpacity(); + DataDrivenPropertyValue<float> getCircleOpacity() const; + void setCircleOpacity(DataDrivenPropertyValue<float>); + void setCircleOpacityTransition(const TransitionOptions&); + TransitionOptions getCircleOpacityTransition() const; static PropertyValue<std::array<float, 2>> getDefaultCircleTranslate(); - PropertyValue<std::array<float, 2>> getCircleTranslate(const optional<std::string>& klass = {}) const; - void setCircleTranslate(PropertyValue<std::array<float, 2>>, const optional<std::string>& klass = {}); + PropertyValue<std::array<float, 2>> getCircleTranslate() const; + void setCircleTranslate(PropertyValue<std::array<float, 2>>); + void setCircleTranslateTransition(const TransitionOptions&); + TransitionOptions getCircleTranslateTransition() const; static PropertyValue<TranslateAnchorType> getDefaultCircleTranslateAnchor(); - PropertyValue<TranslateAnchorType> getCircleTranslateAnchor(const optional<std::string>& klass = {}) const; - void setCircleTranslateAnchor(PropertyValue<TranslateAnchorType>, const optional<std::string>& klass = {}); + PropertyValue<TranslateAnchorType> getCircleTranslateAnchor() const; + void setCircleTranslateAnchor(PropertyValue<TranslateAnchorType>); + void setCircleTranslateAnchorTransition(const TransitionOptions&); + TransitionOptions getCircleTranslateAnchorTransition() const; static PropertyValue<CirclePitchScaleType> getDefaultCirclePitchScale(); - PropertyValue<CirclePitchScaleType> getCirclePitchScale(const optional<std::string>& klass = {}) const; - void setCirclePitchScale(PropertyValue<CirclePitchScaleType>, const optional<std::string>& klass = {}); - - static PropertyValue<float> getDefaultCircleStrokeWidth(); - PropertyValue<float> getCircleStrokeWidth(const optional<std::string>& klass = {}) const; - void setCircleStrokeWidth(PropertyValue<float>, const optional<std::string>& klass = {}); - - static PropertyValue<Color> getDefaultCircleStrokeColor(); - PropertyValue<Color> getCircleStrokeColor(const optional<std::string>& klass = {}) const; - void setCircleStrokeColor(PropertyValue<Color>, const optional<std::string>& klass = {}); - - static PropertyValue<float> getDefaultCircleStrokeOpacity(); - PropertyValue<float> getCircleStrokeOpacity(const optional<std::string>& klass = {}) const; - void setCircleStrokeOpacity(PropertyValue<float>, const optional<std::string>& klass = {}); + PropertyValue<CirclePitchScaleType> getCirclePitchScale() const; + void setCirclePitchScale(PropertyValue<CirclePitchScaleType>); + 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>); + void setCircleStrokeWidthTransition(const TransitionOptions&); + TransitionOptions getCircleStrokeWidthTransition() const; + + static DataDrivenPropertyValue<Color> getDefaultCircleStrokeColor(); + DataDrivenPropertyValue<Color> getCircleStrokeColor() const; + void setCircleStrokeColor(DataDrivenPropertyValue<Color>); + void setCircleStrokeColorTransition(const TransitionOptions&); + TransitionOptions getCircleStrokeColorTransition() const; + + static DataDrivenPropertyValue<float> getDefaultCircleStrokeOpacity(); + DataDrivenPropertyValue<float> getCircleStrokeOpacity() const; + void setCircleStrokeOpacity(DataDrivenPropertyValue<float>); + void setCircleStrokeOpacityTransition(const TransitionOptions&); + TransitionOptions getCircleStrokeOpacityTransition() const; // Private implementation class Impl; - Impl* const impl; + const Impl& impl() const; - CircleLayer(const Impl&); - CircleLayer(const CircleLayer&) = delete; + Mutable<Impl> mutableImpl() const; + CircleLayer(Immutable<Impl>); + std::unique_ptr<Layer> cloneRef(const std::string& id) const final; }; template <> inline bool Layer::is<CircleLayer>() const { - return type == Type::Circle; + return getType() == LayerType::Circle; } } // namespace style diff --git a/include/mbgl/style/layers/custom_layer.hpp b/include/mbgl/style/layers/custom_layer.hpp index 6bde087820..79a353b047 100644 --- a/include/mbgl/style/layers/custom_layer.hpp +++ b/include/mbgl/style/layers/custom_layer.hpp @@ -55,19 +55,26 @@ public: void* context); ~CustomLayer() final; + // Visibility + void setVisibility(VisibilityType) final; + + // Zoom range + void setMinZoom(float) final; + void setMaxZoom(float) final; + // Private implementation class Impl; - Impl* impl; + const Impl& impl() const; + + Mutable<Impl> mutableImpl() const; + std::unique_ptr<Layer> cloneRef(const std::string& id) const final; - CustomLayer(const Impl&); CustomLayer(const CustomLayer&) = delete; }; template <> -inline bool Layer::is<CustomLayer>() const { - return type == Type::Custom; -} +bool Layer::is<CustomLayer>() const; } // namespace style } // namespace mbgl diff --git a/include/mbgl/style/layers/fill_extrusion_layer.hpp b/include/mbgl/style/layers/fill_extrusion_layer.hpp index 08728af309..e1c54f54ee 100644 --- a/include/mbgl/style/layers/fill_extrusion_layer.hpp +++ b/include/mbgl/style/layers/fill_extrusion_layer.hpp @@ -5,12 +5,15 @@ #include <mbgl/style/layer.hpp> #include <mbgl/style/filter.hpp> #include <mbgl/style/property_value.hpp> +#include <mbgl/style/data_driven_property_value.hpp> #include <mbgl/util/color.hpp> namespace mbgl { namespace style { +class TransitionOptions; + class FillExtrusionLayer : public Layer { public: FillExtrusionLayer(const std::string& layerID, const std::string& sourceID); @@ -24,48 +27,70 @@ public: void setFilter(const Filter&); const Filter& getFilter() const; + // Visibility + void setVisibility(VisibilityType) final; + + // Zoom range + void setMinZoom(float) final; + void setMaxZoom(float) final; + // Paint properties static PropertyValue<float> getDefaultFillExtrusionOpacity(); - PropertyValue<float> getFillExtrusionOpacity(const optional<std::string>& klass = {}) const; - void setFillExtrusionOpacity(PropertyValue<float>, const optional<std::string>& klass = {}); + PropertyValue<float> getFillExtrusionOpacity() const; + void setFillExtrusionOpacity(PropertyValue<float>); + void setFillExtrusionOpacityTransition(const TransitionOptions&); + TransitionOptions getFillExtrusionOpacityTransition() const; - static PropertyValue<Color> getDefaultFillExtrusionColor(); - PropertyValue<Color> getFillExtrusionColor(const optional<std::string>& klass = {}) const; - void setFillExtrusionColor(PropertyValue<Color>, const optional<std::string>& klass = {}); + static DataDrivenPropertyValue<Color> getDefaultFillExtrusionColor(); + DataDrivenPropertyValue<Color> getFillExtrusionColor() const; + void setFillExtrusionColor(DataDrivenPropertyValue<Color>); + void setFillExtrusionColorTransition(const TransitionOptions&); + TransitionOptions getFillExtrusionColorTransition() const; static PropertyValue<std::array<float, 2>> getDefaultFillExtrusionTranslate(); - PropertyValue<std::array<float, 2>> getFillExtrusionTranslate(const optional<std::string>& klass = {}) const; - void setFillExtrusionTranslate(PropertyValue<std::array<float, 2>>, const optional<std::string>& klass = {}); + PropertyValue<std::array<float, 2>> getFillExtrusionTranslate() const; + void setFillExtrusionTranslate(PropertyValue<std::array<float, 2>>); + void setFillExtrusionTranslateTransition(const TransitionOptions&); + TransitionOptions getFillExtrusionTranslateTransition() const; static PropertyValue<TranslateAnchorType> getDefaultFillExtrusionTranslateAnchor(); - PropertyValue<TranslateAnchorType> getFillExtrusionTranslateAnchor(const optional<std::string>& klass = {}) const; - void setFillExtrusionTranslateAnchor(PropertyValue<TranslateAnchorType>, const optional<std::string>& klass = {}); + PropertyValue<TranslateAnchorType> getFillExtrusionTranslateAnchor() const; + void setFillExtrusionTranslateAnchor(PropertyValue<TranslateAnchorType>); + void setFillExtrusionTranslateAnchorTransition(const TransitionOptions&); + TransitionOptions getFillExtrusionTranslateAnchorTransition() const; static PropertyValue<std::string> getDefaultFillExtrusionPattern(); - PropertyValue<std::string> getFillExtrusionPattern(const optional<std::string>& klass = {}) const; - void setFillExtrusionPattern(PropertyValue<std::string>, const optional<std::string>& klass = {}); - - static PropertyValue<float> getDefaultFillExtrusionHeight(); - PropertyValue<float> getFillExtrusionHeight(const optional<std::string>& klass = {}) const; - void setFillExtrusionHeight(PropertyValue<float>, const optional<std::string>& klass = {}); - - static PropertyValue<float> getDefaultFillExtrusionBase(); - PropertyValue<float> getFillExtrusionBase(const optional<std::string>& klass = {}) const; - void setFillExtrusionBase(PropertyValue<float>, const optional<std::string>& klass = {}); + PropertyValue<std::string> getFillExtrusionPattern() const; + void setFillExtrusionPattern(PropertyValue<std::string>); + void setFillExtrusionPatternTransition(const TransitionOptions&); + TransitionOptions getFillExtrusionPatternTransition() const; + + static DataDrivenPropertyValue<float> getDefaultFillExtrusionHeight(); + DataDrivenPropertyValue<float> getFillExtrusionHeight() const; + void setFillExtrusionHeight(DataDrivenPropertyValue<float>); + void setFillExtrusionHeightTransition(const TransitionOptions&); + TransitionOptions getFillExtrusionHeightTransition() const; + + static DataDrivenPropertyValue<float> getDefaultFillExtrusionBase(); + DataDrivenPropertyValue<float> getFillExtrusionBase() const; + void setFillExtrusionBase(DataDrivenPropertyValue<float>); + void setFillExtrusionBaseTransition(const TransitionOptions&); + TransitionOptions getFillExtrusionBaseTransition() const; // Private implementation class Impl; - Impl* const impl; + const Impl& impl() const; - FillExtrusionLayer(const Impl&); - FillExtrusionLayer(const FillExtrusionLayer&) = delete; + Mutable<Impl> mutableImpl() const; + FillExtrusionLayer(Immutable<Impl>); + std::unique_ptr<Layer> cloneRef(const std::string& id) const final; }; template <> inline bool Layer::is<FillExtrusionLayer>() const { - return type == Type::FillExtrusion; + return getType() == LayerType::FillExtrusion; } } // namespace style diff --git a/include/mbgl/style/layers/fill_layer.hpp b/include/mbgl/style/layers/fill_layer.hpp index 4b9201641d..dfbe69d7fe 100644 --- a/include/mbgl/style/layers/fill_layer.hpp +++ b/include/mbgl/style/layers/fill_layer.hpp @@ -5,12 +5,15 @@ #include <mbgl/style/layer.hpp> #include <mbgl/style/filter.hpp> #include <mbgl/style/property_value.hpp> +#include <mbgl/style/data_driven_property_value.hpp> #include <mbgl/util/color.hpp> namespace mbgl { namespace style { +class TransitionOptions; + class FillLayer : public Layer { public: FillLayer(const std::string& layerID, const std::string& sourceID); @@ -24,48 +27,70 @@ public: void setFilter(const Filter&); const Filter& getFilter() const; - // Paint properties - - static PropertyValue<bool> getDefaultFillAntialias(); - PropertyValue<bool> getFillAntialias(const optional<std::string>& klass = {}) const; - void setFillAntialias(PropertyValue<bool>, const optional<std::string>& klass = {}); + // Visibility + void setVisibility(VisibilityType) final; - static PropertyValue<float> getDefaultFillOpacity(); - PropertyValue<float> getFillOpacity(const optional<std::string>& klass = {}) const; - void setFillOpacity(PropertyValue<float>, const optional<std::string>& klass = {}); + // Zoom range + void setMinZoom(float) final; + void setMaxZoom(float) final; - static PropertyValue<Color> getDefaultFillColor(); - PropertyValue<Color> getFillColor(const optional<std::string>& klass = {}) const; - void setFillColor(PropertyValue<Color>, const optional<std::string>& klass = {}); + // Paint properties - static PropertyValue<Color> getDefaultFillOutlineColor(); - PropertyValue<Color> getFillOutlineColor(const optional<std::string>& klass = {}) const; - void setFillOutlineColor(PropertyValue<Color>, const optional<std::string>& klass = {}); + static PropertyValue<bool> getDefaultFillAntialias(); + PropertyValue<bool> getFillAntialias() const; + void setFillAntialias(PropertyValue<bool>); + void setFillAntialiasTransition(const TransitionOptions&); + TransitionOptions getFillAntialiasTransition() const; + + static DataDrivenPropertyValue<float> getDefaultFillOpacity(); + DataDrivenPropertyValue<float> getFillOpacity() const; + void setFillOpacity(DataDrivenPropertyValue<float>); + void setFillOpacityTransition(const TransitionOptions&); + TransitionOptions getFillOpacityTransition() const; + + static DataDrivenPropertyValue<Color> getDefaultFillColor(); + DataDrivenPropertyValue<Color> getFillColor() const; + void setFillColor(DataDrivenPropertyValue<Color>); + void setFillColorTransition(const TransitionOptions&); + TransitionOptions getFillColorTransition() const; + + static DataDrivenPropertyValue<Color> getDefaultFillOutlineColor(); + DataDrivenPropertyValue<Color> getFillOutlineColor() const; + void setFillOutlineColor(DataDrivenPropertyValue<Color>); + void setFillOutlineColorTransition(const TransitionOptions&); + TransitionOptions getFillOutlineColorTransition() const; static PropertyValue<std::array<float, 2>> getDefaultFillTranslate(); - PropertyValue<std::array<float, 2>> getFillTranslate(const optional<std::string>& klass = {}) const; - void setFillTranslate(PropertyValue<std::array<float, 2>>, const optional<std::string>& klass = {}); + PropertyValue<std::array<float, 2>> getFillTranslate() const; + void setFillTranslate(PropertyValue<std::array<float, 2>>); + void setFillTranslateTransition(const TransitionOptions&); + TransitionOptions getFillTranslateTransition() const; static PropertyValue<TranslateAnchorType> getDefaultFillTranslateAnchor(); - PropertyValue<TranslateAnchorType> getFillTranslateAnchor(const optional<std::string>& klass = {}) const; - void setFillTranslateAnchor(PropertyValue<TranslateAnchorType>, const optional<std::string>& klass = {}); + PropertyValue<TranslateAnchorType> getFillTranslateAnchor() const; + void setFillTranslateAnchor(PropertyValue<TranslateAnchorType>); + void setFillTranslateAnchorTransition(const TransitionOptions&); + TransitionOptions getFillTranslateAnchorTransition() const; static PropertyValue<std::string> getDefaultFillPattern(); - PropertyValue<std::string> getFillPattern(const optional<std::string>& klass = {}) const; - void setFillPattern(PropertyValue<std::string>, const optional<std::string>& klass = {}); + PropertyValue<std::string> getFillPattern() const; + void setFillPattern(PropertyValue<std::string>); + void setFillPatternTransition(const TransitionOptions&); + TransitionOptions getFillPatternTransition() const; // Private implementation class Impl; - Impl* const impl; + const Impl& impl() const; - FillLayer(const Impl&); - FillLayer(const FillLayer&) = delete; + Mutable<Impl> mutableImpl() const; + FillLayer(Immutable<Impl>); + std::unique_ptr<Layer> cloneRef(const std::string& id) const final; }; template <> inline bool Layer::is<FillLayer>() const { - return type == Type::Fill; + return getType() == LayerType::Fill; } } // namespace style diff --git a/include/mbgl/style/layers/layer.hpp.ejs b/include/mbgl/style/layers/layer.hpp.ejs deleted file mode 100644 index 15d0fcee61..0000000000 --- a/include/mbgl/style/layers/layer.hpp.ejs +++ /dev/null @@ -1,77 +0,0 @@ -<% - const type = locals.type; - const layoutProperties = locals.layoutProperties; - const paintProperties = locals.paintProperties; --%> -// This file is generated. Do not edit. - -#pragma once - -#include <mbgl/style/layer.hpp> -#include <mbgl/style/filter.hpp> -#include <mbgl/style/property_value.hpp> - -#include <mbgl/util/color.hpp> - -<% if (type === 'line' || type === 'symbol') { -%> -#include <vector> - -<% } -%> -namespace mbgl { -namespace style { - -class <%- camelize(type) %>Layer : public Layer { -public: -<% if (type === 'background') { -%> - <%- camelize(type) %>Layer(const std::string& layerID); -<% } else { -%> - <%- camelize(type) %>Layer(const std::string& layerID, const std::string& sourceID); -<% } -%> - ~<%- camelize(type) %>Layer() final; - -<% if (type !== 'background') { -%> - // Source - const std::string& getSourceID() const; -<% if (type !== 'raster') { -%> - const std::string& getSourceLayer() const; - void setSourceLayer(const std::string& sourceLayer); - - void setFilter(const Filter&); - const Filter& getFilter() const; -<% } -%> - -<% } -%> -<% if (layoutProperties.length) { -%> - // Layout properties - -<% for (const property of layoutProperties) { -%> - static PropertyValue<<%- propertyType(property) %>> getDefault<%- camelize(property.name) %>(); - PropertyValue<<%- propertyType(property) %>> get<%- camelize(property.name) %>() const; - void set<%- camelize(property.name) %>(PropertyValue<<%- propertyType(property) %>>); - -<% } -%> -<% } -%> - // Paint properties - -<% for (const property of paintProperties) { -%> - static PropertyValue<<%- propertyType(property) %>> getDefault<%- camelize(property.name) %>(); - PropertyValue<<%- propertyType(property) %>> get<%- camelize(property.name) %>(const optional<std::string>& klass = {}) const; - void set<%- camelize(property.name) %>(PropertyValue<<%- propertyType(property) %>>, const optional<std::string>& klass = {}); - -<% } -%> - // Private implementation - - class Impl; - Impl* const impl; - - <%- camelize(type) %>Layer(const Impl&); - <%- camelize(type) %>Layer(const <%- camelize(type) %>Layer&) = delete; -}; - -template <> -inline bool Layer::is<<%- camelize(type) %>Layer>() const { - return type == Type::<%- camelize(type) %>; -} - -} // namespace style -} // namespace mbgl diff --git a/include/mbgl/style/layers/line_layer.hpp b/include/mbgl/style/layers/line_layer.hpp index c3c1026bcd..4519296323 100644 --- a/include/mbgl/style/layers/line_layer.hpp +++ b/include/mbgl/style/layers/line_layer.hpp @@ -5,6 +5,7 @@ #include <mbgl/style/layer.hpp> #include <mbgl/style/filter.hpp> #include <mbgl/style/property_value.hpp> +#include <mbgl/style/data_driven_property_value.hpp> #include <mbgl/util/color.hpp> @@ -13,6 +14,8 @@ namespace mbgl { namespace style { +class TransitionOptions; + class LineLayer : public Layer { public: LineLayer(const std::string& layerID, const std::string& sourceID); @@ -26,15 +29,22 @@ public: void setFilter(const Filter&); const Filter& getFilter() const; + // Visibility + void setVisibility(VisibilityType) final; + + // Zoom range + void setMinZoom(float) final; + void setMaxZoom(float) final; + // Layout properties static PropertyValue<LineCapType> getDefaultLineCap(); 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; @@ -46,58 +56,79 @@ public: // Paint properties - static PropertyValue<float> getDefaultLineOpacity(); - PropertyValue<float> getLineOpacity(const optional<std::string>& klass = {}) const; - void setLineOpacity(PropertyValue<float>, const optional<std::string>& klass = {}); + static DataDrivenPropertyValue<float> getDefaultLineOpacity(); + DataDrivenPropertyValue<float> getLineOpacity() const; + void setLineOpacity(DataDrivenPropertyValue<float>); + void setLineOpacityTransition(const TransitionOptions&); + TransitionOptions getLineOpacityTransition() const; - static PropertyValue<Color> getDefaultLineColor(); - PropertyValue<Color> getLineColor(const optional<std::string>& klass = {}) const; - void setLineColor(PropertyValue<Color>, const optional<std::string>& klass = {}); + static DataDrivenPropertyValue<Color> getDefaultLineColor(); + DataDrivenPropertyValue<Color> getLineColor() const; + void setLineColor(DataDrivenPropertyValue<Color>); + void setLineColorTransition(const TransitionOptions&); + TransitionOptions getLineColorTransition() const; static PropertyValue<std::array<float, 2>> getDefaultLineTranslate(); - PropertyValue<std::array<float, 2>> getLineTranslate(const optional<std::string>& klass = {}) const; - void setLineTranslate(PropertyValue<std::array<float, 2>>, const optional<std::string>& klass = {}); + PropertyValue<std::array<float, 2>> getLineTranslate() const; + void setLineTranslate(PropertyValue<std::array<float, 2>>); + void setLineTranslateTransition(const TransitionOptions&); + TransitionOptions getLineTranslateTransition() const; static PropertyValue<TranslateAnchorType> getDefaultLineTranslateAnchor(); - PropertyValue<TranslateAnchorType> getLineTranslateAnchor(const optional<std::string>& klass = {}) const; - void setLineTranslateAnchor(PropertyValue<TranslateAnchorType>, const optional<std::string>& klass = {}); - - static PropertyValue<float> getDefaultLineWidth(); - PropertyValue<float> getLineWidth(const optional<std::string>& klass = {}) const; - void setLineWidth(PropertyValue<float>, const optional<std::string>& klass = {}); - - static PropertyValue<float> getDefaultLineGapWidth(); - PropertyValue<float> getLineGapWidth(const optional<std::string>& klass = {}) const; - void setLineGapWidth(PropertyValue<float>, const optional<std::string>& klass = {}); - - static PropertyValue<float> getDefaultLineOffset(); - PropertyValue<float> getLineOffset(const optional<std::string>& klass = {}) const; - void setLineOffset(PropertyValue<float>, const optional<std::string>& klass = {}); - - static PropertyValue<float> getDefaultLineBlur(); - PropertyValue<float> getLineBlur(const optional<std::string>& klass = {}) const; - void setLineBlur(PropertyValue<float>, const optional<std::string>& klass = {}); + PropertyValue<TranslateAnchorType> getLineTranslateAnchor() const; + void setLineTranslateAnchor(PropertyValue<TranslateAnchorType>); + void setLineTranslateAnchorTransition(const TransitionOptions&); + TransitionOptions getLineTranslateAnchorTransition() const; + + static DataDrivenPropertyValue<float> getDefaultLineWidth(); + DataDrivenPropertyValue<float> getLineWidth() const; + void setLineWidth(DataDrivenPropertyValue<float>); + void setLineWidthTransition(const TransitionOptions&); + TransitionOptions getLineWidthTransition() const; + + static DataDrivenPropertyValue<float> getDefaultLineGapWidth(); + DataDrivenPropertyValue<float> getLineGapWidth() const; + void setLineGapWidth(DataDrivenPropertyValue<float>); + void setLineGapWidthTransition(const TransitionOptions&); + TransitionOptions getLineGapWidthTransition() const; + + static DataDrivenPropertyValue<float> getDefaultLineOffset(); + DataDrivenPropertyValue<float> getLineOffset() const; + void setLineOffset(DataDrivenPropertyValue<float>); + void setLineOffsetTransition(const TransitionOptions&); + TransitionOptions getLineOffsetTransition() const; + + static DataDrivenPropertyValue<float> getDefaultLineBlur(); + DataDrivenPropertyValue<float> getLineBlur() const; + void setLineBlur(DataDrivenPropertyValue<float>); + void setLineBlurTransition(const TransitionOptions&); + TransitionOptions getLineBlurTransition() const; static PropertyValue<std::vector<float>> getDefaultLineDasharray(); - PropertyValue<std::vector<float>> getLineDasharray(const optional<std::string>& klass = {}) const; - void setLineDasharray(PropertyValue<std::vector<float>>, const optional<std::string>& klass = {}); + PropertyValue<std::vector<float>> getLineDasharray() const; + void setLineDasharray(PropertyValue<std::vector<float>>); + void setLineDasharrayTransition(const TransitionOptions&); + TransitionOptions getLineDasharrayTransition() const; static PropertyValue<std::string> getDefaultLinePattern(); - PropertyValue<std::string> getLinePattern(const optional<std::string>& klass = {}) const; - void setLinePattern(PropertyValue<std::string>, const optional<std::string>& klass = {}); + PropertyValue<std::string> getLinePattern() const; + void setLinePattern(PropertyValue<std::string>); + void setLinePatternTransition(const TransitionOptions&); + TransitionOptions getLinePatternTransition() const; // Private implementation class Impl; - Impl* const impl; + const Impl& impl() const; - LineLayer(const Impl&); - LineLayer(const LineLayer&) = delete; + Mutable<Impl> mutableImpl() const; + LineLayer(Immutable<Impl>); + std::unique_ptr<Layer> cloneRef(const std::string& id) const final; }; template <> inline bool Layer::is<LineLayer>() const { - return type == Type::Line; + return getType() == LayerType::Line; } } // namespace style diff --git a/include/mbgl/style/layers/raster_layer.hpp b/include/mbgl/style/layers/raster_layer.hpp index ae6ec7f91c..8111364709 100644 --- a/include/mbgl/style/layers/raster_layer.hpp +++ b/include/mbgl/style/layers/raster_layer.hpp @@ -5,12 +5,15 @@ #include <mbgl/style/layer.hpp> #include <mbgl/style/filter.hpp> #include <mbgl/style/property_value.hpp> +#include <mbgl/style/data_driven_property_value.hpp> #include <mbgl/util/color.hpp> namespace mbgl { namespace style { +class TransitionOptions; + class RasterLayer : public Layer { public: RasterLayer(const std::string& layerID, const std::string& sourceID); @@ -19,48 +22,70 @@ public: // Source const std::string& getSourceID() const; + // Visibility + void setVisibility(VisibilityType) final; + + // Zoom range + void setMinZoom(float) final; + void setMaxZoom(float) final; + // Paint properties static PropertyValue<float> getDefaultRasterOpacity(); - PropertyValue<float> getRasterOpacity(const optional<std::string>& klass = {}) const; - void setRasterOpacity(PropertyValue<float>, const optional<std::string>& klass = {}); + PropertyValue<float> getRasterOpacity() const; + void setRasterOpacity(PropertyValue<float>); + void setRasterOpacityTransition(const TransitionOptions&); + TransitionOptions getRasterOpacityTransition() const; static PropertyValue<float> getDefaultRasterHueRotate(); - PropertyValue<float> getRasterHueRotate(const optional<std::string>& klass = {}) const; - void setRasterHueRotate(PropertyValue<float>, const optional<std::string>& klass = {}); + PropertyValue<float> getRasterHueRotate() const; + void setRasterHueRotate(PropertyValue<float>); + void setRasterHueRotateTransition(const TransitionOptions&); + TransitionOptions getRasterHueRotateTransition() const; static PropertyValue<float> getDefaultRasterBrightnessMin(); - PropertyValue<float> getRasterBrightnessMin(const optional<std::string>& klass = {}) const; - void setRasterBrightnessMin(PropertyValue<float>, const optional<std::string>& klass = {}); + PropertyValue<float> getRasterBrightnessMin() const; + void setRasterBrightnessMin(PropertyValue<float>); + void setRasterBrightnessMinTransition(const TransitionOptions&); + TransitionOptions getRasterBrightnessMinTransition() const; static PropertyValue<float> getDefaultRasterBrightnessMax(); - PropertyValue<float> getRasterBrightnessMax(const optional<std::string>& klass = {}) const; - void setRasterBrightnessMax(PropertyValue<float>, const optional<std::string>& klass = {}); + PropertyValue<float> getRasterBrightnessMax() const; + void setRasterBrightnessMax(PropertyValue<float>); + void setRasterBrightnessMaxTransition(const TransitionOptions&); + TransitionOptions getRasterBrightnessMaxTransition() const; static PropertyValue<float> getDefaultRasterSaturation(); - PropertyValue<float> getRasterSaturation(const optional<std::string>& klass = {}) const; - void setRasterSaturation(PropertyValue<float>, const optional<std::string>& klass = {}); + PropertyValue<float> getRasterSaturation() const; + void setRasterSaturation(PropertyValue<float>); + void setRasterSaturationTransition(const TransitionOptions&); + TransitionOptions getRasterSaturationTransition() const; static PropertyValue<float> getDefaultRasterContrast(); - PropertyValue<float> getRasterContrast(const optional<std::string>& klass = {}) const; - void setRasterContrast(PropertyValue<float>, const optional<std::string>& klass = {}); + PropertyValue<float> getRasterContrast() const; + void setRasterContrast(PropertyValue<float>); + void setRasterContrastTransition(const TransitionOptions&); + TransitionOptions getRasterContrastTransition() const; static PropertyValue<float> getDefaultRasterFadeDuration(); - PropertyValue<float> getRasterFadeDuration(const optional<std::string>& klass = {}) const; - void setRasterFadeDuration(PropertyValue<float>, const optional<std::string>& klass = {}); + PropertyValue<float> getRasterFadeDuration() const; + void setRasterFadeDuration(PropertyValue<float>); + void setRasterFadeDurationTransition(const TransitionOptions&); + TransitionOptions getRasterFadeDurationTransition() const; // Private implementation class Impl; - Impl* const impl; + const Impl& impl() const; - RasterLayer(const Impl&); - RasterLayer(const RasterLayer&) = delete; + Mutable<Impl> mutableImpl() const; + RasterLayer(Immutable<Impl>); + std::unique_ptr<Layer> cloneRef(const std::string& id) const final; }; template <> inline bool Layer::is<RasterLayer>() const { - return type == Type::Raster; + return getType() == LayerType::Raster; } } // namespace style diff --git a/include/mbgl/style/layers/symbol_layer.hpp b/include/mbgl/style/layers/symbol_layer.hpp index 1e2e6ac454..6e355c0057 100644 --- a/include/mbgl/style/layers/symbol_layer.hpp +++ b/include/mbgl/style/layers/symbol_layer.hpp @@ -5,6 +5,7 @@ #include <mbgl/style/layer.hpp> #include <mbgl/style/filter.hpp> #include <mbgl/style/property_value.hpp> +#include <mbgl/style/data_driven_property_value.hpp> #include <mbgl/util/color.hpp> @@ -13,6 +14,8 @@ namespace mbgl { namespace style { +class TransitionOptions; + class SymbolLayer : public Layer { public: SymbolLayer(const std::string& layerID, const std::string& sourceID); @@ -26,6 +29,13 @@ public: void setFilter(const Filter&); const Filter& getFilter() const; + // Visibility + void setVisibility(VisibilityType) final; + + // Zoom range + void setMinZoom(float) final; + void setMaxZoom(float) final; + // Layout properties static PropertyValue<SymbolPlacementType> getDefaultSymbolPlacement(); @@ -56,9 +66,9 @@ public: PropertyValue<AlignmentType> getIconRotationAlignment() const; void setIconRotationAlignment(PropertyValue<AlignmentType>); - static PropertyValue<float> getDefaultIconSize(); - PropertyValue<float> getIconSize() const; - void setIconSize(PropertyValue<float>); + static DataDrivenPropertyValue<float> getDefaultIconSize(); + DataDrivenPropertyValue<float> getIconSize() const; + void setIconSize(DataDrivenPropertyValue<float>); static PropertyValue<IconTextFitType> getDefaultIconTextFit(); PropertyValue<IconTextFitType> getIconTextFit() const; @@ -68,13 +78,13 @@ public: PropertyValue<std::array<float, 4>> getIconTextFitPadding() const; void setIconTextFitPadding(PropertyValue<std::array<float, 4>>); - static PropertyValue<std::string> getDefaultIconImage(); - PropertyValue<std::string> getIconImage() const; - void setIconImage(PropertyValue<std::string>); + static DataDrivenPropertyValue<std::string> getDefaultIconImage(); + DataDrivenPropertyValue<std::string> getIconImage() const; + void setIconImage(DataDrivenPropertyValue<std::string>); - static PropertyValue<float> getDefaultIconRotate(); - PropertyValue<float> getIconRotate() const; - void setIconRotate(PropertyValue<float>); + static DataDrivenPropertyValue<float> getDefaultIconRotate(); + DataDrivenPropertyValue<float> getIconRotate() const; + void setIconRotate(DataDrivenPropertyValue<float>); static PropertyValue<float> getDefaultIconPadding(); PropertyValue<float> getIconPadding() const; @@ -84,9 +94,13 @@ public: PropertyValue<bool> getIconKeepUpright() const; void setIconKeepUpright(PropertyValue<bool>); - static PropertyValue<std::array<float, 2>> getDefaultIconOffset(); - PropertyValue<std::array<float, 2>> getIconOffset() const; - void setIconOffset(PropertyValue<std::array<float, 2>>); + static DataDrivenPropertyValue<std::array<float, 2>> getDefaultIconOffset(); + DataDrivenPropertyValue<std::array<float, 2>> getIconOffset() const; + void setIconOffset(DataDrivenPropertyValue<std::array<float, 2>>); + + static PropertyValue<AlignmentType> getDefaultIconPitchAlignment(); + PropertyValue<AlignmentType> getIconPitchAlignment() const; + void setIconPitchAlignment(PropertyValue<AlignmentType>); static PropertyValue<AlignmentType> getDefaultTextPitchAlignment(); PropertyValue<AlignmentType> getTextPitchAlignment() const; @@ -96,17 +110,17 @@ public: PropertyValue<AlignmentType> getTextRotationAlignment() const; void setTextRotationAlignment(PropertyValue<AlignmentType>); - static PropertyValue<std::string> getDefaultTextField(); - PropertyValue<std::string> getTextField() const; - void setTextField(PropertyValue<std::string>); + static DataDrivenPropertyValue<std::string> getDefaultTextField(); + DataDrivenPropertyValue<std::string> getTextField() const; + void setTextField(DataDrivenPropertyValue<std::string>); static PropertyValue<std::vector<std::string>> getDefaultTextFont(); PropertyValue<std::vector<std::string>> getTextFont() const; void setTextFont(PropertyValue<std::vector<std::string>>); - static PropertyValue<float> getDefaultTextSize(); - PropertyValue<float> getTextSize() const; - void setTextSize(PropertyValue<float>); + static DataDrivenPropertyValue<float> getDefaultTextSize(); + DataDrivenPropertyValue<float> getTextSize() const; + void setTextSize(DataDrivenPropertyValue<float>); static PropertyValue<float> getDefaultTextMaxWidth(); PropertyValue<float> getTextMaxWidth() const; @@ -120,21 +134,21 @@ public: PropertyValue<float> getTextLetterSpacing() const; void setTextLetterSpacing(PropertyValue<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<TextAnchorType> getDefaultTextAnchor(); + DataDrivenPropertyValue<TextAnchorType> getTextAnchor() const; + void setTextAnchor(DataDrivenPropertyValue<TextAnchorType>); static PropertyValue<float> getDefaultTextMaxAngle(); PropertyValue<float> getTextMaxAngle() const; void setTextMaxAngle(PropertyValue<float>); - static PropertyValue<float> getDefaultTextRotate(); - PropertyValue<float> getTextRotate() const; - void setTextRotate(PropertyValue<float>); + static DataDrivenPropertyValue<float> getDefaultTextRotate(); + DataDrivenPropertyValue<float> getTextRotate() const; + void setTextRotate(DataDrivenPropertyValue<float>); static PropertyValue<float> getDefaultTextPadding(); PropertyValue<float> getTextPadding() const; @@ -144,13 +158,13 @@ public: PropertyValue<bool> getTextKeepUpright() const; void setTextKeepUpright(PropertyValue<bool>); - static PropertyValue<TextTransformType> getDefaultTextTransform(); - PropertyValue<TextTransformType> getTextTransform() const; - void setTextTransform(PropertyValue<TextTransformType>); + static DataDrivenPropertyValue<TextTransformType> getDefaultTextTransform(); + DataDrivenPropertyValue<TextTransformType> getTextTransform() const; + void setTextTransform(DataDrivenPropertyValue<TextTransformType>); - static PropertyValue<std::array<float, 2>> getDefaultTextOffset(); - PropertyValue<std::array<float, 2>> getTextOffset() const; - void setTextOffset(PropertyValue<std::array<float, 2>>); + static DataDrivenPropertyValue<std::array<float, 2>> getDefaultTextOffset(); + DataDrivenPropertyValue<std::array<float, 2>> getTextOffset() const; + void setTextOffset(DataDrivenPropertyValue<std::array<float, 2>>); static PropertyValue<bool> getDefaultTextAllowOverlap(); PropertyValue<bool> getTextAllowOverlap() const; @@ -166,74 +180,103 @@ public: // Paint properties - static PropertyValue<float> getDefaultIconOpacity(); - PropertyValue<float> getIconOpacity(const optional<std::string>& klass = {}) const; - void setIconOpacity(PropertyValue<float>, const optional<std::string>& klass = {}); - - static PropertyValue<Color> getDefaultIconColor(); - PropertyValue<Color> getIconColor(const optional<std::string>& klass = {}) const; - void setIconColor(PropertyValue<Color>, const optional<std::string>& klass = {}); - - static PropertyValue<Color> getDefaultIconHaloColor(); - PropertyValue<Color> getIconHaloColor(const optional<std::string>& klass = {}) const; - void setIconHaloColor(PropertyValue<Color>, const optional<std::string>& klass = {}); - - static PropertyValue<float> getDefaultIconHaloWidth(); - PropertyValue<float> getIconHaloWidth(const optional<std::string>& klass = {}) const; - void setIconHaloWidth(PropertyValue<float>, const optional<std::string>& klass = {}); - - static PropertyValue<float> getDefaultIconHaloBlur(); - PropertyValue<float> getIconHaloBlur(const optional<std::string>& klass = {}) const; - void setIconHaloBlur(PropertyValue<float>, const optional<std::string>& klass = {}); + static DataDrivenPropertyValue<float> getDefaultIconOpacity(); + DataDrivenPropertyValue<float> getIconOpacity() const; + void setIconOpacity(DataDrivenPropertyValue<float>); + void setIconOpacityTransition(const TransitionOptions&); + TransitionOptions getIconOpacityTransition() const; + + static DataDrivenPropertyValue<Color> getDefaultIconColor(); + DataDrivenPropertyValue<Color> getIconColor() const; + void setIconColor(DataDrivenPropertyValue<Color>); + void setIconColorTransition(const TransitionOptions&); + TransitionOptions getIconColorTransition() const; + + static DataDrivenPropertyValue<Color> getDefaultIconHaloColor(); + DataDrivenPropertyValue<Color> getIconHaloColor() const; + void setIconHaloColor(DataDrivenPropertyValue<Color>); + void setIconHaloColorTransition(const TransitionOptions&); + TransitionOptions getIconHaloColorTransition() const; + + static DataDrivenPropertyValue<float> getDefaultIconHaloWidth(); + DataDrivenPropertyValue<float> getIconHaloWidth() const; + void setIconHaloWidth(DataDrivenPropertyValue<float>); + void setIconHaloWidthTransition(const TransitionOptions&); + TransitionOptions getIconHaloWidthTransition() const; + + static DataDrivenPropertyValue<float> getDefaultIconHaloBlur(); + DataDrivenPropertyValue<float> getIconHaloBlur() const; + void setIconHaloBlur(DataDrivenPropertyValue<float>); + void setIconHaloBlurTransition(const TransitionOptions&); + TransitionOptions getIconHaloBlurTransition() const; static PropertyValue<std::array<float, 2>> getDefaultIconTranslate(); - PropertyValue<std::array<float, 2>> getIconTranslate(const optional<std::string>& klass = {}) const; - void setIconTranslate(PropertyValue<std::array<float, 2>>, const optional<std::string>& klass = {}); + PropertyValue<std::array<float, 2>> getIconTranslate() const; + void setIconTranslate(PropertyValue<std::array<float, 2>>); + void setIconTranslateTransition(const TransitionOptions&); + TransitionOptions getIconTranslateTransition() const; static PropertyValue<TranslateAnchorType> getDefaultIconTranslateAnchor(); - PropertyValue<TranslateAnchorType> getIconTranslateAnchor(const optional<std::string>& klass = {}) const; - void setIconTranslateAnchor(PropertyValue<TranslateAnchorType>, const optional<std::string>& klass = {}); - - static PropertyValue<float> getDefaultTextOpacity(); - PropertyValue<float> getTextOpacity(const optional<std::string>& klass = {}) const; - void setTextOpacity(PropertyValue<float>, const optional<std::string>& klass = {}); - - static PropertyValue<Color> getDefaultTextColor(); - PropertyValue<Color> getTextColor(const optional<std::string>& klass = {}) const; - void setTextColor(PropertyValue<Color>, const optional<std::string>& klass = {}); - - static PropertyValue<Color> getDefaultTextHaloColor(); - PropertyValue<Color> getTextHaloColor(const optional<std::string>& klass = {}) const; - void setTextHaloColor(PropertyValue<Color>, const optional<std::string>& klass = {}); - - static PropertyValue<float> getDefaultTextHaloWidth(); - PropertyValue<float> getTextHaloWidth(const optional<std::string>& klass = {}) const; - void setTextHaloWidth(PropertyValue<float>, const optional<std::string>& klass = {}); - - static PropertyValue<float> getDefaultTextHaloBlur(); - PropertyValue<float> getTextHaloBlur(const optional<std::string>& klass = {}) const; - void setTextHaloBlur(PropertyValue<float>, const optional<std::string>& klass = {}); + PropertyValue<TranslateAnchorType> getIconTranslateAnchor() const; + void setIconTranslateAnchor(PropertyValue<TranslateAnchorType>); + void setIconTranslateAnchorTransition(const TransitionOptions&); + TransitionOptions getIconTranslateAnchorTransition() const; + + static DataDrivenPropertyValue<float> getDefaultTextOpacity(); + DataDrivenPropertyValue<float> getTextOpacity() const; + void setTextOpacity(DataDrivenPropertyValue<float>); + void setTextOpacityTransition(const TransitionOptions&); + TransitionOptions getTextOpacityTransition() const; + + static DataDrivenPropertyValue<Color> getDefaultTextColor(); + DataDrivenPropertyValue<Color> getTextColor() const; + void setTextColor(DataDrivenPropertyValue<Color>); + void setTextColorTransition(const TransitionOptions&); + TransitionOptions getTextColorTransition() const; + + static DataDrivenPropertyValue<Color> getDefaultTextHaloColor(); + DataDrivenPropertyValue<Color> getTextHaloColor() const; + void setTextHaloColor(DataDrivenPropertyValue<Color>); + void setTextHaloColorTransition(const TransitionOptions&); + TransitionOptions getTextHaloColorTransition() const; + + static DataDrivenPropertyValue<float> getDefaultTextHaloWidth(); + DataDrivenPropertyValue<float> getTextHaloWidth() const; + void setTextHaloWidth(DataDrivenPropertyValue<float>); + void setTextHaloWidthTransition(const TransitionOptions&); + TransitionOptions getTextHaloWidthTransition() const; + + static DataDrivenPropertyValue<float> getDefaultTextHaloBlur(); + DataDrivenPropertyValue<float> getTextHaloBlur() const; + void setTextHaloBlur(DataDrivenPropertyValue<float>); + void setTextHaloBlurTransition(const TransitionOptions&); + TransitionOptions getTextHaloBlurTransition() const; static PropertyValue<std::array<float, 2>> getDefaultTextTranslate(); - PropertyValue<std::array<float, 2>> getTextTranslate(const optional<std::string>& klass = {}) const; - void setTextTranslate(PropertyValue<std::array<float, 2>>, const optional<std::string>& klass = {}); + PropertyValue<std::array<float, 2>> getTextTranslate() const; + void setTextTranslate(PropertyValue<std::array<float, 2>>); + void setTextTranslateTransition(const TransitionOptions&); + TransitionOptions getTextTranslateTransition() const; static PropertyValue<TranslateAnchorType> getDefaultTextTranslateAnchor(); - PropertyValue<TranslateAnchorType> getTextTranslateAnchor(const optional<std::string>& klass = {}) const; - void setTextTranslateAnchor(PropertyValue<TranslateAnchorType>, const optional<std::string>& klass = {}); + PropertyValue<TranslateAnchorType> getTextTranslateAnchor() const; + void setTextTranslateAnchor(PropertyValue<TranslateAnchorType>); + void setTextTranslateAnchorTransition(const TransitionOptions&); + TransitionOptions getTextTranslateAnchorTransition() const; // Private implementation class Impl; - Impl* const impl; + const Impl& impl() const; - SymbolLayer(const Impl&); - SymbolLayer(const SymbolLayer&) = delete; + Mutable<Impl> mutableImpl() const; + SymbolLayer(Immutable<Impl>); + std::unique_ptr<Layer> cloneRef(const std::string& id) const final; }; template <> inline bool Layer::is<SymbolLayer>() const { - return type == Type::Symbol; + return getType() == LayerType::Symbol; } } // namespace style diff --git a/include/mbgl/style/light.hpp b/include/mbgl/style/light.hpp new file mode 100644 index 0000000000..c82792b28d --- /dev/null +++ b/include/mbgl/style/light.hpp @@ -0,0 +1,53 @@ +// This file is generated. Do not edit. + +#pragma once + +#include <mbgl/style/property_value.hpp> +#include <mbgl/style/transition_options.hpp> +#include <mbgl/style/types.hpp> +#include <mbgl/util/immutable.hpp> + +namespace mbgl { +namespace style { + +class LightObserver; + +class Light { +public: + Light(); + ~Light(); + + static LightAnchorType getDefaultAnchor(); + PropertyValue<LightAnchorType> getAnchor() const; + void setAnchor(PropertyValue<LightAnchorType>); + void setAnchorTransition(const TransitionOptions&); + TransitionOptions getAnchorTransition() const; + + static Position getDefaultPosition(); + PropertyValue<Position> getPosition() const; + void setPosition(PropertyValue<Position>); + void setPositionTransition(const TransitionOptions&); + TransitionOptions getPositionTransition() const; + + static Color getDefaultColor(); + PropertyValue<Color> getColor() const; + void setColor(PropertyValue<Color>); + void setColorTransition(const TransitionOptions&); + TransitionOptions getColorTransition() const; + + static float getDefaultIntensity(); + PropertyValue<float> getIntensity() const; + void setIntensity(PropertyValue<float>); + void setIntensityTransition(const TransitionOptions&); + TransitionOptions getIntensityTransition() const; + + class Impl; + Immutable<Impl> impl; + Mutable<Impl> mutableImpl() const; + + LightObserver* observer = nullptr; + void setObserver(LightObserver*); +}; + +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/style/position.hpp b/include/mbgl/style/position.hpp new file mode 100644 index 0000000000..3be8d1c55e --- /dev/null +++ b/include/mbgl/style/position.hpp @@ -0,0 +1,68 @@ +#pragma once + +#include <mbgl/util/constants.hpp> + +#include <array> + +namespace mbgl { +namespace style { +class Position { +public: + Position() = default; + Position(std::array<float, 3>& position_) + : radial(position_[0]), azimuthal(position_[1]), polar(position_[2]) { + calculateCartesian(); + }; + + friend bool operator==(const Position& lhs, const Position& rhs) { + return lhs.radial == rhs.radial && lhs.azimuthal == rhs.azimuthal && lhs.polar == rhs.polar; + // TODO this doesn't address wrapping, which would be better addressed by comparing cartesian coordinates but being calculated floats are ont to be trusted. + } + + friend bool operator!=(const Position& lhs, const Position& rhs) { + return !(lhs == rhs); + } + + const std::array<float, 3> getCartesian() const { + return { { x, y, z } }; + }; + + const std::array<float, 3> getSpherical() const { + return { { radial, azimuthal, polar } }; + }; + + void set(std::array<float, 3>& position_) { + radial = position_[0]; + azimuthal = position_[1]; + polar = position_[2]; + calculateCartesian(); + }; + + // Utility function to be used only during interpolation; this leaves spherical coordinates undefined. + void setCartesian(std::array<float, 3>& position_) { + x = position_[0]; + y = position_[1]; + z = position_[2]; + } + +private: + float radial; + float azimuthal; + float polar; + float x; + float y; + float z; + + void calculateCartesian() { + // We abstract "north"/"up" (compass-wise) to be 0° when really this is 90° (π/2): we + // correct for that here + const float _a = (azimuthal + 90) * util::DEG2RAD; + const float _p = polar * util::DEG2RAD; + + x = radial * std::cos(_a) * std::sin(_p); + y = radial * std::sin(_a) * std::sin(_p); + z = radial * std::cos(_p); + }; +}; +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/style/property_value.hpp b/include/mbgl/style/property_value.hpp index 83c4b4cf1b..02d3a31148 100644 --- a/include/mbgl/style/property_value.hpp +++ b/include/mbgl/style/property_value.hpp @@ -1,20 +1,16 @@ #pragma once #include <mbgl/util/variant.hpp> -#include <mbgl/style/function.hpp> +#include <mbgl/style/undefined.hpp> +#include <mbgl/style/function/camera_function.hpp> namespace mbgl { namespace style { -class Undefined {}; - -inline bool operator==(const Undefined&, const Undefined&) { return true; } -inline bool operator!=(const Undefined&, const Undefined&) { return false; } - template <class T> class PropertyValue { private: - using Value = variant<Undefined, T, Function<T>>; + using Value = variant<Undefined, T, CameraFunction<T>>; Value value; friend bool operator==(const PropertyValue& lhs, const PropertyValue& rhs) { @@ -26,23 +22,26 @@ private: } public: - PropertyValue() : value() {} - PropertyValue( T constant) : value(constant) {} - PropertyValue(Function<T> function) : value(function) {} - - bool isUndefined() const { return value.which() == 0; } - bool isConstant() const { return value.which() == 1; } - bool isFunction() const { return value.which() == 2; } + PropertyValue() : value() {} + PropertyValue( T constant) : value(constant) {} + PropertyValue(CameraFunction<T> function) : value(function) {} - const T & asConstant() const { return value.template get< T >(); } - const Function<T>& asFunction() const { return value.template get<Function<T>>(); } + bool isUndefined() const { return value.which() == 0; } + bool isConstant() const { return value.which() == 1; } + bool isCameraFunction() const { return value.which() == 2; } + bool isDataDriven() const { return false; } - explicit operator bool() const { return !isUndefined(); }; + const T & asConstant() const { return value.template get< T >(); } + const CameraFunction<T>& asCameraFunction() const { return value.template get<CameraFunction<T>>(); } template <typename Evaluator> - auto evaluate(const Evaluator& evaluator) const { + auto evaluate(const Evaluator& evaluator, TimePoint = {}) const { return Value::visit(value, evaluator); } + + bool hasDataDrivenPropertyDifference(const PropertyValue<T>&) const { + return false; + } }; } // namespace style diff --git a/include/mbgl/style/source.hpp b/include/mbgl/style/source.hpp index 870c81fda6..cec9619451 100644 --- a/include/mbgl/style/source.hpp +++ b/include/mbgl/style/source.hpp @@ -2,14 +2,24 @@ #include <mbgl/util/noncopyable.hpp> #include <mbgl/util/optional.hpp> +#include <mbgl/util/any.hpp> +#include <mbgl/util/immutable.hpp> #include <mbgl/style/types.hpp> #include <memory> #include <string> namespace mbgl { + +class FileSource; + namespace style { +class VectorSource; +class RasterSource; +class GeoJSONSource; +class SourceObserver; + /** * The runtime representation of a [source](https://www.mapbox.com/mapbox-gl-style-spec/#sources) from the Mapbox Style * Specification. @@ -45,21 +55,28 @@ public: return is<T>() ? reinterpret_cast<const T*>(this) : nullptr; } - const std::string& getID() const; - - // Create a new source with the specified `id`. All other properties - // are copied from this source. - std::unique_ptr<Source> copy(const std::string& id) const; - + SourceType getType() const; + std::string getID() const; optional<std::string> getAttribution() const; // Private implementation class Impl; - const std::unique_ptr<Impl> baseImpl; + Immutable<Impl> baseImpl; + + Source(Immutable<Impl>); + + void setObserver(SourceObserver*); + SourceObserver* observer = nullptr; + + virtual void loadDescription(FileSource&) = 0; + void dumpDebugLogs() const; + + bool loaded = false; -protected: - const SourceType type; - Source(SourceType, std::unique_ptr<Impl>); + // 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; }; } // namespace style diff --git a/include/mbgl/style/sources/geojson_source.hpp b/include/mbgl/style/sources/geojson_source.hpp index ede0301725..2dcfec51aa 100644 --- a/include/mbgl/style/sources/geojson_source.hpp +++ b/include/mbgl/style/sources/geojson_source.hpp @@ -4,26 +4,12 @@ #include <mbgl/util/geojson.hpp> #include <mbgl/util/optional.hpp> -#include <mapbox/geojson.hpp> - -namespace mapbox { - -namespace geojsonvt { -class GeoJSONVT; -} // namespace geojsonvt - -namespace supercluster { -class Supercluster; -} // namespace supercluster +namespace mbgl { -} // namespace mapbox +class AsyncRequest; -namespace mbgl { namespace style { -using GeoJSONVTPointer = std::unique_ptr<mapbox::geojsonvt::GeoJSONVT>; -using SuperclusterPointer = std::unique_ptr<mapbox::supercluster::Supercluster>; - struct GeoJSONOptions { // GeoJSON-VT options uint8_t maxzoom = 18; @@ -38,22 +24,27 @@ struct GeoJSONOptions { class GeoJSONSource : public Source { public: - GeoJSONSource(const std::string& id, const GeoJSONOptions options_ = GeoJSONOptions()); + GeoJSONSource(const std::string& id, const GeoJSONOptions& = {}); + ~GeoJSONSource() final; void setURL(const std::string& url); void setGeoJSON(const GeoJSON&); optional<std::string> getURL() const; - // Private implementation - class Impl; - Impl* const impl; + const Impl& impl() const; + + void loadDescription(FileSource&) final; + +private: + optional<std::string> url; + std::unique_ptr<AsyncRequest> req; }; template <> inline bool Source::is<GeoJSONSource>() const { - return type == SourceType::GeoJSON; + return getType() == SourceType::GeoJSON; } } // namespace style diff --git a/include/mbgl/style/sources/image_source.hpp b/include/mbgl/style/sources/image_source.hpp new file mode 100644 index 0000000000..009764291f --- /dev/null +++ b/include/mbgl/style/sources/image_source.hpp @@ -0,0 +1,41 @@ +#pragma once + +#include <mbgl/style/source.hpp> +#include <mbgl/util/image.hpp> +#include <mbgl/util/optional.hpp> + +namespace mbgl { +class LatLng; +class AsyncRequest; + +namespace style { + +class ImageSource : public Source { +public: + ImageSource(std::string id, const std::array<LatLng, 4>); + ~ImageSource() override; + + optional<std::string> getURL() const; + void setURL(const std::string& url); + + void setImage(PremultipliedImage&&); + + void setCoordinates(const std::array<LatLng, 4>&); + std::array<LatLng, 4> getCoordinates() const; + + class Impl; + const Impl& impl() const; + + void loadDescription(FileSource&) final; +private: + optional<std::string> url; + std::unique_ptr<AsyncRequest> req; +}; + +template <> +inline bool Source::is<ImageSource>() const { + return getType() == SourceType::Image; +} + +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/style/sources/raster_source.hpp b/include/mbgl/style/sources/raster_source.hpp index 395f25e51d..7f23a7ca4b 100644 --- a/include/mbgl/style/sources/raster_source.hpp +++ b/include/mbgl/style/sources/raster_source.hpp @@ -5,23 +5,34 @@ #include <mbgl/util/variant.hpp> namespace mbgl { + +class AsyncRequest; + namespace style { class RasterSource : public Source { public: RasterSource(std::string id, variant<std::string, Tileset> urlOrTileset, uint16_t tileSize); + ~RasterSource() final; + const variant<std::string, Tileset>& getURLOrTileset() const; optional<std::string> getURL() const; - // Private implementation + uint16_t getTileSize() const; class Impl; - Impl* const impl; + const Impl& impl() const; + + void loadDescription(FileSource&) final; + +private: + const variant<std::string, Tileset> urlOrTileset; + std::unique_ptr<AsyncRequest> req; }; template <> inline bool Source::is<RasterSource>() const { - return type == SourceType::Raster; + return getType() == SourceType::Raster; } } // namespace style diff --git a/include/mbgl/style/sources/vector_source.hpp b/include/mbgl/style/sources/vector_source.hpp index 8626ce160a..6f16974b40 100644 --- a/include/mbgl/style/sources/vector_source.hpp +++ b/include/mbgl/style/sources/vector_source.hpp @@ -5,23 +5,32 @@ #include <mbgl/util/variant.hpp> namespace mbgl { + +class AsyncRequest; + namespace style { class VectorSource : public Source { public: VectorSource(std::string id, variant<std::string, Tileset> urlOrTileset); + ~VectorSource() final; + const variant<std::string, Tileset>& getURLOrTileset() const; optional<std::string> getURL() const; - // Private implementation - class Impl; - Impl* const impl; + const Impl& impl() const; + + void loadDescription(FileSource&) final; + +private: + const variant<std::string, Tileset> urlOrTileset; + std::unique_ptr<AsyncRequest> req; }; template <> inline bool Source::is<VectorSource>() const { - return type == SourceType::Vector; + return getType() == SourceType::Vector; } } // namespace style diff --git a/include/mbgl/style/style.hpp b/include/mbgl/style/style.hpp new file mode 100644 index 0000000000..cb84922b4d --- /dev/null +++ b/include/mbgl/style/style.hpp @@ -0,0 +1,81 @@ +#pragma once + +#include <mbgl/style/transition_options.hpp> +#include <mbgl/util/geo.hpp> + +#include <string> +#include <vector> +#include <memory> + +namespace mbgl { + +class FileSource; +class Scheduler; + +namespace style { + +class Light; +class Image; +class Source; +class Layer; + +class Style { +public: + Style(Scheduler&, FileSource&, float pixelRatio); + ~Style(); + + void loadJSON(const std::string&); + void loadURL(const std::string&); + + std::string getJSON() const; + std::string getURL() const; + + // Defaults + std::string getName() const; + LatLng getDefaultLatLng() const; + double getDefaultZoom() const; + double getDefaultBearing() const; + double getDefaultPitch() const; + + // TransitionOptions + TransitionOptions getTransitionOptions() const; + void setTransitionOptions(const TransitionOptions&); + + // Light + Light* getLight(); + const Light* getLight() const; + + void setLight(std::unique_ptr<Light>); + + // Images + const Image* getImage(const std::string&) const; + void addImage(std::unique_ptr<Image>); + void removeImage(const std::string&); + + // Sources + std::vector< Source*> getSources(); + std::vector<const Source*> getSources() const; + + Source* getSource(const std::string&); + const Source* getSource(const std::string&) const; + + void addSource(std::unique_ptr<Source>); + std::unique_ptr<Source> removeSource(const std::string& sourceID); + + // Layers + std::vector< Layer*> getLayers(); + std::vector<const Layer*> getLayers() const; + + Layer* getLayer(const std::string&); + const Layer* getLayer(const std::string&) const; + + void addLayer(std::unique_ptr<Layer>, const optional<std::string>& beforeLayerID = {}); + std::unique_ptr<Layer> removeLayer(const std::string& layerID); + + // Private implementation + class Impl; + const std::unique_ptr<Impl> impl; +}; + +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/style/transition_options.hpp b/include/mbgl/style/transition_options.hpp index eab6ef7039..87a81717a0 100644 --- a/include/mbgl/style/transition_options.hpp +++ b/include/mbgl/style/transition_options.hpp @@ -8,19 +8,22 @@ namespace style { class TransitionOptions { public: - optional<Duration> duration = {}; - optional<Duration> delay = {}; + optional<Duration> duration; + optional<Duration> delay; - TransitionOptions reverseMerge(const TransitionOptions& defaults) const { - TransitionOptions options; - - options.duration = duration ? duration : defaults.duration; - options.delay = delay ? delay : defaults.delay; + TransitionOptions(optional<Duration> duration_ = {}, + optional<Duration> delay_ = {}) + : duration(std::move(duration_)), + delay(std::move(delay_)) {} - return options; + TransitionOptions reverseMerge(const TransitionOptions& defaults) const { + return { + duration ? duration : defaults.duration, + delay ? delay : defaults.delay + }; } - explicit operator bool() const { + bool isDefined() const { return duration || delay; } }; diff --git a/include/mbgl/style/types.hpp b/include/mbgl/style/types.hpp index 1f2f5c3105..44b16f16e7 100644 --- a/include/mbgl/style/types.hpp +++ b/include/mbgl/style/types.hpp @@ -10,7 +10,8 @@ enum class SourceType : uint8_t { Raster, GeoJSON, Video, - Annotations + Annotations, + Image }; namespace style { @@ -92,5 +93,10 @@ enum class IconTextFitType : uint8_t { Height }; +enum class LightAnchorType: bool { + Map, + Viewport +}; + } // namespace style } // namespace mbgl diff --git a/include/mbgl/style/undefined.hpp b/include/mbgl/style/undefined.hpp new file mode 100644 index 0000000000..e43f132a80 --- /dev/null +++ b/include/mbgl/style/undefined.hpp @@ -0,0 +1,12 @@ +#pragma once + +namespace mbgl { +namespace style { + +class Undefined {}; + +inline bool operator==(const Undefined&, const Undefined&) { return true; } +inline bool operator!=(const Undefined&, const Undefined&) { return false; } + +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/util/any.hpp b/include/mbgl/util/any.hpp new file mode 100644 index 0000000000..eea64b188a --- /dev/null +++ b/include/mbgl/util/any.hpp @@ -0,0 +1,10 @@ +#pragma once + +#include <linb/any.hpp> + +namespace mbgl { + +using linb::any; +using linb::any_cast; + +} // namespace mbgl diff --git a/include/mbgl/util/async_task.hpp b/include/mbgl/util/async_task.hpp new file mode 100644 index 0000000000..69746c3eb3 --- /dev/null +++ b/include/mbgl/util/async_task.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include <mbgl/util/noncopyable.hpp> + +#include <memory> +#include <functional> + +namespace mbgl { +namespace util { + +class AsyncTask : private util::noncopyable { +public: + AsyncTask(std::function<void()>&&); + ~AsyncTask(); + + void send(); + +private: + class Impl; + std::unique_ptr<Impl> impl; +}; + +} // namespace util +} // namespace mbgl diff --git a/include/mbgl/util/chrono.hpp b/include/mbgl/util/chrono.hpp index 4adf030331..723cd131e3 100644 --- a/include/mbgl/util/chrono.hpp +++ b/include/mbgl/util/chrono.hpp @@ -29,7 +29,7 @@ std::string rfc1123(Timestamp); std::string iso8601(Timestamp); Timestamp parseTimestamp(const char *); - + Timestamp parseTimestamp(const int32_t timestamp); // C++17 polyfill diff --git a/include/mbgl/util/color.hpp b/include/mbgl/util/color.hpp index b595fdb1d7..300d7fae82 100644 --- a/include/mbgl/util/color.hpp +++ b/include/mbgl/util/color.hpp @@ -1,7 +1,6 @@ #pragma once #include <mbgl/util/optional.hpp> -#include <mbgl/util/util.hpp> #include <cassert> #include <string> diff --git a/include/mbgl/util/compression.hpp b/include/mbgl/util/compression.hpp index 5e232187c3..93a3ddb8bc 100644 --- a/include/mbgl/util/compression.hpp +++ b/include/mbgl/util/compression.hpp @@ -4,9 +4,9 @@ namespace mbgl { namespace util { - + std::string compress(const std::string& raw); std::string decompress(const std::string& raw); - + } // namespace util } // namespace mbgl diff --git a/include/mbgl/util/constants.hpp b/include/mbgl/util/constants.hpp index a5a83287bc..c2250c6f2e 100644 --- a/include/mbgl/util/constants.hpp +++ b/include/mbgl/util/constants.hpp @@ -2,7 +2,6 @@ #include <mbgl/util/chrono.hpp> #include <mbgl/util/unitbezier.hpp> -#include <mbgl/util/util.hpp> #include <cmath> #include <string> @@ -39,17 +38,19 @@ constexpr double MAX_ZOOM = 25.5; constexpr float MIN_ZOOM_F = MIN_ZOOM; constexpr float MAX_ZOOM_F = MAX_ZOOM; +constexpr uint8_t DEFAULT_PREFETCH_ZOOM_DELTA = 4; + constexpr uint64_t DEFAULT_MAX_CACHE_SIZE = 50 * 1024 * 1024; -constexpr Duration DEFAULT_FADE_DURATION = Milliseconds(300); +constexpr Duration DEFAULT_TRANSITION_DURATION = Milliseconds(300); constexpr Seconds CLOCK_SKEW_RETRY_TIMEOUT { 30 }; -const UnitBezier DEFAULT_TRANSITION_EASE = { 0, 0, 0.25, 1 }; - +constexpr UnitBezier DEFAULT_TRANSITION_EASE = { 0, 0, 0.25, 1 }; + constexpr int DEFAULT_RATE_LIMIT_TIMEOUT = 5; constexpr const char* API_BASE_URL = "https://api.mapbox.com"; - + } // namespace util namespace debug { diff --git a/include/mbgl/util/convert.hpp b/include/mbgl/util/convert.hpp index bedb1a4a08..02ec7feef9 100644 --- a/include/mbgl/util/convert.hpp +++ b/include/mbgl/util/convert.hpp @@ -10,7 +10,7 @@ namespace util { template<typename To, typename From, std::size_t Size, typename = std::enable_if_t<std::is_convertible<From, To>::value>> MBGL_CONSTEXPR std::array<To, Size> convert(const std::array<From, Size>&from) { - std::array<To, Size> to {}; + 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 48ffda463e..369ca86bfd 100644 --- a/include/mbgl/util/enum.hpp +++ b/include/mbgl/util/enum.hpp @@ -11,26 +11,26 @@ namespace mbgl { template <typename T> class Enum { public: - using Value = std::pair<const T, const char *>; - - static const char * toString(T t) { - auto it = std::find_if(begin, end, [&] (const auto& v) { return t == v.first; }); - assert(it != end); return it->second; - } - - static optional<T> toEnum(const std::string& s) { - auto it = std::find_if(begin, end, [&] (const auto& v) { return s == v.second; }); - return it == end ? optional<T>() : it->first; - } - -private: - static const Value* begin; - static const Value* end; + static const char * toString(T); + static optional<T> toEnum(const std::string&); }; -#define MBGL_DEFINE_ENUM(type, strings...) \ -const constexpr Enum<type>::Value type##_names[] = strings; \ -template <> const Enum<type>::Value* Enum<type>::begin = std::begin(type##_names); \ -template <> const Enum<type>::Value* Enum<type>::end = std::end(type##_names) +#define MBGL_DEFINE_ENUM(T, values...) \ + \ +static const constexpr std::pair<const T, const char *> T##_names[] = values; \ + \ +template <> \ +const char * Enum<T>::toString(T t) { \ + auto it = std::find_if(std::begin(T##_names), std::end(T##_names), \ + [&] (const auto& v) { return t == v.first; }); \ + assert(it != std::end(T##_names)); return it->second; \ +} \ + \ +template <> \ +optional<T> Enum<T>::toEnum(const std::string& s) { \ + auto it = std::find_if(std::begin(T##_names), std::end(T##_names), \ + [&] (const auto& v) { return s == v.second; }); \ + return it == std::end(T##_names) ? optional<T>() : it->first; \ +} } // namespace mbgl diff --git a/include/mbgl/util/event.hpp b/include/mbgl/util/event.hpp index 7ad3d914e8..5fe0baae3c 100644 --- a/include/mbgl/util/event.hpp +++ b/include/mbgl/util/event.hpp @@ -40,7 +40,7 @@ struct EventPermutation { }; constexpr EventSeverity disabledEventSeverities[] = { -#ifdef NDEBUG +#ifndef NDEBUG EventSeverity(-1) // Avoid zero size array #else EventSeverity::Debug diff --git a/include/mbgl/util/exception.hpp b/include/mbgl/util/exception.hpp index 46de8528c7..a9804e96c5 100644 --- a/include/mbgl/util/exception.hpp +++ b/include/mbgl/util/exception.hpp @@ -20,5 +20,20 @@ struct MisuseException : Exception { MisuseException(const std::string &msg) : Exception(msg) {} }; +struct StyleParseException : Exception { + StyleParseException(const char *msg) : Exception(msg) {} + StyleParseException(const std::string &msg) : Exception(msg) {} +}; + +struct StyleLoadException : Exception { + StyleLoadException(const char *msg) : Exception(msg) {} + StyleLoadException(const std::string &msg) : Exception(msg) {} +}; + +struct NotFoundException : Exception { + NotFoundException(const char *msg) : Exception(msg) {} + NotFoundException(const std::string &msg) : Exception(msg) {} +}; + } // namespace util } // namespace mbgl diff --git a/include/mbgl/util/feature.hpp b/include/mbgl/util/feature.hpp index b72aa15ddd..4eeceda944 100644 --- a/include/mbgl/util/feature.hpp +++ b/include/mbgl/util/feature.hpp @@ -12,4 +12,21 @@ using PropertyMap = mapbox::geometry::property_map; using FeatureIdentifier = mapbox::geometry::identifier; using Feature = mapbox::geometry::feature<double>; +template <class T> +optional<T> numericValue(const Value& value) { + return value.match( + [] (uint64_t t) { + return optional<T>(t); + }, + [] (int64_t t) { + return optional<T>(t); + }, + [] (double t) { + return optional<T>(t); + }, + [] (auto) { + return optional<T>(); + }); +} + } // namespace mbgl diff --git a/include/mbgl/util/geo.hpp b/include/mbgl/util/geo.hpp index 9dca10eb84..6d725b102b 100644 --- a/include/mbgl/util/geo.hpp +++ b/include/mbgl/util/geo.hpp @@ -1,5 +1,6 @@ #pragma once +#include <mbgl/math/clamp.hpp> #include <mbgl/math/wrap.hpp> #include <mbgl/util/constants.hpp> @@ -9,6 +10,7 @@ #include <mapbox/geometry/box.hpp> #include <cmath> +#include <stdexcept> namespace mbgl { @@ -20,68 +22,63 @@ using ScreenLineString = mapbox::geometry::line_string<double>; using ScreenBox = mapbox::geometry::box<double>; class LatLng { -public: - struct null {}; - - double latitude; - double longitude; +private: + double lat; + double lon; +public: enum WrapMode : bool { Unwrapped, Wrapped }; - LatLng(null) : latitude(std::numeric_limits<double>::quiet_NaN()), longitude(latitude) {} - - LatLng(double lat = 0, double lon = 0, WrapMode mode = Unwrapped) - : latitude(lat), longitude(lon) { if (mode == Wrapped) wrap(); } - - LatLng wrapped() const { return { latitude, longitude, Wrapped }; } + LatLng(double lat_ = 0, double lon_ = 0, WrapMode mode = Unwrapped) + : lat(lat_), lon(lon_) { + if (std::isnan(lat)) { + throw std::domain_error("latitude must not be NaN"); + } + if (std::isnan(lon)) { + throw std::domain_error("longitude must not be NaN"); + } + if (std::abs(lat) > 90.0) { + throw std::domain_error("latitude must be between -90 and 90"); + } + if (!std::isfinite(lon)) { + throw std::domain_error("longitude must not be infinite"); + } + if (mode == Wrapped) { + wrap(); + } + } + + double latitude() const { return lat; } + double longitude() const { return lon; } + + LatLng wrapped() const { return { lat, lon, Wrapped }; } void wrap() { - longitude = util::wrap(longitude, -util::LONGITUDE_MAX, util::LONGITUDE_MAX); + lon = util::wrap(lon, -util::LONGITUDE_MAX, util::LONGITUDE_MAX); } // If the distance from start to end longitudes is between half and full // world, unwrap the start longitude to ensure the shortest path is taken. void unwrapForShortestPath(const LatLng& end) { - const double delta = std::abs(end.longitude - longitude); + const double delta = std::abs(end.lon - lon); if (delta < util::LONGITUDE_MAX || delta > util::DEGREES_MAX) return; - if (longitude > 0 && end.longitude < 0) longitude -= util::DEGREES_MAX; - else if (longitude < 0 && end.longitude > 0) longitude += util::DEGREES_MAX; - } - - explicit operator bool() const { - return !(std::isnan(latitude) || std::isnan(longitude)); + if (lon > 0 && end.lon < 0) lon -= util::DEGREES_MAX; + else if (lon < 0 && end.lon > 0) lon += util::DEGREES_MAX; } // Constructs a LatLng object with the top left position of the specified tile. LatLng(const CanonicalTileID& id); LatLng(const UnwrappedTileID& id); -}; -constexpr bool operator==(const LatLng& a, const LatLng& b) { - return a.latitude == b.latitude && a.longitude == b.longitude; -} - -constexpr bool operator!=(const LatLng& a, const LatLng& b) { - return !(a == b); -} - -class ProjectedMeters { -public: - double northing; - double easting; - - ProjectedMeters(double n = 0, double e = 0) - : northing(n), easting(e) {} + friend bool operator==(const LatLng& a, const LatLng& b) { + return a.lat == b.lat && a.lon == b.lon; + } - explicit operator bool() const { - return !(std::isnan(northing) || std::isnan(easting)); + friend bool operator!=(const LatLng& a, const LatLng& b) { + return !(a == b); } }; -constexpr bool operator==(const ProjectedMeters& a, const ProjectedMeters& b) { - return a.northing == b.northing && a.easting == b.easting; -} - class LatLngBounds { public: // Return a bounds covering the entire (unwrapped) world. @@ -111,10 +108,14 @@ public: // Constructs a LatLngBounds object with the tile's exact boundaries. LatLngBounds(const CanonicalTileID&); - double south() const { return sw.latitude; } - double west() const { return sw.longitude; } - double north() const { return ne.latitude; } - double east() const { return ne.longitude; } + bool valid() const { + return (sw.latitude() <= ne.latitude()) && (sw.longitude() <= ne.longitude()); + } + + double south() const { return sw.latitude(); } + double west() const { return sw.longitude(); } + double north() const { return ne.latitude(); } + double east() const { return ne.longitude(); } LatLng southwest() const { return sw; } LatLng northeast() const { return ne; } @@ -122,15 +123,25 @@ public: LatLng northwest() const { return LatLng(north(), west()); } LatLng center() const { - return LatLng((sw.latitude + ne.latitude) / 2, - (sw.longitude + ne.longitude) / 2); + return LatLng((sw.latitude() + ne.latitude()) / 2, + (sw.longitude() + ne.longitude()) / 2); + } + + LatLng constrain(const LatLng& p) const { + if (contains(p)) { + return p; + } + return LatLng { + util::clamp(p.latitude(), sw.latitude(), ne.latitude()), + util::clamp(p.longitude(), sw.longitude(), ne.longitude()) + }; } void extend(const LatLng& point) { - if (point.latitude < sw.latitude) sw.latitude = point.latitude; - if (point.latitude > ne.latitude) ne.latitude = point.latitude; - if (point.longitude < sw.longitude) sw.longitude = point.longitude; - if (point.longitude > ne.longitude) ne.longitude = point.longitude; + sw = LatLng(std::min(point.latitude(), sw.latitude()), + std::min(point.longitude(), sw.longitude())); + ne = LatLng(std::max(point.latitude(), ne.latitude()), + std::max(point.longitude(), ne.longitude())); } void extend(const LatLngBounds& bounds) { @@ -139,22 +150,22 @@ public: } bool isEmpty() const { - return sw.latitude > ne.latitude || - sw.longitude > ne.longitude; + return sw.latitude() > ne.latitude() || + 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); + 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); + return (area.ne.latitude() > sw.latitude() && + area.sw.latitude() < ne.latitude() && + area.ne.longitude() > sw.longitude() && + area.sw.longitude() < ne.longitude()); } private: @@ -164,17 +175,14 @@ private: LatLngBounds(LatLng sw_, LatLng ne_) : sw(std::move(sw_)), ne(std::move(ne_)) {} - friend constexpr bool operator==(const LatLngBounds&, const LatLngBounds&); - friend constexpr bool operator!=(const LatLngBounds&, const LatLngBounds&); -}; - -constexpr bool operator==(const LatLngBounds& a, const LatLngBounds& b) { - return a.sw == b.sw && a.ne == b.ne; -} + friend bool operator==(const LatLngBounds& a, const LatLngBounds& b) { + return a.sw == b.sw && a.ne == b.ne; + } -constexpr bool operator!=(const LatLngBounds& a, const LatLngBounds& b) { - return !(a == b); -} + friend bool operator!=(const LatLngBounds& a, const LatLngBounds& b) { + return !(a == b); + } +}; // Determines the orientation of the map. enum class NorthOrientation : uint8_t { @@ -186,36 +194,60 @@ enum class NorthOrientation : uint8_t { /// The distance on each side between a rectangle and a rectangle within. class EdgeInsets { -public: - double top = 0; ///< Number of pixels inset from the top edge. - double left = 0; ///< Number of pixels inset from the left edge. - double bottom = 0; ///< Number of pixels inset from the bottom edge. - double right = 0; ///< Number of pixels inset from the right edge. - - EdgeInsets() {} - - EdgeInsets(const double t, const double l, const double b, const double r) - : top(t), left(l), bottom(b), right(r) {} +private: + double _top; // Number of pixels inset from the top edge. + double _left; // Number of pixels inset from the left edge. + double _bottom; // Number of pixels inset from the bottom edge. + double _right; // Number of pixels inset from the right edge. - explicit operator bool() const { - return !(std::isnan(top) || std::isnan(left) || std::isnan(bottom) || std::isnan(right)) - && (top || left || bottom || right); +public: + EdgeInsets(double t_ = 0, double l_ = 0, double b_ = 0, double r_ = 0) + : _top(t_), _left(l_), _bottom(b_), _right(r_) { + if (std::isnan(_top)) { + throw std::domain_error("top must not be NaN"); + } + if (std::isnan(_left)) { + throw std::domain_error("left must not be NaN"); + } + if (std::isnan(_bottom)) { + throw std::domain_error("bottom must not be NaN"); + } + if (std::isnan(_right)) { + throw std::domain_error("right must not be NaN"); + } + } + + double top() const { return _top; } + double left() const { return _left; } + double bottom() const { return _bottom; } + double right() const { return _right; } + + bool isFlush() const { + return _top == 0 && _left == 0 && _bottom == 0 && _right == 0; } void operator+=(const EdgeInsets& o) { - top += o.top; - left += o.left; - bottom += o.bottom; - right += o.right; + _top += o._top; + _left += o._left; + _bottom += o._bottom; + _right += o._right; } EdgeInsets operator+(const EdgeInsets& o) const { return { - top + o.top, left + o.left, bottom + o.bottom, right + o.right, + _top + o._top, _left + o._left, _bottom + o._bottom, _right + o._right, }; } ScreenCoordinate getCenter(uint16_t width, uint16_t height) const; + + friend bool operator==(const EdgeInsets& a, const EdgeInsets& b) { + return a._top == b._top && a._left == b._left && a._bottom == b._bottom && a._right == b._right; + } + + friend bool operator!=(const EdgeInsets& a, const EdgeInsets& b) { + return !(a == b); + } }; } // namespace mbgl diff --git a/include/mbgl/util/ignore.hpp b/include/mbgl/util/ignore.hpp new file mode 100644 index 0000000000..577bcf4d91 --- /dev/null +++ b/include/mbgl/util/ignore.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include <initializer_list> + +namespace mbgl { +namespace util { + +// Accept any number of parameters of any types, and do nothing with them. +// Useful for providing a context for parameter pack expansion where a legal +// expansion context is not otherwise available. +// +// See https://github.com/mapbox/cpp/blob/master/C%2B%2B%20Structural%20Metaprogramming.md +// for more details. +// +template <class... Ts> void ignore(Ts&&...) {} + +// std::initializer_list overload, for situations where you need sequenced +// modifications. +// +template <class T> void ignore(const std::initializer_list<T>&) {} + +// Handle the zero-argument case. +inline void ignore(const std::initializer_list<int>&) {} + +} // namespace util +} // namespace mbgl diff --git a/include/mbgl/util/image.hpp b/include/mbgl/util/image.hpp index 1d84d4824a..cb28f3da8d 100644 --- a/include/mbgl/util/image.hpp +++ b/include/mbgl/util/image.hpp @@ -1,6 +1,7 @@ #pragma once #include <mbgl/util/noncopyable.hpp> +#include <mbgl/util/geometry.hpp> #include <mbgl/util/size.hpp> #include <string> @@ -24,33 +25,116 @@ public: : size(std::move(size_)), data(std::make_unique<uint8_t[]>(bytes())) {} + Image(Size size_, const uint8_t* srcData, std::size_t srcLength) + : size(std::move(size_)) { + if (srcLength != bytes()) { + throw std::invalid_argument("mismatched image size"); + } + data = std::make_unique<uint8_t[]>(bytes()); + std::copy(srcData, srcData + srcLength, data.get()); + } + Image(Size size_, std::unique_ptr<uint8_t[]> data_) : size(std::move(size_)), data(std::move(data_)) {} Image(Image&& o) : size(o.size), - data(std::move(o.data)) {} + data(std::move(o.data)) { + o.size.width = o.size.height = 0; + } Image& operator=(Image&& o) { size = o.size; data = std::move(o.data); + o.size.width = o.size.height = 0; return *this; } - bool operator==(const Image& rhs) const { - return size == rhs.size && - std::equal(data.get(), data.get() + bytes(), rhs.data.get(), - rhs.data.get() + rhs.bytes()); + friend bool operator==(const Image& lhs, const Image& rhs) { + return std::equal(lhs.data.get(), lhs.data.get() + lhs.bytes(), + rhs.data.get(), rhs.data.get() + rhs.bytes()); + } + + friend bool operator!=(const Image& lhs, const Image& rhs) { + return !(lhs == rhs); } bool valid() const { - return size && data.get() != nullptr; + return !size.isEmpty() && data.get() != nullptr; + } + + template <typename T = Image> + T clone() const { + T copy_(size); + std::copy(data.get(), data.get() + bytes(), copy_.data.get()); + return copy_; } size_t stride() const { return channels * size.width; } size_t bytes() const { return stride() * size.height; } + void fill(uint8_t value) { + std::fill(data.get(), data.get() + bytes(), value); + } + + void resize(Size size_) { + if (size == size_) { + return; + } + Image newImage(size_); + newImage.fill(0); + copy(*this, newImage, {0, 0}, {0, 0}, { + std::min(size.width, size_.width), + std::min(size.height, size_.height) + }); + operator=(std::move(newImage)); + } + + // 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. + static void copy(const Image& srcImg, Image& dstImg, const Point<uint32_t>& srcPt, const Point<uint32_t>& dstPt, const Size& size) { + if (size.isEmpty()) { + return; + } + + if (!srcImg.valid()) { + throw std::invalid_argument("invalid source for image copy"); + } + + if (!dstImg.valid()) { + throw std::invalid_argument("invalid destination for image copy"); + } + + if (size.width > srcImg.size.width || + size.height > srcImg.size.height || + srcPt.x > srcImg.size.width - size.width || + srcPt.y > srcImg.size.height - size.height) { + throw std::out_of_range("out of range source coordinates for image copy"); + } + + if (size.width > dstImg.size.width || + size.height > dstImg.size.height || + dstPt.x > dstImg.size.width - size.width || + dstPt.y > dstImg.size.height - size.height) { + throw std::out_of_range("out of range destination coordinates for image copy"); + } + + const uint8_t* srcData = srcImg.data.get(); + uint8_t* dstData = dstImg.data.get(); + + assert(srcData != dstData); + + for (uint32_t y = 0; y < size.height; y++) { + const std::size_t srcOffset = (srcPt.y + y) * srcImg.stride() + srcPt.x * channels; + const std::size_t dstOffset = (dstPt.y + y) * dstImg.stride() + dstPt.x * channels; + std::copy(srcData + srcOffset, + srcData + srcOffset + size.width * channels, + dstData + dstOffset); + } + } + Size size; static constexpr size_t channels = Mode == ImageAlphaMode::Exclusive ? 1 : 4; std::unique_ptr<uint8_t[]> data; diff --git a/include/mbgl/util/immutable.hpp b/include/mbgl/util/immutable.hpp new file mode 100644 index 0000000000..eb26c0d282 --- /dev/null +++ b/include/mbgl/util/immutable.hpp @@ -0,0 +1,133 @@ +#pragma once + +#include <memory> + +namespace mbgl { + +/** + * `Mutable<T>` is a non-nullable uniquely owning reference to a `T`. It can be efficiently converted + * to `Immutable<T>`. + * + * The lifecycle of `Mutable<T>` and `Immutable<T>` is as follows: + * + * 1. Create a `Mutable<T>` using `makeMutable(...)` + * 2. Mutate it freely + * 3. When you're ready to freeze its state and enable safe cross-thread sharing, move assign or + * move construct it to `Immutable<T>` + * + * The reason that `Mutable<T>` exists, rather than simply using a `std::unique_ptr<T>`, is to take advantage + * of the underlying single-allocation optimization provided by `std::make_shared`. + */ +template <class T> +class Mutable { +public: + Mutable(Mutable&&) = default; + Mutable& operator=(Mutable&&) = default; + + Mutable(const Mutable&) = delete; + Mutable& operator=(const Mutable&) = delete; + + T* get() { return ptr.get(); } + T* operator->() { return ptr.get(); } + T& operator*() { return *ptr; } + +private: + Mutable(std::shared_ptr<T>&& s) + : ptr(std::move(s)) {} + + std::shared_ptr<T> ptr; + + template <class S> friend class Immutable; + template <class S, class... Args> friend Mutable<S> makeMutable(Args&&...); +}; + +template <class T, class... Args> +Mutable<T> makeMutable(Args&&... args) { + return Mutable<T>(std::make_shared<T>(std::forward<Args>(args)...)); +} + +/** + * `Immutable<T>` is a non-nullable shared reference to a `const T`. Construction requires + * a transfer of unique ownership from a `Mutable<T>`; once constructed it has the same behavior + * as `std::shared_ptr<const T>` but with better indication of intent. + * + * Normally one should not share state between threads because it's difficult to verify the + * absence of read/write data races. `Immutable` provides a guarantee that no writes are + * possible, and instances therefore can be freely transferred and shared between threads. + */ +template <class T> +class Immutable { +public: + template <class S> + Immutable(Mutable<S>&& s) + : ptr(std::const_pointer_cast<const S>(std::move(s.ptr))) {} + + template <class S> + Immutable(Immutable<S>&& s) + : ptr(std::move(s.ptr)) {} + + template <class S> + Immutable(const Immutable<S>& s) + : ptr(s.ptr) {} + + template <class S> + Immutable& operator=(Mutable<S>&& s) { + ptr = std::const_pointer_cast<const S>(std::move(s.ptr)); + return *this; + } + + template <class S> + Immutable& operator=(Immutable<S>&& s) { + ptr = std::move(s.ptr); + return *this; + } + + template <class S> + Immutable& operator=(const Immutable<S>& s) { + ptr = s.ptr; + return *this; + } + + const T* get() const { return ptr.get(); } + const T* operator->() const { return ptr.get(); } + const T& operator*() const { return *ptr; } + + friend bool operator==(const Immutable<T>& lhs, const Immutable<T>& rhs) { + return lhs.ptr == rhs.ptr; + } + + friend bool operator!=(const Immutable<T>& lhs, const Immutable<T>& rhs) { + return lhs.ptr != rhs.ptr; + } + +private: + Immutable(std::shared_ptr<const T>&& s) + : ptr(std::move(s)) {} + + std::shared_ptr<const T> ptr; + + template <class S> friend class Immutable; + template <class S, class U> friend Immutable<S> staticImmutableCast(const Immutable<U>&); +}; + +template <class S, class U> +Immutable<S> staticImmutableCast(const Immutable<U>& u) { + return Immutable<S>(std::static_pointer_cast<const S>(u.ptr)); +} + +/** + * Constrained mutation of an immutable reference. Makes a temporarily-mutable copy of the + * input Immutable using the inner type's copy constructor, runs the given callable on the + * mutable copy, and then freezes the copy and reassigns it to the input reference. + * + * Note that other Immutables referring to the same inner instance are not affected; they + * continue to referencing the original immutable instance. + */ +template <class T, class Fn> +void mutate(Immutable<T>& immutable, Fn&& fn) { + Mutable<T> mut = makeMutable<T>(*immutable); + std::forward<Fn>(fn)(*mut); + immutable = std::move(mut); +} + +} // namespace mbgl diff --git a/include/mbgl/util/indexed_tuple.hpp b/include/mbgl/util/indexed_tuple.hpp new file mode 100644 index 0000000000..fd0b931d36 --- /dev/null +++ b/include/mbgl/util/indexed_tuple.hpp @@ -0,0 +1,53 @@ +#pragma once + +#include <mbgl/util/type_list.hpp> + +#include <type_traits> +#include <tuple> + +namespace mbgl { + +template <class T, class... Ts> +struct TypeIndex; + +template <class T, class... Ts> +struct TypeIndex<T, T, Ts...> : std::integral_constant<std::size_t, 0> {}; + +template <class T, class U, class... Ts> +struct TypeIndex<T, U, Ts...> : std::integral_constant<std::size_t, 1 + TypeIndex<T, Ts...>::value> {}; + +template <class...> class IndexedTuple; + +// A tuple of Ts, where individual members can be accessed via `t.get<I>()` for I ∈ Is. +// +// See https://github.com/mapbox/cpp/blob/master/C%2B%2B%20Structural%20Metaprogramming.md +// for motivation. +// +template <class... Is, class... Ts> +class IndexedTuple<TypeList<Is...>, TypeList<Ts...>> : public std::tuple<Ts...> { +public: + static_assert(sizeof...(Is) == sizeof...(Ts), "IndexedTuple size mismatch"); + + using std::tuple<Ts...>::tuple; + + template <class I> + auto& get() { + return std::get<TypeIndex<I, Is...>::value>(*this); + } + + template <class I> + const auto& get() const { + return std::get<TypeIndex<I, Is...>::value>(*this); + } + + template <class... Js, class... Us> + IndexedTuple<TypeList<Is..., Js...>, TypeList<Ts..., Us...>> + concat(const IndexedTuple<TypeList<Js...>, TypeList<Us...>>& other) const { + return IndexedTuple<TypeList<Is..., Js...>, TypeList<Ts..., Us...>> { + get<Is>()..., + other.template get<Js>()... + }; + } +}; + +} // namespace mbgl diff --git a/include/mbgl/util/interpolate.hpp b/include/mbgl/util/interpolate.hpp new file mode 100644 index 0000000000..6738987598 --- /dev/null +++ b/include/mbgl/util/interpolate.hpp @@ -0,0 +1,105 @@ +#pragma once + +#include <mbgl/util/color.hpp> +#include <mbgl/util/range.hpp> +#include <mbgl/style/position.hpp> + +#include <array> +#include <vector> +#include <string> +#include <type_traits> +#include <utility> + +namespace mbgl { +namespace util { + +float interpolationFactor(float base, Range<float> range, float z); + +template <class T, class Enabled = void> +struct Interpolator; + +template <typename T> +T interpolate(const T& a, const T& b, const double t) { + return Interpolator<T>()(a, b, t); +} + + +template <class T, class Enabled> +struct Interpolator { + T operator()(const T& a, const T& b, const double t) const { + return a * (1.0 - t) + b * t; + } +}; + +template <class T, std::size_t N> +struct Interpolator<std::array<T, N>> { +private: + using Array = std::array<T, N>; + + template <std::size_t... I> + Array operator()(const Array& a, const Array& b, const double t, std::index_sequence<I...>) { + return {{ interpolate(a[I], b[I], t)... }}; + } + +public: + Array operator()(const Array& a, const Array& b, const double t) { + return operator()(a, b, t, std::make_index_sequence<N>()); + } +}; + +template <> +struct Interpolator<style::Position> { +public: + style::Position operator()(const style::Position& a, const style::Position& b, const double t) { + auto pos = style::Position(); + auto interpolated = interpolate(a.getCartesian(), b.getCartesian(), t); + pos.setCartesian(interpolated); + return { pos }; + } +}; + +template <> +struct Interpolator<Color> { +public: + Color operator()(const Color& a, const Color& b, const double t) { + return { + interpolate(a.r, b.r, t), + interpolate(a.g, b.g, t), + interpolate(a.b, b.b, t), + interpolate(a.a, b.a, t) + }; + } +}; + +struct Uninterpolated { + template <class T> + T operator()(const T& a, const T&, const double) const { + return a; + } +}; + +template <> +struct Interpolator<bool> + : Uninterpolated {}; + +template <class T> +struct Interpolator<T, typename std::enable_if_t<std::is_enum<T>::value>> + : Uninterpolated {}; + +template <> +struct Interpolator<std::string> + : Uninterpolated {}; + +template <class T> +struct Interpolator<std::vector<T>> + : Uninterpolated {}; + +template <class T> +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/noncopyable.hpp b/include/mbgl/util/noncopyable.hpp index 105a76a9a0..8cb7e198d9 100644 --- a/include/mbgl/util/noncopyable.hpp +++ b/include/mbgl/util/noncopyable.hpp @@ -7,15 +7,17 @@ namespace non_copyable_ class noncopyable { +public: + noncopyable( noncopyable const& ) = delete; + noncopyable& operator=(noncopyable const& ) = delete; + protected: constexpr noncopyable() = default; ~noncopyable() = default; - noncopyable( noncopyable const& ) = delete; - noncopyable& operator=(noncopyable const& ) = delete; }; } // namespace non_copyable_ -typedef non_copyable_::noncopyable noncopyable; +using noncopyable = non_copyable_::noncopyable; } // namespace util } // namespace mbgl diff --git a/include/mbgl/util/premultiply.hpp b/include/mbgl/util/premultiply.hpp new file mode 100644 index 0000000000..532c7cb11d --- /dev/null +++ b/include/mbgl/util/premultiply.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include <mbgl/util/image.hpp> + +namespace mbgl { +namespace util { + +PremultipliedImage premultiply(UnassociatedImage&&); +UnassociatedImage unpremultiply(PremultipliedImage&&); + +} // namespace util +} // namespace mbgl diff --git a/include/mbgl/util/projection.hpp b/include/mbgl/util/projection.hpp index 9ca3cd4ab5..3cc1146513 100644 --- a/include/mbgl/util/projection.hpp +++ b/include/mbgl/util/projection.hpp @@ -5,9 +5,36 @@ #include <mbgl/util/geometry.hpp> #include <mbgl/math/clamp.hpp> - namespace mbgl { +class ProjectedMeters { +private: + double _northing; // Distance measured northwards. + double _easting; // Distance measured eastwards. + +public: + ProjectedMeters(double n_ = 0, double e_ = 0) + : _northing(n_), _easting(e_) { + if (std::isnan(_northing)) { + throw std::domain_error("northing must not be NaN"); + } + if (std::isnan(_easting)) { + throw std::domain_error("easting must not be NaN"); + } + } + + double northing() const { return _northing; } + double easting() const { return _easting; } + + friend bool operator==(const ProjectedMeters& a, const ProjectedMeters& b) { + return a._northing == b._northing && a._easting == b._easting; + } + + friend bool operator!=(const ProjectedMeters& a, const ProjectedMeters& b) { + return !(a == b); + } +}; + // Spherical Mercator projection // http://docs.openlayers.org/library/spherical_mercator.html class Projection { @@ -25,8 +52,8 @@ public: } static ProjectedMeters projectedMetersForLatLng(const LatLng& latLng) { - const double constrainedLatitude = util::clamp(latLng.latitude, -util::LATITUDE_MAX, util::LATITUDE_MAX); - const double constrainedLongitude = util::clamp(latLng.longitude, -util::LONGITUDE_MAX, util::LONGITUDE_MAX); + const double constrainedLatitude = util::clamp(latLng.latitude(), -util::LATITUDE_MAX, util::LATITUDE_MAX); + const double constrainedLongitude = util::clamp(latLng.longitude(), -util::LONGITUDE_MAX, util::LONGITUDE_MAX); const double m = 1 - 1e-15; const double f = util::clamp(std::sin(util::DEG2RAD * constrainedLatitude), -m, m); @@ -38,8 +65,8 @@ public: } static LatLng latLngForProjectedMeters(const ProjectedMeters& projectedMeters) { - double latitude = (2 * std::atan(std::exp(projectedMeters.northing / util::EARTH_RADIUS_M)) - (M_PI / 2.0)) * util::RAD2DEG; - double longitude = projectedMeters.easting * util::RAD2DEG / util::EARTH_RADIUS_M; + double latitude = (2 * std::atan(std::exp(projectedMeters.northing() / util::EARTH_RADIUS_M)) - (M_PI / 2.0)) * util::RAD2DEG; + double longitude = projectedMeters.easting() * util::RAD2DEG / util::EARTH_RADIUS_M; latitude = util::clamp(latitude, -util::LATITUDE_MAX, util::LATITUDE_MAX); longitude = util::clamp(longitude, -util::LONGITUDE_MAX, util::LONGITUDE_MAX); @@ -49,8 +76,8 @@ 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)) + 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; } diff --git a/include/mbgl/util/range.hpp b/include/mbgl/util/range.hpp index f7fa92eb8b..5591a22a1f 100644 --- a/include/mbgl/util/range.hpp +++ b/include/mbgl/util/range.hpp @@ -1,3 +1,5 @@ +#include <utility> + #pragma once namespace mbgl { @@ -5,8 +7,8 @@ namespace mbgl { template <class T> class Range { public: - constexpr Range(const T& min_, const T& max_) - : min(min_), max(max_) {} + constexpr Range(T min_, T max_) + : min(std::move(min_)), max(std::move(max_)) {} T min; T max; diff --git a/include/mbgl/util/run_loop.hpp b/include/mbgl/util/run_loop.hpp index 5236850d83..14352ca823 100644 --- a/include/mbgl/util/run_loop.hpp +++ b/include/mbgl/util/run_loop.hpp @@ -16,7 +16,7 @@ namespace mbgl { namespace util { -typedef void * LOOP_HANDLE; +using LOOP_HANDLE = void *; class RunLoop : public Scheduler, private util::noncopyable { @@ -63,15 +63,6 @@ public: return std::make_unique<WorkRequest>(task); } - // Invoke fn(args...) on this RunLoop, then invoke callback(results...) on the current RunLoop. - template <class Fn, class... Args> - std::unique_ptr<AsyncRequest> - invokeWithCallback(Fn&& fn, Args&&... args) { - std::shared_ptr<WorkTask> task = WorkTask::makeWithCallback(std::forward<Fn>(fn), std::forward<Args>(args)...); - push(task); - return std::make_unique<WorkRequest>(task); - } - class Impl; private: diff --git a/include/mbgl/util/size.hpp b/include/mbgl/util/size.hpp index 1af85bcff5..12c0ad056b 100644 --- a/include/mbgl/util/size.hpp +++ b/include/mbgl/util/size.hpp @@ -7,18 +7,25 @@ namespace mbgl { class Size { public: - constexpr Size() : width(0), height(0) { - } + constexpr Size() = default; constexpr Size(const uint32_t width_, const uint32_t height_) : width(width_), height(height_) { } - constexpr explicit operator bool() const { - return width > 0 && height > 0; + 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; } - uint32_t width; - uint32_t height; + uint32_t width = 0; + uint32_t height = 0; }; constexpr inline bool operator==(const Size& a, const Size& b) { diff --git a/include/mbgl/util/string.hpp b/include/mbgl/util/string.hpp index 6c33866a0b..82d317c620 100644 --- a/include/mbgl/util/string.hpp +++ b/include/mbgl/util/string.hpp @@ -6,8 +6,7 @@ #include <cstdlib> #include <exception> -#include <mbgl/util/dtoa.hpp> - +// Polyfill needed by Qt when building for Android with GCC #if defined(__ANDROID__) && defined(__GLIBCXX__) namespace std { @@ -46,17 +45,9 @@ inline std::string toString(uint8_t num) { return std::to_string(unsigned(num)); } -inline std::string toString(float num) { - return dtoa(num); -} - -inline std::string toString(double num) { - return dtoa(num); -} - -inline std::string toString(long double num) { - return dtoa(num); -} +std::string toString(float); +std::string toString(double); +std::string toString(long double); inline std::string toString(std::exception_ptr error) { assert(error); diff --git a/include/mbgl/util/tileset.hpp b/include/mbgl/util/tileset.hpp index 1f28a5039a..2fa19d3f53 100644 --- a/include/mbgl/util/tileset.hpp +++ b/include/mbgl/util/tileset.hpp @@ -13,11 +13,25 @@ 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, 22 }, + 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. + + friend bool operator==(const Tileset& lhs, const Tileset& rhs) { + return std::tie(lhs.tiles, lhs.zoomRange, lhs.attribution, lhs.scheme) + == std::tie(rhs.tiles, rhs.zoomRange, rhs.attribution, rhs.scheme); + } }; } // namespace mbgl diff --git a/include/mbgl/util/type_list.hpp b/include/mbgl/util/type_list.hpp new file mode 100644 index 0000000000..4a5e95c8a4 --- /dev/null +++ b/include/mbgl/util/type_list.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include <type_traits> +#include <tuple> + +namespace mbgl { + +template <class...> +class TypeList {}; + +namespace detail { + +template <class, class> +struct TypeCons; + +template <class T, class... Ts> +struct TypeCons<T, TypeList<Ts...>> { + using Type = TypeList<T, Ts...>; +}; + +template <class, template <class> class> +struct TypeFilter; + +template <template <class> class Predicate> +struct TypeFilter<TypeList<>, Predicate> { + using Type = TypeList<>; +}; + +template <template <class> class Predicate, class T, class... Ts> +struct TypeFilter<TypeList<T, Ts...>, Predicate> { + using Tail = typename TypeFilter<TypeList<Ts...>, Predicate>::Type; + using Type = std::conditional_t<Predicate<T>::value, typename TypeCons<T, Tail>::Type, Tail>; +}; + +} // namespace detail + +template <class TypeList, template <class> class Predicate> +using FilteredTypeList = typename detail::TypeFilter<TypeList, Predicate>::Type; + +} // namespace mbgl diff --git a/include/mbgl/util/unitbezier.hpp b/include/mbgl/util/unitbezier.hpp index 16e356ac6e..6e644e2d1f 100644 --- a/include/mbgl/util/unitbezier.hpp +++ b/include/mbgl/util/unitbezier.hpp @@ -25,8 +25,6 @@ #pragma once -#include <mbgl/util/util.hpp> - #include <cmath> namespace mbgl { @@ -34,13 +32,13 @@ namespace util { struct UnitBezier { // Calculate the polynomial coefficients, implicit first and last control points are (0,0) and (1,1). - MBGL_CONSTEXPR UnitBezier(double p1x, double p1y, double p2x, double p2y) + 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 { diff --git a/include/mbgl/util/version.hpp b/include/mbgl/util/version.hpp deleted file mode 100644 index 4ce8b98f40..0000000000 --- a/include/mbgl/util/version.hpp +++ /dev/null @@ -1,20 +0,0 @@ -// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED. -#pragma once - -#define MBGL_VERSION 0x30402 -#define MBGL_VERSION_STRING "3.4.2" -#define MBGL_VERSION_MAJOR 3 -#define MBGL_VERSION_MINOR 4 -#define MBGL_VERSION_PATCH 2 -#define MBGL_VERSION_REV "2fae03b4" - -namespace mbgl { -namespace version { - -extern const int major, minor, patch; -extern const char *revision; -extern const char *string; -extern const unsigned int number; - -} // namespace version -} // namespace mbgl diff --git a/include/mbgl/util/work_task.hpp b/include/mbgl/util/work_task.hpp index dda8e5d00f..f2dcfcfe86 100644 --- a/include/mbgl/util/work_task.hpp +++ b/include/mbgl/util/work_task.hpp @@ -18,9 +18,6 @@ public: template <class Fn, class... Args> static std::shared_ptr<WorkTask> make(Fn&&, Args&&...); - - template <class Fn, class... Args> - static std::shared_ptr<WorkTask> makeWithCallback(Fn&&, Args&&...); }; } // namespace mbgl diff --git a/include/mbgl/util/work_task_impl.hpp b/include/mbgl/util/work_task_impl.hpp index 8ebc7d45f8..276e0d6237 100644 --- a/include/mbgl/util/work_task_impl.hpp +++ b/include/mbgl/util/work_task_impl.hpp @@ -62,48 +62,4 @@ std::shared_ptr<WorkTask> WorkTask::make(Fn&& fn, Args&&... args) { flag); } -namespace detail { -template <class Tuple, size_t... Indexes> -auto packageArgumentsAndCallback(std::shared_ptr<std::atomic<bool>> flag, - Tuple&& args, - std::index_sequence<Indexes...>) { - auto callback = std::get<sizeof...(Indexes)>(args); - - // Create a lambda L1 that invokes another lambda L2 on the current RunLoop R, that calls - // the callback C. Both lambdas check the flag before proceeding. L1 needs to check the flag - // because if the request was cancelled, then R might have been destroyed. L2 needs to check - // the flag because the request may have been cancelled after L2 was invoked but before it - // began executing. - - auto l2 = [flag, callback] (auto&&... results) { - if (!*flag) { - callback(std::forward<decltype(results)>(results)...); - } - }; - - auto l1 = [flag, current = util::RunLoop::Get(), l2_ = l2] (auto&&... results) { - if (!*flag) { - current->invoke(l2_, std::forward<decltype(results)>(results)...); - } - }; - - return std::make_tuple(std::get<Indexes>(std::forward<Tuple>(args))..., l1); -} -} // namespace detail - -template <class Fn, class... Args> -std::shared_ptr<WorkTask> WorkTask::makeWithCallback(Fn&& fn, Args&&... args) { - auto flag = std::make_shared<std::atomic<bool>>(); - *flag = false; - - auto tuple = detail::packageArgumentsAndCallback(flag, - std::forward_as_tuple(std::forward<Args>(args)...), - std::make_index_sequence<sizeof...(Args) - 1>()); - - return std::make_shared<WorkTaskImpl<std::decay_t<Fn>, decltype(tuple)>>( - std::forward<Fn>(fn), - std::move(tuple), - flag); -} - } // namespace mbgl |