Merge pull request #1021 from mapbox/point-annotations-api
refs #893 #992: point annotations API
This commit is contained in:
72
include/mbgl/map/annotation.hpp
Normal file
72
include/mbgl/map/annotation.hpp
Normal file
@@ -0,0 +1,72 @@
|
||||
#ifndef MBGL_MAP_ANNOTATIONS
|
||||
#define MBGL_MAP_ANNOTATIONS
|
||||
|
||||
#include <mbgl/map/tile.hpp>
|
||||
#include <mbgl/map/live_tile.hpp>
|
||||
#include <mbgl/util/geo.hpp>
|
||||
#include <mbgl/util/noncopyable.hpp>
|
||||
#include <mbgl/util/std.hpp>
|
||||
#include <mbgl/util/vec.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <memory>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
class Annotation;
|
||||
class Map;
|
||||
|
||||
typedef std::vector<LatLng> AnnotationSegment;
|
||||
|
||||
enum class AnnotationType : uint8_t {
|
||||
Point,
|
||||
Shape
|
||||
};
|
||||
|
||||
class AnnotationManager : private util::noncopyable {
|
||||
public:
|
||||
AnnotationManager();
|
||||
|
||||
void setDefaultPointAnnotationSymbol(std::string& symbol) { defaultPointAnnotationSymbol = symbol; }
|
||||
std::pair<std::vector<Tile::ID>, std::vector<uint32_t>> addPointAnnotations(std::vector<LatLng>, std::vector<std::string>& symbols, const Map&);
|
||||
std::vector<Tile::ID> removeAnnotations(std::vector<uint32_t>);
|
||||
std::vector<uint32_t> getAnnotationsInBounds(LatLngBounds, const Map&) const;
|
||||
LatLngBounds getBoundsForAnnotations(std::vector<uint32_t>) const;
|
||||
|
||||
const std::unique_ptr<LiveTile>& getTile(Tile::ID const& id);
|
||||
|
||||
private:
|
||||
uint32_t nextID() { return nextID_++; }
|
||||
static vec2<double> projectPoint(LatLng& point);
|
||||
|
||||
private:
|
||||
std::mutex mtx;
|
||||
std::string defaultPointAnnotationSymbol;
|
||||
std::map<uint32_t, std::unique_ptr<Annotation>> annotations;
|
||||
std::map<Tile::ID, std::pair<std::vector<uint32_t>, std::unique_ptr<LiveTile>>> annotationTiles;
|
||||
std::unique_ptr<LiveTile> nullTile;
|
||||
uint32_t nextID_ = 0;
|
||||
};
|
||||
|
||||
class Annotation : private util::noncopyable {
|
||||
friend class AnnotationManager;
|
||||
public:
|
||||
Annotation(AnnotationType, std::vector<AnnotationSegment>);
|
||||
|
||||
private:
|
||||
LatLng getPoint() const;
|
||||
LatLngBounds getBounds() const { return bounds; }
|
||||
|
||||
private:
|
||||
const AnnotationType type = AnnotationType::Point;
|
||||
const std::vector<AnnotationSegment> geometry;
|
||||
std::map<Tile::ID, std::vector<std::weak_ptr<const LiveTileFeature>>> tileFeatures;
|
||||
LatLngBounds bounds;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -37,6 +37,7 @@ class GlyphAtlas;
|
||||
class SpriteAtlas;
|
||||
class LineAtlas;
|
||||
class Environment;
|
||||
class AnnotationManager;
|
||||
|
||||
class Map : private util::noncopyable {
|
||||
friend class View;
|
||||
@@ -140,6 +141,15 @@ public:
|
||||
inline const vec2<double> pixelForLatLng(const LatLng latLng) const { return state.pixelForLatLng(latLng); }
|
||||
inline const LatLng latLngForPixel(const vec2<double> pixel) const { return state.latLngForPixel(pixel); }
|
||||
|
||||
// Annotations
|
||||
void setDefaultPointAnnotationSymbol(std::string&);
|
||||
uint32_t addPointAnnotation(LatLng, std::string& symbol);
|
||||
std::vector<uint32_t> addPointAnnotations(std::vector<LatLng>, std::vector<std::string>& symbols);
|
||||
void removeAnnotation(uint32_t);
|
||||
void removeAnnotations(std::vector<uint32_t>);
|
||||
std::vector<uint32_t> getAnnotationsInBounds(LatLngBounds) const;
|
||||
LatLngBounds getBoundsForAnnotations(std::vector<uint32_t>) const;
|
||||
|
||||
// Debug
|
||||
void setDebug(bool value);
|
||||
void toggleDebug();
|
||||
@@ -147,6 +157,7 @@ public:
|
||||
|
||||
inline const TransformState &getState() const { return state; }
|
||||
inline std::chrono::steady_clock::time_point getTime() const { return animationTime; }
|
||||
inline AnnotationManager& getAnnotationManager() const { return *annotationManager; }
|
||||
|
||||
private:
|
||||
// This may only be called by the View object.
|
||||
@@ -170,6 +181,8 @@ private:
|
||||
// the stylesheet.
|
||||
void prepare();
|
||||
|
||||
void updateAnnotationTiles(std::vector<Tile::ID>&);
|
||||
|
||||
enum class Mode : uint8_t {
|
||||
None, // we're not doing any processing
|
||||
Continuous, // continually updating map
|
||||
@@ -219,8 +232,8 @@ private:
|
||||
util::ptr<Sprite> sprite;
|
||||
const std::unique_ptr<LineAtlas> lineAtlas;
|
||||
util::ptr<TexturePool> texturePool;
|
||||
|
||||
const std::unique_ptr<Painter> painter;
|
||||
util::ptr<AnnotationManager> annotationManager;
|
||||
|
||||
std::string styleURL;
|
||||
std::string styleJSON = "";
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define MBGL_UTIL_CONSTANTS
|
||||
|
||||
#include <cmath>
|
||||
#include <string>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
@@ -15,6 +16,8 @@ extern const double M2PI;
|
||||
extern const double EARTH_RADIUS_M;
|
||||
extern const double LATITUDE_MAX;
|
||||
|
||||
extern const std::string ANNOTATIONS_POINTS_LAYER_ID;
|
||||
|
||||
}
|
||||
|
||||
namespace debug {
|
||||
|
||||
@@ -19,6 +19,21 @@ struct ProjectedMeters {
|
||||
: northing(n), easting(e) {}
|
||||
};
|
||||
|
||||
struct LatLngBounds {
|
||||
LatLng sw = {90, 180};
|
||||
LatLng ne = {-90, -180};
|
||||
|
||||
inline LatLngBounds(LatLng sw_ = {90, 180}, LatLng ne_ = {-90, -180})
|
||||
: sw(sw_), ne(ne_) {}
|
||||
|
||||
inline 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;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
'product_extension': 'app',
|
||||
'mac_bundle': 1,
|
||||
'mac_bundle_resources': [
|
||||
'Icon.icns',
|
||||
'Icon.icns'
|
||||
],
|
||||
|
||||
'dependencies': [
|
||||
|
||||
190
src/mbgl/map/annotation.cpp
Normal file
190
src/mbgl/map/annotation.cpp
Normal file
@@ -0,0 +1,190 @@
|
||||
#include <mbgl/map/annotation.hpp>
|
||||
#include <mbgl/map/map.hpp>
|
||||
#include <mbgl/util/ptr.hpp>
|
||||
#include <mbgl/util/std.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
using namespace mbgl;
|
||||
|
||||
Annotation::Annotation(AnnotationType type_, std::vector<AnnotationSegment> geometry_)
|
||||
: type(type_),
|
||||
geometry(geometry_) {
|
||||
if (type == AnnotationType::Point) {
|
||||
bounds = LatLngBounds(getPoint(), getPoint());
|
||||
} else {
|
||||
for (auto segment : geometry) {
|
||||
for (auto point : segment) {
|
||||
bounds.extend(point);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LatLng Annotation::getPoint() const {
|
||||
return geometry[0][0];
|
||||
}
|
||||
|
||||
AnnotationManager::AnnotationManager()
|
||||
: nullTile(util::make_unique<LiveTile>()) {}
|
||||
|
||||
vec2<double> AnnotationManager::projectPoint(LatLng& point) {
|
||||
double sine = std::sin(point.latitude * M_PI / 180);
|
||||
double x = point.longitude / 360 + 0.5;
|
||||
double y = 0.5 - 0.25 * std::log((1 + sine) / (1 - sine)) / M_PI;
|
||||
return vec2<double>(x, y);
|
||||
}
|
||||
|
||||
std::pair<std::vector<Tile::ID>, std::vector<uint32_t>> AnnotationManager::addPointAnnotations(std::vector<LatLng> points, std::vector<std::string>& symbols, const Map& map) {
|
||||
|
||||
uint16_t extent = 4096;
|
||||
|
||||
std::vector<uint32_t> annotationIDs(points.size());
|
||||
std::vector<Tile::ID> affectedTiles;
|
||||
|
||||
for (uint32_t i = 0; i < points.size(); ++i) {
|
||||
uint32_t annotationID = nextID();
|
||||
|
||||
auto anno_it = annotations.emplace(annotationID, util::make_unique<Annotation>(AnnotationType::Point, std::vector<AnnotationSegment>({{ points[i] }})));
|
||||
|
||||
uint8_t maxZoom = map.getMaxZoom();
|
||||
|
||||
uint32_t z2 = 1 << maxZoom;
|
||||
|
||||
vec2<double> p = projectPoint(points[i]);
|
||||
|
||||
uint32_t x = p.x * z2;
|
||||
uint32_t y = p.y * z2;
|
||||
|
||||
for (int8_t z = maxZoom; z >= 0; z--) {
|
||||
affectedTiles.emplace_back(z, x, y);
|
||||
Tile::ID tileID = affectedTiles.back();
|
||||
|
||||
Coordinate coordinate(extent * (p.x * z2 - x), extent * (p.y * z2 - y));
|
||||
|
||||
GeometryCollection geometries({{ {{ coordinate }} }});
|
||||
|
||||
std::map<std::string, std::string> properties = {{ "sprite", (symbols[i].length() ? symbols[i] : defaultPointAnnotationSymbol) }};
|
||||
|
||||
auto feature = std::make_shared<const LiveTileFeature>(FeatureType::Point,
|
||||
geometries,
|
||||
properties);
|
||||
|
||||
auto tile_it = annotationTiles.find(tileID);
|
||||
if (tile_it != annotationTiles.end()) {
|
||||
// get point layer & add feature
|
||||
auto layer = tile_it->second.second->getMutableLayer(util::ANNOTATIONS_POINTS_LAYER_ID);
|
||||
layer->addFeature(feature);
|
||||
// record annotation association with tile
|
||||
tile_it->second.first.push_back(annotationID);
|
||||
} else {
|
||||
// create point layer & add feature
|
||||
util::ptr<LiveTileLayer> layer = std::make_shared<LiveTileLayer>();
|
||||
layer->addFeature(feature);
|
||||
// create tile & record annotation association
|
||||
auto tile_pos = annotationTiles.emplace(tileID, std::make_pair(std::vector<uint32_t>({ annotationID }), util::make_unique<LiveTile>()));
|
||||
// add point layer to tile
|
||||
tile_pos.first->second.second->addLayer(util::ANNOTATIONS_POINTS_LAYER_ID, layer);
|
||||
}
|
||||
|
||||
// record annotation association with tile feature
|
||||
anno_it.first->second->tileFeatures.emplace(tileID, std::vector<std::weak_ptr<const LiveTileFeature>>({ feature }));
|
||||
|
||||
z2 /= 2;
|
||||
x /= 2;
|
||||
y /= 2;
|
||||
}
|
||||
|
||||
annotationIDs.push_back(annotationID);
|
||||
}
|
||||
|
||||
return std::make_pair(affectedTiles, annotationIDs);
|
||||
}
|
||||
|
||||
std::vector<Tile::ID> AnnotationManager::removeAnnotations(std::vector<uint32_t> ids) {
|
||||
std::vector<Tile::ID> affectedTiles;
|
||||
|
||||
for (auto& annotationID : ids) {
|
||||
auto annotation_it = annotations.find(annotationID);
|
||||
if (annotation_it != annotations.end()) {
|
||||
auto& annotation = annotation_it->second;
|
||||
for (auto& tile_it : annotationTiles) {
|
||||
auto& tileAnnotations = tile_it.second.first;
|
||||
util::erase_if(tileAnnotations, tileAnnotations.begin(),
|
||||
tileAnnotations.end(), [&](const uint32_t annotationID_) -> bool {
|
||||
return (annotationID_ == annotationID);
|
||||
});
|
||||
auto features_it = annotation->tileFeatures.find(tile_it.first);
|
||||
if (features_it != annotation->tileFeatures.end()) {
|
||||
auto layer = tile_it.second.second->getMutableLayer(util::ANNOTATIONS_POINTS_LAYER_ID);
|
||||
auto& features = features_it->second;
|
||||
layer->removeFeature(features[0]);
|
||||
affectedTiles.push_back(tile_it.first);
|
||||
}
|
||||
}
|
||||
annotations.erase(annotationID);
|
||||
}
|
||||
}
|
||||
|
||||
return affectedTiles;
|
||||
}
|
||||
|
||||
std::vector<uint32_t> AnnotationManager::getAnnotationsInBounds(LatLngBounds queryBounds, const Map& map) const {
|
||||
uint8_t z = map.getMaxZoom();
|
||||
uint32_t z2 = 1 << z;
|
||||
vec2<double> swPoint = projectPoint(queryBounds.sw);
|
||||
vec2<double> nePoint = projectPoint(queryBounds.ne);
|
||||
|
||||
// tiles number y from top down
|
||||
Tile::ID nwTile(z, swPoint.x * z2, nePoint.y * z2);
|
||||
Tile::ID seTile(z, nePoint.x * z2, swPoint.y * z2);
|
||||
|
||||
std::vector<uint32_t> matchingAnnotations;
|
||||
|
||||
for (auto& tile : annotationTiles) {
|
||||
Tile::ID id = tile.first;
|
||||
if (id.z == z) {
|
||||
if (id.x >= nwTile.x && id.x <= seTile.x && id.y >= nwTile.y && id.y <= seTile.y) {
|
||||
if (id.x > nwTile.x && id.x < seTile.x && id.y > nwTile.y && id.y < seTile.y) {
|
||||
// trivial accept; grab all of the tile's annotations
|
||||
std::copy(tile.second.first.begin(), tile.second.first.end(), std::back_inserter(matchingAnnotations));
|
||||
} else {
|
||||
// check tile's annotations' bounding boxes
|
||||
std::copy_if(tile.second.first.begin(), tile.second.first.end(),
|
||||
std::back_inserter(matchingAnnotations), [&](const uint32_t annotationID) -> bool {
|
||||
LatLngBounds annoBounds = this->annotations.find(annotationID)->second->getBounds();
|
||||
return (annoBounds.sw.latitude >= queryBounds.sw.latitude &&
|
||||
annoBounds.ne.latitude <= queryBounds.ne.latitude &&
|
||||
annoBounds.sw.longitude >= queryBounds.sw.longitude &&
|
||||
annoBounds.ne.longitude <= queryBounds.ne.longitude);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return matchingAnnotations;
|
||||
}
|
||||
|
||||
LatLngBounds AnnotationManager::getBoundsForAnnotations(std::vector<uint32_t> ids) const {
|
||||
LatLngBounds bounds;
|
||||
for (auto id : ids) {
|
||||
auto annotation_it = annotations.find(id);
|
||||
if (annotation_it != annotations.end()) {
|
||||
bounds.extend(annotation_it->second->getPoint());
|
||||
}
|
||||
}
|
||||
|
||||
return bounds;
|
||||
}
|
||||
|
||||
const std::unique_ptr<LiveTile>& AnnotationManager::getTile(Tile::ID const& id) {
|
||||
std::lock_guard<std::mutex> lock(mtx);
|
||||
|
||||
auto tile_it = annotationTiles.find(id);
|
||||
if (tile_it != annotationTiles.end()) {
|
||||
return tile_it->second.second;
|
||||
}
|
||||
return nullTile;
|
||||
}
|
||||
@@ -23,22 +23,22 @@ enum class FeatureType : uint8_t {
|
||||
|
||||
typedef std::vector<std::vector<Coordinate>> GeometryCollection;
|
||||
|
||||
class GeometryTileFeature : public mbgl::util::noncopyable {
|
||||
class GeometryTileFeature : private util::noncopyable {
|
||||
public:
|
||||
virtual FeatureType getType() const = 0;
|
||||
virtual mapbox::util::optional<Value> getValue(const std::string& key) const = 0;
|
||||
virtual GeometryCollection getGeometries() const = 0;
|
||||
};
|
||||
|
||||
class GeometryTileLayer : public mbgl::util::noncopyable {
|
||||
class GeometryTileLayer : private util::noncopyable {
|
||||
public:
|
||||
virtual std::size_t featureCount() const = 0;
|
||||
virtual util::ptr<const GeometryTileFeature> feature(std::size_t i) const = 0;
|
||||
virtual util::ptr<const GeometryTileFeature> getFeature(std::size_t) const = 0;
|
||||
};
|
||||
|
||||
class GeometryTile : public mbgl::util::noncopyable {
|
||||
class GeometryTile : private util::noncopyable {
|
||||
public:
|
||||
virtual util::ptr<const GeometryTileLayer> getLayer(const std::string&) const = 0;
|
||||
virtual util::ptr<GeometryTileLayer> getLayer(const std::string&) const = 0;
|
||||
};
|
||||
|
||||
class GeometryTileFeatureExtractor {
|
||||
|
||||
56
src/mbgl/map/live_tile.cpp
Normal file
56
src/mbgl/map/live_tile.cpp
Normal file
@@ -0,0 +1,56 @@
|
||||
#include <mbgl/map/live_tile.hpp>
|
||||
#include <mbgl/util/constants.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
LiveTileFeature::LiveTileFeature(FeatureType type_, GeometryCollection geometries_, std::map<std::string, std::string> properties_)
|
||||
: type(type_),
|
||||
properties(properties_),
|
||||
geometries(geometries_) {}
|
||||
|
||||
mapbox::util::optional<Value> LiveTileFeature::getValue(const std::string& key) const {
|
||||
auto it = properties.find(key);
|
||||
if (it != properties.end()) {
|
||||
return mapbox::util::optional<Value>(it->second);
|
||||
}
|
||||
return mapbox::util::optional<Value>();
|
||||
}
|
||||
|
||||
LiveTileLayer::LiveTileLayer() {}
|
||||
|
||||
void LiveTileLayer::prepareToAddFeatures(size_t count) {
|
||||
features.reserve(features.size() + count);
|
||||
}
|
||||
|
||||
void LiveTileLayer::addFeature(util::ptr<const LiveTileFeature> feature) {
|
||||
features.push_back(std::move(feature));
|
||||
}
|
||||
|
||||
void LiveTileLayer::removeFeature(util::ptr<const LiveTileFeature> feature) {
|
||||
for (auto it = features.begin(); it != features.end(); ++it) {
|
||||
if (feature == *it) {
|
||||
features.erase(it);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LiveTile::LiveTile() {}
|
||||
|
||||
void LiveTile::addLayer(const std::string& name, util::ptr<LiveTileLayer> layer) {
|
||||
layers.emplace(name, std::move(layer));
|
||||
}
|
||||
|
||||
util::ptr<GeometryTileLayer> LiveTile::getLayer(const std::string& name) const {
|
||||
return getMutableLayer(name);
|
||||
}
|
||||
|
||||
util::ptr<LiveTileLayer> LiveTile::getMutableLayer(const std::string& name) const {
|
||||
auto layer_it = layers.find(name);
|
||||
if (layer_it != layers.end()) {
|
||||
return layer_it->second;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
}
|
||||
53
src/mbgl/map/live_tile.hpp
Normal file
53
src/mbgl/map/live_tile.hpp
Normal file
@@ -0,0 +1,53 @@
|
||||
#ifndef MBGL_MAP_LIVE_TILE
|
||||
#define MBGL_MAP_LIVE_TILE
|
||||
|
||||
#include <map>
|
||||
|
||||
#include <mbgl/map/geometry_tile.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
class LiveTileFeature : public GeometryTileFeature {
|
||||
public:
|
||||
LiveTileFeature(FeatureType, GeometryCollection, std::map<std::string, std::string> properties = {{}});
|
||||
|
||||
FeatureType getType() const override { return type; }
|
||||
mapbox::util::optional<Value> getValue(const std::string&) const override;
|
||||
GeometryCollection getGeometries() const override { return geometries; }
|
||||
|
||||
private:
|
||||
FeatureType type = FeatureType::Unknown;
|
||||
std::map<std::string, std::string> properties;
|
||||
GeometryCollection geometries;
|
||||
};
|
||||
|
||||
class LiveTileLayer : public GeometryTileLayer {
|
||||
public:
|
||||
LiveTileLayer();
|
||||
|
||||
void prepareToAddFeatures(size_t count);
|
||||
void addFeature(util::ptr<const LiveTileFeature>);
|
||||
void removeFeature(util::ptr<const LiveTileFeature>);
|
||||
std::size_t featureCount() const override { return features.size(); }
|
||||
util::ptr<const GeometryTileFeature> getFeature(std::size_t i) const override { return features[i]; }
|
||||
|
||||
private:
|
||||
std::vector<util::ptr<const LiveTileFeature>> features;
|
||||
};
|
||||
|
||||
class LiveTile : public GeometryTile {
|
||||
public:
|
||||
LiveTile();
|
||||
|
||||
void addLayer(const std::string&, util::ptr<LiveTileLayer>);
|
||||
util::ptr<GeometryTileLayer> getLayer(const std::string&) const override;
|
||||
util::ptr<LiveTileLayer> getMutableLayer(const std::string&) const;
|
||||
bool operator()(const LiveTile&) const { return layers.size() > 0; }
|
||||
|
||||
private:
|
||||
std::map<std::string, util::ptr<LiveTileLayer>> layers;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
67
src/mbgl/map/live_tile_data.cpp
Normal file
67
src/mbgl/map/live_tile_data.cpp
Normal file
@@ -0,0 +1,67 @@
|
||||
#include <mbgl/map/annotation.hpp>
|
||||
#include <mbgl/map/live_tile_data.hpp>
|
||||
#include <mbgl/map/tile_parser.hpp>
|
||||
#include <mbgl/style/style_source.hpp>
|
||||
#include <mbgl/map/vector_tile.hpp>
|
||||
#include <mbgl/platform/log.hpp>
|
||||
|
||||
using namespace mbgl;
|
||||
|
||||
LiveTileData::LiveTileData(Tile::ID const& id_,
|
||||
AnnotationManager& annotationManager_,
|
||||
float mapMaxZoom,
|
||||
util::ptr<Style> style_,
|
||||
GlyphAtlas& glyphAtlas_,
|
||||
GlyphStore& glyphStore_,
|
||||
SpriteAtlas& spriteAtlas_,
|
||||
util::ptr<Sprite> sprite_,
|
||||
const SourceInfo& source_,
|
||||
Environment& env_)
|
||||
: VectorTileData::VectorTileData(id_, mapMaxZoom, style_, glyphAtlas_, glyphStore_,
|
||||
spriteAtlas_, sprite_, source_, env_),
|
||||
annotationManager(annotationManager_) {
|
||||
// live features are always ready
|
||||
state = State::loaded;
|
||||
}
|
||||
|
||||
LiveTileData::~LiveTileData() {}
|
||||
|
||||
void LiveTileData::parse() {
|
||||
if (state != State::loaded) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (!style) {
|
||||
throw std::runtime_error("style isn't present in LiveTileData object anymore");
|
||||
}
|
||||
|
||||
if (source.type == SourceType::Annotations) {
|
||||
const std::unique_ptr<LiveTile>& tile = annotationManager.getTile(id);
|
||||
|
||||
if (tile) {
|
||||
// Parsing creates state that is encapsulated in TileParser. While parsing,
|
||||
// the TileParser object writes results into this objects. All other state
|
||||
// is going to be discarded afterwards.
|
||||
TileParser parser(*tile, *this, style, glyphAtlas, glyphStore, spriteAtlas, sprite);
|
||||
|
||||
// Clear the style so that we don't have a cycle in the shared_ptr references.
|
||||
style.reset();
|
||||
|
||||
parser.parse();
|
||||
} else {
|
||||
state = State::obsolete;
|
||||
}
|
||||
} else {
|
||||
throw std::runtime_error("unknown live tile source type");
|
||||
}
|
||||
} catch (const std::exception& ex) {
|
||||
Log::Error(Event::ParseTile, "Live-parsing [%d/%d/%d] failed: %s", id.z, id.x, id.y, ex.what());
|
||||
state = State::obsolete;
|
||||
return;
|
||||
}
|
||||
|
||||
if (state != State::obsolete) {
|
||||
state = State::parsed;
|
||||
}
|
||||
}
|
||||
32
src/mbgl/map/live_tile_data.hpp
Normal file
32
src/mbgl/map/live_tile_data.hpp
Normal file
@@ -0,0 +1,32 @@
|
||||
#ifndef MBGL_MAP_LIVE_TILE_DATA
|
||||
#define MBGL_MAP_LIVE_TILE_DATA
|
||||
|
||||
#include <mbgl/map/vector_tile_data.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
class AnnotationManager;
|
||||
|
||||
class LiveTileData : public VectorTileData {
|
||||
public:
|
||||
LiveTileData(Tile::ID const&,
|
||||
AnnotationManager&,
|
||||
float mapMaxZoom,
|
||||
util::ptr<Style>,
|
||||
GlyphAtlas&,
|
||||
GlyphStore&,
|
||||
SpriteAtlas&,
|
||||
util::ptr<Sprite>,
|
||||
const SourceInfo&,
|
||||
Environment&);
|
||||
~LiveTileData();
|
||||
|
||||
void parse() override;
|
||||
|
||||
private:
|
||||
AnnotationManager& annotationManager;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <mbgl/platform/platform.hpp>
|
||||
#include <mbgl/map/source.hpp>
|
||||
#include <mbgl/renderer/painter.hpp>
|
||||
#include <mbgl/map/annotation.hpp>
|
||||
#include <mbgl/map/sprite.hpp>
|
||||
#include <mbgl/util/transition.hpp>
|
||||
#include <mbgl/util/math.hpp>
|
||||
@@ -70,7 +71,8 @@ Map::Map(View& view_, FileSource& fileSource_)
|
||||
spriteAtlas(util::make_unique<SpriteAtlas>(512, 512)),
|
||||
lineAtlas(util::make_unique<LineAtlas>(512, 512)),
|
||||
texturePool(std::make_shared<TexturePool>()),
|
||||
painter(util::make_unique<Painter>(*spriteAtlas, *glyphAtlas, *lineAtlas))
|
||||
painter(util::make_unique<Painter>(*spriteAtlas, *glyphAtlas, *lineAtlas)),
|
||||
annotationManager(util::make_unique<AnnotationManager>())
|
||||
{
|
||||
view.initialize(this);
|
||||
}
|
||||
@@ -536,6 +538,57 @@ const std::string &Map::getAccessToken() const {
|
||||
return accessToken;
|
||||
}
|
||||
|
||||
#pragma mark - Annotations
|
||||
|
||||
void Map::setDefaultPointAnnotationSymbol(std::string& symbol) {
|
||||
assert(std::this_thread::get_id() == mainThread);
|
||||
annotationManager->setDefaultPointAnnotationSymbol(symbol);
|
||||
}
|
||||
|
||||
uint32_t Map::addPointAnnotation(LatLng point, std::string& symbol) {
|
||||
assert(std::this_thread::get_id() == mainThread);
|
||||
std::vector<LatLng> points({ point });
|
||||
std::vector<std::string> symbols({ symbol });
|
||||
return addPointAnnotations(points, symbols)[0];
|
||||
}
|
||||
|
||||
std::vector<uint32_t> Map::addPointAnnotations(std::vector<LatLng> points, std::vector<std::string>& symbols) {
|
||||
assert(std::this_thread::get_id() == mainThread);
|
||||
auto result = annotationManager->addPointAnnotations(points, symbols, *this);
|
||||
updateAnnotationTiles(result.first);
|
||||
return result.second;
|
||||
}
|
||||
|
||||
void Map::removeAnnotation(uint32_t annotation) {
|
||||
assert(std::this_thread::get_id() == mainThread);
|
||||
removeAnnotations({ annotation });
|
||||
}
|
||||
|
||||
void Map::removeAnnotations(std::vector<uint32_t> annotations) {
|
||||
assert(std::this_thread::get_id() == mainThread);
|
||||
auto result = annotationManager->removeAnnotations(annotations);
|
||||
updateAnnotationTiles(result);
|
||||
}
|
||||
|
||||
std::vector<uint32_t> Map::getAnnotationsInBounds(LatLngBounds bounds) const {
|
||||
assert(std::this_thread::get_id() == mainThread);
|
||||
return annotationManager->getAnnotationsInBounds(bounds, *this);
|
||||
}
|
||||
|
||||
LatLngBounds Map::getBoundsForAnnotations(std::vector<uint32_t> annotations) const {
|
||||
assert(std::this_thread::get_id() == mainThread);
|
||||
return annotationManager->getBoundsForAnnotations(annotations);
|
||||
}
|
||||
|
||||
void Map::updateAnnotationTiles(std::vector<Tile::ID>& ids) {
|
||||
for (const auto &source : activeSources) {
|
||||
if (source->info.type == SourceType::Annotations) {
|
||||
source->source->invalidateTiles(*this, ids);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Toggles
|
||||
|
||||
void Map::setDebug(bool value) {
|
||||
@@ -643,6 +696,7 @@ void Map::updateSources(const util::ptr<StyleLayerGroup> &group) {
|
||||
if (layer->bucket && layer->bucket->style_source) {
|
||||
(*activeSources.emplace(layer->bucket->style_source).first)->enabled = true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
#include <mbgl/map/vector_tile_data.hpp>
|
||||
#include <mbgl/map/raster_tile_data.hpp>
|
||||
#include <mbgl/map/live_tile_data.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
@@ -190,13 +191,22 @@ TileData::State Source::addTile(Map &map, Environment &env, uv::worker &worker,
|
||||
glyphAtlas, glyphStore,
|
||||
spriteAtlas, sprite,
|
||||
info, env);
|
||||
new_tile.data->request(worker, map.getState().getPixelRatio(), callback);
|
||||
} else if (info.type == SourceType::Raster) {
|
||||
new_tile.data = std::make_shared<RasterTileData>(normalized_id, texturePool, info, env);
|
||||
new_tile.data->request(worker, map.getState().getPixelRatio(), callback);
|
||||
} else if (info.type == SourceType::Annotations) {
|
||||
AnnotationManager& annotationManager = map.getAnnotationManager();
|
||||
new_tile.data = std::make_shared<LiveTileData>(normalized_id,
|
||||
annotationManager,
|
||||
map.getMaxZoom(), style,
|
||||
glyphAtlas, glyphStore,
|
||||
spriteAtlas, sprite,
|
||||
info, env);
|
||||
new_tile.data->reparse(worker, callback);
|
||||
} else {
|
||||
throw std::runtime_error("source type not implemented");
|
||||
}
|
||||
|
||||
new_tile.data->request(worker, map.getState().getPixelRatio(), callback);
|
||||
tile_data.emplace(new_tile.data->id, new_tile.data);
|
||||
}
|
||||
|
||||
@@ -368,4 +378,12 @@ void Source::update(Map &map,
|
||||
updated = map.getTime();
|
||||
}
|
||||
|
||||
void Source::invalidateTiles(Map& map, std::vector<Tile::ID>& ids) {
|
||||
for (auto& id : ids) {
|
||||
tiles.erase(id);
|
||||
tile_data.erase(id);
|
||||
}
|
||||
map.triggerUpdate();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ public:
|
||||
void load(Map &, Environment &);
|
||||
void update(Map &, Environment &, uv::worker &, util::ptr<Style>, GlyphAtlas &, GlyphStore &,
|
||||
SpriteAtlas &, util::ptr<Sprite>, TexturePool &, std::function<void()> callback);
|
||||
void invalidateTiles(Map&, std::vector<Tile::ID>&);
|
||||
|
||||
void updateMatrices(const mat4 &projMatrix, const TransformState &transform);
|
||||
void drawClippingMasks(Painter &painter);
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
#include <mbgl/text/collision.hpp>
|
||||
#include <mbgl/text/glyph.hpp>
|
||||
#include <mbgl/map/map.hpp>
|
||||
|
||||
#include <mbgl/util/std.hpp>
|
||||
#include <mbgl/util/utf.hpp>
|
||||
|
||||
@@ -30,14 +29,14 @@ namespace mbgl {
|
||||
// its header file.
|
||||
TileParser::~TileParser() = default;
|
||||
|
||||
TileParser::TileParser(const std::string& rawData_,
|
||||
TileParser::TileParser(const GeometryTile& geometryTile_,
|
||||
VectorTileData& tile_,
|
||||
const util::ptr<const Style>& style_,
|
||||
GlyphAtlas& glyphAtlas_,
|
||||
GlyphStore& glyphStore_,
|
||||
SpriteAtlas& spriteAtlas_,
|
||||
const util::ptr<Sprite>& sprite_)
|
||||
: vectorTile(pbf((const uint8_t *)rawData_.data(), rawData_.size())),
|
||||
: geometryTile(geometryTile_),
|
||||
tile(tile_),
|
||||
style(style_),
|
||||
glyphAtlas(glyphAtlas_),
|
||||
@@ -188,7 +187,7 @@ std::unique_ptr<Bucket> TileParser::createBucket(const StyleBucket &bucketDesc)
|
||||
if (tile.id.z >= std::ceil(bucketDesc.max_zoom)) return nullptr;
|
||||
if (bucketDesc.visibility == mbgl::VisibilityType::None) return nullptr;
|
||||
|
||||
auto layer = vectorTile.getLayer(bucketDesc.source_layer);
|
||||
auto layer = geometryTile.getLayer(bucketDesc.source_layer);
|
||||
if (layer) {
|
||||
if (bucketDesc.type == StyleLayerType::Fill) {
|
||||
return createFillBucket(*layer, bucketDesc);
|
||||
@@ -216,7 +215,7 @@ std::unique_ptr<Bucket> TileParser::createBucket(const StyleBucket &bucketDesc)
|
||||
template <class Bucket>
|
||||
void TileParser::addBucketGeometries(Bucket& bucket, const GeometryTileLayer& layer, const FilterExpression &filter) {
|
||||
for (std::size_t i = 0; i < layer.featureCount(); i++) {
|
||||
auto feature = layer.feature(i);
|
||||
auto feature = layer.getFeature(i);
|
||||
|
||||
if (obsolete())
|
||||
return;
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <mbgl/text/glyph.hpp>
|
||||
#include <mbgl/util/ptr.hpp>
|
||||
#include <mbgl/util/noncopyable.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
#include <iosfwd>
|
||||
#include <string>
|
||||
@@ -16,7 +17,6 @@
|
||||
namespace mbgl {
|
||||
|
||||
class Bucket;
|
||||
class TexturePool;
|
||||
class FontStack;
|
||||
class GlyphAtlas;
|
||||
class GlyphStore;
|
||||
@@ -31,12 +31,10 @@ class StyleLayoutSymbol;
|
||||
class StyleLayerGroup;
|
||||
class VectorTileData;
|
||||
class Collision;
|
||||
class TexturePool;
|
||||
|
||||
class TileParser : private util::noncopyable
|
||||
{
|
||||
class TileParser : private util::noncopyable {
|
||||
public:
|
||||
TileParser(const std::string& rawData,
|
||||
TileParser(const GeometryTile& geometryTile,
|
||||
VectorTileData& tile,
|
||||
const util::ptr<const Style>& style,
|
||||
GlyphAtlas& glyphAtlas,
|
||||
@@ -61,7 +59,7 @@ private:
|
||||
void addBucketGeometries(Bucket&, const GeometryTileLayer&, const FilterExpression&);
|
||||
|
||||
private:
|
||||
const VectorTile vectorTile;
|
||||
const GeometryTile& geometryTile;
|
||||
VectorTileData& tile;
|
||||
|
||||
// Cross-thread shared data.
|
||||
|
||||
@@ -133,7 +133,7 @@ VectorTile::VectorTile(pbf tile_pbf) {
|
||||
}
|
||||
}
|
||||
|
||||
util::ptr<const GeometryTileLayer> VectorTile::getLayer(const std::string& name) const {
|
||||
util::ptr<GeometryTileLayer> VectorTile::getLayer(const std::string& name) const {
|
||||
auto layer_it = layers.find(name);
|
||||
if (layer_it != layers.end()) {
|
||||
return layer_it->second;
|
||||
@@ -159,7 +159,7 @@ VectorTileLayer::VectorTileLayer(pbf layer_pbf) {
|
||||
}
|
||||
}
|
||||
|
||||
util::ptr<const GeometryTileFeature> VectorTileLayer::feature(std::size_t i) const {
|
||||
util::ptr<const GeometryTileFeature> VectorTileLayer::getFeature(std::size_t i) const {
|
||||
return std::make_shared<VectorTileFeature>(features.at(i), *this);
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ public:
|
||||
VectorTileLayer(pbf);
|
||||
|
||||
std::size_t featureCount() const override { return features.size(); }
|
||||
util::ptr<const GeometryTileFeature> feature(std::size_t) const override;
|
||||
util::ptr<const GeometryTileFeature> getFeature(std::size_t) const override;
|
||||
|
||||
private:
|
||||
friend class VectorTile;
|
||||
@@ -48,10 +48,10 @@ class VectorTile : public GeometryTile {
|
||||
public:
|
||||
VectorTile(pbf);
|
||||
|
||||
util::ptr<const GeometryTileLayer> getLayer(const std::string&) const override;
|
||||
util::ptr<GeometryTileLayer> getLayer(const std::string&) const override;
|
||||
|
||||
private:
|
||||
std::unordered_map<std::string, util::ptr<const GeometryTileLayer>> layers;
|
||||
std::unordered_map<std::string, util::ptr<GeometryTileLayer>> layers;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -1,20 +1,24 @@
|
||||
#include <mbgl/map/vector_tile_data.hpp>
|
||||
#include <mbgl/map/tile_parser.hpp>
|
||||
#include <mbgl/util/std.hpp>
|
||||
#include <mbgl/map/map.hpp>
|
||||
#include <mbgl/style/style_layer.hpp>
|
||||
#include <mbgl/style/style_bucket.hpp>
|
||||
#include <mbgl/style/style_source.hpp>
|
||||
#include <mbgl/geometry/glyph_atlas.hpp>
|
||||
#include <mbgl/platform/log.hpp>
|
||||
#include <mbgl/util/pbf.hpp>
|
||||
|
||||
using namespace mbgl;
|
||||
|
||||
VectorTileData::VectorTileData(Tile::ID const& id_,
|
||||
float mapMaxZoom, util::ptr<Style> style_,
|
||||
GlyphAtlas& glyphAtlas_, GlyphStore& glyphStore_,
|
||||
SpriteAtlas& spriteAtlas_, util::ptr<Sprite> sprite_,
|
||||
const SourceInfo& source_, Environment &env_)
|
||||
float mapMaxZoom,
|
||||
util::ptr<Style> style_,
|
||||
GlyphAtlas& glyphAtlas_,
|
||||
GlyphStore& glyphStore_,
|
||||
SpriteAtlas& spriteAtlas_,
|
||||
util::ptr<Sprite> sprite_,
|
||||
const SourceInfo& source_,
|
||||
Environment& env_)
|
||||
: TileData(id_, source_, env_),
|
||||
glyphAtlas(glyphAtlas_),
|
||||
glyphStore(glyphStore_),
|
||||
@@ -28,7 +32,6 @@ VectorTileData::~VectorTileData() {
|
||||
glyphAtlas.removeGlyphs(reinterpret_cast<uintptr_t>(this));
|
||||
}
|
||||
|
||||
|
||||
void VectorTileData::parse() {
|
||||
if (state != State::loaded) {
|
||||
return;
|
||||
@@ -42,9 +45,10 @@ void VectorTileData::parse() {
|
||||
// Parsing creates state that is encapsulated in TileParser. While parsing,
|
||||
// the TileParser object writes results into this objects. All other state
|
||||
// is going to be discarded afterwards.
|
||||
TileParser parser(data, *this, style,
|
||||
glyphAtlas, glyphStore,
|
||||
spriteAtlas, sprite);
|
||||
VectorTile vectorTile(pbf((const uint8_t *)data.data(), data.size()));
|
||||
const VectorTile* vt = &vectorTile;
|
||||
TileParser parser(*vt, *this, style, glyphAtlas, glyphStore, spriteAtlas, sprite);
|
||||
|
||||
// Clear the style so that we don't have a cycle in the shared_ptr references.
|
||||
style.reset();
|
||||
|
||||
|
||||
@@ -30,8 +30,15 @@ class VectorTileData : public TileData {
|
||||
friend class TileParser;
|
||||
|
||||
public:
|
||||
VectorTileData(Tile::ID const &, float mapMaxZoom, util::ptr<Style>, GlyphAtlas &, GlyphStore &,
|
||||
SpriteAtlas &, util::ptr<Sprite>, const SourceInfo &, Environment &);
|
||||
VectorTileData(Tile::ID const&,
|
||||
float mapMaxZoom,
|
||||
util::ptr<Style>,
|
||||
GlyphAtlas&,
|
||||
GlyphStore&,
|
||||
SpriteAtlas&,
|
||||
util::ptr<Sprite>,
|
||||
const SourceInfo&,
|
||||
Environment&);
|
||||
~VectorTileData();
|
||||
|
||||
void parse() override;
|
||||
|
||||
@@ -60,7 +60,7 @@ std::vector<SymbolFeature> SymbolBucket::processFeatures(const GeometryTileLayer
|
||||
std::set<GlyphRange> ranges;
|
||||
|
||||
for (std::size_t i = 0; i < layer.featureCount(); i++) {
|
||||
auto feature = layer.feature(i);
|
||||
auto feature = layer.getFeature(i);
|
||||
|
||||
GeometryTileFeatureExtractor extractor(*feature);
|
||||
if (!evaluate(filter, extractor))
|
||||
|
||||
@@ -29,6 +29,37 @@ void StyleParser::parse(JSVal document) {
|
||||
if (document.HasMember("layers")) {
|
||||
root = createLayers(document["layers"]);
|
||||
parseLayers();
|
||||
|
||||
// create point annotations layer
|
||||
//
|
||||
std::string id = util::ANNOTATIONS_POINTS_LAYER_ID;
|
||||
|
||||
std::map<ClassID, ClassProperties> paints;
|
||||
util::ptr<StyleLayer> annotations = std::make_shared<StyleLayer>(id, std::move(paints));
|
||||
annotations->type = StyleLayerType::Symbol;
|
||||
layers.emplace(id, std::pair<JSVal, util::ptr<StyleLayer>> { JSVal(id), annotations });
|
||||
root->layers.emplace_back(annotations);
|
||||
|
||||
util::ptr<StyleBucket> pointBucket = std::make_shared<StyleBucket>(annotations->type);
|
||||
pointBucket->name = annotations->id;
|
||||
pointBucket->source_layer = annotations->id;
|
||||
|
||||
rapidjson::Document d;
|
||||
rapidjson::Value iconImage(rapidjson::kObjectType);
|
||||
iconImage.AddMember("icon-image", "{sprite}", d.GetAllocator());
|
||||
parseLayout(iconImage, pointBucket);
|
||||
rapidjson::Value iconOverlap(rapidjson::kObjectType);
|
||||
iconOverlap.AddMember("icon-allow-overlap", true, d.GetAllocator());
|
||||
parseLayout(iconOverlap, pointBucket);
|
||||
|
||||
SourceInfo& info = sources.emplace(id, std::make_shared<StyleSource>()).first->second->info;
|
||||
info.type = SourceType::Annotations;
|
||||
|
||||
auto source_it = sources.find(id);
|
||||
pointBucket->style_source = source_it->second;
|
||||
annotations->bucket = pointBucket;
|
||||
//
|
||||
// end point annotations
|
||||
}
|
||||
|
||||
if (document.HasMember("sprite")) {
|
||||
|
||||
@@ -48,7 +48,8 @@ enum class SourceType : uint8_t {
|
||||
Vector,
|
||||
Raster,
|
||||
GeoJSON,
|
||||
Video
|
||||
Video,
|
||||
Annotations
|
||||
};
|
||||
|
||||
MBGL_DEFINE_ENUM_CLASS(SourceTypeClass, SourceType, {
|
||||
@@ -56,6 +57,7 @@ MBGL_DEFINE_ENUM_CLASS(SourceTypeClass, SourceType, {
|
||||
{ SourceType::Raster, "raster" },
|
||||
{ SourceType::GeoJSON, "geojson" },
|
||||
{ SourceType::Video, "video" },
|
||||
{ SourceType::Annotations, "annotations" },
|
||||
});
|
||||
|
||||
// -------------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -8,6 +8,8 @@ const double mbgl::util::M2PI = 2 * M_PI;
|
||||
const double mbgl::util::EARTH_RADIUS_M = 6378137;
|
||||
const double mbgl::util::LATITUDE_MAX = 85.05112878;
|
||||
|
||||
const std::string mbgl::util::ANNOTATIONS_POINTS_LAYER_ID = "com.mapbox.annotations.points";
|
||||
|
||||
#if defined(DEBUG)
|
||||
const bool mbgl::debug::tileParseWarnings = false;
|
||||
const bool mbgl::debug::styleParseWarnings = false;
|
||||
|
||||
Reference in New Issue
Block a user