Merge pull request #1034 from mapbox/1034-separate-data-object
Isolate Map data into separate object
This commit is contained in:
@@ -39,6 +39,7 @@ class LineAtlas;
|
||||
class Environment;
|
||||
class EnvironmentScope;
|
||||
class AnnotationManager;
|
||||
class MapData;
|
||||
|
||||
class Map : private util::noncopyable {
|
||||
friend class View;
|
||||
@@ -75,7 +76,15 @@ public:
|
||||
void render();
|
||||
|
||||
// Notifies the Map thread that the state has changed and an update might be necessary.
|
||||
void triggerUpdate();
|
||||
using UpdateType = uint32_t;
|
||||
enum class Update : UpdateType {
|
||||
Nothing = 0,
|
||||
StyleInfo = 1 << 0,
|
||||
Debug = 1 << 1,
|
||||
DefaultTransitionDuration = 1 << 2,
|
||||
Classes = 1 << 3,
|
||||
};
|
||||
void triggerUpdate(Update = Update::Nothing);
|
||||
|
||||
// Triggers a render. Can be called from any thread.
|
||||
void triggerRender();
|
||||
@@ -92,8 +101,8 @@ public:
|
||||
|
||||
void setDefaultTransitionDuration(std::chrono::steady_clock::duration duration = std::chrono::steady_clock::duration::zero());
|
||||
std::chrono::steady_clock::duration getDefaultTransitionDuration();
|
||||
void setStyleURL(const std::string &url);
|
||||
void setStyleJSON(std::string newStyleJSON, const std::string &base = "");
|
||||
void setStyleURL(const std::string& url);
|
||||
void setStyleJSON(const std::string& json, const std::string& base = "");
|
||||
std::string getStyleJSON() const;
|
||||
|
||||
// Transition
|
||||
@@ -131,7 +140,7 @@ public:
|
||||
|
||||
// API
|
||||
void setAccessToken(const std::string &token);
|
||||
const std::string &getAccessToken() const;
|
||||
std::string getAccessToken() const;
|
||||
|
||||
// Projection
|
||||
inline void getWorldBoundsMeters(ProjectedMeters &sw, ProjectedMeters &ne) const { Projection::getWorldBoundsMeters(sw, ne); }
|
||||
@@ -157,7 +166,7 @@ public:
|
||||
bool getDebug() const;
|
||||
|
||||
inline const TransformState &getState() const { return state; }
|
||||
inline std::chrono::steady_clock::time_point getTime() const { return animationTime; }
|
||||
std::chrono::steady_clock::time_point getTime() const;
|
||||
inline AnnotationManager& getAnnotationManager() const { return *annotationManager; }
|
||||
|
||||
private:
|
||||
@@ -178,6 +187,13 @@ private:
|
||||
void updateSources();
|
||||
void updateSources(const util::ptr<StyleLayerGroup> &group);
|
||||
|
||||
// Triggered by triggerUpdate();
|
||||
void update();
|
||||
|
||||
// Loads the style set in the data object. Called by Update::StyleInfo
|
||||
void reloadStyle();
|
||||
void loadStyleJSON(const std::string& json, const std::string& base);
|
||||
|
||||
// Prepares a map render by updating the tiles we need for the current view, as well as updating
|
||||
// the stylesheet.
|
||||
void prepare();
|
||||
@@ -234,17 +250,11 @@ private:
|
||||
std::unique_ptr<Painter> painter;
|
||||
util::ptr<AnnotationManager> annotationManager;
|
||||
|
||||
std::string styleURL;
|
||||
std::string styleJSON = "";
|
||||
std::vector<std::string> classes;
|
||||
std::string accessToken;
|
||||
|
||||
std::chrono::steady_clock::duration defaultTransitionDuration;
|
||||
|
||||
bool debug = false;
|
||||
std::chrono::steady_clock::time_point animationTime = std::chrono::steady_clock::time_point::min();
|
||||
const std::unique_ptr<MapData> data;
|
||||
|
||||
std::set<util::ptr<StyleSource>> activeSources;
|
||||
|
||||
std::atomic<UpdateType> updated;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <mbgl/map/map.hpp>
|
||||
#include <mbgl/map/environment.hpp>
|
||||
#include <mbgl/map/view.hpp>
|
||||
#include <mbgl/map/map_data.hpp>
|
||||
#include <mbgl/platform/platform.hpp>
|
||||
#include <mbgl/map/source.hpp>
|
||||
#include <mbgl/renderer/painter.hpp>
|
||||
@@ -71,7 +72,8 @@ Map::Map(View& view_, FileSource& fileSource_)
|
||||
lineAtlas(util::make_unique<LineAtlas>(512, 512)),
|
||||
texturePool(std::make_shared<TexturePool>()),
|
||||
painter(util::make_unique<Painter>(*spriteAtlas, *glyphAtlas, *lineAtlas)),
|
||||
annotationManager(util::make_unique<AnnotationManager>())
|
||||
annotationManager(util::make_unique<AnnotationManager>()),
|
||||
data(util::make_unique<MapData>())
|
||||
{
|
||||
view.initialize(this);
|
||||
}
|
||||
@@ -138,11 +140,7 @@ void Map::start(bool startPaused) {
|
||||
});
|
||||
|
||||
asyncUpdate = util::make_unique<uv::async>(env->loop, [this] {
|
||||
assert(Environment::currentlyOn(ThreadType::Map));
|
||||
|
||||
if (state.hasSize()) {
|
||||
prepare();
|
||||
}
|
||||
update();
|
||||
});
|
||||
|
||||
asyncRender = util::make_unique<uv::async>(env->loop, [this] {
|
||||
@@ -175,6 +173,8 @@ void Map::start(bool startPaused) {
|
||||
isStopped = true;
|
||||
view.notify();
|
||||
});
|
||||
|
||||
triggerUpdate();
|
||||
}
|
||||
|
||||
void Map::stop(std::function<void ()> callback) {
|
||||
@@ -252,7 +252,9 @@ void Map::run() {
|
||||
checkForPause();
|
||||
}
|
||||
|
||||
if (mode == Mode::Static && !style && styleURL.empty()) {
|
||||
auto styleInfo = data->getStyleInfo();
|
||||
|
||||
if (mode == Mode::Static && !style && (styleInfo.url.empty() && styleInfo.json.empty())) {
|
||||
throw util::Exception("Style is not set");
|
||||
}
|
||||
|
||||
@@ -297,7 +299,9 @@ void Map::renderSync() {
|
||||
rendered = false;
|
||||
}
|
||||
|
||||
void Map::triggerUpdate() {
|
||||
void Map::triggerUpdate(const Update u) {
|
||||
updated |= static_cast<UpdateType>(u);
|
||||
|
||||
if (mode == Mode::Static) {
|
||||
prepare();
|
||||
} else if (asyncUpdate) {
|
||||
@@ -345,36 +349,27 @@ void Map::setup() {
|
||||
}
|
||||
|
||||
void Map::setStyleURL(const std::string &url) {
|
||||
// TODO: Make threadsafe.
|
||||
assert(Environment::currentlyOn(ThreadType::Main));
|
||||
|
||||
styleURL = url;
|
||||
if (mode == Mode::Continuous) {
|
||||
stop();
|
||||
start();
|
||||
const size_t pos = url.rfind('/');
|
||||
std::string base = "";
|
||||
if (pos != std::string::npos) {
|
||||
base = url.substr(0, pos + 1);
|
||||
}
|
||||
|
||||
data->setStyleInfo({ url, base, "" });
|
||||
triggerUpdate(Update::StyleInfo);
|
||||
}
|
||||
|
||||
void Map::setStyleJSON(std::string newStyleJSON, const std::string &base) {
|
||||
// TODO: Make threadsafe.
|
||||
styleJSON.swap(newStyleJSON);
|
||||
sprite.reset();
|
||||
if (!style) {
|
||||
style = std::make_shared<Style>();
|
||||
}
|
||||
void Map::setStyleJSON(const std::string& json, const std::string& base) {
|
||||
assert(Environment::currentlyOn(ThreadType::Main));
|
||||
|
||||
style->base = base;
|
||||
style->loadJSON((const uint8_t *)styleJSON.c_str());
|
||||
style->cascadeClasses(classes);
|
||||
style->setDefaultTransitionDuration(defaultTransitionDuration);
|
||||
|
||||
const std::string glyphURL = util::mapbox::normalizeGlyphsURL(style->glyph_url, getAccessToken());
|
||||
glyphStore->setURL(glyphURL);
|
||||
|
||||
triggerUpdate();
|
||||
data->setStyleInfo({ "", base, json });
|
||||
triggerUpdate(Update::StyleInfo);
|
||||
}
|
||||
|
||||
std::string Map::getStyleJSON() const {
|
||||
return styleJSON;
|
||||
return data->getStyleInfo().json;
|
||||
}
|
||||
|
||||
util::ptr<Sprite> Map::getSprite() {
|
||||
@@ -535,11 +530,11 @@ void Map::stopRotating() {
|
||||
#pragma mark - Access Token
|
||||
|
||||
void Map::setAccessToken(const std::string &token) {
|
||||
accessToken = token;
|
||||
data->setAccessToken(token);
|
||||
}
|
||||
|
||||
const std::string &Map::getAccessToken() const {
|
||||
return accessToken;
|
||||
std::string Map::getAccessToken() const {
|
||||
return data->getAccessToken();
|
||||
}
|
||||
|
||||
#pragma mark - Annotations
|
||||
@@ -596,69 +591,58 @@ void Map::updateAnnotationTiles(std::vector<Tile::ID>& ids) {
|
||||
#pragma mark - Toggles
|
||||
|
||||
void Map::setDebug(bool value) {
|
||||
debug = value;
|
||||
assert(painter);
|
||||
painter->setDebug(debug);
|
||||
triggerUpdate();
|
||||
data->setDebug(value);
|
||||
triggerUpdate(Update::Debug);
|
||||
}
|
||||
|
||||
void Map::toggleDebug() {
|
||||
setDebug(!debug);
|
||||
data->toggleDebug();
|
||||
triggerUpdate(Update::Debug);
|
||||
}
|
||||
|
||||
bool Map::getDebug() const {
|
||||
return debug;
|
||||
return data->getDebug();
|
||||
}
|
||||
|
||||
std::chrono::steady_clock::time_point Map::getTime() const {
|
||||
return data->getAnimationTime();
|
||||
}
|
||||
|
||||
void Map::addClass(const std::string& klass) {
|
||||
if (hasClass(klass)) return;
|
||||
classes.push_back(klass);
|
||||
if (style) {
|
||||
style->cascadeClasses(classes);
|
||||
if (style->hasTransitions()) {
|
||||
triggerUpdate();
|
||||
}
|
||||
if (data->addClass(klass)) {
|
||||
triggerUpdate(Update::Classes);
|
||||
}
|
||||
}
|
||||
|
||||
void Map::removeClass(const std::string& klass) {
|
||||
if (!hasClass(klass)) return;
|
||||
classes.erase(std::remove(classes.begin(), classes.end(), klass), classes.end());
|
||||
if (style) {
|
||||
style->cascadeClasses(classes);
|
||||
if (style->hasTransitions()) {
|
||||
triggerUpdate();
|
||||
}
|
||||
if (data->removeClass(klass)) {
|
||||
triggerUpdate(Update::Classes);
|
||||
}
|
||||
}
|
||||
|
||||
void Map::setClasses(const std::vector<std::string>& classes_) {
|
||||
classes = classes_;
|
||||
if (style) {
|
||||
style->cascadeClasses(classes);
|
||||
if (style->hasTransitions()) {
|
||||
triggerUpdate();
|
||||
}
|
||||
}
|
||||
void Map::setClasses(const std::vector<std::string>& classes) {
|
||||
data->setClasses(classes);
|
||||
triggerUpdate(Update::Classes);
|
||||
}
|
||||
|
||||
bool Map::hasClass(const std::string& klass) const {
|
||||
return std::find(classes.begin(), classes.end(), klass) != classes.end();
|
||||
return data->hasClass(klass);
|
||||
}
|
||||
|
||||
std::vector<std::string> Map::getClasses() const {
|
||||
return classes;
|
||||
return data->getClasses();
|
||||
}
|
||||
|
||||
void Map::setDefaultTransitionDuration(std::chrono::steady_clock::duration duration) {
|
||||
defaultTransitionDuration = duration;
|
||||
if (style) {
|
||||
style->setDefaultTransitionDuration(duration);
|
||||
}
|
||||
assert(Environment::currentlyOn(ThreadType::Main));
|
||||
|
||||
data->setDefaultTransitionDuration(duration);
|
||||
triggerUpdate(Update::DefaultTransitionDuration);
|
||||
}
|
||||
|
||||
std::chrono::steady_clock::duration Map::getDefaultTransitionDuration() {
|
||||
return defaultTransitionDuration;
|
||||
assert(Environment::currentlyOn(ThreadType::Main));
|
||||
return data->getDefaultTransitionDuration();
|
||||
}
|
||||
|
||||
void Map::updateSources() {
|
||||
@@ -670,7 +654,9 @@ void Map::updateSources() {
|
||||
}
|
||||
|
||||
// Then, reenable all of those that we actually use when drawing this layer.
|
||||
updateSources(style->layers);
|
||||
if (style) {
|
||||
updateSources(style->layers);
|
||||
}
|
||||
|
||||
// Then, construct or destroy the actual source object, depending on enabled state.
|
||||
for (const auto& source : activeSources) {
|
||||
@@ -715,45 +701,94 @@ void Map::updateTiles() {
|
||||
}
|
||||
}
|
||||
|
||||
void Map::prepare() {
|
||||
void Map::update() {
|
||||
assert(Environment::currentlyOn(ThreadType::Map));
|
||||
|
||||
if (!style) {
|
||||
style = std::make_shared<Style>();
|
||||
if (state.hasSize()) {
|
||||
prepare();
|
||||
}
|
||||
}
|
||||
|
||||
env->request({ Resource::Kind::JSON, styleURL}, [&](const Response &res) {
|
||||
void Map::reloadStyle() {
|
||||
assert(Environment::currentlyOn(ThreadType::Map));
|
||||
|
||||
style = std::make_shared<Style>();
|
||||
|
||||
const auto styleInfo = data->getStyleInfo();
|
||||
|
||||
if (!styleInfo.url.empty()) {
|
||||
// We have a style URL
|
||||
env->request({ Resource::Kind::JSON, styleInfo.url }, [&](const Response &res) {
|
||||
if (res.status == Response::Successful) {
|
||||
// Calculate the base
|
||||
const size_t pos = styleURL.rfind('/');
|
||||
std::string base = "";
|
||||
if (pos != std::string::npos) {
|
||||
base = styleURL.substr(0, pos + 1);
|
||||
}
|
||||
|
||||
setStyleJSON(res.data, base);
|
||||
loadStyleJSON(res.data, styleInfo.base);
|
||||
} else {
|
||||
Log::Error(Event::Setup, "loading style failed: %s", res.message.c_str());
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// We got JSON data directly.
|
||||
loadStyleJSON(styleInfo.json, styleInfo.base);
|
||||
}
|
||||
}
|
||||
|
||||
void Map::loadStyleJSON(const std::string& json, const std::string& base) {
|
||||
assert(Environment::currentlyOn(ThreadType::Map));
|
||||
|
||||
sprite.reset();
|
||||
style = std::make_shared<Style>();
|
||||
style->base = base;
|
||||
style->loadJSON((const uint8_t *)json.c_str());
|
||||
style->cascadeClasses(data->getClasses());
|
||||
style->setDefaultTransitionDuration(data->getDefaultTransitionDuration());
|
||||
|
||||
const std::string glyphURL = util::mapbox::normalizeGlyphsURL(style->glyph_url, getAccessToken());
|
||||
glyphStore->setURL(glyphURL);
|
||||
|
||||
triggerUpdate();
|
||||
}
|
||||
|
||||
void Map::prepare() {
|
||||
assert(Environment::currentlyOn(ThreadType::Map));
|
||||
|
||||
const auto u = updated.exchange(static_cast<UpdateType>(Update::Nothing));
|
||||
if (u & static_cast<UpdateType>(Update::StyleInfo)) {
|
||||
reloadStyle();
|
||||
}
|
||||
if (u & static_cast<UpdateType>(Update::Debug)) {
|
||||
assert(painter);
|
||||
painter->setDebug(data->getDebug());
|
||||
}
|
||||
if (u & static_cast<UpdateType>(Update::DefaultTransitionDuration)) {
|
||||
if (style) {
|
||||
style->setDefaultTransitionDuration(data->getDefaultTransitionDuration());
|
||||
}
|
||||
}
|
||||
if (u & static_cast<UpdateType>(Update::Classes)) {
|
||||
if (style) {
|
||||
style->cascadeClasses(data->getClasses());
|
||||
}
|
||||
}
|
||||
|
||||
// Update transform transitions.
|
||||
animationTime = std::chrono::steady_clock::now();
|
||||
|
||||
const auto animationTime = std::chrono::steady_clock::now();
|
||||
data->setAnimationTime(animationTime);
|
||||
if (transform.needsTransition()) {
|
||||
transform.updateTransitions(animationTime);
|
||||
}
|
||||
|
||||
state = transform.currentState();
|
||||
|
||||
animationTime = std::chrono::steady_clock::now();
|
||||
updateSources();
|
||||
style->updateProperties(state.getNormalizedZoom(), animationTime);
|
||||
if (style) {
|
||||
updateSources();
|
||||
style->updateProperties(state.getNormalizedZoom(), animationTime);
|
||||
|
||||
// Allow the sprite atlas to potentially pull new sprite images if needed.
|
||||
spriteAtlas->resize(state.getPixelRatio());
|
||||
spriteAtlas->setSprite(getSprite());
|
||||
// Allow the sprite atlas to potentially pull new sprite images if needed.
|
||||
spriteAtlas->resize(state.getPixelRatio());
|
||||
spriteAtlas->setSprite(getSprite());
|
||||
|
||||
updateTiles();
|
||||
updateTiles();
|
||||
}
|
||||
|
||||
if (mode == Mode::Continuous) {
|
||||
view.invalidate();
|
||||
@@ -768,7 +803,7 @@ void Map::render() {
|
||||
|
||||
assert(painter);
|
||||
painter->render(*style, activeSources,
|
||||
state, animationTime);
|
||||
state, data->getAnimationTime());
|
||||
// Schedule another rerender when we definitely need a next frame.
|
||||
if (transform.needsTransition() || style->hasTransitions()) {
|
||||
triggerUpdate();
|
||||
|
||||
44
src/mbgl/map/map_data.cpp
Normal file
44
src/mbgl/map/map_data.cpp
Normal file
@@ -0,0 +1,44 @@
|
||||
#include "map_data.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
// Adds the class if it's not yet set. Returns true when it added the class, and false when it
|
||||
// was already present.
|
||||
bool MapData::addClass(const std::string& klass) {
|
||||
Lock lock(mtx);
|
||||
if (std::find(classes.begin(), classes.end(), klass) != classes.end()) return false;
|
||||
classes.push_back(klass);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Removes the class if it's present. Returns true when it remvoed the class, and false when it
|
||||
// was not present.
|
||||
bool MapData::removeClass(const std::string& klass) {
|
||||
Lock lock(mtx);
|
||||
const auto it = std::find(classes.begin(), classes.end(), klass);
|
||||
if (it != classes.end()) {
|
||||
classes.erase(it);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Returns true when class is present in the list of currently set classes.
|
||||
bool MapData::hasClass(const std::string& klass) const {
|
||||
Lock lock(mtx);
|
||||
return std::find(classes.begin(), classes.end(), klass) != classes.end();
|
||||
}
|
||||
|
||||
void MapData::setClasses(const std::vector<std::string>& klasses) {
|
||||
Lock lock(mtx);
|
||||
classes = klasses;
|
||||
}
|
||||
|
||||
std::vector<std::string> MapData::getClasses() const {
|
||||
Lock lock(mtx);
|
||||
return classes;
|
||||
}
|
||||
|
||||
}
|
||||
102
src/mbgl/map/map_data.hpp
Normal file
102
src/mbgl/map/map_data.hpp
Normal file
@@ -0,0 +1,102 @@
|
||||
#ifndef MBGL_MAP_MAP_DATA
|
||||
#define MBGL_MAP_MAP_DATA
|
||||
|
||||
#include <string>
|
||||
#include <mutex>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <vector>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
struct StyleInfo {
|
||||
std::string url;
|
||||
std::string base;
|
||||
std::string json;
|
||||
};
|
||||
|
||||
class MapData {
|
||||
using Lock = std::lock_guard<std::mutex>;
|
||||
|
||||
public:
|
||||
inline MapData() {
|
||||
setAnimationTime(std::chrono::steady_clock::time_point::min());
|
||||
setDefaultTransitionDuration(std::chrono::steady_clock::duration::zero());
|
||||
}
|
||||
|
||||
inline StyleInfo getStyleInfo() const {
|
||||
Lock lock(mtx);
|
||||
return styleInfo;
|
||||
}
|
||||
inline void setStyleInfo(StyleInfo&& info) {
|
||||
Lock lock(mtx);
|
||||
styleInfo = info;
|
||||
}
|
||||
|
||||
inline std::string getAccessToken() const {
|
||||
Lock lock(mtx);
|
||||
return accessToken;
|
||||
}
|
||||
inline void setAccessToken(const std::string &token) {
|
||||
Lock lock(mtx);
|
||||
accessToken = token;
|
||||
}
|
||||
|
||||
// Adds the class if it's not yet set. Returns true when it added the class, and false when it
|
||||
// was already present.
|
||||
bool addClass(const std::string& klass);
|
||||
|
||||
// Removes the class if it's present. Returns true when it remvoed the class, and false when it
|
||||
// was not present.
|
||||
bool removeClass(const std::string& klass);
|
||||
|
||||
// Returns true when class is present in the list of currently set classes.
|
||||
bool hasClass(const std::string& klass) const;
|
||||
|
||||
// Changes the list of currently set classes to the new list.
|
||||
void setClasses(const std::vector<std::string>& klasses);
|
||||
|
||||
// Returns a list of all currently set classes.
|
||||
std::vector<std::string> getClasses() const;
|
||||
|
||||
|
||||
inline bool getDebug() const {
|
||||
return debug;
|
||||
}
|
||||
inline bool toggleDebug() {
|
||||
return debug ^= 1u;
|
||||
}
|
||||
inline void setDebug(bool value) {
|
||||
debug = value;
|
||||
}
|
||||
|
||||
inline std::chrono::steady_clock::time_point getAnimationTime() const {
|
||||
// We're casting the time_point to and from a duration because libstdc++
|
||||
// has a bug that doesn't allow time_points to be atomic.
|
||||
return std::chrono::steady_clock::time_point(animationTime);
|
||||
}
|
||||
inline void setAnimationTime(std::chrono::steady_clock::time_point timePoint) {
|
||||
animationTime = timePoint.time_since_epoch();
|
||||
};
|
||||
|
||||
inline std::chrono::steady_clock::duration getDefaultTransitionDuration() const {
|
||||
return defaultTransitionDuration;
|
||||
}
|
||||
inline void setDefaultTransitionDuration(std::chrono::steady_clock::duration duration) {
|
||||
defaultTransitionDuration = duration;
|
||||
};
|
||||
|
||||
private:
|
||||
mutable std::mutex mtx;
|
||||
|
||||
StyleInfo styleInfo;
|
||||
std::string accessToken;
|
||||
std::vector<std::string> classes;
|
||||
std::atomic<uint8_t> debug { false };
|
||||
std::atomic<std::chrono::steady_clock::time_point::duration> animationTime;
|
||||
std::atomic<std::chrono::steady_clock::duration> defaultTransitionDuration;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user