diff options
Diffstat (limited to 'src/mbgl/map/annotation.cpp')
-rw-r--r-- | src/mbgl/map/annotation.cpp | 582 |
1 files changed, 446 insertions, 136 deletions
diff --git a/src/mbgl/map/annotation.cpp b/src/mbgl/map/annotation.cpp index 6df49ec647..c946264593 100644 --- a/src/mbgl/map/annotation.cpp +++ b/src/mbgl/map/annotation.cpp @@ -4,6 +4,7 @@ #include <mbgl/map/live_tile.hpp> #include <mbgl/map/map_data.hpp> #include <mbgl/util/constants.hpp> +#include <mbgl/util/geojsonvt/geojsonvt_convert.hpp> #include <mbgl/util/ptr.hpp> #include <algorithm> @@ -11,35 +12,15 @@ namespace mbgl { -enum class AnnotationType : uint8_t { - Point, - Shape -}; - -using AnnotationSegment = std::vector<LatLng>; -using AnnotationSegments = std::vector<AnnotationSegment>; - -class Annotation : private util::noncopyable { - friend class AnnotationManager; -public: - Annotation(AnnotationType, const AnnotationSegments&); - -private: - LatLng getPoint() const; - LatLngBounds getBounds() const { return bounds; } - -private: - const AnnotationType type = AnnotationType::Point; - const AnnotationSegments geometry; - std::unordered_map<TileID, std::weak_ptr<const LiveTileFeature>, TileID::Hash> tileFeatures; - const LatLngBounds bounds; -}; - -Annotation::Annotation(AnnotationType type_, const AnnotationSegments& geometry_) - : type(type_), +Annotation::Annotation(AnnotationType type_, + const AnnotationSegments& geometry_, + const StyleProperties& styleProperties_) + : styleProperties(styleProperties_), + type(type_), geometry(geometry_), bounds([this] { LatLngBounds bounds_; + assert(type != AnnotationType::Any); if (type == AnnotationType::Point) { bounds_ = { getPoint(), getPoint() }; } else { @@ -67,6 +48,16 @@ AnnotationManager::~AnnotationManager() { // Annotation so we can't destruct the object with just the header file. } +void AnnotationManager::markStaleTiles(std::unordered_set<TileID, TileID::Hash> ids) { + std::lock_guard<std::mutex> lock(mtx); + std::copy(ids.begin(), ids.end(), std::inserter(staleTiles, staleTiles.begin())); +} + +std::unordered_set<TileID, TileID::Hash> AnnotationManager::resetStaleTiles() { + std::lock_guard<std::mutex> lock(mtx); + return std::move(staleTiles); +} + void AnnotationManager::setDefaultPointAnnotationSymbol(const std::string& symbol) { std::lock_guard<std::mutex> lock(mtx); defaultPointAnnotationSymbol = symbol; @@ -77,118 +68,314 @@ uint32_t AnnotationManager::nextID() { } vec2<double> AnnotationManager::projectPoint(const LatLng& point) { + // Clamp to the latitude limits of Mercator. + const double constrainedLatitude = std::fmin(std::fmax(point.latitude, -util::LATITUDE_MAX), util::LATITUDE_MAX); + // Project a coordinate into unit space in a square map. - const double sine = std::sin(point.latitude * M_PI / 180.0); + const double sine = std::sin(constrainedLatitude * M_PI / 180.0); const double x = point.longitude / 360.0 + 0.5; const double y = 0.5 - 0.25 * std::log((1.0 + sine) / (1.0 - sine)) / M_PI; return { x, y }; } -std::pair<std::vector<TileID>, AnnotationIDs> -AnnotationManager::addPointAnnotations(const std::vector<LatLng>& points, - const std::vector<std::string>& symbols, - const MapData& data) { +std::pair<std::unordered_set<TileID, TileID::Hash>, AnnotationIDs> +AnnotationManager::addAnnotations(const AnnotationType type, + const std::vector<AnnotationSegments>& segments, + const std::vector<StyleProperties>& styleProperties, + const AnnotationsProperties& annotationsProperties, + const MapData& data) { std::lock_guard<std::mutex> lock(mtx); + assert(type != AnnotationType::Any); + // We pre-generate tiles to contain each annotation up to the map's max zoom. // We do this for fast rendering without projection conversions on the fly, as well as // to simplify bounding box queries of annotations later. Tiles get invalidated when // annotations are added or removed in order to refresh the map render without // touching the base map underneath. - const uint16_t extent = 4096; + AnnotationIDs annotationIDs; + annotationIDs.reserve((type == AnnotationType::Shape ? + segments.size() : // shapes + segments[0][0].size())); // points + + std::unordered_set<TileID, TileID::Hash> affectedTiles; + + const uint8_t maxZoom = data.transform.getMaxZoom(); + + for (size_t s = 0; s < segments.size(); ++s) { + + if (type == AnnotationType::Point) { - std::vector<uint32_t> annotationIDs; - annotationIDs.reserve(points.size()); + for (size_t l = 0; l < segments[s].size(); ++l) { - std::vector<TileID> affectedTiles; + for (size_t p = 0; p < segments[s][l].size(); ++p) { - for (size_t i = 0; i < points.size(); ++i) { - const uint32_t annotationID = nextID(); + auto& point = segments[s][l][p]; - // track the annotation global ID and its geometry - auto anno_it = annotations.emplace( - annotationID, - std::make_unique<Annotation>(AnnotationType::Point, - AnnotationSegments({ { points[i] } }))); + // projection conversion into unit space + const auto pp = projectPoint(point); + + const uint32_t pointAnnotationID = nextID(); + + // at render time we style the point according to its {sprite} field + std::unordered_map<std::string, std::string> pointFeatureProperties; + const std::string& symbol = annotationsProperties.at("symbols")[p]; + if (symbol.length()) { + pointFeatureProperties.emplace("sprite", symbol); + } else { + pointFeatureProperties.emplace("sprite", defaultPointAnnotationSymbol); + } + + // add individual point tile feature + auto featureAffectedTiles = addTileFeature( + pointAnnotationID, + AnnotationSegments({{ point }}), + std::vector<std::vector<vec2<double>>>({{ pp }}), + AnnotationType::Point, + {{ }}, + pointFeatureProperties, + maxZoom + ); + + std::copy(featureAffectedTiles.begin(), featureAffectedTiles.end(), std::inserter(affectedTiles, affectedTiles.begin())); + + annotationIDs.push_back(pointAnnotationID); + } + } + } else { + + const uint32_t shapeAnnotationID = nextID(); + + // current shape tiles are on-the-fly, so we don't get any "affected tiles" + // and just expire all annotation tiles for shape adds + + addTileFeature( + shapeAnnotationID, + segments[s], + {{ }}, + AnnotationType::Shape, + styleProperties[s], + {{ }}, + maxZoom + ); + + annotationIDs.push_back(shapeAnnotationID); + } + } + + // Tile:IDs that need refreshed and the annotation identifiers held onto by the client. + return std::make_pair(affectedTiles, annotationIDs); +} - const uint8_t maxZoom = data.transform.getMaxZoom(); +std::unordered_set<TileID, TileID::Hash> +AnnotationManager::addTileFeature(const uint32_t annotationID, + const AnnotationSegments& segments, + const std::vector<std::vector<vec2<double>>>& projectedFeature, + const AnnotationType& type, + const StyleProperties& styleProperties, + const std::unordered_map<std::string, std::string>& featureProperties, + const uint8_t maxZoom) { - // side length of map at this zoom + assert(type != AnnotationType::Any); + + // track the annotation global ID and its original geometry + auto anno_it = annotations.emplace(annotationID, + std::make_unique<Annotation>(type, segments, styleProperties)); + + std::unordered_set<TileID, TileID::Hash> affectedTiles; + + if (type == AnnotationType::Shape) { + + orderedShapeAnnotations.push_back(annotationID); + + using namespace mapbox::util::geojsonvt; + + const uint32_t z2 = 1 << maxZoom; + const double baseTolerance = 3; + const uint16_t extent = 4096; + + const double tolerance = baseTolerance / (z2 * extent); + + ProjectedGeometryContainer rings; + + std::vector<LonLat> points; + + for (size_t i = 0; i < segments[0].size(); ++i) { // first segment for now (no holes) + const double constraintedLatitude = std::fmin(std::fmax(segments[0][i].latitude, -util::LATITUDE_MAX), util::LATITUDE_MAX); + points.push_back(LonLat(segments[0][i].longitude, constraintedLatitude)); + } + + ProjectedFeatureType featureType; + + if (styleProperties.is<FillProperties>()) { + featureType = ProjectedFeatureType::Polygon; + + if (points.front().lon != points.back().lon || points.front().lat != points.back().lat) { + points.push_back(LonLat(points.front().lon, points.front().lat)); + } + } else { + featureType = ProjectedFeatureType::LineString; + } + + ProjectedGeometryContainer ring = Convert::project(points, tolerance); + + rings.members.push_back(ring); + + std::vector<ProjectedFeature> features; + + features.push_back(Convert::create(Tags(), featureType, rings)); + + shapeTilers.emplace(annotationID, std::make_unique<GeoJSONVT>(features, maxZoom)); + + } else { + + // side length of map at max zoom uint32_t z2 = 1 << maxZoom; - // projection conversion into unit space - const vec2<double> p = projectPoint(points[i]); + const uint16_t extent = 4096; - uint32_t x = p.x * z2; - uint32_t y = p.y * z2; + uint32_t x = 0; + uint32_t y = 0; for (int8_t z = maxZoom; z >= 0; z--) { - affectedTiles.emplace_back(z, x, y, z); - TileID tileID = affectedTiles.back(); - - // calculate tile coordinate - const Coordinate coordinate(extent * (p.x * z2 - x), extent * (p.y * z2 - y)); - - const GeometryCollection geometries({ { { { coordinate } } } }); - - // at render time we style the annotation according to its {sprite} field - const 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 = tiles.find(tileID); - if (tile_it != tiles.end()) { - // - // We have this tile created already. Add this feature to it. - // - // get point layer & add feature - auto layer = - tile_it->second.second->getMutableLayer(layerID); - layer->addFeature(feature); - // record annotation association with tile - tile_it->second.first.insert(annotationID); + + std::unordered_map<TileID, GeometryCollection, TileID::Hash> featureTiles; + + if (type == AnnotationType::Point) { + auto& pp = projectedFeature[0][0]; + + x = pp.x * z2; + y = pp.y * z2; + + const Coordinate coordinate(extent * (pp.x * z2 - x), extent * (pp.y * z2 - y)); + + GeometryCollection geometries = {{ {{ coordinate }} }}; + + featureTiles.emplace(TileID(z, x, y, z), geometries); } else { - // - // We need to create a new tile for this feature. - // - // create point layer & add feature - util::ptr<LiveTileLayer> layer = std::make_shared<LiveTileLayer>(); - layer->addFeature(feature); - // create tile & record annotation association - auto tile_pos = tiles.emplace( - tileID, std::make_pair(std::unordered_set<uint32_t>({ annotationID }), - std::make_unique<LiveTile>())); - // add point layer to tile - tile_pos.first->second.second->addLayer(layerID, layer); + for (size_t l = 0; l < projectedFeature.size(); ++l) { + + std::vector<Coordinate> line; + + for (size_t p = 0; p < projectedFeature[l].size(); ++p) { + + auto& pp = projectedFeature[l][p]; + + x = pp.x * z2; + y = pp.y * z2; + + const Coordinate coordinate(extent * (pp.x * z2 - x), extent * (pp.y * z2 - y)); + + auto tile_it = featureTiles.find(TileID(z, x, y, z)); + + if (tile_it != featureTiles.end()) { + GeometryCollection& geometries = featureTiles.find(TileID(z, x, y, z))->second; + if (geometries.size()) { + geometries.back().push_back(coordinate); + } else { + geometries.push_back({{ coordinate }}); + } + } else { + GeometryCollection geometries = {{ {{ coordinate }} }}; + featureTiles.emplace(TileID(z, x, y, z), geometries); + } + } + } } - // Record annotation association with tile and tile feature. This is used to determine stale tiles, - // as well as to remove the feature from the tile upon annotation deletion. - anno_it.first->second->tileFeatures.emplace( - tileID, std::weak_ptr<const LiveTileFeature>(feature)); + for (auto& featureTile : featureTiles) { + // determine feature type + FeatureType featureType; + if (type == AnnotationType::Point) { + featureType = FeatureType::Point; + } else if (styleProperties.is<LineProperties>()) { + featureType = FeatureType::LineString; + } else if (styleProperties.is<FillProperties>()) { + featureType = FeatureType::Polygon; + } else { + throw std::runtime_error("Invalid feature type"); + } + + // create tile feature + auto feature = std::make_shared<const LiveTileFeature>( + featureType, + featureTile.second, + featureProperties + ); + + // check for tile & create if necessary + auto tile_pos = tiles.emplace(featureTile.first, + std::make_pair(std::unordered_set<uint32_t>({ annotationID }), + std::make_unique<LiveTile>())); + + // check for annotation layer & create if necessary + util::ptr<LiveTileLayer> layer; + std::string layerID = ""; + if (type == AnnotationType::Point) { + layerID = PointLayerID; + } else { + layerID = ShapeLayerID + "." + std::to_string(annotationID); + } + if (tile_pos.second || tile_pos.first->second.second->getMutableLayer(layerID) == nullptr) { + layer = std::make_shared<LiveTileLayer>(); + tile_pos.first->second.second->addLayer(layerID, layer); + } else { + layer = tile_pos.first->second.second->getMutableLayer(layerID); + + // associate annotation with tile + tile_pos.first->second.first.insert(annotationID); + } + + // add feature to layer + layer->addFeature(feature); + + // Record annotation association with tile and tile feature. This is used to determine stale tiles, + // as well as to remove the feature from the tile upon annotation deletion. + anno_it.first->second->tilePointFeatures.emplace(featureTile.first, std::weak_ptr<const LiveTileFeature>(feature)); + + // track affected tile + affectedTiles.insert(featureTile.first); + } // get ready for the next-lower zoom number z2 /= 2; x /= 2; y /= 2; } - - annotationIDs.push_back(annotationID); } - // Tile:IDs that need refreshed and the annotation identifiers held onto by the client. - return std::make_pair(affectedTiles, annotationIDs); + return affectedTiles; +} + +std::pair<std::unordered_set<TileID, TileID::Hash>, AnnotationIDs> +AnnotationManager::addPointAnnotations(const AnnotationSegment& points, + const AnnotationsProperties& annotationsProperties, + const MapData& data) { + return addAnnotations(AnnotationType::Point, + {{ points }}, + {{ }}, + annotationsProperties, + data); } -std::vector<TileID> AnnotationManager::removeAnnotations(const AnnotationIDs& ids, - const MapData& data) { +std::pair<std::unordered_set<TileID, TileID::Hash>, AnnotationIDs> +AnnotationManager::addShapeAnnotations(const std::vector<AnnotationSegments>& shapes, + const std::vector<StyleProperties>& styleProperties, + const AnnotationsProperties& annotationsProperties, + const MapData& data) { + return addAnnotations(AnnotationType::Shape, + shapes, + styleProperties, + annotationsProperties, + data); +} + +std::unordered_set<TileID, TileID::Hash> AnnotationManager::removeAnnotations(const AnnotationIDs& ids, + const MapData& data) { std::lock_guard<std::mutex> lock(mtx); - std::vector<TileID> affectedTiles; + std::unordered_set<TileID, TileID::Hash> affectedTiles; std::vector<uint32_t> z2s; const uint8_t zoomCount = data.transform.getMaxZoom() + 1; @@ -207,25 +394,44 @@ std::vector<TileID> AnnotationManager::removeAnnotations(const AnnotationIDs& id const auto& annotation_it = annotations.find(annotationID); if (annotation_it != annotations.end()) { const auto& annotation = annotation_it->second; - // calculate annotation's affected tile for each zoom - for (uint8_t z = 0; z < zoomCount; ++z) { - latLng = annotation->getPoint(); - p = projectPoint(latLng); - x = z2s[z] * p.x; - y = z2s[z] * p.y; - TileID tid(z, x, y, z); - // erase annotation from tile's list - auto& tileAnnotations = tiles[tid].first; - tileAnnotations.erase(annotationID); - // remove annotation's features from tile - const auto& features_it = annotation->tileFeatures.find(tid); - if (features_it != annotation->tileFeatures.end()) { - const auto& layer = - tiles[tid].second->getMutableLayer(layerID); - layer->removeFeature(features_it->second); - affectedTiles.push_back(tid); + // remove feature(s) from relevant tiles + if (annotation->type == AnnotationType::Point) { + // calculate annotation's affected tile for each zoom + for (uint8_t z = 0; z < zoomCount; ++z) { + latLng = annotation->getPoint(); + p = projectPoint(latLng); + x = z2s[z] * p.x; + y = z2s[z] * p.y; + TileID tid(z, x, y, z); + // erase annotation from tile's list + auto& tileAnnotations = tiles[tid].first; + tileAnnotations.erase(annotationID); + // remove annotation's features from tile + const auto& features_it = annotation->tilePointFeatures.find(tid); + if (features_it != annotation->tilePointFeatures.end()) { + // points share a layer; remove feature + auto layer = tiles[tid].second->getMutableLayer(PointLayerID); + layer->removeFeature(features_it->second); + affectedTiles.insert(tid); + } + } + } else { + // remove shape layer from tiles if relevant + for (auto tile_it = tiles.begin(); tile_it != tiles.end(); ++tile_it) { + if (tile_it->second.first.count(annotationID)) { + tile_it->second.second->removeLayer(ShapeLayerID + "." + std::to_string(annotationID)); + affectedTiles.insert(tile_it->first); + } } + + // clear shape from render order + auto shape_it = std::find(orderedShapeAnnotations.begin(), orderedShapeAnnotations.end(), annotationID); + orderedShapeAnnotations.erase(shape_it); + + // clear shape tiler + shapeTilers.erase(annotationID); } + annotations.erase(annotationID); } } @@ -234,8 +440,18 @@ std::vector<TileID> AnnotationManager::removeAnnotations(const AnnotationIDs& id return affectedTiles; } -std::vector<uint32_t> AnnotationManager::getAnnotationsInBounds(const LatLngBounds& queryBounds, - const MapData& data) const { +const StyleProperties AnnotationManager::getAnnotationStyleProperties(uint32_t annotationID) const { + std::lock_guard<std::mutex> lock(mtx); + + auto anno_it = annotations.find(annotationID); + assert(anno_it != annotations.end()); + + return anno_it->second->styleProperties; +} + +AnnotationIDs AnnotationManager::getAnnotationsInBounds(const LatLngBounds& queryBounds, + const MapData& data, + const AnnotationType& type) const { std::lock_guard<std::mutex> lock(mtx); const uint8_t z = data.transform.getMaxZoom(); @@ -247,7 +463,7 @@ std::vector<uint32_t> AnnotationManager::getAnnotationsInBounds(const LatLngBoun const TileID nwTile(z, swPoint.x * z2, nePoint.y * z2, z); const TileID seTile(z, nePoint.x * z2, swPoint.y * z2, z); - std::vector<uint32_t> matchingAnnotations; + std::unordered_set<uint32_t> matchingAnnotations; for (auto& tile : tiles) { TileID id = tile.first; @@ -255,32 +471,50 @@ std::vector<uint32_t> AnnotationManager::getAnnotationsInBounds(const LatLngBoun 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; this tile is completely inside the query bounds, so - // we'll return all of its annotations. - std::copy(tile.second.first.begin(), tile.second.first.end(), - std::back_inserter(matchingAnnotations)); + // we'll return all of its annotations that match type (if specified). + if (type != AnnotationType::Any) { + std::copy_if(tile.second.first.begin(), tile.second.first.end(), + std::inserter(matchingAnnotations, matchingAnnotations.begin()), + [&](const uint32_t annotationID) -> bool { + const auto it = annotations.find(annotationID); + if (it != annotations.end()) { + return (it->second->type == type); + } else { + return false; + } + }); + } else { + std::copy(tile.second.first.begin(), tile.second.first.end(), + std::inserter(matchingAnnotations, matchingAnnotations.begin())); + } } else { // This tile is intersected by the query bounds. We need to check the // tile's annotations' bounding boxes individually. std::copy_if(tile.second.first.begin(), tile.second.first.end(), - std::back_inserter(matchingAnnotations), + std::inserter(matchingAnnotations, matchingAnnotations.begin()), [&](const uint32_t annotationID) -> bool { const auto it = annotations.find(annotationID); if (it != annotations.end()) { - const LatLngBounds annoBounds = it->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); - } else { - return false; + // check type + if (type != AnnotationType::Any && it->second->type != type) { + return false; + } + + // check bounds + if (it->second->type == AnnotationType::Point) { + return queryBounds.contains(it->second->getPoint()); + } else if (it->second->type == AnnotationType::Shape) { + return queryBounds.intersects(it->second->getBounds()); + } } + return false; }); } } } } - return matchingAnnotations; + return AnnotationIDs(matchingAnnotations.begin(), matchingAnnotations.end()); } LatLngBounds AnnotationManager::getBoundsForAnnotations(const AnnotationIDs& ids) const { @@ -290,7 +524,7 @@ LatLngBounds AnnotationManager::getBoundsForAnnotations(const AnnotationIDs& ids for (auto id : ids) { const auto annotation_it = annotations.find(id); if (annotation_it != annotations.end()) { - bounds.extend(annotation_it->second->getPoint()); + bounds.extend(annotation_it->second->getBounds()); } } @@ -300,13 +534,89 @@ LatLngBounds AnnotationManager::getBoundsForAnnotations(const AnnotationIDs& ids const LiveTile* AnnotationManager::getTile(const TileID& id) { std::lock_guard<std::mutex> lock(mtx); - const auto tile_it = tiles.find(id); - if (tile_it != tiles.end()) { - return tile_it->second.second.get(); + // look up any existing annotation tile + LiveTile *renderTile = nullptr; + const auto tile_lookup_it = tiles.find(id); + if (tile_lookup_it != tiles.end()) { + // it exists and may have annotations already + renderTile = tile_lookup_it->second.second.get(); + } else if (orderedShapeAnnotations.size()) { + // it needs created, but only for on-demand shapes + renderTile = tiles.emplace(id, + std::make_pair(std::unordered_set<uint32_t>(), std::make_unique<LiveTile>()) + ).first->second.second.get(); + } + + if (renderTile != nullptr && orderedShapeAnnotations.size()) { + + // create shape tile layers from GeoJSONVT queries + for (auto& tiler_it : shapeTilers) { + const auto annotationID = tiler_it.first; + const std::string layerID = ShapeLayerID + "." + std::to_string(annotationID); + + // check for existing render layer + auto renderLayer = renderTile->getMutableLayer(layerID); + + if (renderLayer == nullptr) { + // we might need to create a tile layer for this shape + const auto& shapeTile = tiler_it.second->getTile(id.z, id.x, id.y); + + if (shapeTile) { + + // shape exists on this tile; let's make a layer + renderLayer = std::make_shared<LiveTileLayer>(); + + // convert the features and add to render layer + for (auto& shapeFeature : shapeTile.features) { + + using namespace mapbox::util::geojsonvt; + + FeatureType renderType = FeatureType::Unknown; + + if (shapeFeature.type == TileFeatureType::LineString) { + renderType = FeatureType::LineString; + } else if (shapeFeature.type == TileFeatureType::Polygon) { + renderType = FeatureType::Polygon; + } + + assert(renderType != FeatureType::Unknown); + + GeometryCollection renderGeometry; + + for (auto& shapeGeometry : shapeFeature.geometry) { + + std::vector<Coordinate> renderLine; + + auto& shapeRing = shapeGeometry.get<TileRing>(); + + for (auto& shapePoint : shapeRing.points) { + renderLine.emplace_back(shapePoint.x, shapePoint.y); + } + + renderGeometry.push_back(renderLine); + } + + auto renderFeature = std::make_shared<LiveTileFeature>(renderType, renderGeometry); + + renderLayer->addFeature(renderFeature); + } + + // move the layer to the render tile + renderTile->addLayer(layerID, renderLayer); + + // associate the annotation with the tile + auto tile_update_it = tiles.find(id); + assert(tile_update_it != tiles.end()); + tile_update_it->second.first.insert(annotationID); + } + } + } } - return nullptr; + + return renderTile; } -const std::string AnnotationManager::layerID = "com.mapbox.annotations.points"; +const std::string AnnotationManager::PointLayerID = "com.mapbox.annotations.points"; +const std::string AnnotationManager::ShapeLayerID = "com.mapbox.annotations.shape"; } |