From af392b9434fd176704e564dfc22ee74f3a50cdf6 Mon Sep 17 00:00:00 2001 From: John Firebaugh Date: Mon, 21 Sep 2015 13:15:58 -0700 Subject: [PATCH] Rewrite annotation invalidation strategy First, move style mutation code out of StyleParser and into AnnotationManager, coalescing it with the mutation code for shape layers. Second, allow AnnotationManager to keep track of stale tiles entirely internally. There's no reason to pass sets of TileIDs around. Third, correct the logic for invalidating the shape source. Since AnnotationManager does not track shape invalidations on a tile-by-tile basis, don't try to invalidate the shape source tile-by-tile. Fixes #1675 Fixes #2322 Fixes #2095 --- include/mbgl/map/update.hpp | 1 + src/mbgl/annotation/annotation_manager.cpp | 148 +++++++++------------ src/mbgl/annotation/annotation_manager.hpp | 27 ++-- src/mbgl/map/map.cpp | 12 +- src/mbgl/map/map_context.cpp | 19 +-- src/mbgl/map/map_context.hpp | 3 +- src/mbgl/map/source.cpp | 18 +-- src/mbgl/map/source.hpp | 1 + src/mbgl/style/style.cpp | 40 ++++-- src/mbgl/style/style.hpp | 6 +- src/mbgl/style/style_parser.cpp | 42 ------ test/api/annotations.cpp | 100 +++++++++----- test/style/resource_loading.cpp | 4 - 13 files changed, 198 insertions(+), 223 deletions(-) diff --git a/include/mbgl/map/update.hpp b/include/mbgl/map/update.hpp index fe9250efc..83c263755 100644 --- a/include/mbgl/map/update.hpp +++ b/include/mbgl/map/update.hpp @@ -13,6 +13,7 @@ enum class Update : uint32_t { Zoom = 1 << 4, RenderStill = 1 << 5, Repaint = 1 << 6, + Annotations = 1 << 7, }; inline Update operator| (const Update& lhs, const Update& rhs) { diff --git a/src/mbgl/annotation/annotation_manager.cpp b/src/mbgl/annotation/annotation_manager.cpp index 31b50b1c5..786ca4243 100644 --- a/src/mbgl/annotation/annotation_manager.cpp +++ b/src/mbgl/annotation/annotation_manager.cpp @@ -137,8 +137,13 @@ AnnotationManager::addShapeAnnotation(const ShapeAnnotation& shape, const uint8_ } uint32_t -AnnotationManager::addPointAnnotation(const PointAnnotation& point, const uint8_t maxZoom, - std::unordered_set& affectedTiles) { +AnnotationManager::addPointAnnotation(const PointAnnotation& point, const uint8_t maxZoom) { + // 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 uint32_t annotationID = nextID(); // at render time we style the point according to its {sprite} field @@ -178,7 +183,7 @@ AnnotationManager::addPointAnnotation(const PointAnnotation& point, const uint8_ // check for annotation layer & create if necessary util::ptr layer; - std::string layerID = PointLayerID; + std::string layerID = PointSourceID; if (tile_pos.second || tile_pos.first->second.second->getMutableLayer(layerID) == nullptr) { layer = std::make_shared(); @@ -198,7 +203,7 @@ AnnotationManager::addPointAnnotation(const PointAnnotation& point, const uint8_ annotation->tilePointFeatures.emplace(featureTileID, std::weak_ptr(feature)); // track affected tile - affectedTiles.insert(featureTileID); + stalePointTileIDs.insert(featureTileID); } annotations.emplace(annotationID, std::move(annotation)); @@ -206,37 +211,22 @@ AnnotationManager::addPointAnnotation(const PointAnnotation& point, const uint8_ return annotationID; } -std::pair +AnnotationIDs AnnotationManager::addPointAnnotations(const std::vector& points, const uint8_t maxZoom) { - // 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. - AnnotationIDs annotationIDs; annotationIDs.reserve(points.size()); - AffectedTiles affectedTiles; - for (const auto& point : points) { - annotationIDs.push_back(addPointAnnotation(point, maxZoom, affectedTiles)); + annotationIDs.push_back(addPointAnnotation(point, maxZoom)); } - // Tile:IDs that need refreshed and the annotation identifiers held onto by the client. - return std::make_pair(affectedTiles, annotationIDs); + return annotationIDs; } -std::pair +AnnotationIDs AnnotationManager::addShapeAnnotations(const std::vector& shapes, const uint8_t maxZoom) { - // 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. - AnnotationIDs annotationIDs; annotationIDs.reserve(shapes.size()); @@ -244,16 +234,10 @@ AnnotationManager::addShapeAnnotations(const std::vector& shape annotationIDs.push_back(addShapeAnnotation(shape, maxZoom)); } - // Tile:IDs that need refreshed and the annotation identifiers held onto by the client. - // Shapes are tiled "on-the-fly", so we don't get any "affected tiles" and just expire - // all annotation tiles for shape adds. - return std::make_pair(AffectedTiles(), annotationIDs); + return annotationIDs; } -std::unordered_set AnnotationManager::removeAnnotations(const AnnotationIDs& ids, - const uint8_t maxZoom) { - std::unordered_set affectedTiles; - +void AnnotationManager::removeAnnotations(const AnnotationIDs& ids, const uint8_t maxZoom) { std::vector z2s; const uint8_t zoomCount = maxZoom + 1; z2s.reserve(zoomCount); @@ -287,17 +271,16 @@ std::unordered_set AnnotationManager::removeAnnotations(co 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); + auto layer = tiles[tid].second->getMutableLayer(PointSourceID); layer->removeFeature(features_it->second); - affectedTiles.insert(tid); + stalePointTileIDs.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 + "." + util::toString(annotationID)); - affectedTiles.insert(tile_it->first); + tile_it->second.second->removeLayer(ShapeSourceID + "." + util::toString(annotationID)); } } @@ -312,9 +295,6 @@ std::unordered_set AnnotationManager::removeAnnotations(co annotations.erase(annotationID); } } - - // TileIDs for tiles that need refreshed. - return affectedTiles; } AnnotationIDs AnnotationManager::getAnnotationsInBounds(const LatLngBounds& queryBounds, @@ -414,7 +394,7 @@ const LiveTile* AnnotationManager::getTile(const TileID& id) { // create shape tile layers from GeoJSONVT queries for (auto& tiler_it : shapeTilers) { const auto annotationID = tiler_it.first; - const std::string layerID = ShapeLayerID + "." + util::toString(annotationID); + const std::string layerID = ShapeSourceID + "." + util::toString(annotationID); // check for existing render layer auto renderLayer = renderTile->getMutableLayer(layerID); @@ -478,42 +458,48 @@ const LiveTile* AnnotationManager::getTile(const TileID& id) { return renderTile; } -void AnnotationManager::updateTilesIfNeeded(Style* style) { - if (!staleTiles.empty()) { - updateTiles(staleTiles, style); - } -} - -void AnnotationManager::updateTiles(const AffectedTiles& ids, Style* style) { - std::copy(ids.begin(), ids.end(), std::inserter(staleTiles, staleTiles.begin())); - - if (!style) { - return; +void AnnotationManager::updateStyle(Style& style) { + // Create shape source + if (!style.getSource(ShapeSourceID)) { + std::unique_ptr shapeSource = std::make_unique(); + shapeSource->info.type = SourceType::Annotations; + shapeSource->info.source_id = ShapeSourceID; + shapeSource->enabled = true; + style.addSource(std::move(shapeSource)); } - // grab existing, single shape annotations source - const auto& shapeID = AnnotationManager::ShapeLayerID; - Source* shapeAnnotationSource = style->getSource(shapeID); + // Create point source and singular layer and bucket + if (!style.getSource(PointSourceID)) { + std::unique_ptr pointSource = std::make_unique(); + pointSource->info.type = SourceType::Annotations; + pointSource->info.source_id = PointSourceID; + pointSource->enabled = true; + style.addSource(std::move(pointSource)); - // Style not parsed yet - if (!shapeAnnotationSource) { - return; + std::map pointPaints; + pointPaints.emplace(ClassID::Default, ClassProperties()); + std::unique_ptr pointLayer = std::make_unique(PointSourceID, std::move(pointPaints)); + pointLayer->type = StyleLayerType::Symbol; + + util::ptr pointBucket = std::make_shared(pointLayer->type); + pointBucket->name = pointLayer->id; + pointBucket->source = PointSourceID; + pointBucket->source_layer = pointLayer->id; + pointBucket->layout.set(PropertyKey::IconImage, ConstantFunction("{sprite}")); + pointBucket->layout.set(PropertyKey::IconAllowOverlap, ConstantFunction(true)); + + pointLayer->bucket = pointBucket; + style.addLayer(std::move(pointLayer)); } - shapeAnnotationSource->enabled = true; - - const auto& layers = style->layers; - - // create (if necessary) layers and buckets for each shape - for (const auto& shapeAnnotationID : orderedShapeAnnotations) { - const std::string shapeLayerID = shapeID + "." + util::toString(shapeAnnotationID); - - if (std::find_if(layers.begin(), layers.end(), [&](auto l) { return l->id == shapeLayerID; }) != layers.end()) { + // Create new shape layers and buckets + for (const auto& shapeID : orderedShapeAnnotations) { + const std::string shapeLayerID = ShapeSourceID + "." + util::toString(shapeID); + if (style.getLayer(shapeLayerID)) { continue; } - // apply shape paint properties - const StyleProperties& shapeStyle = annotations.at(shapeAnnotationID)->styleProperties; + const StyleProperties& shapeStyle = annotations.at(shapeID)->styleProperties; ClassProperties paintProperties; if (shapeStyle.is()) { @@ -530,40 +516,28 @@ void AnnotationManager::updateTiles(const AffectedTiles& ids, Style* style) { std::map shapePaints; shapePaints.emplace(ClassID::Default, std::move(paintProperties)); - - // create shape layer - util::ptr shapeLayer = std::make_shared(shapeLayerID, std::move(shapePaints)); + std::unique_ptr shapeLayer = std::make_unique(shapeLayerID, std::move(shapePaints)); shapeLayer->type = (shapeStyle.is() ? StyleLayerType::Line : StyleLayerType::Fill); - // add to end of other shape layers just before (last) point layer - style->layers.emplace((style->layers.end() - 1), shapeLayer); - - // create shape bucket & connect to source util::ptr shapeBucket = std::make_shared(shapeLayer->type); shapeBucket->name = shapeLayer->id; - shapeBucket->source = shapeID; + shapeBucket->source = ShapeSourceID; shapeBucket->source_layer = shapeLayer->id; - - // apply line layout properties to bucket if (shapeStyle.is()) { shapeBucket->layout.set(PropertyKey::LineJoin, ConstantFunction(JoinType::Round)); } - // connect layer to bucket shapeLayer->bucket = shapeBucket; + style.addLayer(std::move(shapeLayer), PointSourceID); } - // invalidate annotations layer tiles - for (const auto &source : style->sources) { - if (source->info.type == SourceType::Annotations) { - source->invalidateTiles(ids); - } - } + style.getSource(PointSourceID)->invalidateTiles(stalePointTileIDs); + style.getSource(ShapeSourceID)->invalidateTiles(); - staleTiles.clear(); + stalePointTileIDs.clear(); } -const std::string AnnotationManager::PointLayerID = "com.mapbox.annotations.points"; -const std::string AnnotationManager::ShapeLayerID = "com.mapbox.annotations.shape"; +const std::string AnnotationManager::PointSourceID = "com.mapbox.annotations.points"; +const std::string AnnotationManager::ShapeSourceID = "com.mapbox.annotations.shape"; } diff --git a/src/mbgl/annotation/annotation_manager.hpp b/src/mbgl/annotation/annotation_manager.hpp index cf8d8e3e7..248fb798d 100644 --- a/src/mbgl/annotation/annotation_manager.hpp +++ b/src/mbgl/annotation/annotation_manager.hpp @@ -29,48 +29,37 @@ using GeoJSONVT = mapbox::util::geojsonvt::GeoJSONVT; class AnnotationManager : private util::noncopyable { public: - typedef std::unordered_set AffectedTiles; - AnnotationManager(); ~AnnotationManager(); void setDefaultPointAnnotationSymbol(const std::string& symbol); - std::pair - addPointAnnotations(const std::vector&, const uint8_t maxZoom); - - std::pair - addShapeAnnotations(const std::vector&, const uint8_t maxZoom); - - AffectedTiles - removeAnnotations(const AnnotationIDs&, const uint8_t maxZoom); - - void updateTilesIfNeeded(Style*); - void updateTiles(const AffectedTiles&, Style*); + AnnotationIDs addPointAnnotations(const std::vector&, const uint8_t maxZoom); + AnnotationIDs addShapeAnnotations(const std::vector&, const uint8_t maxZoom); + void removeAnnotations(const AnnotationIDs&, const uint8_t maxZoom); AnnotationIDs getAnnotationsInBounds(const LatLngBounds&, const uint8_t maxZoom, const AnnotationType& = AnnotationType::Any) const; LatLngBounds getBoundsForAnnotations(const AnnotationIDs&) const; + void updateStyle(Style&); const LiveTile* getTile(const TileID& id); - static const std::string PointLayerID; - static const std::string ShapeLayerID; + static const std::string PointSourceID; + static const std::string ShapeSourceID; private: inline uint32_t nextID(); static vec2 projectPoint(const LatLng& point); uint32_t addShapeAnnotation(const ShapeAnnotation&, const uint8_t maxZoom); - uint32_t addPointAnnotation(const PointAnnotation&, const uint8_t maxZoom, AffectedTiles&); - - const StyleProperties getAnnotationStyleProperties(uint32_t) const; + uint32_t addPointAnnotation(const PointAnnotation&, const uint8_t maxZoom); std::string defaultPointAnnotationSymbol; std::unordered_map> annotations; std::vector orderedShapeAnnotations; std::unordered_map, std::unique_ptr>, TileID::Hash> tiles; std::unordered_map> shapeTilers; - std::unordered_set staleTiles; + std::unordered_set stalePointTileIDs; uint32_t nextID_ = 0; }; diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp index 3e7a61a4e..4ba8d6cd8 100644 --- a/src/mbgl/map/map.cpp +++ b/src/mbgl/map/map.cpp @@ -348,8 +348,8 @@ uint32_t Map::addPointAnnotation(const PointAnnotation& annotation) { AnnotationIDs Map::addPointAnnotations(const std::vector& annotations) { auto result = data->getAnnotationManager()->addPointAnnotations(annotations, getMaxZoom()); - context->invoke(&MapContext::updateAnnotationTiles, result.first); - return result.second; + update(Update::Annotations); + return result; } uint32_t Map::addShapeAnnotation(const ShapeAnnotation& annotation) { @@ -358,8 +358,8 @@ uint32_t Map::addShapeAnnotation(const ShapeAnnotation& annotation) { AnnotationIDs Map::addShapeAnnotations(const std::vector& annotations) { auto result = data->getAnnotationManager()->addShapeAnnotations(annotations, getMaxZoom()); - context->invoke(&MapContext::updateAnnotationTiles, result.first); - return result.second; + update(Update::Annotations); + return result; } void Map::removeAnnotation(uint32_t annotation) { @@ -367,8 +367,8 @@ void Map::removeAnnotation(uint32_t annotation) { } void Map::removeAnnotations(const std::vector& annotations) { - auto result = data->getAnnotationManager()->removeAnnotations(annotations, getMaxZoom()); - context->invoke(&MapContext::updateAnnotationTiles, result); + data->getAnnotationManager()->removeAnnotations(annotations, getMaxZoom()); + update(Update::Annotations); } std::vector Map::getAnnotationsInBounds(const LatLngBounds& bounds, const AnnotationType& type) { diff --git a/src/mbgl/map/map_context.cpp b/src/mbgl/map/map_context.cpp index 1c0c98b59..c2b5900b1 100644 --- a/src/mbgl/map/map_context.cpp +++ b/src/mbgl/map/map_context.cpp @@ -137,13 +137,7 @@ void MapContext::loadStyleJSON(const std::string& json, const std::string& base) // force style cascade, causing all pending transitions to complete. style->cascade(); - updateFlags |= Update::DefaultTransition | Update::Classes | Update::Zoom; - asyncUpdate->send(); -} - -void MapContext::updateAnnotationTiles(const std::unordered_set& ids) { - data.getAnnotationManager()->updateTiles(ids, style.get()); - updateFlags |= Update::Classes; + updateFlags |= Update::DefaultTransition | Update::Classes | Update::Zoom | Update::Annotations; asyncUpdate->send(); } @@ -160,6 +154,11 @@ void MapContext::update() { data.setAnimationTime(Clock::now()); + if (style->sprite && updateFlags & Update::Annotations) { + data.getAnnotationManager()->updateStyle(*style); + updateFlags |= Update::Classes; + } + if (updateFlags & Update::Classes) { style->cascade(); } @@ -313,10 +312,4 @@ void MapContext::onResourceLoadingFailed(std::exception_ptr error) { } } -void MapContext::onSpriteStoreLoaded() { - data.getAnnotationManager()->updateTilesIfNeeded(style.get()); - updateFlags |= Update::Classes; - asyncUpdate->send(); -} - } diff --git a/src/mbgl/map/map_context.hpp b/src/mbgl/map/map_context.hpp index f3ac636a4..b78d3283a 100644 --- a/src/mbgl/map/map_context.hpp +++ b/src/mbgl/map/map_context.hpp @@ -54,7 +54,7 @@ public: bool isLoaded() const; double getTopOffsetPixelsForAnnotationSymbol(const std::string& symbol); - void updateAnnotationTiles(const std::unordered_set&); + void updateAnnotations(); void setSourceTileCacheSize(size_t size); void onLowMemory(); @@ -66,7 +66,6 @@ public: // Style::Observer implementation. void onTileDataChanged() override; void onResourceLoadingFailed(std::exception_ptr error) override; - void onSpriteStoreLoaded() override; private: // Update the state indicated by the accumulated Update flags, then render. diff --git a/src/mbgl/map/source.cpp b/src/mbgl/map/source.cpp index 572973b79..989d2eb75 100644 --- a/src/mbgl/map/source.cpp +++ b/src/mbgl/map/source.cpp @@ -515,18 +515,20 @@ bool Source::update(MapData& data, void Source::invalidateTiles(const std::unordered_set& ids) { cache.clear(); - if (!ids.empty()) { - for (auto& id : ids) { - tiles.erase(id); - tile_data.erase(id); - } - } else { - tiles.clear(); - tile_data.clear(); + for (const auto& id : ids) { + tiles.erase(id); + tile_data.erase(id); } updateTilePtrs(); } +void Source::invalidateTiles() { + cache.clear(); + tiles.clear(); + tile_data.clear(); + updateTilePtrs(); +} + void Source::updateTilePtrs() { tilePtrs.clear(); for (const auto& pair : tiles) { diff --git a/src/mbgl/map/source.hpp b/src/mbgl/map/source.hpp index 336caa5b9..421f81964 100644 --- a/src/mbgl/map/source.hpp +++ b/src/mbgl/map/source.hpp @@ -78,6 +78,7 @@ public: bool shouldReparsePartialTiles); void invalidateTiles(const std::unordered_set&); + void invalidateTiles(); void updateMatrices(const mat4 &projMatrix, const TransformState &transform); void drawClippingMasks(Painter &painter); diff --git a/src/mbgl/style/style.cpp b/src/mbgl/style/style.cpp index d4c9bd4e5..7945a45b1 100644 --- a/src/mbgl/style/style.cpp +++ b/src/mbgl/style/style.cpp @@ -44,18 +44,18 @@ void Style::setJSON(const std::string& json, const std::string&) { StyleParser parser(data); parser.parse(doc); - sources = parser.getSources(); - layers = parser.getLayers(); + for (auto& source : parser.getSources()) { + addSource(std::move(source)); + } + + for (auto& layer : parser.getLayers()) { + addLayer(std::move(layer)); + } sprite = std::make_unique(parser.getSprite(), data.pixelRatio); sprite->setObserver(this); glyphStore->setURL(parser.getGlyphURL()); - - for (const auto& source : sources) { - source->setObserver(this); - source->load(); - } } Style::~Style() { @@ -70,6 +70,20 @@ Style::~Style() { } } +void Style::addSource(std::unique_ptr source) { + source->setObserver(this); + source->load(); + sources.emplace_back(std::move(source)); +} + +void Style::addLayer(util::ptr layer) { + layers.emplace_back(std::move(layer)); +} + +void Style::addLayer(util::ptr layer, const std::string& before) { + layers.emplace(std::find_if(layers.begin(), layers.end(), [&](const auto& l) { return l->id == before; }), std::move(layer)); +} + void Style::update(const TransformState& transform, TexturePool& texturePool) { bool allTilesUpdated = true; @@ -126,6 +140,14 @@ Source* Style::getSource(const std::string& id) const { return it != sources.end() ? it->get() : nullptr; } +StyleLayer* Style::getLayer(const std::string& id) const { + const auto it = std::find_if(layers.begin(), layers.end(), [&](const auto& layer) { + return layer->id == id; + }); + + return it != layers.end() ? it->get() : nullptr; +} + bool Style::hasTransitions() const { for (const auto& layer : layers) { if (layer->hasTransitions()) { @@ -190,10 +212,6 @@ void Style::onSpriteLoaded(const Sprites& sprites) { // Add all sprite images to the SpriteStore object spriteStore->setSprites(sprites); - if (observer) { - observer->onSpriteStoreLoaded(); - } - shouldReparsePartialTiles = true; emitTileDataChanged(); } diff --git a/src/mbgl/style/style.hpp b/src/mbgl/style/style.hpp index 3f9696ffb..9c5091863 100644 --- a/src/mbgl/style/style.hpp +++ b/src/mbgl/style/style.hpp @@ -39,7 +39,6 @@ public: virtual ~Observer() = default; virtual void onTileDataChanged() = 0; - virtual void onSpriteStoreLoaded() = 0; virtual void onResourceLoadingFailed(std::exception_ptr error) = 0; }; @@ -63,6 +62,11 @@ public: } Source* getSource(const std::string& id) const; + StyleLayer* getLayer(const std::string& id) const; + + void addSource(std::unique_ptr); + void addLayer(util::ptr); + void addLayer(util::ptr, const std::string& beforeLayerID); MapData& data; std::unique_ptr glyphStore; diff --git a/src/mbgl/style/style_parser.cpp b/src/mbgl/style/style_parser.cpp index 2e298e9a6..04c3a4555 100644 --- a/src/mbgl/style/style_parser.cpp +++ b/src/mbgl/style/style_parser.cpp @@ -1,7 +1,6 @@ #include #include #include -#include #include #include #include @@ -40,47 +39,6 @@ void StyleParser::parse(JSVal document) { if (document.HasMember("layers")) { parseLayers(document["layers"]); - - // create shape annotations source - const std::string& shapeID = AnnotationManager::ShapeLayerID; - - std::unique_ptr shapeAnnotationsSource = std::make_unique(); - shapeAnnotationsSource->info.type = SourceType::Annotations; - shapeAnnotationsSource->info.source_id = shapeID; - sourcesMap.emplace(shapeID, shapeAnnotationsSource.get()); - sources.emplace_back(std::move(shapeAnnotationsSource)); - - // create point annotations layer - const std::string& pointID = AnnotationManager::PointLayerID; - - std::map pointPaints; - util::ptr pointAnnotationsLayer = std::make_shared(pointID, std::move(pointPaints)); - pointAnnotationsLayer->type = StyleLayerType::Symbol; - layersMap.emplace(pointID, std::pair> { JSVal(pointID), pointAnnotationsLayer }); - layers.emplace_back(pointAnnotationsLayer); - - // create point annotations symbol bucket - util::ptr pointBucket = std::make_shared(pointAnnotationsLayer->type); - pointBucket->name = pointAnnotationsLayer->id; - pointBucket->source = pointID; - pointBucket->source_layer = pointAnnotationsLayer->id; - - // build up point annotations style - 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); - - // create point annotations source & connect to bucket & layer - std::unique_ptr pointAnnotationsSource = std::make_unique(); - pointAnnotationsSource->info.type = SourceType::Annotations; - pointAnnotationsSource->info.source_id = pointID; - pointAnnotationsLayer->bucket = pointBucket; - sourcesMap.emplace(pointID, pointAnnotationsSource.get()); - sources.emplace_back(std::move(pointAnnotationsSource)); } if (document.HasMember("sprite")) { diff --git a/test/api/annotations.cpp b/test/api/annotations.cpp index a9ebb4ab0..a00a449a6 100644 --- a/test/api/annotations.cpp +++ b/test/api/annotations.cpp @@ -14,9 +14,19 @@ #include #include -TEST(Annotations, PointAnnotation) { - using namespace mbgl; +using namespace mbgl; +std::string renderPNG(Map& map) { + std::promise> promise; + map.renderStill([&](std::exception_ptr, std::unique_ptr image) { + promise.set_value(std::move(image)); + }); + + auto result = promise.get_future().get(); + return util::compress_png(result->width, result->height, result->pixels.get()); +} + +TEST(Annotations, PointAnnotation) { auto display = std::make_shared(); HeadlessView view(display, 1); DefaultFileSource fileSource(nullptr); @@ -25,19 +35,10 @@ TEST(Annotations, PointAnnotation) { map.setStyleJSON(util::read_file("test/fixtures/api/empty.json"), ""); map.addPointAnnotation(PointAnnotation({ 0, 0 }, "default_marker")); - std::promise> promise; - map.renderStill([&promise](std::exception_ptr, std::unique_ptr image) { - promise.set_value(std::move(image)); - }); - - auto result = promise.get_future().get(); - const std::string png = util::compress_png(result->width, result->height, result->pixels.get()); - util::write_file("test/output/point_annotation.png", png); + util::write_file("test/output/point_annotation.png", renderPNG(map)); } TEST(Annotations, LineAnnotation) { - using namespace mbgl; - auto display = std::make_shared(); HeadlessView view(display, 1); DefaultFileSource fileSource(nullptr); @@ -56,19 +57,10 @@ TEST(Annotations, LineAnnotation) { map.addShapeAnnotation(ShapeAnnotation(segments, styleProperties)); - std::promise> promise; - map.renderStill([&promise](std::exception_ptr, std::unique_ptr image) { - promise.set_value(std::move(image)); - }); - - auto result = promise.get_future().get(); - const std::string png = util::compress_png(result->width, result->height, result->pixels.get()); - util::write_file("test/output/line_annotation.png", png); + util::write_file("test/output/line_annotation.png", renderPNG(map)); } TEST(Annotations, FillAnnotation) { - using namespace mbgl; - auto display = std::make_shared(); HeadlessView view(display, 1); DefaultFileSource fileSource(nullptr); @@ -86,12 +78,60 @@ TEST(Annotations, FillAnnotation) { map.addShapeAnnotation(ShapeAnnotation(segments, styleProperties)); - std::promise> promise; - map.renderStill([&promise](std::exception_ptr, std::unique_ptr image) { - promise.set_value(std::move(image)); - }); - - auto result = promise.get_future().get(); - const std::string png = util::compress_png(result->width, result->height, result->pixels.get()); - util::write_file("test/output/fill_annotation.png", png); + util::write_file("test/output/fill_annotation.png", renderPNG(map)); +} + +TEST(Annotations, AddMultiple) { + auto display = std::make_shared(); + HeadlessView view(display, 1); + DefaultFileSource fileSource(nullptr); + + Map map(view, fileSource, MapMode::Still); + map.setStyleJSON(util::read_file("test/fixtures/api/empty.json"), ""); + map.addPointAnnotation(PointAnnotation({ 0, -20 }, "default_marker")); + + renderPNG(map); + + map.addPointAnnotation(PointAnnotation({ 0, 20 }, "default_marker")); + + util::write_file("test/output/add_multiple.png", renderPNG(map)); +} + +TEST(Annotations, NonImmediateAdd) { + auto display = std::make_shared(); + HeadlessView view(display, 1); + DefaultFileSource fileSource(nullptr); + + Map map(view, fileSource, MapMode::Still); + map.setStyleJSON(util::read_file("test/fixtures/api/empty.json"), ""); + + renderPNG(map); + + AnnotationSegments segments = {{ {{ { 0, 0 }, { 0, 45 }, { 45, 45 }, { 45, 0 } }} }}; + + FillProperties fillProperties; + fillProperties.fill_color = {{ 255, 0, 0, 1 }}; + + StyleProperties styleProperties; + styleProperties.set(fillProperties); + + map.addShapeAnnotation(ShapeAnnotation(segments, styleProperties)); + + util::write_file("test/output/non_immediate_add.png", renderPNG(map)); +} + +TEST(Annotations, SwitchStyle) { + auto display = std::make_shared(); + HeadlessView view(display, 1); + DefaultFileSource fileSource(nullptr); + + Map map(view, fileSource, MapMode::Still); + map.setStyleJSON(util::read_file("test/fixtures/api/empty.json"), ""); + map.addPointAnnotation(PointAnnotation({ 0, 0 }, "default_marker")); + + renderPNG(map); + + map.setStyleJSON(util::read_file("test/fixtures/api/empty.json"), ""); + + util::write_file("test/output/switch_style.png", renderPNG(map)); } diff --git a/test/style/resource_loading.cpp b/test/style/resource_loading.cpp index 5ed09065a..056243458 100644 --- a/test/style/resource_loading.cpp +++ b/test/style/resource_loading.cpp @@ -63,10 +63,6 @@ public: callback_(error); } - void onSpriteStoreLoaded() override { - // no-op - } - private: MapData data_; Transform transform_;