#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; Style::Impl::Impl(Scheduler& scheduler_, FileSource& fileSource_, float pixelRatio) : scheduler(scheduler_), fileSource(fileSource_), spriteLoader(std::make_unique(pixelRatio)), light(std::make_unique()), observer(&nullObserver) { spriteLoader->setObserver(this); light->setObserver(this); } Style::Impl::~Impl() = default; void Style::Impl::loadJSON(const std::string& json_) { observer->onStyleLoading(); url.clear(); parse(json_); } void Style::Impl::loadURL(const std::string& url_) { observer->onStyleLoading(); loaded = false; url = url_; styleRequest = fileSource.request(Resource::style(url), [this](Response res) { // Once we get a fresh style, or the style is mutated, stop revalidating. if (res.isFresh() || mutated) { styleRequest.reset(); } // Don't allow a loaded, mutated style to be overwritten with a new version. if (mutated && loaded) { return; } if (res.error) { const std::string message = "loading style failed: " + res.error->message; Log::Error(Event::Setup, message.c_str()); observer->onStyleError(std::make_exception_ptr(util::StyleLoadException(message))); observer->onResourceError(std::make_exception_ptr(std::runtime_error(res.error->message))); } else if (res.notModified || res.noContent) { return; } else { parse(*res.data); } }); } void Style::Impl::parse(const std::string& json_) { Parser parser; if (auto error = parser.parse(json_)) { 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; } mutated = false; loaded = true; json = json_; sources.clear(); layers.clear(); images.clear(); transitionOptions = {}; transitionOptions.duration = util::DEFAULT_TRANSITION_DURATION; 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)); spriteLoader->load(parser.spriteURL, scheduler, fileSource); glyphURL = parser.glyphURL; observer->onStyleLoaded(); } std::string Style::Impl::getJSON() const { return json; } std::string Style::Impl::getURL() const { return url; } void Style::Impl::setTransitionOptions(const TransitionOptions& options) { transitionOptions = options; } TransitionOptions Style::Impl::getTransitionOptions() const { return transitionOptions; } void Style::Impl::addSource(std::unique_ptr source) { if (sources.get(source->getID())) { std::string msg = "Source " + source->getID() + " already exists"; throw std::runtime_error(msg.c_str()); } source->setObserver(this); source->loadDescription(fileSource); sources.add(std::move(source)); } struct SourceIdUsageEvaluator { const std::string& sourceId; bool operator()(BackgroundLayer&) { return false; } bool operator()(CustomLayer&) { return false; } template bool operator()(LayerType& layer) { return layer.getSourceID() == sourceId; } }; std::unique_ptr Style::Impl::removeSource(const std::string& id) { // Check if source is in use SourceIdUsageEvaluator sourceIdEvaluator {id}; auto layerIt = std::find_if(layers.begin(), layers.end(), [&](const auto& layer) { return layer->accept(sourceIdEvaluator); }); if (layerIt != layers.end()) { Log::Warning(Event::General, "Source '%s' is in use, cannot remove", id.c_str()); return nullptr; } std::unique_ptr source = sources.remove(id); if (source) { source->setObserver(nullptr); } return source; } std::vector Style::Impl::getLayers() { return layers.getWrappers(); } std::vector Style::Impl::getLayers() const { auto wrappers = layers.getWrappers(); return std::vector(wrappers.begin(), wrappers.end()); } Layer* Style::Impl::getLayer(const std::string& id) const { return layers.get(id); } Layer* Style::Impl::addLayer(std::unique_ptr layer, optional before) { // TODO: verify source if (layers.get(layer->getID())) { throw std::runtime_error(std::string{"Layer "} + layer->getID() + " already exists"); } layer->setObserver(this); observer->onUpdate(Update::Repaint); return layers.add(std::move(layer), before); } std::unique_ptr Style::Impl::removeLayer(const std::string& id) { std::unique_ptr layer = layers.remove(id); if (layer) { layer->setObserver(nullptr); observer->onUpdate(Update::Repaint); } return layer; } void Style::Impl::setLight(std::unique_ptr light_) { light = std::move(light_); light->setObserver(this); onLightChanged(*light); } Light* Style::Impl::getLight() const { return light.get(); } std::string Style::Impl::getName() const { return name; } LatLng Style::Impl::getDefaultLatLng() const { return defaultLatLng; } double Style::Impl::getDefaultZoom() const { return defaultZoom; } double Style::Impl::getDefaultBearing() const { return defaultBearing; } double Style::Impl::getDefaultPitch() const { return defaultPitch; } std::vector Style::Impl::getSources() { return sources.getWrappers(); } std::vector Style::Impl::getSources() const { auto wrappers = sources.getWrappers(); return std::vector(wrappers.begin(), wrappers.end()); } Source* Style::Impl::getSource(const std::string& id) const { return sources.get(id); } bool Style::Impl::isLoaded() const { if (!loaded) { return false; } if (!spriteLoaded) { return false; } for (const auto& source: sources) { if (!source->loaded) { return false; } } return true; } void Style::Impl::addImage(std::unique_ptr image) { images.remove(image->getID()); // We permit using addImage to update. images.add(std::move(image)); } void Style::Impl::removeImage(const std::string& id) { images.remove(id); } const style::Image* Style::Impl::getImage(const std::string& id) const { return images.get(id); } void Style::Impl::setObserver(style::Observer* observer_) { observer = observer_; } void Style::Impl::onSourceLoaded(Source& source) { sources.update(source); observer->onSourceLoaded(source); observer->onUpdate(Update::Repaint); } void Style::Impl::onSourceChanged(Source& source) { sources.update(source); observer->onSourceChanged(source); observer->onUpdate(Update::Repaint); } void Style::Impl::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::Impl::onSourceDescriptionChanged(Source& source) { sources.update(source); observer->onSourceDescriptionChanged(source); if (!source.loaded) { source.loadDescription(fileSource); } } void Style::Impl::onSpriteLoaded(std::vector>&& images_) { for (auto& image : images_) { addImage(std::move(image)); } spriteLoaded = true; observer->onUpdate(Update::Repaint); // For *-pattern properties. } void Style::Impl::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::Impl::onLayerChanged(Layer& layer) { layers.update(layer); observer->onUpdate(Update::Repaint); } void Style::Impl::onLightChanged(const Light&) { observer->onUpdate(Update::Repaint); } void Style::Impl::dumpDebugLogs() const { Log::Info(Event::General, "styleURL: %s", url.c_str()); for (const auto& source : sources) { source->dumpDebugLogs(); } } const std::string& Style::Impl::getGlyphURL() const { return glyphURL; } Immutable>> Style::Impl::getImageImpls() const { return images.getImpls(); } Immutable>> Style::Impl::getSourceImpls() const { return sources.getImpls(); } Immutable>> Style::Impl::getLayerImpls() const { return layers.getImpls(); } } // namespace style } // namespace mbgl