summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSudarsana Babu Nagineni <sudarsana.babu@mapbox.com>2019-05-27 17:47:12 +0300
committerSudarsana Babu Nagineni <sudarsana.babu@mapbox.com>2019-06-10 11:25:55 +0300
commit93506919477f8efad5ad0af8f7d6d8a9c03e3660 (patch)
tree4667aaf4e1101f85dec2b7278f7759b2ffacd5fd
parentfc802daa629d11174cd868820f584399501a33c7 (diff)
downloadqtlocation-mapboxgl-upstream/nagineni-map-refactor.tar.gz
[core] Refactor Map interfaceupstream/nagineni-map-refactor
-rw-r--r--CMakeLists.txt4
-rw-r--r--Makefile8
-rw-r--r--cmake/mapbox-glfw-app.cmake21
-rw-r--r--examples/glfw/main.cpp33
-rw-r--r--examples/glfw/window.cpp208
-rw-r--r--examples/glfw/window.hpp78
-rw-r--r--include/mapbox/common/config.hpp26
-rw-r--r--include/mapbox/common/optional.hpp27
-rw-r--r--include/mapbox/common/peer.hpp37
-rw-r--r--include/mapbox/map/constrain_mode.hpp15
-rw-r--r--include/mapbox/map/map.hpp98
-rw-r--r--include/mapbox/map/map_change.hpp14
-rw-r--r--include/mapbox/map/map_client.hpp35
-rw-r--r--include/mapbox/map/map_load_error.hpp16
-rw-r--r--include/mapbox/map/map_mode.hpp15
-rw-r--r--include/mapbox/map/map_observer.hpp26
-rw-r--r--include/mapbox/map/north_orientation.hpp16
-rw-r--r--include/mapbox/map/still_image_callback.hpp13
-rw-r--r--include/mapbox/map/viewport_mode.hpp14
-rw-r--r--platform/linux/config.cmake29
-rw-r--r--src/mapbox/map/map.cpp210
-rw-r--r--src/mapbox/map/map_impl.cpp416
-rw-r--r--src/mapbox/map/map_impl.hpp180
-rw-r--r--src/mapbox/map/renderer_backend.cpp61
-rw-r--r--src/mapbox/map/renderer_backend.hpp49
-rw-r--r--src/mapbox/map/scheduler.cpp51
-rw-r--r--src/mapbox/map/scheduler.hpp39
27 files changed, 1738 insertions, 1 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index ca33c36be2..3bc5ebf1d7 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -194,6 +194,10 @@ if(COMMAND mbgl_platform_glfw)
include(cmake/glfw.cmake)
endif()
+if(COMMAND mapbox_glfw_app)
+ include(cmake/mapbox-glfw-app.cmake)
+endif()
+
if(COMMAND mbgl_platform_render)
include(cmake/render.cmake)
endif()
diff --git a/Makefile b/Makefile
index 63a3a20d33..a98c6d7f18 100644
--- a/Makefile
+++ b/Makefile
@@ -370,6 +370,14 @@ glfw-app: $(LINUX_BUILD)
run-glfw-app: glfw-app
cd $(LINUX_OUTPUT_PATH) && ./mbgl-glfw
+.PHONY: mapbox-glfw-app
+mapbox-glfw-app: $(LINUX_BUILD)
+ $(NINJA) $(NINJA_ARGS) -j$(JOBS) -C $(LINUX_OUTPUT_PATH) mapbox-glfw-app
+
+.PHONY: run-mapbox-glfw-app
+run-mapbox-glfw-app: mapbox-glfw-app
+ cd $(LINUX_OUTPUT_PATH) && ./mapbox-glfw-app
+
.PHONY: node
node: $(LINUX_BUILD)
$(NINJA) $(NINJA_ARGS) -j$(JOBS) -C $(LINUX_OUTPUT_PATH) mbgl-node.active
diff --git a/cmake/mapbox-glfw-app.cmake b/cmake/mapbox-glfw-app.cmake
new file mode 100644
index 0000000000..33cdf1f4be
--- /dev/null
+++ b/cmake/mapbox-glfw-app.cmake
@@ -0,0 +1,21 @@
+add_executable(mapbox-glfw-app
+ ${CMAKE_SOURCE_DIR}/examples/glfw/main.cpp
+)
+
+target_sources(mapbox-glfw-app
+ PRIVATE ${CMAKE_SOURCE_DIR}/examples/glfw/window.cpp
+)
+
+target_include_directories(mapbox-glfw-app
+ PRIVATE platform/default/include
+ PRIVATE include/mapbox/map
+)
+
+target_link_libraries(mapbox-glfw-app
+ PRIVATE mbgl-core
+ PRIVATE glfw
+ PRIVATE cheap-ruler-cpp
+ PRIVATE args
+)
+
+mapbox_glfw_app()
diff --git a/examples/glfw/main.cpp b/examples/glfw/main.cpp
new file mode 100644
index 0000000000..dbb27a7283
--- /dev/null
+++ b/examples/glfw/main.cpp
@@ -0,0 +1,33 @@
+#include "window.hpp"
+
+#include <GLFW/glfw3.h>
+
+#include <iostream>
+
+void glfwError(int error, const char* description) {
+ std::cerr << "GLFW error (" << error << "): " << description << std::endl;
+ exit(1);
+}
+
+int main() {
+ glfwSetErrorCallback(glfwError);
+ glfwInit();
+
+#ifdef MBGL_USE_GLES2
+ glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
+ glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API);
+#endif
+
+ glfwWindowHint(GLFW_RED_BITS, 8);
+ glfwWindowHint(GLFW_GREEN_BITS, 8);
+ glfwWindowHint(GLFW_BLUE_BITS, 8);
+ glfwWindowHint(GLFW_ALPHA_BITS, 8);
+ glfwWindowHint(GLFW_STENCIL_BITS, 8);
+ glfwWindowHint(GLFW_DEPTH_BITS, 16);
+ Window window;
+ window.run();
+
+ return 0;
+}
diff --git a/examples/glfw/window.cpp b/examples/glfw/window.cpp
new file mode 100644
index 0000000000..38ec6bbdce
--- /dev/null
+++ b/examples/glfw/window.cpp
@@ -0,0 +1,208 @@
+#include "window.hpp"
+
+#include <mbgl/map/map_options.hpp>
+#include <mbgl/storage/resource_options.hpp>
+#include <mbgl/map/camera.hpp>
+#include <mbgl/util/geo.hpp>
+#include <mbgl/util/optional.hpp>
+
+#include <GLFW/glfw3.h>
+
+#include <cassert>
+#include <iostream>
+
+std::string getEnvVar(const std::string& var) {
+ const char* value = getenv(var.c_str());
+ if (value != nullptr) {
+ return value;
+ }
+
+ return "";
+}
+
+Window::Window() : window_(glfwCreateWindow(mapSize_.width, mapSize_.height, "Mapbox GLFW lite", nullptr, nullptr)) {
+ glfwMakeContextCurrent(window_);
+ glfwSwapInterval(1);
+ glfwSetWindowUserPointer(window_, this);
+ glfwSetKeyCallback(window_, onKey);
+ glfwSetCursorPosCallback(window_, onMouseMove);
+ glfwSetMouseButtonCallback(window_, onMouseClick);
+ glfwSetScrollCallback(window_, onScroll);
+ glfwSetWindowSizeCallback(window_, onWindowResize);
+
+ // Get the FB size to determine the pixel ratio
+ int fbWidth = 0;
+ int fbHeight = 0;
+ glfwGetFramebufferSize(window_, &fbWidth, &fbHeight);
+ float aspectRatio_ = float(fbWidth) / float(mapSize_.width);
+ assert(aspectRatio_ == float(fbHeight) / float(mapSize_.height));
+
+ std::string accessToken;
+ accessToken.append(getEnvVar("MAPBOX_ACCESS_TOKEN"));
+ if (accessToken.empty()) {
+ std::cerr << "No MAPBOX_ACCESS_TOKEN set" << std::endl;
+ }
+
+ mbgl::MapOptions mapOptions;
+ mapOptions.withSize(mapSize_).withPixelRatio(aspectRatio_);
+
+ mbgl::ResourceOptions resourceOptions;
+ resourceOptions.withAccessToken(accessToken)
+ .withCachePath("/tmp/mbgl-cache.db");
+
+ map_ = std::make_unique<mapbox::map::Map>(std::make_shared<MapClient>(*this),
+ std::make_shared<Observer>(), std::move(mapOptions), std::move(resourceOptions));
+ map_->setStyleURL("mapbox://styles/mapbox/streets-v11");
+
+ map_->jumpTo(mbgl::CameraOptions()
+ .withCenter(mbgl::LatLng {60.170448, 24.942046})
+ .withZoom(10));
+}
+
+Window::~Window() {
+ map_.reset();
+ glfwDestroyWindow(window_);
+ glfwTerminate();
+}
+
+void Window::render() {
+ if (needsRepaint_) {
+ needsRepaint_ = false;
+ map_->render();
+ glfwSwapBuffers(window_);
+ }
+}
+
+bool Window::shouldClose() const {
+ return glfwWindowShouldClose(window_) == 1;
+}
+
+void Window::scheduleRepaint() {
+ needsRepaint_ = true;
+ glfwPostEmptyEvent();
+}
+
+void Window::onKey(GLFWwindow* glfwWindow, int key, int /*scancode*/, int action, int mods) {
+ auto* window = reinterpret_cast<Window *>(glfwGetWindowUserPointer(glfwWindow));
+
+ // Handle key release events only
+ if (action != GLFW_RELEASE) {
+ return;
+ }
+
+ switch (key) {
+ case GLFW_KEY_ESCAPE: {
+ glfwSetWindowShouldClose(glfwWindow, 1);
+ glfwPostEmptyEvent();
+ } break;
+ case GLFW_KEY_X:
+ if (!mods)
+ window->map_->jumpTo(mbgl::CameraOptions().withCenter(mbgl::LatLng {}).withZoom(0.0).withBearing(0.0).withPitch(0.0));
+ break;
+ default:
+ break;
+ }
+}
+
+void Window::onMouseMove(GLFWwindow* glfwWindow, double x, double y) {
+ auto *window = reinterpret_cast<Window *>(glfwGetWindowUserPointer(glfwWindow));
+ if (window->tracking_) {
+ const double dx = x - window->lastX_;
+ const double dy = y - window->lastY_;
+ if (dx || dy) {
+ window->map_->moveBy(mbgl::ScreenCoordinate { dx, dy }, mbgl::nullopt);
+ }
+ } else if (window->rotating_) {
+ window->map_->rotateBy({ window->lastX_, window->lastY_ }, { x, y }, mbgl::nullopt);
+ } else if (window->pitching_) {
+ const double dy = y - window->lastY_;
+ if (dy) {
+ window->map_->pitchBy(dy / 2, mbgl::nullopt);
+ }
+ }
+ window->lastX_ = x;
+ window->lastY_ = y;
+}
+
+void Window::onMouseClick(GLFWwindow* glfwWindow, int button, int action, int modifiers) {
+ auto *window = reinterpret_cast<Window *>(glfwGetWindowUserPointer(glfwWindow));
+
+ if (button == GLFW_MOUSE_BUTTON_RIGHT ||
+ (button == GLFW_MOUSE_BUTTON_LEFT && modifiers & GLFW_MOD_CONTROL)) {
+ window->rotating_ = action == GLFW_PRESS;
+ window->map_->setGestureInProgress(window->rotating_);
+ } else if (button == GLFW_MOUSE_BUTTON_LEFT && (modifiers & GLFW_MOD_SHIFT)) {
+ window->pitching_ = action == GLFW_PRESS;
+ window->map_->setGestureInProgress(window->pitching_);
+ } else if (button == GLFW_MOUSE_BUTTON_LEFT) {
+ window->tracking_ = action == GLFW_PRESS;
+ window->map_->setGestureInProgress(window->tracking_);
+
+ if (action == GLFW_RELEASE) {
+ double now = glfwGetTime();
+ if (now - window->lastClick_ < 0.4 /* ms */) {
+ if (modifiers & GLFW_MOD_SHIFT) {
+ window->map_->scaleBy(0.5, mbgl::ScreenCoordinate { window->lastX_, window->lastY_ }, mbgl::AnimationOptions{{mbgl::Milliseconds(500)}});
+ } else {
+ window->map_->scaleBy(2.0, mbgl::ScreenCoordinate { window->lastX_, window->lastY_ }, mbgl::AnimationOptions{{mbgl::Milliseconds(500)}});
+ }
+ }
+ window->lastClick_ = now;
+ }
+ }
+}
+
+void Window::onWindowResize(GLFWwindow* glfwWindow, int width, int height) {
+ auto* window = reinterpret_cast<Window*>(glfwGetWindowUserPointer(glfwWindow));
+
+ window->mapSize_ = mbgl::Size{uint32_t(width), uint32_t(height)};
+ window->map_->setSize(window->mapSize_);
+}
+
+void Window::onScroll(GLFWwindow* glfwWindow, double /*xoffset*/, double yOffset) {
+ auto *window = reinterpret_cast<Window *>(glfwGetWindowUserPointer(glfwWindow));
+ double delta = yOffset * 40;
+
+ bool isWheel = delta != 0 && std::fmod(delta, 4.000244140625) == 0;
+
+ double absDelta = delta < 0 ? -delta : delta;
+ double scale = 2.0 / (1.0 + std::exp(-absDelta / 100.0));
+
+ // Make the scroll wheel a bit slower.
+ if (!isWheel) {
+ scale = (scale - 1.0) / 2.0 + 1.0;
+ }
+
+ // Zooming out.
+ if (delta < 0 && scale != 0) {
+ scale = 1.0 / scale;
+ }
+
+ window->map_->scaleBy(scale, mbgl::ScreenCoordinate { window->lastX_, window->lastY_ }, mbgl::nullopt);
+}
+
+MapClient::MapClient(Window& window) : window_(window) {}
+MapClient::~MapClient() = default;
+
+void MapClient::scheduleRepaint() {
+ window_.scheduleRepaint();
+}
+
+mapbox::map::MapClient::GLProcAddress MapClient::getGLProcAddress(const char* name) {
+ return glfwGetProcAddress(name);
+}
+
+void Window::run() {
+ auto callback = [&] {
+ if (glfwWindowShouldClose(window_)) {
+ runLoop.stop();
+ return;
+ }
+
+ glfwPollEvents();
+ render();
+ };
+
+ frameTick.start(mbgl::Duration::zero(), mbgl::Milliseconds(1000 / 60), callback);
+ runLoop.run();
+}
diff --git a/examples/glfw/window.hpp b/examples/glfw/window.hpp
new file mode 100644
index 0000000000..d4a7d8f699
--- /dev/null
+++ b/examples/glfw/window.hpp
@@ -0,0 +1,78 @@
+#pragma once
+
+#include "map.hpp"
+#include "mapbox/map/map_client.hpp"
+#include "map_observer.hpp"
+#include "map_change.hpp"
+#include "map_load_error.hpp"
+
+#include <mbgl/util/run_loop.hpp>
+#include <mbgl/util/timer.hpp>
+
+#include <atomic>
+#include <iostream>
+#include <memory>
+#include <thread>
+
+struct GLFWwindow;
+
+class Observer : public mapbox::map::MapObserver {
+ void onMapChanged(mapbox::map::MapChange change) override {
+ if (change == mapbox::map::MapChange::DidFinishLoadingMap)
+ std::cout << "Map loading finished." << std::endl;
+ else if (change == mapbox::map::MapChange::DidFinishLoadingStyle)
+ std::cout << "Style loading finished." << std::endl;
+ }
+
+ void onMapLoadError(mapbox::map::MapLoadError, const std::string& message) override {
+ std::cout << "Error: " << message << std::endl;
+ }
+};
+
+class Window {
+public:
+ Window();
+ virtual ~Window();
+
+ void render();
+ void scheduleRepaint();
+ bool shouldClose() const;
+ void run();
+
+private:
+ bool needsRepaint_ = true;
+ mbgl::Size mapSize_{1024, 768};
+
+ // GLFW callbacks.
+ static void onKey(GLFWwindow *glfwWindow, int key, int, int, int);
+ static void onWindowResize(GLFWwindow *glfwWindow, int width, int height);
+ static void onMouseMove(GLFWwindow *glfwWindow, double x, double y);
+ static void onMouseClick(GLFWwindow *glfwWindow, int button, int action, int modifiers);
+ static void onScroll(GLFWwindow *glfwWindow, double xoffset, double yOffset);
+
+ bool tracking_ = false;
+ bool rotating_ = false;
+ bool pitching_ = false;
+
+ double lastX_ = 0, lastY_ = 0;
+
+ double lastClick_ = -1;
+
+ GLFWwindow *window_;
+ mbgl::util::RunLoop runLoop;
+ mbgl::util::Timer frameTick;
+
+ std::unique_ptr<mapbox::map::Map> map_;
+};
+
+class MapClient : public mapbox::map::MapClient {
+public:
+ MapClient(Window& window);
+ ~MapClient() override;
+
+ void scheduleRepaint() final;
+ mapbox::map::MapClient::GLProcAddress getGLProcAddress(const char *name) final;
+
+private:
+ Window& window_;
+};
diff --git a/include/mapbox/common/config.hpp b/include/mapbox/common/config.hpp
new file mode 100644
index 0000000000..3c67af1f65
--- /dev/null
+++ b/include/mapbox/common/config.hpp
@@ -0,0 +1,26 @@
+#pragma once
+
+// Compiler defines for making symbols visible, otherwise they
+// will be defined as hidden by default.
+
+// clang-format off
+
+#if defined WIN32
+ #ifdef MAPBOX_MAP_BUILDING_LIB
+ #ifdef __GNUC__
+ #define MAPBOX_MAP_PUBLIC __attribute__((dllexport))
+ #else
+ #define MAPBOX_MAP_PUBLIC __declspec(dllexport)
+ #endif
+ #else
+ #ifdef __GNUC__
+ #define MAPBOX_MAP_PUBLIC __attribute__((dllimport))
+ #else
+ #define MAPBOX_MAP_PUBLIC __declspec(dllimport)
+ #endif
+ #endif
+#else
+ #define MAPBOX_MAP_PUBLIC __attribute__((visibility ("default")))
+#endif
+
+// clang-format on
diff --git a/include/mapbox/common/optional.hpp b/include/mapbox/common/optional.hpp
new file mode 100644
index 0000000000..e5f7e71584
--- /dev/null
+++ b/include/mapbox/common/optional.hpp
@@ -0,0 +1,27 @@
+#pragma once
+
+#if __cplusplus >= 201703L
+#define WITH_OPTIONALS
+#endif
+
+#if defined(WITH_OPTIONALS)
+#include <optional>
+#else
+#include <experimental/optional>
+#endif
+
+namespace mapbox {
+namespace common {
+
+#if defined(WITH_OPTIONALS)
+using nullopt_t = std::nullopt_t;
+constexpr nullopt_t nullopt = std::nullopt;
+template<class T> using optional = std::optional<T>;
+#else
+using nullopt_t = std::experimental::nullopt_t;
+constexpr nullopt_t nullopt = std::experimental::nullopt;
+template<class T> using optional = std::experimental::optional<T>;
+#endif
+
+} // common
+} // mapbox
diff --git a/include/mapbox/common/peer.hpp b/include/mapbox/common/peer.hpp
new file mode 100644
index 0000000000..b00889ca8d
--- /dev/null
+++ b/include/mapbox/common/peer.hpp
@@ -0,0 +1,37 @@
+#pragma once
+
+#include <memory>
+#include <type_traits>
+#include <utility>
+
+namespace mapbox {
+namespace common {
+
+class peer {
+public:
+ peer() = default;
+
+ template <class T>
+ peer(T&& value) : ptr(std::make_unique<DataHolder<T>>(std::forward<T>(value))) {}
+
+ bool has_value() const { return static_cast<bool>(ptr); }
+
+ template <class T>
+ T& get() {
+ return static_cast<DataHolder<T>*>(ptr.get())->data;
+ }
+private:
+ struct DataHolderBase {
+ virtual ~DataHolderBase() = default;
+ };
+
+ template <typename T>
+ struct DataHolder : public DataHolderBase {
+ DataHolder(T&& data_) : data(std::forward<T>(data_)) {}
+ typename std::decay<T>::type data;
+ };
+ std::unique_ptr<DataHolderBase> ptr;
+};
+
+} // namespace common
+} // namespace mapbox
diff --git a/include/mapbox/map/constrain_mode.hpp b/include/mapbox/map/constrain_mode.hpp
new file mode 100644
index 0000000000..ca942b8a2e
--- /dev/null
+++ b/include/mapbox/map/constrain_mode.hpp
@@ -0,0 +1,15 @@
+#pragma once
+
+#include <mapbox/common/config.hpp>
+
+namespace mapbox {
+namespace map {
+
+enum class ConstrainMode {
+ None,
+ HeightOnly,
+ WidthAndHeight
+};
+
+} // namespace map
+} // namespace mapbox
diff --git a/include/mapbox/map/map.hpp b/include/mapbox/map/map.hpp
new file mode 100644
index 0000000000..9acd653589
--- /dev/null
+++ b/include/mapbox/map/map.hpp
@@ -0,0 +1,98 @@
+#pragma once
+
+#include <mapbox/common/config.hpp>
+
+#include "constrain_mode.hpp"
+#include "mbgl/annotation/annotation.hpp"
+#include "mbgl/map/bound_options.hpp"
+#include "mbgl/map/camera.hpp"
+#include "mbgl/map/map_options.hpp"
+#include "mbgl/map/mode.hpp"
+#include "mbgl/map/projection_mode.hpp"
+#include "mbgl/storage/resource_options.hpp"
+#include "mbgl/util/geo.hpp"
+#include "mbgl/util/size.hpp"
+#include "north_orientation.hpp"
+#include "still_image_callback.hpp"
+#include "viewport_mode.hpp"
+#include <cstdint>
+#include <mapbox/common/optional.hpp>
+#include <mapbox/common/peer.hpp>
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace mapbox {
+namespace map {
+
+class MapImpl;
+
+class MapClient;
+class MapObserver;
+
+class MAPBOX_MAP_PUBLIC Map {
+public:
+ Map(std::shared_ptr<MapClient> client, std::shared_ptr<MapObserver> observer, ::mbgl::MapOptions mapOptions, ::mbgl::ResourceOptions resourceOptions);
+ ~Map();
+
+ void createRenderer();
+ void destroyRenderer();
+ void render();
+ void setSize(const ::mbgl::Size& size);
+ void setStyleURL(const std::string& styleURL);
+ void setDefaultFramebufferObject(uint32_t framebufferId);
+ void renderStill(StillImageCallback callback);
+ void renderStill(const ::mbgl::CameraOptions& camera, const ::mbgl::MapDebugOptions& options, StillImageCallback callback);
+ void triggerRepaint();
+ void jumpTo(const ::mbgl::CameraOptions& camera);
+ ::mbgl::CameraOptions getCameraOptions(const ::mbgl::EdgeInsets& padding);
+ void easeTo(const ::mbgl::CameraOptions& camera, const ::mbgl::AnimationOptions& animation);
+ void flyTo(const ::mbgl::CameraOptions& camera, const ::mbgl::AnimationOptions& animation);
+ void moveBy(const ::mbgl::ScreenCoordinate& point, const mapbox::common::optional<::mbgl::AnimationOptions>& animation);
+ void scaleBy(double scale, const mapbox::common::optional<::mbgl::ScreenCoordinate>& anchor, const mapbox::common::optional<::mbgl::AnimationOptions>& animation);
+ void pitchBy(double pitch, const mapbox::common::optional<::mbgl::AnimationOptions>& animation);
+ void rotateBy(const ::mbgl::ScreenCoordinate& first, const ::mbgl::ScreenCoordinate& second, const mapbox::common::optional<::mbgl::AnimationOptions>& animation);
+ ::mbgl::CameraOptions cameraForLatLngBounds(const ::mbgl::LatLngBounds& bounds, const ::mbgl::EdgeInsets& padding, const mapbox::common::optional<double>& bearing, const mapbox::common::optional<double>& pitch);
+ ::mbgl::CameraOptions cameraForLatLngs(const std::vector<::mbgl::LatLng>& latlng, const ::mbgl::EdgeInsets& padding, const mapbox::common::optional<double>& bearing, const mapbox::common::optional<double>& pitch);
+ ::mbgl::LatLngBounds latLngBoundsForCamera(const ::mbgl::CameraOptions& camera);
+ void setProjectionMode(const ::mbgl::ProjectionMode& mode);
+ ::mbgl::ProjectionMode getProjectionMode();
+ void cancelTransitions();
+ void setGestureInProgress(bool inProgress);
+ bool isGestureInProgress();
+ bool isRotating();
+ bool isScaling();
+ bool isPanning();
+ void setBounds(const ::mbgl::BoundOptions& options);
+ ::mbgl::BoundOptions getBounds();
+ void setPrefetchZoomDelta(uint8_t delta);
+ uint8_t getPrefetchZoomDelta();
+ void setNorthOrientation(NorthOrientation orientation);
+ void setConstrainMode(ConstrainMode mode);
+ void setViewportMode(ViewportMode mode);
+ ::mbgl::MapOptions getMapOptions();
+ ::mbgl::ScreenCoordinate pixelForLatLng(const ::mbgl::LatLng& latLng);
+ ::mbgl::LatLng latLngForPixel(const ::mbgl::ScreenCoordinate& pixel);
+ void removeAnnotationImage(const std::string& id);
+ double getTopOffsetPixelsForAnnotationImage(const std::string& id);
+ uint64_t addAnnotation(const ::mbgl::Annotation& annotation);
+ void updateAnnotation(uint64_t id, const ::mbgl::Annotation& annotation);
+ void removeAnnotation(uint64_t id);
+ void setDebug(const ::mbgl::MapDebugOptions& debugOptions);
+ void cycleDebugOptions();
+ ::mbgl::MapDebugOptions getDebug();
+ bool isFullyLoaded();
+ void dumpDebugLogs();
+
+private:
+ using Impl = MapImpl;
+ std::unique_ptr<Impl> impl;
+
+public:
+ /// @cond For use only by generated bindings.
+ mapbox::common::peer peer;
+ /// @endcond
+};
+
+} // namespace map
+} // namespace mapbox
diff --git a/include/mapbox/map/map_change.hpp b/include/mapbox/map/map_change.hpp
new file mode 100644
index 0000000000..9bde7aee73
--- /dev/null
+++ b/include/mapbox/map/map_change.hpp
@@ -0,0 +1,14 @@
+#pragma once
+
+#include <mapbox/common/config.hpp>
+
+namespace mapbox {
+namespace map {
+
+enum class MapChange {
+ DidFinishLoadingMap,
+ DidFinishLoadingStyle
+};
+
+} // namespace map
+} // namespace mapbox
diff --git a/include/mapbox/map/map_client.hpp b/include/mapbox/map/map_client.hpp
new file mode 100644
index 0000000000..21fa6ba176
--- /dev/null
+++ b/include/mapbox/map/map_client.hpp
@@ -0,0 +1,35 @@
+#pragma once
+
+namespace mapbox {
+namespace map {
+
+/**
+* @brief Interface that must be implement by a Map client.
+*/
+class MapClient {
+public:
+ /**
+ * @brief Alias for an opaque function pointer.
+ */
+ using GLProcAddress = void (*)();
+
+ /**
+ * @brief Returns the GL function pointer address for a given name.
+ *
+ * @param name a string containing a GL function name.
+ * @return GLProcAddress an opaque function pointer for a GL function.
+ */
+ virtual GLProcAddress getGLProcAddress(const char* name) = 0;
+
+ /**
+ * @brief Notifies the client to schedule a repaint. This must eventually trigger a Map::render() call,
+ * otherwise the map will be left in incomplete state.
+ */
+ virtual void scheduleRepaint() = 0;
+
+protected:
+ virtual ~MapClient() = default;
+};
+
+} // namespace map
+} // namespace mapbox
diff --git a/include/mapbox/map/map_load_error.hpp b/include/mapbox/map/map_load_error.hpp
new file mode 100644
index 0000000000..0dfb3e2093
--- /dev/null
+++ b/include/mapbox/map/map_load_error.hpp
@@ -0,0 +1,16 @@
+#pragma once
+
+#include <mapbox/common/config.hpp>
+
+namespace mapbox {
+namespace map {
+
+enum class MapLoadError {
+ StyleParseError,
+ StyleLoadError,
+ NotFoundError,
+ UnknownError
+};
+
+} // namespace map
+} // namespace mapbox
diff --git a/include/mapbox/map/map_mode.hpp b/include/mapbox/map/map_mode.hpp
new file mode 100644
index 0000000000..bd0e5af96e
--- /dev/null
+++ b/include/mapbox/map/map_mode.hpp
@@ -0,0 +1,15 @@
+#pragma once
+
+#include <mapbox/common/config.hpp>
+
+namespace mapbox {
+namespace map {
+
+enum class MapMode {
+ Continuous,
+ Static,
+ Tile
+};
+
+} // namespace map
+} // namespace mapbox
diff --git a/include/mapbox/map/map_observer.hpp b/include/mapbox/map/map_observer.hpp
new file mode 100644
index 0000000000..429e3c3ee1
--- /dev/null
+++ b/include/mapbox/map/map_observer.hpp
@@ -0,0 +1,26 @@
+#pragma once
+
+#include "map_change.hpp"
+#include "map_load_error.hpp"
+#include <mapbox/common/peer.hpp>
+#include <string>
+
+namespace mapbox {
+namespace map {
+
+class MapObserver {
+public:
+ virtual void onMapChanged(MapChange change) = 0;
+ virtual void onMapLoadError(MapLoadError error, const std::string& message) = 0;
+
+protected:
+ virtual ~MapObserver() = default;
+
+public:
+ /// @cond For use only by generated bindings.
+ mapbox::common::peer peer;
+ /// @endcond
+};
+
+} // namespace map
+} // namespace mapbox
diff --git a/include/mapbox/map/north_orientation.hpp b/include/mapbox/map/north_orientation.hpp
new file mode 100644
index 0000000000..6f5dd0e556
--- /dev/null
+++ b/include/mapbox/map/north_orientation.hpp
@@ -0,0 +1,16 @@
+#pragma once
+
+#include <mapbox/common/config.hpp>
+
+namespace mapbox {
+namespace map {
+
+enum class NorthOrientation {
+ Upwards,
+ Rightwards,
+ Downwards,
+ Leftwards
+};
+
+} // namespace map
+} // namespace mapbox
diff --git a/include/mapbox/map/still_image_callback.hpp b/include/mapbox/map/still_image_callback.hpp
new file mode 100644
index 0000000000..20f58cebfe
--- /dev/null
+++ b/include/mapbox/map/still_image_callback.hpp
@@ -0,0 +1,13 @@
+#pragma once
+
+#include <functional>
+#include <mapbox/common/optional.hpp>
+#include <string>
+
+namespace mapbox {
+namespace map {
+
+using StillImageCallback = std::function<void(const mapbox::common::optional<std::string>&)>;
+
+} // namespace map
+} // namespace mapbox
diff --git a/include/mapbox/map/viewport_mode.hpp b/include/mapbox/map/viewport_mode.hpp
new file mode 100644
index 0000000000..2badc3a075
--- /dev/null
+++ b/include/mapbox/map/viewport_mode.hpp
@@ -0,0 +1,14 @@
+#pragma once
+
+#include <mapbox/common/config.hpp>
+
+namespace mapbox {
+namespace map {
+
+enum class ViewportMode {
+ Default,
+ FlippedY
+};
+
+} // namespace map
+} // namespace mapbox
diff --git a/platform/linux/config.cmake b/platform/linux/config.cmake
index 7cc1f1fe4d..532c3a50ad 100644
--- a/platform/linux/config.cmake
+++ b/platform/linux/config.cmake
@@ -99,7 +99,6 @@ macro(mbgl_platform_core)
endif()
endmacro()
-
macro(mbgl_filesource)
# Modify platform/linux/filesource-files.json to change the source files for this target.
target_sources_from_file(mbgl-filesource PRIVATE platform/linux/filesource-files.json)
@@ -127,6 +126,34 @@ macro(mbgl_platform_glfw)
)
endmacro()
+macro(mapbox_glfw_app)
+ target_sources(mbgl-core
+ # Snapshotting
+ PRIVATE src/mapbox/map/map.cpp
+ PRIVATE src/mapbox/map/map_impl.cpp
+ PRIVATE src/mapbox/map/renderer_backend.cpp
+ PRIVATE src/mapbox/map/scheduler.cpp
+ )
+
+ target_include_directories(mbgl-core
+ PRIVATE include
+ PRIVATE include/mapbox/
+ PRIVATE include/mapbox/map
+ PRIVATE src/
+ )
+
+ target_link_libraries(mapbox-glfw-app
+ PRIVATE mbgl-filesource
+ PRIVATE mbgl-loop-uv
+ )
+
+ add_custom_command(
+ TARGET mapbox-glfw-app POST_BUILD
+ COMMAND ${CMAKE_COMMAND} -E copy
+ ${CMAKE_SOURCE_DIR}/misc/ca-bundle.crt
+ ${CMAKE_CURRENT_BINARY_DIR}/ca-bundle.crt
+ )
+endmacro()
macro(mbgl_platform_render)
target_link_libraries(mbgl-render
diff --git a/src/mapbox/map/map.cpp b/src/mapbox/map/map.cpp
new file mode 100644
index 0000000000..a0df8a948f
--- /dev/null
+++ b/src/mapbox/map/map.cpp
@@ -0,0 +1,210 @@
+#include <mapbox/common/config.hpp>
+
+#include "map.hpp"
+#include "map_impl.hpp"
+
+#include <utility>
+
+namespace mapbox {
+namespace map {
+
+Map::Map(std::shared_ptr<MapClient> client, std::shared_ptr<MapObserver> observer, ::mbgl::MapOptions mapOptions, ::mbgl::ResourceOptions resourceOptions)
+: impl(std::make_unique<Impl>(std::move(client), std::move(observer), std::move(mapOptions), std::move(resourceOptions)))
+{}
+
+Map::~Map() = default;
+
+void Map::createRenderer() {
+ impl->createRenderer();
+}
+
+void Map::destroyRenderer() {
+ impl->destroyRenderer();
+}
+
+void Map::render() {
+ impl->render();
+}
+
+void Map::setSize(const ::mbgl::Size& size) {
+ impl->setSize(size);
+}
+
+void Map::setStyleURL(const std::string& styleURL) {
+ impl->setStyleURL(styleURL);
+}
+
+void Map::setDefaultFramebufferObject(uint32_t framebufferId) {
+ impl->setDefaultFramebufferObject(framebufferId);
+}
+
+void Map::renderStill(StillImageCallback callback) {
+ impl->renderStill(std::move(callback));
+}
+
+void Map::renderStill(const ::mbgl::CameraOptions& camera, const ::mbgl::MapDebugOptions& options, StillImageCallback callback) {
+ impl->renderStill(camera, options, std::move(callback));
+}
+
+void Map::triggerRepaint() {
+ impl->triggerRepaint();
+}
+
+void Map::jumpTo(const ::mbgl::CameraOptions& camera) {
+ impl->jumpTo(camera);
+}
+
+::mbgl::CameraOptions Map::getCameraOptions(const ::mbgl::EdgeInsets& padding) {
+ return impl->getCameraOptions(padding);
+}
+
+void Map::easeTo(const ::mbgl::CameraOptions& camera, const ::mbgl::AnimationOptions& animation) {
+ impl->easeTo(camera, animation);
+}
+
+void Map::flyTo(const ::mbgl::CameraOptions& camera, const ::mbgl::AnimationOptions& animation) {
+ impl->flyTo(camera, animation);
+}
+
+void Map::moveBy(const ::mbgl::ScreenCoordinate& point, const mapbox::common::optional<::mbgl::AnimationOptions>& animation) {
+ impl->moveBy(point, animation);
+}
+
+void Map::scaleBy(double scale, const mapbox::common::optional<::mbgl::ScreenCoordinate>& anchor, const mapbox::common::optional<::mbgl::AnimationOptions>& animation) {
+ impl->scaleBy(scale, anchor, animation);
+}
+
+void Map::pitchBy(double pitch, const mapbox::common::optional<::mbgl::AnimationOptions>& animation) {
+ impl->pitchBy(pitch, animation);
+}
+
+void Map::rotateBy(const ::mbgl::ScreenCoordinate& first, const ::mbgl::ScreenCoordinate& second, const mapbox::common::optional<::mbgl::AnimationOptions>& animation) {
+ impl->rotateBy(first, second, animation);
+}
+
+::mbgl::CameraOptions Map::cameraForLatLngBounds(const ::mbgl::LatLngBounds& bounds, const ::mbgl::EdgeInsets& padding, const mapbox::common::optional<double>& bearing, const mapbox::common::optional<double>& pitch) {
+ return impl->cameraForLatLngBounds(bounds, padding, bearing, pitch);
+}
+
+::mbgl::CameraOptions Map::cameraForLatLngs(const std::vector<::mbgl::LatLng>& latlng, const ::mbgl::EdgeInsets& padding, const mapbox::common::optional<double>& bearing, const mapbox::common::optional<double>& pitch) {
+ return impl->cameraForLatLngs(latlng, padding, bearing, pitch);
+}
+
+::mbgl::LatLngBounds Map::latLngBoundsForCamera(const ::mbgl::CameraOptions& camera) {
+ return impl->latLngBoundsForCamera(camera);
+}
+
+void Map::setProjectionMode(const ::mbgl::ProjectionMode& mode) {
+ impl->setProjectionMode(mode);
+}
+
+::mbgl::ProjectionMode Map::getProjectionMode() {
+ return impl->getProjectionMode();
+}
+
+void Map::cancelTransitions() {
+ impl->cancelTransitions();
+}
+
+void Map::setGestureInProgress(bool inProgress) {
+ impl->setGestureInProgress(inProgress);
+}
+
+bool Map::isGestureInProgress() {
+ return impl->isGestureInProgress();
+}
+
+bool Map::isRotating() {
+ return impl->isRotating();
+}
+
+bool Map::isScaling() {
+ return impl->isScaling();
+}
+
+bool Map::isPanning() {
+ return impl->isPanning();
+}
+
+void Map::setBounds(const ::mbgl::BoundOptions& options) {
+ impl->setBounds(options);
+}
+
+::mbgl::BoundOptions Map::getBounds() {
+ return impl->getBounds();
+}
+
+void Map::setPrefetchZoomDelta(uint8_t delta) {
+ impl->setPrefetchZoomDelta(delta);
+}
+
+uint8_t Map::getPrefetchZoomDelta() {
+ return impl->getPrefetchZoomDelta();
+}
+
+void Map::setNorthOrientation(NorthOrientation orientation) {
+ impl->setNorthOrientation(orientation);
+}
+
+void Map::setConstrainMode(ConstrainMode mode) {
+ impl->setConstrainMode(mode);
+}
+
+void Map::setViewportMode(ViewportMode mode) {
+ impl->setViewportMode(mode);
+}
+
+::mbgl::MapOptions Map::getMapOptions() {
+ return impl->getMapOptions();
+}
+
+::mbgl::ScreenCoordinate Map::pixelForLatLng(const ::mbgl::LatLng& latLng) {
+ return impl->pixelForLatLng(latLng);
+}
+
+::mbgl::LatLng Map::latLngForPixel(const ::mbgl::ScreenCoordinate& pixel) {
+ return impl->latLngForPixel(pixel);
+}
+
+void Map::removeAnnotationImage(const std::string& id) {
+ impl->removeAnnotationImage(id);
+}
+
+double Map::getTopOffsetPixelsForAnnotationImage(const std::string& id) {
+ return impl->getTopOffsetPixelsForAnnotationImage(id);
+}
+
+uint64_t Map::addAnnotation(const ::mbgl::Annotation& annotation) {
+ return impl->addAnnotation(annotation);
+}
+
+void Map::updateAnnotation(uint64_t id, const ::mbgl::Annotation& annotation) {
+ impl->updateAnnotation(id, annotation);
+}
+
+void Map::removeAnnotation(uint64_t id) {
+ impl->removeAnnotation(id);
+}
+
+void Map::setDebug(const ::mbgl::MapDebugOptions& debugOptions) {
+ impl->setDebug(debugOptions);
+}
+
+void Map::cycleDebugOptions() {
+ impl->cycleDebugOptions();
+}
+
+::mbgl::MapDebugOptions Map::getDebug() {
+ return impl->getDebug();
+}
+
+bool Map::isFullyLoaded() {
+ return impl->isFullyLoaded();
+}
+
+void Map::dumpDebugLogs() {
+ impl->dumpDebugLogs();
+}
+
+} // namespace map
+} // namespace mapbox
diff --git a/src/mapbox/map/map_impl.cpp b/src/mapbox/map/map_impl.cpp
new file mode 100644
index 0000000000..6150da8b6c
--- /dev/null
+++ b/src/mapbox/map/map_impl.cpp
@@ -0,0 +1,416 @@
+#include <mapbox/common/optional.hpp>
+#include <mapbox/map/map_impl.hpp>
+#include <mapbox/map/scheduler.hpp>
+
+#include <mbgl/gfx/backend_scope.hpp>
+#include <mbgl/map/map.hpp>
+#include <mbgl/platform/gl_functions.hpp>
+#include <mbgl/map/map_options.hpp>
+#include <mbgl/renderer/renderer.hpp>
+#include <mbgl/storage/default_file_source.hpp>
+#include <mbgl/storage/resource_options.hpp>
+#include <mbgl/style/style.hpp>
+#include <mbgl/util/exception.hpp>
+#include <mbgl/util/premultiply.hpp>
+#include <mbgl/util/run_loop.hpp>
+#include <mbgl/util/traits.hpp>
+
+#include "map_load_error.hpp"
+
+#include <algorithm>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <memory>
+
+#ifndef GL_VIEWPORT
+#define GL_VIEWPORT 0x0BA2
+#endif
+
+namespace mapbox {
+namespace map {
+
+// MapLoadError
+static_assert(mbgl::underlying_type(MapLoadError::StyleLoadError) == mbgl::underlying_type(mbgl::MapLoadError::StyleLoadError), "error");
+static_assert(mbgl::underlying_type(MapLoadError::StyleParseError) == mbgl::underlying_type(mbgl::MapLoadError::StyleParseError), "error");
+static_assert(mbgl::underlying_type(MapLoadError::NotFoundError) == mbgl::underlying_type(mbgl::MapLoadError::NotFoundError), "error");
+static_assert(mbgl::underlying_type(MapLoadError::UnknownError) == mbgl::underlying_type(mbgl::MapLoadError::UnknownError), "error");
+
+// MapMode
+static_assert(mbgl::underlying_type(MapMode::Continuous) == mbgl::underlying_type(mbgl::MapMode::Continuous), "error");
+static_assert(mbgl::underlying_type(MapMode::Static) == mbgl::underlying_type(mbgl::MapMode::Static), "error");
+static_assert(mbgl::underlying_type(MapMode::Tile) == mbgl::underlying_type(mbgl::MapMode::Tile), "error");
+
+// ConstrainMode
+static_assert(mbgl::underlying_type(ConstrainMode::None) == mbgl::underlying_type(mbgl::ConstrainMode::None), "error");
+static_assert(mbgl::underlying_type(ConstrainMode::HeightOnly) == mbgl::underlying_type(mbgl::ConstrainMode::HeightOnly), "error");
+static_assert(mbgl::underlying_type(ConstrainMode::WidthAndHeight) == mbgl::underlying_type(mbgl::ConstrainMode::WidthAndHeight), "error");
+
+// ViewportMode
+static_assert(mbgl::underlying_type(ViewportMode::Default) == mbgl::underlying_type(mbgl::ViewportMode::Default), "error");
+static_assert(mbgl::underlying_type(ViewportMode::FlippedY) == mbgl::underlying_type(mbgl::ViewportMode::FlippedY), "error");
+
+// NorthOrientation
+static_assert(mbgl::underlying_type(NorthOrientation::Upwards) == mbgl::underlying_type(mbgl::NorthOrientation::Upwards), "error");
+static_assert(mbgl::underlying_type(NorthOrientation::Rightwards) == mbgl::underlying_type(mbgl::NorthOrientation::Rightwards), "error");
+static_assert(mbgl::underlying_type(NorthOrientation::Downwards) == mbgl::underlying_type(mbgl::NorthOrientation::Downwards), "error");
+static_assert(mbgl::underlying_type(NorthOrientation::Leftwards) == mbgl::underlying_type(mbgl::NorthOrientation::Leftwards), "error");
+
+namespace {
+
+Scheduler* getScheduler() {
+ static thread_local std::shared_ptr<Scheduler> scheduler = std::make_shared<Scheduler>();
+ return scheduler.get();
+}
+
+} // namespace
+
+MapImpl::MapImpl(std::shared_ptr<MapClient> client,
+ std::shared_ptr<mapbox::map::MapObserver> observer,
+ const mbgl::MapOptions& mapOptions,
+ const mbgl::ResourceOptions& resourceOptions)
+ : size_(mapOptions.size()), pixelRatio_(mapOptions.pixelRatio())
+ , observer_(std::move(observer))
+ , client_(std::move(client)) {
+ map_ = std::make_unique<mbgl::Map>(*this, *this, mapOptions, resourceOptions);
+}
+
+MapImpl::~MapImpl() = default;
+
+void MapImpl::createRenderer() {
+ assert(!rendererBackend_);
+ assert(!renderer_);
+
+ renderThreadId_ = std::make_unique<std::thread::id>(std::this_thread::get_id());
+
+ rendererBackend_ = std::make_unique<RendererBackend>(
+ std::bind(&MapClient::getGLProcAddress, client_, std::placeholders::_1),
+ mbgl::Size{uint32_t(size_.width * pixelRatio_), uint32_t(size_.height * pixelRatio_)}, defaultFramebufferID_);
+
+ renderer_ = std::make_unique<mbgl::Renderer>(*rendererBackend_, pixelRatio_);
+
+ renderer_->setObserver(rendererObserver_);
+
+ schedulerNotify_ = std::make_shared<Scheduler::NotifyFn>([this] { client_->scheduleRepaint(); });
+ getScheduler()->attach(schedulerNotify_);
+ mbgl::Scheduler::SetCurrent(getScheduler());
+}
+
+void MapImpl::destroyRenderer() {
+ assert(*renderThreadId_ == std::this_thread::get_id());
+ schedulerNotify_.reset();
+ renderer_.reset();
+ rendererBackend_.reset();
+}
+
+void MapImpl::render() {
+ assert(!renderThreadId_ || (*renderThreadId_ == std::this_thread::get_id()));
+ mbgl::gfx::BackendScope scope(*rendererBackend_, mbgl::gfx::BackendScope::ScopeType::Implicit);
+
+ std::shared_ptr<mbgl::UpdateParameters> params;
+ {
+ // Lock on the parameters
+ std::lock_guard<std::mutex> lock(updateMutex_);
+
+ if (!updateParameters_) {
+ return;
+ }
+
+ // Hold on to the update parameters during render
+ params = updateParameters_;
+ }
+
+ if (params) {
+ if (!renderer_) {
+ createRenderer();
+ }
+
+ assert(*renderThreadId_ == std::this_thread::get_id());
+ {
+ std::lock_guard<std::mutex> lock(resizeMutex_);
+
+ mbgl::platform::GLint viewport[4];
+ mbgl::platform::glGetIntegerv(GL_VIEWPORT, viewport);
+ x_ = viewport[0];
+ y_ = viewport[1];
+ rendererBackend_->move(x_, y_);
+
+ if (sizeChanged_) {
+ rendererBackend_->resize(mbgl::Size(static_cast<uint32_t>(size_.width * pixelRatio_),
+ static_cast<uint32_t>(size_.height * pixelRatio_)));
+ rendererBackend_->bind();
+ sizeChanged_ = false;
+ }
+ renderer_->render(*params);
+ }
+ }
+
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-static-cast-downcast)
+ static_cast<Scheduler*>(mbgl::Scheduler::GetCurrent())->processEvents();
+}
+
+void MapImpl::setSize(const ::mbgl::Size& size) {
+ std::lock_guard<std::mutex> lock(resizeMutex_);
+ size_ = size;
+ sizeChanged_ = true;
+
+ map_->setSize(size_);
+}
+
+void MapImpl::setDefaultFramebufferObject(uint32_t framebufferID) {
+ defaultFramebufferID_ = framebufferID;
+ if (rendererBackend_) {
+ rendererBackend_->setDefaultFramebufferObject(defaultFramebufferID_);
+ }
+}
+
+void MapImpl::setStyleURL(const std::string& styleURL) {
+ map_->getStyle().loadURL(styleURL);
+}
+
+void MapImpl::triggerRepaint() {
+ map_->triggerRepaint();
+}
+
+void MapImpl::renderStill(StillImageCallback callback) {
+ map_->renderStill([cb = std::move(callback)](const std::exception_ptr eptr) {
+ mapbox::common::optional<std::string> error;
+ try {
+ if (eptr) {
+ std::rethrow_exception(eptr);
+ }
+ } catch(const std::exception& e) {
+ error = std::string(e.what());
+ }
+
+ if (cb) {
+ cb(error);
+ }
+ });
+}
+
+void MapImpl::renderStill(const ::mbgl::CameraOptions& camera, const ::mbgl::MapDebugOptions& options, StillImageCallback callback) {
+ map_->renderStill(camera, options, [cb = std::move(callback)](const std::exception_ptr eptr) {
+ mapbox::common::optional<std::string> error;
+ try {
+ if (eptr) {
+ std::rethrow_exception(eptr);
+ }
+ } catch(const std::exception& e) {
+ error = std::string(e.what());
+ }
+
+ if (cb) {
+ cb(error);
+ }
+ });
+}
+
+mbgl::CameraOptions MapImpl::getCameraOptions(const mbgl::EdgeInsets& padding) const {
+ return map_->getCameraOptions(padding);
+}
+
+void MapImpl::jumpTo(const mbgl::CameraOptions& camera) {
+ try {
+ map_->jumpTo(camera);
+ } catch (const std::exception& e) {
+ }
+}
+
+void MapImpl::easeTo(const mbgl::CameraOptions& camera, const mbgl::AnimationOptions& animation) {
+ map_->easeTo(camera, animation);
+}
+
+void MapImpl::flyTo(const mbgl::CameraOptions& camera, const mbgl::AnimationOptions& animation) {
+ map_->flyTo(camera, animation);
+}
+
+void MapImpl::moveBy(const mbgl::ScreenCoordinate& point, const std::experimental::optional<::mbgl::AnimationOptions>& animation) {
+ map_->moveBy(point, animation.value_or(mbgl::AnimationOptions{}));
+}
+
+void MapImpl::scaleBy(double scale, const std::experimental::optional<mbgl::ScreenCoordinate>& anchor, const std::experimental::optional<::mbgl::AnimationOptions>& animation){
+ map_->scaleBy(scale, anchor, animation.value_or(mbgl::AnimationOptions{}));
+}
+
+void MapImpl::pitchBy(double pitch, const std::experimental::optional<::mbgl::AnimationOptions>& animation) {
+ map_->pitchBy(pitch, animation.value_or(mbgl::AnimationOptions{}));
+}
+
+void MapImpl::rotateBy(const mbgl::ScreenCoordinate& first, const mbgl::ScreenCoordinate& second, const std::experimental::optional<::mbgl::AnimationOptions>& animation) {
+ map_->rotateBy(first, second, animation.value_or(mbgl::AnimationOptions{}));
+}
+
+mbgl::CameraOptions MapImpl::cameraForLatLngBounds(const mbgl::LatLngBounds& bounds, const mbgl::EdgeInsets& padding, const std::experimental::optional<double>& bearing, const std::experimental::optional<double>& pitch) {
+ return map_->cameraForLatLngBounds(bounds, padding, bearing, pitch);
+}
+
+mbgl::CameraOptions MapImpl::cameraForLatLngs(const std::vector<mbgl::LatLng>& latlng, const mbgl::EdgeInsets& padding, const std::experimental::optional<double>& bearing, const std::experimental::optional<double>& pitch) {
+ return map_->cameraForLatLngs(latlng, padding, bearing, pitch);
+}
+
+mbgl::LatLngBounds MapImpl::latLngBoundsForCamera(const mbgl::CameraOptions& camera) {
+ return map_->latLngBoundsForCamera(camera);
+}
+
+void MapImpl::setProjectionMode(const mbgl::ProjectionMode& options) {
+ map_->setProjectionMode(options);
+}
+
+mbgl::ProjectionMode MapImpl::getProjectionMode() const {
+ return map_->getProjectionMode();
+}
+
+void MapImpl::cancelTransitions() {
+ map_->cancelTransitions();
+}
+
+void MapImpl::setGestureInProgress(bool inProgress) {
+ map_->setGestureInProgress(inProgress);
+}
+
+bool MapImpl::isGestureInProgress() const {
+ return map_->isGestureInProgress();
+}
+
+bool MapImpl::isRotating() const {
+ return map_->isRotating();
+}
+
+bool MapImpl::isScaling() const {
+ return map_->isScaling();
+}
+
+bool MapImpl::isPanning() const {
+ return map_->isPanning();
+}
+
+void MapImpl::setBounds(const ::mbgl::BoundOptions& options) {
+ map_->setBounds(options);
+}
+
+::mbgl::BoundOptions MapImpl::getBounds() const {
+ return map_->getBounds();
+}
+
+void MapImpl::setPrefetchZoomDelta(uint8_t delta) {
+ map_->setPrefetchZoomDelta(delta);
+}
+
+uint8_t MapImpl::getPrefetchZoomDelta() const {
+ return map_->getPrefetchZoomDelta();
+}
+
+void MapImpl::setNorthOrientation(NorthOrientation orientation) {
+ map_->setNorthOrientation(static_cast<mbgl::NorthOrientation>(orientation));
+}
+
+void MapImpl::setConstrainMode(ConstrainMode mode) {
+ map_->setConstrainMode(static_cast<mbgl::ConstrainMode>(mode));
+}
+
+void MapImpl::setViewportMode(ViewportMode mode) {
+ map_->setViewportMode(static_cast<mbgl::ViewportMode>(mode));
+}
+
+mbgl::MapOptions MapImpl::getMapOptions() const {
+ return map_->getMapOptions();
+}
+
+mbgl::ScreenCoordinate MapImpl::pixelForLatLng(const mbgl::LatLng& latLng) {
+ return map_->pixelForLatLng(latLng);
+}
+
+mbgl::LatLng MapImpl::latLngForPixel(const mbgl::ScreenCoordinate& pixel) {
+ return map_->latLngForPixel(pixel);
+}
+
+void MapImpl::removeAnnotationImage(const std::string& id) {
+ map_->removeAnnotationImage(id);
+}
+
+double MapImpl::getTopOffsetPixelsForAnnotationImage(const std::string& id) {
+ return map_->getTopOffsetPixelsForAnnotationImage(id);
+}
+
+mbgl::AnnotationID MapImpl::addAnnotation(const mbgl::Annotation& annotation) {
+ return map_->addAnnotation(annotation);
+}
+
+void MapImpl::updateAnnotation(uint64_t id, const mbgl::Annotation& annotation) {
+ map_->updateAnnotation(id, annotation);
+}
+
+void MapImpl::removeAnnotation(uint64_t id) {
+ map_->removeAnnotation(id);
+}
+
+void MapImpl::setDebug(const mbgl::MapDebugOptions& debugOptions) {
+ map_->setDebug(debugOptions);
+}
+
+void MapImpl::cycleDebugOptions() {
+ map_->cycleDebugOptions();
+}
+
+mbgl::MapDebugOptions MapImpl::getDebug() const {
+ return map_->getDebug();
+}
+
+bool MapImpl::isFullyLoaded() const {
+ return map_->isFullyLoaded();
+}
+
+void MapImpl::dumpDebugLogs() const {
+ map_->dumpDebugLogs();
+}
+
+void MapImpl::reset() {
+ assert(clientThreadId_ == std::this_thread::get_id());
+}
+
+void MapImpl::setObserver(mbgl::RendererObserver& observer) {
+ rendererObserver_ = &observer;
+ if (renderer_) {
+ renderer_->setObserver(&observer);
+ }
+}
+
+void MapImpl::update(std::shared_ptr<mbgl::UpdateParameters> updateParameters) {
+ std::lock_guard<std::mutex> lock(updateMutex_);
+ updateParameters_ = std::move(updateParameters);
+ client_->scheduleRepaint();
+}
+
+void MapImpl::onCameraWillChange(CameraChangeMode) {}
+
+void MapImpl::onCameraIsChanging() {}
+
+void MapImpl::onCameraDidChange(CameraChangeMode) {}
+
+void MapImpl::onWillStartLoadingMap() {}
+
+void MapImpl::onDidFinishLoadingMap() {
+ observer_->onMapChanged(MapChange::DidFinishLoadingMap);
+}
+
+void MapImpl::onDidFailLoadingMap(mbgl::MapLoadError error, const std::string& message) {
+ observer_->onMapLoadError(static_cast<MapLoadError>(error), message);
+}
+
+void MapImpl::onWillStartRenderingFrame() {}
+
+void MapImpl::onDidFinishRenderingFrame(RenderMode) {}
+
+void MapImpl::onWillStartRenderingMap() {}
+
+void MapImpl::onDidFinishRenderingMap(RenderMode) {}
+
+void MapImpl::onDidFinishLoadingStyle() {
+ observer_->onMapChanged(MapChange::DidFinishLoadingStyle);
+}
+
+void MapImpl::onSourceChanged(mbgl::style::Source&) {}
+
+} // namespace map
+} // namespace mapbox
diff --git a/src/mapbox/map/map_impl.hpp b/src/mapbox/map/map_impl.hpp
new file mode 100644
index 0000000000..796820c1e9
--- /dev/null
+++ b/src/mapbox/map/map_impl.hpp
@@ -0,0 +1,180 @@
+#pragma once
+
+#include <mapbox/map/map_client.hpp>
+#include <mapbox/map/renderer_backend.hpp>
+#include <mapbox/map/scheduler.hpp>
+
+#include <mbgl/annotation/annotation.hpp>
+#include <mbgl/map/bound_options.hpp>
+#include <mbgl/map/map_observer.hpp>
+#include <mbgl/map/map_options.hpp>
+#include <mbgl/map/projection_mode.hpp>
+#include <mbgl/storage/resource_options.hpp>
+#include <mbgl/map/camera.hpp>
+#include <mbgl/renderer/renderer_frontend.hpp>
+#include <mbgl/util/size.hpp>
+
+#include "constrain_mode.hpp"
+#include "map_mode.hpp"
+#include "map_observer.hpp"
+#include "north_orientation.hpp"
+#include "renderer_backend.hpp"
+#include "still_image_callback.hpp"
+#include "viewport_mode.hpp"
+
+#include <future>
+#include <memory>
+#include <mutex>
+#include <string>
+#include <thread>
+#include <vector>
+
+namespace mbgl {
+class Map;
+class Renderer;
+class RendererObserver;
+class UpdateParameters;
+
+namespace style {
+class Layer;
+} //namespace style
+
+namespace util {
+class RunLoop;
+} // namespace util
+} // namespace mbgl
+
+namespace mapbox {
+namespace map {
+
+class MapImpl : public mbgl::RendererFrontend, public mbgl::MapObserver {
+public:
+ MapImpl(std::shared_ptr<MapClient> client,
+ std::shared_ptr<mapbox::map::MapObserver> observer,
+ const mbgl::MapOptions& mapOptions,
+ const mbgl::ResourceOptions& resourceOptions);
+ ~MapImpl() override;
+
+ // Called from the render thread - these functions are _not_ thread-safe.
+ void createRenderer();
+ void destroyRenderer();
+ void render();
+ void setSize(const mbgl::Size& size);
+ void setDefaultFramebufferObject(uint32_t);
+
+ void renderStill(StillImageCallback callback);
+ void renderStill(const mbgl::CameraOptions& camera, const mbgl::MapDebugOptions& options, StillImageCallback callback);
+
+ // Triggers a repaint.
+ void triggerRepaint();
+
+ void jumpTo(const mbgl::CameraOptions& camera);
+ mbgl::CameraOptions getCameraOptions(const mbgl::EdgeInsets& padding) const;
+
+ void easeTo(const mbgl::CameraOptions& camera, const mbgl::AnimationOptions& animation);
+ void flyTo(const mbgl::CameraOptions& camera, const mbgl::AnimationOptions& animation);
+ void moveBy(const mbgl::ScreenCoordinate& point, const std::experimental::optional<::mbgl::AnimationOptions>& animation);
+ void scaleBy(double scale, const std::experimental::optional<mbgl::ScreenCoordinate>& anchor, const std::experimental::optional<::mbgl::AnimationOptions>& animation);
+ void pitchBy(double pitch, const std::experimental::optional<::mbgl::AnimationOptions>& animation);
+ void rotateBy(const mbgl::ScreenCoordinate& first, const mbgl::ScreenCoordinate& second, const std::experimental::optional<::mbgl::AnimationOptions>& animation);
+ mbgl::CameraOptions cameraForLatLngBounds(const mbgl::LatLngBounds& bounds, const mbgl::EdgeInsets& padding, const std::experimental::optional<double>& bearing, const std::experimental::optional<double>& pitch);
+ mbgl::CameraOptions cameraForLatLngs(const std::vector<mbgl::LatLng>& latlng, const mbgl::EdgeInsets& padding, const std::experimental::optional<double>& bearing, const std::experimental::optional<double>& pitch);
+ mbgl::LatLngBounds latLngBoundsForCamera(const mbgl::CameraOptions& cameraOptions);
+
+ void setProjectionMode(const mbgl::ProjectionMode& mode);
+ mbgl::ProjectionMode getProjectionMode() const;
+
+ // Transition
+ void cancelTransitions();
+ void setGestureInProgress(bool);
+ bool isGestureInProgress() const;
+ bool isRotating() const;
+ bool isScaling() const;
+ bool isPanning() const;
+
+ void setBounds(const mbgl::BoundOptions& options);
+ /// Returns the current map bound options. All optional fields in BoundOptions are set.
+ mbgl::BoundOptions getBounds() const;
+
+ // Tile prefetching
+ void setPrefetchZoomDelta(uint8_t delta);
+ uint8_t getPrefetchZoomDelta() const;
+
+ // Map options
+ void setNorthOrientation(NorthOrientation orientation);
+ void setConstrainMode(ConstrainMode mode);
+ void setViewportMode(ViewportMode mode);
+ mbgl::MapOptions getMapOptions() const;
+
+ // Thread safe
+ void setStyleURL(const std::string& styleURL);
+
+ // Projection
+ mbgl::ScreenCoordinate pixelForLatLng(const mbgl::LatLng& latLng);
+ mbgl::LatLng latLngForPixel(const mbgl::ScreenCoordinate& pixel);
+
+ // Annotations
+ void removeAnnotationImage(const std::string& id);
+ double getTopOffsetPixelsForAnnotationImage(const std::string& id);
+
+ uint64_t addAnnotation(const mbgl::Annotation& annotation);
+ void updateAnnotation(uint64_t id, const mbgl::Annotation& annotation);
+ void removeAnnotation(uint64_t id);
+
+ // Debug
+ void setDebug(const mbgl::MapDebugOptions& debugOptions);
+ void cycleDebugOptions();
+ mbgl::MapDebugOptions getDebug() const;
+
+ bool isFullyLoaded() const;
+ void dumpDebugLogs() const;
+
+private:
+ // mbgl::RendererFrontend implementation.
+ void reset() final;
+ void setObserver(mbgl::RendererObserver& observer) final;
+ void update(std::shared_ptr<mbgl::UpdateParameters>) final;
+
+ // mbgl::MapObserver implementation.
+ void onCameraWillChange(CameraChangeMode) final;
+ void onCameraIsChanging() final;
+ void onCameraDidChange(CameraChangeMode) final;
+ void onWillStartLoadingMap() final;
+ void onDidFinishLoadingMap() final;
+ void onDidFailLoadingMap(mbgl::MapLoadError, const std::string&) final;
+ void onWillStartRenderingFrame() final;
+ void onDidFinishRenderingFrame(RenderMode) final;
+ void onWillStartRenderingMap() final;
+ void onDidFinishRenderingMap(RenderMode) final;
+ void onDidFinishLoadingStyle() final;
+ void onSourceChanged(mbgl::style::Source&) final;
+
+ std::mutex resizeMutex_;
+ mbgl::Size size_;
+ bool sizeChanged_ = true;
+ double pixelRatio_;
+
+ std::thread::id clientThreadId_ = std::this_thread::get_id();
+ std::unique_ptr<std::thread::id> renderThreadId_;
+
+ mutable std::mutex updateMutex_;
+ std::shared_ptr<mbgl::UpdateParameters> updateParameters_;
+ std::unique_ptr<mbgl::Map> map_;
+
+ // Render thread
+ std::unique_ptr<RendererBackend> rendererBackend_;
+ std::unique_ptr<mbgl::Renderer> renderer_;
+
+ mbgl::RendererObserver* rendererObserver_ = nullptr;
+ uint32_t defaultFramebufferID_{0};
+ int32_t x_ = 0;
+ int32_t y_ = 0;
+
+ std::shared_ptr<mapbox::map::MapObserver> observer_;
+ std::shared_ptr<MapClient> client_;
+
+ std::shared_ptr<Scheduler::NotifyFn> schedulerNotify_{};
+};
+
+} // namespace map
+} // namespace mapbox
diff --git a/src/mapbox/map/renderer_backend.cpp b/src/mapbox/map/renderer_backend.cpp
new file mode 100644
index 0000000000..65af09f494
--- /dev/null
+++ b/src/mapbox/map/renderer_backend.cpp
@@ -0,0 +1,61 @@
+#include <mapbox/map/renderer_backend.hpp>
+
+#include <mbgl/gfx/backend_scope.hpp>
+#include <mbgl/gl/renderable_resource.hpp>
+
+namespace mapbox {
+namespace map {
+
+class RenderableResource final : public mbgl::gl::RenderableResource {
+public:
+ RenderableResource(RendererBackend& backend_) : backend(backend_) {}
+
+ void bind() override {
+ backend.bind();
+ }
+
+private:
+ RendererBackend& backend;
+};
+
+RendererBackend::RendererBackend(GetGLProcAddressFn&& getProcAddressFn, mbgl::Size fbSize, uint32_t fbo)
+ : mbgl::gl::RendererBackend(mbgl::gfx::ContextMode::Shared)
+ , mbgl::gfx::Renderable(fbSize, std::make_unique<RenderableResource>(*this))
+ , getProcAddressFn_(std::move(getProcAddressFn)), size_(fbSize), fbo_(fbo) {}
+
+RendererBackend::~RendererBackend() = default;
+
+void RendererBackend::resize(mbgl::Size fbSize) {
+ size_ = fbSize;
+}
+
+void RendererBackend::move(int32_t x, int32_t y) {
+ x_ = x;
+ y_ = y;
+}
+
+void RendererBackend::updateAssumedState() {
+ assumeFramebufferBinding(ImplicitFramebufferBinding);
+ assumeViewport(x_, y_, getFramebufferSize());
+}
+
+void RendererBackend::bind() {
+ assert(mbgl::gfx::BackendScope::exists());
+ setFramebufferBinding(fbo_);
+ setViewport(x_, y_, size_);
+}
+
+mbgl::Size RendererBackend::getFramebufferSize() const {
+ return size_;
+}
+
+mbgl::gl::ProcAddress RendererBackend::getExtensionFunctionPointer(const char* name) {
+ return getProcAddressFn_(name);
+}
+
+void RendererBackend::activate() {}
+
+void RendererBackend::deactivate() {}
+
+} // namespace map
+} // namespace mapbox
diff --git a/src/mapbox/map/renderer_backend.hpp b/src/mapbox/map/renderer_backend.hpp
new file mode 100644
index 0000000000..a305ed401d
--- /dev/null
+++ b/src/mapbox/map/renderer_backend.hpp
@@ -0,0 +1,49 @@
+#pragma once
+
+#include <mapbox/map/map_client.hpp>
+
+#include <mbgl/gfx/renderable.hpp>
+#include <mbgl/gl/renderer_backend.hpp>
+
+#include <functional>
+
+namespace mapbox {
+namespace map {
+
+class RendererBackend : public mbgl::gl::RendererBackend,
+ public mbgl::gfx::Renderable {
+public:
+ using GetGLProcAddressFn = std::function<MapClient::GLProcAddress(const char *)>;
+
+ RendererBackend(GetGLProcAddressFn &&, mbgl::Size, uint32_t fbo);
+ ~RendererBackend() override;
+
+ void resize(mbgl::Size);
+ void move(int32_t x, int32_t y);
+
+ void bind();
+ mbgl::Size getFramebufferSize() const;
+
+ void setDefaultFramebufferObject(uint32_t framebufferID) { fbo_ = framebufferID; }
+
+ // mbgl::gfx::RendererBackend implementation
+ mbgl::gfx::Renderable& getDefaultRenderable() override {
+ return *this;
+ }
+
+protected:
+ mbgl::gl::ProcAddress getExtensionFunctionPointer(const char *) final;
+ void activate() final;
+ void deactivate() final;
+ void updateAssumedState() final;
+
+private:
+ GetGLProcAddressFn getProcAddressFn_;
+ mbgl::Size size_;
+ int32_t x_ = 0;
+ int32_t y_ = 0;
+ uint32_t fbo_;
+};
+
+} // namespace map
+} // namespace mapbox
diff --git a/src/mapbox/map/scheduler.cpp b/src/mapbox/map/scheduler.cpp
new file mode 100644
index 0000000000..c9ef3ea03f
--- /dev/null
+++ b/src/mapbox/map/scheduler.cpp
@@ -0,0 +1,51 @@
+#include <mapbox/map/scheduler.hpp>
+
+#include <cassert>
+
+namespace mapbox {
+namespace map {
+
+Scheduler::Scheduler() {
+ assert(threadId_ == std::this_thread::get_id());
+}
+
+Scheduler::~Scheduler() {
+ assert(threadId_ == std::this_thread::get_id());
+}
+
+void Scheduler::schedule(std::weak_ptr<mbgl::Mailbox> mailbox) {
+ std::lock_guard<std::mutex> lock(taskQueueMutex_);
+ taskQueue_.push(mailbox);
+
+ for (auto it = notifyFns_.begin(); it != notifyFns_.end();) {
+ auto fn = it->lock();
+ if (fn) {
+ fn->operator()();
+ it++;
+ } else {
+ it = notifyFns_.erase(it);
+ }
+ }
+}
+
+void Scheduler::attach(std::weak_ptr<NotifyFn>&& fn) {
+ notifyFns_.push_back(fn);
+}
+
+void Scheduler::processEvents() {
+ assert(threadId_ == std::this_thread::get_id());
+
+ std::queue<std::weak_ptr<mbgl::Mailbox>> taskQueue;
+ {
+ std::unique_lock<std::mutex> lock(taskQueueMutex_);
+ std::swap(taskQueue, taskQueue_);
+ }
+
+ while (!taskQueue.empty()) {
+ mbgl::Mailbox::maybeReceive(taskQueue.front());
+ taskQueue.pop();
+ }
+}
+
+} // namespace map
+} // namespace mapbox
diff --git a/src/mapbox/map/scheduler.hpp b/src/mapbox/map/scheduler.hpp
new file mode 100644
index 0000000000..d3b5daef11
--- /dev/null
+++ b/src/mapbox/map/scheduler.hpp
@@ -0,0 +1,39 @@
+#pragma once
+
+#include <mbgl/actor/mailbox.hpp>
+#include <mbgl/actor/scheduler.hpp>
+
+#include <functional>
+#include <list>
+#include <memory>
+#include <mutex>
+#include <queue>
+#include <thread>
+
+namespace mapbox {
+namespace map {
+
+class Scheduler : public mbgl::Scheduler {
+public:
+ using NotifyFn = std::function<void()>;
+
+ explicit Scheduler();
+ ~Scheduler() override;
+
+ // mbgl::Scheduler implementation.
+ void schedule(std::weak_ptr<mbgl::Mailbox>) final;
+ void processEvents();
+
+ void attach(std::weak_ptr<NotifyFn>&&);
+
+private:
+ const std::thread::id threadId_ = std::this_thread::get_id();
+
+ std::list<std::weak_ptr<NotifyFn>> notifyFns_;
+
+ std::mutex taskQueueMutex_;
+ std::queue<std::weak_ptr<mbgl::Mailbox>> taskQueue_;
+};
+
+} // namespace map
+} // namespace mapbox