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
This commit is contained in:
John Firebaugh
2015-09-21 13:15:58 -07:00
parent 24a5834428
commit af392b9434
13 changed files with 198 additions and 223 deletions

View File

@@ -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) {

View File

@@ -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<TileID, TileID::Hash>& 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<LiveTileLayer> 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<LiveTileLayer>();
@@ -198,7 +203,7 @@ AnnotationManager::addPointAnnotation(const PointAnnotation& point, const uint8_
annotation->tilePointFeatures.emplace(featureTileID, std::weak_ptr<const LiveTileFeature>(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<AnnotationManager::AffectedTiles, AnnotationIDs>
AnnotationIDs
AnnotationManager::addPointAnnotations(const std::vector<PointAnnotation>& 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<AnnotationManager::AffectedTiles, AnnotationIDs>
AnnotationIDs
AnnotationManager::addShapeAnnotations(const std::vector<ShapeAnnotation>& 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<ShapeAnnotation>& 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<TileID, TileID::Hash> AnnotationManager::removeAnnotations(const AnnotationIDs& ids,
const uint8_t maxZoom) {
std::unordered_set<TileID, TileID::Hash> affectedTiles;
void AnnotationManager::removeAnnotations(const AnnotationIDs& ids, const uint8_t maxZoom) {
std::vector<uint32_t> z2s;
const uint8_t zoomCount = maxZoom + 1;
z2s.reserve(zoomCount);
@@ -287,17 +271,16 @@ std::unordered_set<TileID, TileID::Hash> 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<TileID, TileID::Hash> 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<Source> shapeSource = std::make_unique<Source>();
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<Source> pointSource = std::make_unique<Source>();
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<ClassID, ClassProperties> pointPaints;
pointPaints.emplace(ClassID::Default, ClassProperties());
std::unique_ptr<StyleLayer> pointLayer = std::make_unique<StyleLayer>(PointSourceID, std::move(pointPaints));
pointLayer->type = StyleLayerType::Symbol;
util::ptr<StyleBucket> pointBucket = std::make_shared<StyleBucket>(pointLayer->type);
pointBucket->name = pointLayer->id;
pointBucket->source = PointSourceID;
pointBucket->source_layer = pointLayer->id;
pointBucket->layout.set(PropertyKey::IconImage, ConstantFunction<std::string>("{sprite}"));
pointBucket->layout.set(PropertyKey::IconAllowOverlap, ConstantFunction<bool>(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<LineProperties>()) {
@@ -530,40 +516,28 @@ void AnnotationManager::updateTiles(const AffectedTiles& ids, Style* style) {
std::map<ClassID, ClassProperties> shapePaints;
shapePaints.emplace(ClassID::Default, std::move(paintProperties));
// create shape layer
util::ptr<StyleLayer> shapeLayer = std::make_shared<StyleLayer>(shapeLayerID, std::move(shapePaints));
std::unique_ptr<StyleLayer> shapeLayer = std::make_unique<StyleLayer>(shapeLayerID, std::move(shapePaints));
shapeLayer->type = (shapeStyle.is<LineProperties>() ? 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<StyleBucket> shapeBucket = std::make_shared<StyleBucket>(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<LineProperties>()) {
shapeBucket->layout.set(PropertyKey::LineJoin, ConstantFunction<JoinType>(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";
}

View File

@@ -29,48 +29,37 @@ using GeoJSONVT = mapbox::util::geojsonvt::GeoJSONVT;
class AnnotationManager : private util::noncopyable {
public:
typedef std::unordered_set<TileID, TileID::Hash> AffectedTiles;
AnnotationManager();
~AnnotationManager();
void setDefaultPointAnnotationSymbol(const std::string& symbol);
std::pair<AffectedTiles, AnnotationIDs>
addPointAnnotations(const std::vector<PointAnnotation>&, const uint8_t maxZoom);
std::pair<AffectedTiles, AnnotationIDs>
addShapeAnnotations(const std::vector<ShapeAnnotation>&, 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<PointAnnotation>&, const uint8_t maxZoom);
AnnotationIDs addShapeAnnotations(const std::vector<ShapeAnnotation>&, 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<double> 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<uint32_t, std::unique_ptr<Annotation>> annotations;
std::vector<uint32_t> orderedShapeAnnotations;
std::unordered_map<TileID, std::pair<std::unordered_set<uint32_t>, std::unique_ptr<LiveTile>>, TileID::Hash> tiles;
std::unordered_map<uint32_t, std::unique_ptr<GeoJSONVT>> shapeTilers;
std::unordered_set<TileID, TileID::Hash> staleTiles;
std::unordered_set<TileID, TileID::Hash> stalePointTileIDs;
uint32_t nextID_ = 0;
};

View File

@@ -348,8 +348,8 @@ uint32_t Map::addPointAnnotation(const PointAnnotation& annotation) {
AnnotationIDs Map::addPointAnnotations(const std::vector<PointAnnotation>& 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<ShapeAnnotation>& 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<uint32_t>& annotations) {
auto result = data->getAnnotationManager()->removeAnnotations(annotations, getMaxZoom());
context->invoke(&MapContext::updateAnnotationTiles, result);
data->getAnnotationManager()->removeAnnotations(annotations, getMaxZoom());
update(Update::Annotations);
}
std::vector<uint32_t> Map::getAnnotationsInBounds(const LatLngBounds& bounds, const AnnotationType& type) {

View File

@@ -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<TileID, TileID::Hash>& 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();
}
}

View File

@@ -54,7 +54,7 @@ public:
bool isLoaded() const;
double getTopOffsetPixelsForAnnotationSymbol(const std::string& symbol);
void updateAnnotationTiles(const std::unordered_set<TileID, TileID::Hash>&);
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.

View File

@@ -515,18 +515,20 @@ bool Source::update(MapData& data,
void Source::invalidateTiles(const std::unordered_set<TileID, TileID::Hash>& 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) {

View File

@@ -78,6 +78,7 @@ public:
bool shouldReparsePartialTiles);
void invalidateTiles(const std::unordered_set<TileID, TileID::Hash>&);
void invalidateTiles();
void updateMatrices(const mat4 &projMatrix, const TransformState &transform);
void drawClippingMasks(Painter &painter);

View File

@@ -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<Sprite>(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) {
source->setObserver(this);
source->load();
sources.emplace_back(std::move(source));
}
void Style::addLayer(util::ptr<StyleLayer> layer) {
layers.emplace_back(std::move(layer));
}
void Style::addLayer(util::ptr<StyleLayer> 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();
}

View File

@@ -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<Source>);
void addLayer(util::ptr<StyleLayer>);
void addLayer(util::ptr<StyleLayer>, const std::string& beforeLayerID);
MapData& data;
std::unique_ptr<GlyphStore> glyphStore;

View File

@@ -1,7 +1,6 @@
#include <mbgl/style/style_parser.hpp>
#include <mbgl/map/source.hpp>
#include <mbgl/style/style_layer.hpp>
#include <mbgl/annotation/annotation_manager.hpp>
#include <mbgl/map/map_data.hpp>
#include <mbgl/util/constants.hpp>
#include <mbgl/util/vec.hpp>
@@ -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<Source> shapeAnnotationsSource = std::make_unique<Source>();
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<ClassID, ClassProperties> pointPaints;
util::ptr<StyleLayer> pointAnnotationsLayer = std::make_shared<StyleLayer>(pointID, std::move(pointPaints));
pointAnnotationsLayer->type = StyleLayerType::Symbol;
layersMap.emplace(pointID, std::pair<JSVal, util::ptr<StyleLayer>> { JSVal(pointID), pointAnnotationsLayer });
layers.emplace_back(pointAnnotationsLayer);
// create point annotations symbol bucket
util::ptr<StyleBucket> pointBucket = std::make_shared<StyleBucket>(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<Source> pointAnnotationsSource = std::make_unique<Source>();
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")) {

View File

@@ -14,9 +14,19 @@
#include <future>
#include <vector>
TEST(Annotations, PointAnnotation) {
using namespace mbgl;
using namespace mbgl;
std::string renderPNG(Map& map) {
std::promise<std::unique_ptr<const StillImage>> promise;
map.renderStill([&](std::exception_ptr, std::unique_ptr<const StillImage> 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<mbgl::HeadlessDisplay>();
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<std::unique_ptr<const StillImage>> promise;
map.renderStill([&promise](std::exception_ptr, std::unique_ptr<const StillImage> 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<mbgl::HeadlessDisplay>();
HeadlessView view(display, 1);
DefaultFileSource fileSource(nullptr);
@@ -56,19 +57,10 @@ TEST(Annotations, LineAnnotation) {
map.addShapeAnnotation(ShapeAnnotation(segments, styleProperties));
std::promise<std::unique_ptr<const StillImage>> promise;
map.renderStill([&promise](std::exception_ptr, std::unique_ptr<const StillImage> 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<mbgl::HeadlessDisplay>();
HeadlessView view(display, 1);
DefaultFileSource fileSource(nullptr);
@@ -86,12 +78,60 @@ TEST(Annotations, FillAnnotation) {
map.addShapeAnnotation(ShapeAnnotation(segments, styleProperties));
std::promise<std::unique_ptr<const StillImage>> promise;
map.renderStill([&promise](std::exception_ptr, std::unique_ptr<const StillImage> 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<mbgl::HeadlessDisplay>();
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<mbgl::HeadlessDisplay>();
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>(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<mbgl::HeadlessDisplay>();
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));
}

View File

@@ -63,10 +63,6 @@ public:
callback_(error);
}
void onSpriteStoreLoaded() override {
// no-op
}
private:
MapData data_;
Transform transform_;