#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace mbgl { namespace style { static Observer nullObserver; struct QueueSourceReloadVisitor { UpdateBatch& updateBatch; // No need to reload sources for these types; their visibility can change but // they don't participate in layout. void operator()(CustomLayer&) {} void operator()(RasterLayer&) {} void operator()(BackgroundLayer&) {} template void operator()(VectorLayer& layer) { updateBatch.sourceIDs.insert(layer.getSourceID()); } }; Style::Style(Scheduler& scheduler_, FileSource& fileSource_, float pixelRatio) : scheduler(scheduler_), fileSource(fileSource_), glyphAtlas(std::make_unique(Size{ 2048, 2048 }, fileSource)), spriteLoader(std::make_unique(pixelRatio)), spriteAtlas(std::make_unique(Size{ 1024, 1024 }, pixelRatio)), lineAtlas(std::make_unique(Size{ 256, 512 })), light(std::make_unique()), renderLight(light->impl), observer(&nullObserver) { glyphAtlas->setObserver(this); spriteLoader->setObserver(this); light->setObserver(this); } Style::~Style() { for (const auto& layer : layers) { if (auto* customLayer = layer->as()) { customLayer->impl().deinitialize(); } } } bool Style::addClass(const std::string& className) { if (hasClass(className)) return false; classes.push_back(className); return true; } bool Style::hasClass(const std::string& className) const { return std::find(classes.begin(), classes.end(), className) != classes.end(); } bool Style::removeClass(const std::string& className) { const auto it = std::find(classes.begin(), classes.end(), className); if (it != classes.end()) { classes.erase(it); return true; } return false; } void Style::setClasses(const std::vector& classNames) { classes = classNames; } std::vector Style::getClasses() const { return classes; } void Style::setTransitionOptions(const TransitionOptions& options) { transitionOptions = options; } TransitionOptions Style::getTransitionOptions() const { return transitionOptions; } void Style::setJSON(const std::string& json) { sources.clear(); renderSources.clear(); layers.clear(); classes.clear(); transitionOptions = {}; updateBatch = {}; Parser parser; auto error = parser.parse(json); if (error) { std::string message = "Failed to parse style: " + util::toString(error); Log::Error(Event::ParseStyle, message.c_str()); observer->onStyleError(std::make_exception_ptr(util::StyleParseException(message))); observer->onResourceError(error); return; } for (auto& source : parser.sources) { addSource(std::move(source)); } for (auto& layer : parser.layers) { addLayer(std::move(layer)); } name = parser.name; defaultLatLng = parser.latLng; defaultZoom = parser.zoom; defaultBearing = parser.bearing; defaultPitch = parser.pitch; setLight(std::make_unique(parser.light)); glyphAtlas->setURL(parser.glyphURL); spriteLoader->load(parser.spriteURL, scheduler, fileSource); loaded = true; observer->onStyleLoaded(); } void Style::addSource(std::unique_ptr source) { // Guard against duplicate source ids auto it = std::find_if(sources.begin(), sources.end(), [&](const auto& existing) { return existing->getID() == source->getID(); }); if (it != sources.end()) { std::string msg = "Source " + source->getID() + " already exists"; throw std::runtime_error(msg.c_str()); } source->setObserver(this); source->loadDescription(fileSource); sources.emplace_back(std::move(source)); } std::unique_ptr Style::removeSource(const std::string& id) { auto it = std::find_if(sources.begin(), sources.end(), [&](const auto& source) { return source->getID() == id; }); if (it == sources.end()) { return nullptr; } auto source = std::move(*it); source->setObserver(nullptr); sources.erase(it); updateBatch.sourceIDs.erase(id); return source; } std::vector Style::getLayers() const { std::vector result; result.reserve(layers.size()); for (const auto& layer : layers) { result.push_back(layer.get()); } return result; } std::vector Style::getLayers() { std::vector result; result.reserve(layers.size()); for (auto& layer : layers) { result.push_back(layer.get()); } return result; } std::vector>::const_iterator Style::findLayer(const std::string& id) const { return std::find_if(layers.begin(), layers.end(), [&](const auto& layer) { return layer->baseImpl->id == id; }); } Layer* Style::getLayer(const std::string& id) const { auto it = findLayer(id); return it != layers.end() ? it->get() : nullptr; } Layer* Style::addLayer(std::unique_ptr layer, optional before) { // TODO: verify source // Guard against duplicate layer ids auto it = std::find_if(layers.begin(), layers.end(), [&](const auto& existing) { return existing->getID() == layer->getID(); }); if (it != layers.end()) { throw std::runtime_error(std::string{"Layer "} + layer->getID() + " already exists"); } if (auto* customLayer = layer->as()) { customLayer->impl().initialize(); } layer->setObserver(this); layer->accept(QueueSourceReloadVisitor { updateBatch }); return layers.emplace(before ? findLayer(*before) : layers.end(), std::move(layer))->get(); } std::unique_ptr Style::removeLayer(const std::string& id) { auto it = std::find_if(layers.begin(), layers.end(), [&](const auto& layer) { return layer->baseImpl->id == id; }); if (it == layers.end()) return nullptr; auto layer = std::move(*it); if (auto* customLayer = layer->as()) { customLayer->impl().deinitialize(); } layer->setObserver(nullptr); layers.erase(it); return layer; } std::vector Style::getRenderLayers() const { std::vector result; result.reserve(renderLayers.size()); for (const auto& entry : renderLayers) { result.push_back(entry.second.get()); } return result; } std::vector Style::getRenderLayers() { std::vector result; result.reserve(renderLayers.size()); for (auto& entry : renderLayers) { result.push_back(entry.second.get()); } return result; } RenderLayer* Style::getRenderLayer(const std::string& id) const { auto it = renderLayers.find(id); return it != renderLayers.end() ? it->second.get() : nullptr; } void Style::setLight(std::unique_ptr light_) { light = std::move(light_); light->setObserver(this); onLightChanged(*light); } Light* Style::getLight() const { return light.get(); } const RenderLight& Style::getRenderLight() const { return renderLight; } std::string Style::getName() const { return name; } LatLng Style::getDefaultLatLng() const { return defaultLatLng; } double Style::getDefaultZoom() const { return defaultZoom; } double Style::getDefaultBearing() const { return defaultBearing; } double Style::getDefaultPitch() const { return defaultPitch; } void Style::update(const UpdateParameters& parameters) { const bool zoomChanged = zoomHistory.update(parameters.transformState.getZoom(), parameters.timePoint); const bool classesChanged = parameters.updateFlags & Update::Classes; std::vector classIDs; for (const auto& className : classes) { classIDs.push_back(ClassDictionary::Get().lookup(className)); } classIDs.push_back(ClassID::Default); const TransitionParameters transitionParameters { classIDs, parameters.timePoint, parameters.mode == MapMode::Continuous ? transitionOptions : TransitionOptions() }; const PropertyEvaluationParameters evaluationParameters { zoomHistory, parameters.timePoint, parameters.mode == MapMode::Continuous ? util::DEFAULT_FADE_DURATION : Duration::zero() }; const TileParameters tileParameters(parameters.pixelRatio, parameters.debugOptions, parameters.transformState, parameters.scheduler, parameters.fileSource, parameters.mode, parameters.annotationManager, *this); // Update light. const bool lightChanged = renderLight.impl != light->impl; if (lightChanged) { renderLight.impl = light->impl; renderLight.transition(transitionParameters); } if (lightChanged || zoomChanged || renderLight.hasTransition()) { renderLight.evaluate(evaluationParameters); } std::vector> newSourceImpls; newSourceImpls.reserve(sources.size()); for (const auto& source : sources) { newSourceImpls.push_back(source->baseImpl); } const SourceDifference sourceDiff = diffSources(sourceImpls, newSourceImpls); sourceImpls = std::move(newSourceImpls); // Remove render layers for removed sources. for (const auto& entry : sourceDiff.removed) { renderLayers.erase(entry.first); } // Create render sources for newly added sources. for (const auto& entry : sourceDiff.added) { std::unique_ptr renderSource = RenderSource::create(entry.second); renderSource->setObserver(this); renderSources.emplace(entry.first, std::move(renderSource)); } // Update render sources for changed sources. for (const auto& entry : sourceDiff.changed) { renderSources.at(entry.first)->setImpl(entry.second); } std::vector> newLayerImpls; newLayerImpls.reserve(layers.size()); for (const auto& layer : layers) { newLayerImpls.push_back(layer->baseImpl); } const LayerDifference layerDiff = diffLayers(layerImpls, newLayerImpls); layerImpls = std::move(newLayerImpls); // Remove render layers for removed layers. for (const auto& entry : layerDiff.removed) { renderLayers.erase(entry.first); } // Create render layers for newly added layers. for (const auto& entry : layerDiff.added) { renderLayers.emplace(entry.first, RenderLayer::create(entry.second)); } // Update render layers for changed layers. for (const auto& entry : layerDiff.changed) { renderLayers.at(entry.first)->setImpl(entry.second); } // Update layers for class and zoom changes. for (const auto& entry : renderLayers) { RenderLayer& layer = *entry.second; const bool layerAdded = layerDiff.added.count(entry.first); const bool layerChanged = layerDiff.changed.count(entry.first); if (classesChanged || layerAdded || layerChanged) { layer.transition(transitionParameters); } if (classesChanged || layerAdded || layerChanged || zoomChanged || layer.hasTransition()) { layer.evaluate(evaluationParameters); } } // Update tiles for each source. for (const auto& entry : renderSources) { entry.second->enabled = false; } for (const auto& entry : renderLayers) { RenderLayer& layer = *entry.second; if (layer.needsRendering(zoomHistory.lastZoom)) { if (RenderSource* source = getRenderSource(layer.baseImpl->source)) { source->enabled = true; } } } for (const auto& entry : renderSources) { bool updated = updateBatch.sourceIDs.count(entry.first); if (entry.second->enabled) { if (updated) { entry.second->reloadTiles(); } entry.second->updateTiles(tileParameters); } else if (updated) { entry.second->invalidateTiles(); } else { entry.second->removeTiles(); } } updateBatch.sourceIDs.clear(); } std::vector Style::getSources() const { std::vector result; result.reserve(sources.size()); for (const auto& source : sources) { result.push_back(source.get()); } return result; } std::vector Style::getSources() { std::vector result; result.reserve(sources.size()); for (auto& source : sources) { result.push_back(source.get()); } return result; } Source* Style::getSource(const std::string& id) const { const auto it = std::find_if(sources.begin(), sources.end(), [&](const auto& source) { return source->getID() == id; }); return it != sources.end() ? it->get() : nullptr; } RenderSource* Style::getRenderSource(const std::string& id) const { auto it = renderSources.find(id); return it != renderSources.end() ? it->second.get() : nullptr; } bool Style::hasTransitions() const { if (renderLight.hasTransition()) { return true; } for (const auto& entry : renderLayers) { if (entry.second->hasTransition()) { return true; } } return false; } bool Style::isLoaded() const { if (!loaded) { return false; } for (const auto& source: sources) { if (!source->loaded) { return false; } } for (const auto& entry: renderSources) { if (!entry.second->isLoaded()) { return false; } } if (!spriteAtlas->isLoaded()) { return false; } return true; } void Style::addImage(const std::string& id, std::unique_ptr image) { addSpriteImage(spriteImages, id, std::move(image), [&](style::Image& added) { spriteAtlas->addImage(id, added.impl); observer->onUpdate(Update::Repaint); }); } void Style::removeImage(const std::string& id) { removeSpriteImage(spriteImages, id, [&] () { spriteAtlas->removeImage(id); observer->onUpdate(Update::Repaint); }); } const style::Image* Style::getImage(const std::string& id) const { auto it = spriteImages.find(id); return it == spriteImages.end() ? nullptr : it->second.get(); } RenderData Style::getRenderData(MapDebugOptions debugOptions, float angle) const { RenderData result; for (const auto& entry : renderSources) { if (entry.second->enabled) { result.sources.insert(entry.second.get()); } } for (const auto& layerImpl : layerImpls) { const RenderLayer* layer = getRenderLayer(layerImpl->id); assert(layer); if (!layer->needsRendering(zoomHistory.lastZoom)) { continue; } if (const RenderBackgroundLayer* background = layer->as()) { if (debugOptions & MapDebugOptions::Overdraw) { // We want to skip glClear optimization in overdraw mode. result.order.emplace_back(*layer); continue; } const BackgroundPaintProperties::PossiblyEvaluated& paint = background->evaluated; if (layerImpl.get() == layerImpls[0].get() && paint.get().from.empty()) { // This is a solid background. We can use glClear(). result.backgroundColor = paint.get() * paint.get(); } else { // This is a textured background, or not the bottommost layer. We need to render it with a quad. result.order.emplace_back(*layer); } continue; } if (layer->is()) { result.order.emplace_back(*layer); continue; } RenderSource* source = getRenderSource(layer->baseImpl->source); if (!source) { Log::Warning(Event::Render, "can't find source for layer '%s'", layer->baseImpl->id.c_str()); continue; } auto& renderTiles = source->getRenderTiles(); const bool symbolLayer = layer->is(); // Sort symbol tiles in opposite y position, so tiles with overlapping // symbols are drawn on top of each other, with lower symbols being // drawn on top of higher symbols. std::vector> sortedTiles; std::transform(renderTiles.begin(), renderTiles.end(), std::back_inserter(sortedTiles), [](auto& pair) { return std::ref(pair.second); }); if (symbolLayer) { std::sort(sortedTiles.begin(), sortedTiles.end(), [angle](const RenderTile& a, const RenderTile& b) { Point pa(a.id.canonical.x, a.id.canonical.y); Point pb(b.id.canonical.x, b.id.canonical.y); auto par = util::rotate(pa, angle); auto pbr = util::rotate(pb, angle); return std::tie(par.y, par.x) < std::tie(pbr.y, pbr.x); }); } std::vector> sortedTilesForInsertion; for (auto& sortedTile : sortedTiles) { auto& tile = sortedTile.get(); if (!tile.tile.isRenderable()) { continue; } // We're not clipping symbol layers, so when we have both parents and children of symbol // layers, we drop all children in favor of their parent to avoid duplicate labels. // See https://github.com/mapbox/mapbox-gl-native/issues/2482 if (symbolLayer) { bool skip = false; // Look back through the buckets we decided to render to find out whether there is // already a bucket from this layer that is a parent of this tile. Tiles are ordered // by zoom level when we obtain them from getTiles(). for (auto it = sortedTilesForInsertion.rbegin(); it != sortedTilesForInsertion.rend(); ++it) { if (tile.tile.id.isChildOf(it->get().tile.id)) { skip = true; break; } } if (skip) { continue; } } auto bucket = tile.tile.getBucket(*layer->baseImpl); if (bucket) { sortedTilesForInsertion.emplace_back(tile); tile.used = true; } } result.order.emplace_back(*layer, std::move(sortedTilesForInsertion)); } return result; } std::vector Style::queryRenderedFeatures(const ScreenLineString& geometry, const TransformState& transformState, const RenderedQueryOptions& options) const { std::unordered_map> resultsByLayer; if (options.layerIDs) { std::unordered_set sourceIDs; for (const auto& layerID : *options.layerIDs) { if (Layer* layer = getLayer(layerID)) { sourceIDs.emplace(layer->baseImpl->source); } } for (const auto& sourceID : sourceIDs) { if (RenderSource* renderSource = getRenderSource(sourceID)) { auto sourceResults = renderSource->queryRenderedFeatures(geometry, transformState, options); std::move(sourceResults.begin(), sourceResults.end(), std::inserter(resultsByLayer, resultsByLayer.begin())); } } } else { for (const auto& entry : renderSources) { auto sourceResults = entry.second->queryRenderedFeatures(geometry, transformState, options); std::move(sourceResults.begin(), sourceResults.end(), std::inserter(resultsByLayer, resultsByLayer.begin())); } } std::vector result; if (resultsByLayer.empty()) { return result; } // Combine all results based on the style layer order. for (const auto& layerImpl : layerImpls) { const RenderLayer* layer = getRenderLayer(layerImpl->id); if (!layer->needsRendering(zoomHistory.lastZoom)) { continue; } auto it = resultsByLayer.find(layer->baseImpl->id); if (it != resultsByLayer.end()) { std::move(it->second.begin(), it->second.end(), std::back_inserter(result)); } } return result; } void Style::setSourceTileCacheSize(size_t size) { for (const auto& entry : renderSources) { entry.second->setCacheSize(size); } } void Style::onLowMemory() { for (const auto& entry : renderSources) { entry.second->onLowMemory(); } } void Style::setObserver(style::Observer* observer_) { observer = observer_; } void Style::onGlyphsError(const FontStack& fontStack, const GlyphRange& glyphRange, std::exception_ptr error) { lastError = error; Log::Error(Event::Style, "Failed to load glyph range %d-%d for font stack %s: %s", glyphRange.first, glyphRange.second, fontStackToString(fontStack).c_str(), util::toString(error).c_str()); observer->onResourceError(error); } void Style::onSourceLoaded(Source& source) { observer->onSourceLoaded(source); observer->onUpdate(Update::Repaint); } void Style::onSourceChanged(Source& source) { observer->onSourceChanged(source); } void Style::onSourceError(Source& source, std::exception_ptr error) { lastError = error; Log::Error(Event::Style, "Failed to load source %s: %s", source.getID().c_str(), util::toString(error).c_str()); observer->onSourceError(source, error); observer->onResourceError(error); } void Style::onSourceDescriptionChanged(Source& source) { observer->onSourceDescriptionChanged(source); if (!source.loaded) { source.loadDescription(fileSource); } } void Style::onTileChanged(RenderSource&, const OverscaledTileID&) { observer->onUpdate(Update::Repaint); } void Style::onTileError(RenderSource& source, const OverscaledTileID& tileID, std::exception_ptr error) { lastError = error; Log::Error(Event::Style, "Failed to load tile %s for source %s: %s", util::toString(tileID).c_str(), source.baseImpl->id.c_str(), util::toString(error).c_str()); observer->onResourceError(error); } void Style::onSpriteLoaded(SpriteLoader::Images&& images) { // Add images to collection Images addedImages; for (auto& entry : images) { addSpriteImage(spriteImages, entry.first, std::move(entry.second), [&] (style::Image& added) { addedImages.emplace(entry.first, std::make_unique(added)); }); } // Update render sprite atlas spriteAtlas->onSpriteLoaded(std::move(addedImages)); // Update observer observer->onUpdate(Update::Repaint); // For *-pattern properties. } void Style::onSpriteError(std::exception_ptr error) { lastError = error; Log::Error(Event::Style, "Failed to load sprite: %s", util::toString(error).c_str()); observer->onResourceError(error); } void Style::onLayerFilterChanged(Layer& layer) { layer.accept(QueueSourceReloadVisitor { updateBatch }); observer->onUpdate(Update::Repaint); } void Style::onLayerVisibilityChanged(Layer& layer) { layer.accept(QueueSourceReloadVisitor { updateBatch }); observer->onUpdate(Update::Repaint); } void Style::onLayerPaintPropertyChanged(Layer&) { observer->onUpdate(Update::Repaint); } void Style::onLayerDataDrivenPaintPropertyChanged(Layer& layer) { layer.accept(QueueSourceReloadVisitor { updateBatch }); observer->onUpdate(Update::Repaint); } void Style::onLayerLayoutPropertyChanged(Layer& layer, const char *) { layer.accept(QueueSourceReloadVisitor { updateBatch }); observer->onUpdate(Update::Repaint); } void Style::onLightChanged(const Light&) { observer->onUpdate(Update::Repaint); } void Style::dumpDebugLogs() const { for (const auto& source : sources) { source->dumpDebugLogs(); } for (const auto& entry : renderSources) { entry.second->dumpDebugLogs(); } spriteAtlas->dumpDebugLogs(); } } // namespace style } // namespace mbgl