From e049ce6df13c3ad77e9e2ecb2e1afe2992384a35 Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Tue, 17 Mar 2015 17:30:24 -0700 Subject: refs #893 #992: point annotations API --- include/mbgl/map/annotation.hpp | 72 +++++++++++++++ include/mbgl/map/map.hpp | 15 +++- include/mbgl/util/constants.hpp | 3 + include/mbgl/util/geo.hpp | 15 ++++ macosx/mapboxgl-app.gyp | 2 +- src/mbgl/map/annotation.cpp | 184 ++++++++++++++++++++++++++++++++++++++ src/mbgl/map/geometry_tile.hpp | 10 +-- src/mbgl/map/live_tile.cpp | 56 ++++++++++++ src/mbgl/map/live_tile.hpp | 53 +++++++++++ src/mbgl/map/live_tile_data.cpp | 67 ++++++++++++++ src/mbgl/map/live_tile_data.hpp | 32 +++++++ src/mbgl/map/map.cpp | 56 +++++++++++- src/mbgl/map/source.cpp | 22 ++++- src/mbgl/map/source.hpp | 1 + src/mbgl/map/tile_parser.cpp | 7 +- src/mbgl/map/tile_parser.hpp | 10 +-- src/mbgl/map/vector_tile.cpp | 2 +- src/mbgl/map/vector_tile.hpp | 4 +- src/mbgl/map/vector_tile_data.cpp | 22 +++-- src/mbgl/map/vector_tile_data.hpp | 11 ++- src/mbgl/style/style_parser.cpp | 31 +++++++ src/mbgl/style/types.hpp | 4 +- src/mbgl/util/constants.cpp | 2 + 23 files changed, 646 insertions(+), 35 deletions(-) create mode 100644 include/mbgl/map/annotation.hpp create mode 100644 src/mbgl/map/annotation.cpp create mode 100644 src/mbgl/map/live_tile.cpp create mode 100644 src/mbgl/map/live_tile.hpp create mode 100644 src/mbgl/map/live_tile_data.cpp create mode 100644 src/mbgl/map/live_tile_data.hpp diff --git a/include/mbgl/map/annotation.hpp b/include/mbgl/map/annotation.hpp new file mode 100644 index 0000000000..e88d98b5c6 --- /dev/null +++ b/include/mbgl/map/annotation.hpp @@ -0,0 +1,72 @@ +#ifndef MBGL_MAP_ANNOTATIONS +#define MBGL_MAP_ANNOTATIONS + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace mbgl { + +class Annotation; +class Map; + +typedef std::vector 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> addPointAnnotations(std::vector, std::vector& symbols, const Map&); + std::vector removeAnnotations(std::vector); + std::vector getAnnotationsInBounds(LatLngBounds, const Map&) const; + LatLngBounds getBoundsForAnnotations(std::vector) const; + + const std::unique_ptr& getTile(Tile::ID const& id); + +private: + uint32_t nextID() { return nextID_++; } + static vec2 projectPoint(LatLng& point); + +private: + std::mutex mtx; + std::string defaultPointAnnotationSymbol; + std::map> annotations; + std::map, std::unique_ptr>> annotationTiles; + std::unique_ptr nullTile; + uint32_t nextID_ = 0; +}; + +class Annotation : private util::noncopyable { + friend class AnnotationManager; +public: + Annotation(AnnotationType, std::vector); + +private: + LatLng getPoint() const; + LatLngBounds getBounds() const { return bounds; } + +private: + const AnnotationType type = AnnotationType::Point; + const std::vector geometry; + std::map>> tileFeatures; + LatLngBounds bounds; +}; + +} + +#endif diff --git a/include/mbgl/map/map.hpp b/include/mbgl/map/map.hpp index 44a560a468..b03217570b 100644 --- a/include/mbgl/map/map.hpp +++ b/include/mbgl/map/map.hpp @@ -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 pixelForLatLng(const LatLng latLng) const { return state.pixelForLatLng(latLng); } inline const LatLng latLngForPixel(const vec2 pixel) const { return state.latLngForPixel(pixel); } + // Annotations + void setDefaultPointAnnotationSymbol(std::string&); + uint32_t addPointAnnotation(LatLng, std::string& symbol); + std::vector addPointAnnotations(std::vector, std::vector& symbols); + void removeAnnotation(uint32_t); + void removeAnnotations(std::vector); + std::vector getAnnotationsInBounds(LatLngBounds) const; + LatLngBounds getBoundsForAnnotations(std::vector) 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&); + enum class Mode : uint8_t { None, // we're not doing any processing Continuous, // continually updating map @@ -219,8 +232,8 @@ private: util::ptr sprite; const std::unique_ptr lineAtlas; util::ptr texturePool; - const std::unique_ptr painter; + util::ptr annotationManager; std::string styleURL; std::string styleJSON = ""; diff --git a/include/mbgl/util/constants.hpp b/include/mbgl/util/constants.hpp index 069e6e41ae..9e0856b68a 100644 --- a/include/mbgl/util/constants.hpp +++ b/include/mbgl/util/constants.hpp @@ -2,6 +2,7 @@ #define MBGL_UTIL_CONSTANTS #include +#include 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 { diff --git a/include/mbgl/util/geo.hpp b/include/mbgl/util/geo.hpp index 1d9986bd91..b99a6e6614 100644 --- a/include/mbgl/util/geo.hpp +++ b/include/mbgl/util/geo.hpp @@ -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 diff --git a/macosx/mapboxgl-app.gyp b/macosx/mapboxgl-app.gyp index 37280286e5..39aef6435b 100644 --- a/macosx/mapboxgl-app.gyp +++ b/macosx/mapboxgl-app.gyp @@ -9,7 +9,7 @@ 'product_extension': 'app', 'mac_bundle': 1, 'mac_bundle_resources': [ - 'Icon.icns', + 'Icon.icns' ], 'dependencies': [ diff --git a/src/mbgl/map/annotation.cpp b/src/mbgl/map/annotation.cpp new file mode 100644 index 0000000000..b2a90f53c6 --- /dev/null +++ b/src/mbgl/map/annotation.cpp @@ -0,0 +1,184 @@ +#include +#include +#include + +#include +#include + +using namespace mbgl; + +Annotation::Annotation(AnnotationType type_, std::vector 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()) {} + +vec2 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(x, y); +} + +std::pair, std::vector> AnnotationManager::addPointAnnotations(std::vector points, std::vector& symbols, const Map& map) { + + uint16_t extent = 4096; + + std::vector annotationIDs(points.size()); + std::vector affectedTiles; + + for (uint32_t i = 0; i < points.size(); ++i) { + uint32_t annotationID = nextID(); + + auto anno_it = annotations.emplace(annotationID, util::make_unique(AnnotationType::Point, std::vector({{ points[i] }}))); + + uint8_t maxZoom = map.getMaxZoom(); + + uint32_t z2 = 1 << maxZoom; + + vec2 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 properties = {{ "sprite", (symbols[i].length() ? symbols[i] : defaultPointAnnotationSymbol) }}; + + auto feature = std::make_shared(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 layer = std::make_shared(); + layer->addFeature(feature); + // create tile & record annotation association + auto tile_pos = annotationTiles.emplace(tileID, std::make_pair(std::vector({ annotationID }), util::make_unique())); + // 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>({ feature })); + + z2 /= 2; + x /= 2; + y /= 2; + } + + annotationIDs.push_back(annotationID); + } + + return std::make_pair(affectedTiles, annotationIDs); +} + +std::vector AnnotationManager::removeAnnotations(std::vector ids) { + std::vector 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 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 AnnotationManager::getAnnotationsInBounds(LatLngBounds queryBounds, const Map& map) const { + uint8_t z = map.getMaxZoom(); + uint32_t z2 = 1 << z; + vec2 swPoint = projectPoint(queryBounds.sw); + vec2 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 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 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& AnnotationManager::getTile(Tile::ID const& id) { + std::lock_guard lock(mtx); + + auto tile_it = annotationTiles.find(id); + if (tile_it != annotationTiles.end()) { + return tile_it->second.second; + } + return nullTile; +} diff --git a/src/mbgl/map/geometry_tile.hpp b/src/mbgl/map/geometry_tile.hpp index 3df0661fbf..18222b622b 100644 --- a/src/mbgl/map/geometry_tile.hpp +++ b/src/mbgl/map/geometry_tile.hpp @@ -23,22 +23,22 @@ enum class FeatureType : uint8_t { typedef std::vector> GeometryCollection; -class GeometryTileFeature : public mbgl::util::noncopyable { +class GeometryTileFeature : private util::noncopyable { public: virtual FeatureType getType() const = 0; virtual mapbox::util::optional 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 feature(std::size_t i) const = 0; + virtual util::ptr feature(std::size_t) const = 0; }; -class GeometryTile : public mbgl::util::noncopyable { +class GeometryTile : private util::noncopyable { public: - virtual util::ptr getLayer(const std::string&) const = 0; + virtual util::ptr getLayer(const std::string&) const = 0; }; class GeometryTileFeatureExtractor { diff --git a/src/mbgl/map/live_tile.cpp b/src/mbgl/map/live_tile.cpp new file mode 100644 index 0000000000..06337af184 --- /dev/null +++ b/src/mbgl/map/live_tile.cpp @@ -0,0 +1,56 @@ +#include +#include + +namespace mbgl { + +LiveTileFeature::LiveTileFeature(FeatureType type_, GeometryCollection geometries_, std::map properties_) + : type(type_), + properties(properties_), + geometries(geometries_) {} + +mapbox::util::optional LiveTileFeature::getValue(const std::string& key) const { + auto it = properties.find(key); + if (it != properties.end()) { + return mapbox::util::optional(it->second); + } + return mapbox::util::optional(); +} + +LiveTileLayer::LiveTileLayer() {} + +void LiveTileLayer::prepareToAddFeatures(size_t count) { + features.reserve(features.size() + count); +} + +void LiveTileLayer::addFeature(util::ptr feature) { + features.push_back(std::move(feature)); +} + +void LiveTileLayer::removeFeature(util::ptr 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 layer) { + layers.emplace(name, std::move(layer)); +} + +util::ptr LiveTile::getLayer(const std::string& name) const { + return getMutableLayer(name); +} + +util::ptr LiveTile::getMutableLayer(const std::string& name) const { + auto layer_it = layers.find(name); + if (layer_it != layers.end()) { + return layer_it->second; + } + return nullptr; +} + +} diff --git a/src/mbgl/map/live_tile.hpp b/src/mbgl/map/live_tile.hpp new file mode 100644 index 0000000000..019c9da740 --- /dev/null +++ b/src/mbgl/map/live_tile.hpp @@ -0,0 +1,53 @@ +#ifndef MBGL_MAP_LIVE_TILE +#define MBGL_MAP_LIVE_TILE + +#include + +#include + +namespace mbgl { + +class LiveTileFeature : public GeometryTileFeature { +public: + LiveTileFeature(FeatureType, GeometryCollection, std::map properties = {{}}); + + FeatureType getType() const override { return type; } + mapbox::util::optional getValue(const std::string&) const override; + GeometryCollection getGeometries() const override { return geometries; } + +private: + FeatureType type = FeatureType::Unknown; + std::map properties; + GeometryCollection geometries; +}; + + class LiveTileLayer : public GeometryTileLayer { +public: + LiveTileLayer(); + + void prepareToAddFeatures(size_t count); + void addFeature(util::ptr); + void removeFeature(util::ptr); + std::size_t featureCount() const override { return features.size(); } + util::ptr feature(std::size_t i) const override { return features[i]; } + +private: + std::vector> features; +}; + +class LiveTile : public GeometryTile { +public: + LiveTile(); + + void addLayer(const std::string&, util::ptr); + util::ptr getLayer(const std::string&) const override; + util::ptr getMutableLayer(const std::string&) const; + bool operator()(const LiveTile&) const { return layers.size() > 0; } + +private: + std::map> layers; +}; + +} + +#endif diff --git a/src/mbgl/map/live_tile_data.cpp b/src/mbgl/map/live_tile_data.cpp new file mode 100644 index 0000000000..34743f02ce --- /dev/null +++ b/src/mbgl/map/live_tile_data.cpp @@ -0,0 +1,67 @@ +#include +#include +#include +#include +#include +#include + +using namespace mbgl; + +LiveTileData::LiveTileData(Tile::ID const& id_, + util::ptr annotationManager_, + float mapMaxZoom, + util::ptr