From 900568cfb0b84a298395f4d84488fd9323552c63 Mon Sep 17 00:00:00 2001 From: John Firebaugh Date: Tue, 14 Jun 2016 16:07:21 -0700 Subject: [core] Runtime source API: private impls --- include/mbgl/style/source.hpp | 63 +++++ include/mbgl/style/sources/geojson_source.hpp | 27 ++ include/mbgl/style/sources/raster_source.hpp | 20 ++ include/mbgl/style/sources/vector_source.hpp | 20 ++ include/mbgl/util/tileset.hpp | 20 ++ platform/default/mbgl/storage/offline_download.cpp | 28 +-- src/mbgl/annotation/annotation_manager.cpp | 2 +- src/mbgl/annotation/annotation_source.cpp | 18 +- src/mbgl/annotation/annotation_source.hpp | 8 + src/mbgl/annotation/annotation_tile.cpp | 3 +- src/mbgl/annotation/annotation_tile.hpp | 3 +- src/mbgl/renderer/painter.cpp | 5 +- src/mbgl/style/parser.cpp | 12 +- src/mbgl/style/source.cpp | 279 -------------------- src/mbgl/style/source.hpp | 98 -------- src/mbgl/style/source_impl.cpp | 280 +++++++++++++++++++++ src/mbgl/style/source_impl.hpp | 101 ++++++++ src/mbgl/style/sources/geojson_source.cpp | 113 --------- src/mbgl/style/sources/geojson_source.hpp | 48 ---- src/mbgl/style/sources/geojson_source_impl.cpp | 117 +++++++++ src/mbgl/style/sources/geojson_source_impl.hpp | 50 ++++ src/mbgl/style/sources/raster_source.cpp | 40 --- src/mbgl/style/sources/raster_source.hpp | 19 -- src/mbgl/style/sources/raster_source_impl.cpp | 40 +++ src/mbgl/style/sources/raster_source_impl.hpp | 20 ++ src/mbgl/style/sources/vector_source.cpp | 25 -- src/mbgl/style/sources/vector_source.hpp | 19 -- src/mbgl/style/sources/vector_source_impl.cpp | 25 ++ src/mbgl/style/sources/vector_source_impl.hpp | 20 ++ src/mbgl/style/style.cpp | 36 +-- src/mbgl/style/tile_source.cpp | 197 --------------- src/mbgl/style/tile_source.hpp | 52 ---- src/mbgl/style/tile_source_impl.cpp | 196 +++++++++++++++ src/mbgl/style/tile_source_impl.hpp | 51 ++++ src/mbgl/util/tileset.hpp | 20 -- test/style/source.cpp | 73 +++--- test/style/style.cpp | 10 +- test/style/tile_source.cpp | 10 +- 38 files changed, 1161 insertions(+), 1007 deletions(-) create mode 100644 include/mbgl/style/source.hpp create mode 100644 include/mbgl/style/sources/geojson_source.hpp create mode 100644 include/mbgl/style/sources/raster_source.hpp create mode 100644 include/mbgl/style/sources/vector_source.hpp create mode 100644 include/mbgl/util/tileset.hpp delete mode 100644 src/mbgl/style/source.cpp delete mode 100644 src/mbgl/style/source.hpp create mode 100644 src/mbgl/style/source_impl.cpp create mode 100644 src/mbgl/style/source_impl.hpp delete mode 100644 src/mbgl/style/sources/geojson_source.cpp delete mode 100644 src/mbgl/style/sources/geojson_source.hpp create mode 100644 src/mbgl/style/sources/geojson_source_impl.cpp create mode 100644 src/mbgl/style/sources/geojson_source_impl.hpp delete mode 100644 src/mbgl/style/sources/raster_source.cpp delete mode 100644 src/mbgl/style/sources/raster_source.hpp create mode 100644 src/mbgl/style/sources/raster_source_impl.cpp create mode 100644 src/mbgl/style/sources/raster_source_impl.hpp delete mode 100644 src/mbgl/style/sources/vector_source.cpp delete mode 100644 src/mbgl/style/sources/vector_source.hpp create mode 100644 src/mbgl/style/sources/vector_source_impl.cpp create mode 100644 src/mbgl/style/sources/vector_source_impl.hpp delete mode 100644 src/mbgl/style/tile_source.cpp delete mode 100644 src/mbgl/style/tile_source.hpp create mode 100644 src/mbgl/style/tile_source_impl.cpp create mode 100644 src/mbgl/style/tile_source_impl.hpp delete mode 100644 src/mbgl/util/tileset.hpp diff --git a/include/mbgl/style/source.hpp b/include/mbgl/style/source.hpp new file mode 100644 index 0000000000..92341066b1 --- /dev/null +++ b/include/mbgl/style/source.hpp @@ -0,0 +1,63 @@ +#pragma once + +#include +#include + +#include +#include + +namespace mbgl { +namespace style { + +/** + * The runtime representation of a [source](https://www.mapbox.com/mapbox-gl-style-spec/#sources) from the Mapbox Style + * Specification. + * + * `Source` is an abstract base class; concrete derived classes are provided for each source type. `Source` contains + * functionality that is common to all layer types: + * + * * Runtime type information: type predicates and casting + * * Accessors for properties common to all source types: ID, etc. + * * Cloning and copying + * + * All other functionality lives in the derived classes. To instantiate a source, create an instance of the desired + * type, passing the ID: + * + * auto vectorSource = std::make_unique("my-vector-source"); + */ +class Source : public mbgl::util::noncopyable { +public: + virtual ~Source(); + + // Check whether this source is of the given subtype. + template + bool is() const; + + // Dynamically cast this source to the given subtype. + template + T* as() { + return is() ? reinterpret_cast(this) : nullptr; + } + + template + const T* as() const { + return is() ? reinterpret_cast(this) : nullptr; + } + + const std::string& getID() const; + + // Create a new source with the specified `id`. All other properties + // are copied from this source. + std::unique_ptr copy(const std::string& id) const; + + // Private implementation + class Impl; + const std::unique_ptr baseImpl; + +protected: + const SourceType type; + Source(SourceType, std::unique_ptr); +}; + +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/style/sources/geojson_source.hpp b/include/mbgl/style/sources/geojson_source.hpp new file mode 100644 index 0000000000..96b9a99606 --- /dev/null +++ b/include/mbgl/style/sources/geojson_source.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include + +namespace mbgl { +namespace style { + +class GeoJSONSource : public Source { +public: + // Future public API: + // void setData(FeatureCollection&&); + // void setJSON(const std::string& json); + // void loadData(const std::string& url); + + + // Private implementation + + class Impl; + + template + GeoJSONSource(Fn&& fn) + : Source(SourceType::GeoJSON, fn(*this)) { + } +}; + +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/style/sources/raster_source.hpp b/include/mbgl/style/sources/raster_source.hpp new file mode 100644 index 0000000000..2d1d648eec --- /dev/null +++ b/include/mbgl/style/sources/raster_source.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include +#include +#include + +namespace mbgl { +namespace style { + +class RasterSource : public Source { +public: + RasterSource(std::string id, variant urlOrTileset, uint16_t tileSize); + + // Private implementation + + class Impl; +}; + +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/style/sources/vector_source.hpp b/include/mbgl/style/sources/vector_source.hpp new file mode 100644 index 0000000000..3d53362734 --- /dev/null +++ b/include/mbgl/style/sources/vector_source.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include +#include +#include + +namespace mbgl { +namespace style { + +class VectorSource : public Source { +public: + VectorSource(std::string id, variant urlOrTileset); + + // Private implementation + + class Impl; +}; + +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/util/tileset.hpp b/include/mbgl/util/tileset.hpp new file mode 100644 index 0000000000..8a7fbe9b73 --- /dev/null +++ b/include/mbgl/util/tileset.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include + +#include +#include +#include + +namespace mbgl { + +class Tileset { +public: + std::vector tiles; + Range zoomRange { 0, 22 }; + std::string attribution; + + // TileJSON also includes center, zoom, and bounds, but they are not used by mbgl. +}; + +} // namespace mbgl diff --git a/platform/default/mbgl/storage/offline_download.cpp b/platform/default/mbgl/storage/offline_download.cpp index 3f21b8571e..2aaeb30fab 100644 --- a/platform/default/mbgl/storage/offline_download.cpp +++ b/platform/default/mbgl/storage/offline_download.cpp @@ -4,8 +4,8 @@ #include #include #include -#include -#include +#include +#include #include #include #include @@ -103,21 +103,21 @@ OfflineRegionStatus OfflineDownload::getStatus() const { result.requiredResourceCountIsPrecise = true; for (const auto& source : parser.sources) { - switch (source->type) { + switch (source->baseImpl->type) { case SourceType::Vector: case SourceType::Raster: { - style::TileSource* tileSource = static_cast(source.get()); + style::TileSourceImpl* tileSource = static_cast(source->baseImpl.get()); const variant& urlOrTileset = tileSource->getURLOrTileset(); if (urlOrTileset.is()) { - result.requiredResourceCount += tileResources(source->type, tileSource->getTileSize(), urlOrTileset.get()).size(); + result.requiredResourceCount += tileResources(source->baseImpl->type, tileSource->getTileSize(), urlOrTileset.get()).size(); } else { result.requiredResourceCount += 1; const std::string& url = urlOrTileset.get(); optional sourceResponse = offlineDatabase.get(Resource::source(url)); if (sourceResponse) { - result.requiredResourceCount += tileResources(source->type, tileSource->getTileSize(), - style::TileSource::parseTileJSON(*sourceResponse->data, url, source->type, tileSource->getTileSize())).size(); + result.requiredResourceCount += tileResources(source->baseImpl->type, tileSource->getTileSize(), + style::TileSourceImpl::parseTileJSON(*sourceResponse->data, url, source->baseImpl->type, tileSource->getTileSize())).size(); } else { result.requiredResourceCountIsPrecise = false; } @@ -126,8 +126,8 @@ OfflineRegionStatus OfflineDownload::getStatus() const { } case SourceType::GeoJSON: { - style::GeoJSONSource* geojsonSource = static_cast(source.get()); - const variant& urlOrGeoJSON = geojsonSource->getURLOrGeoJSON(); + style::GeoJSONSource::Impl* geojsonSource = static_cast(source->baseImpl.get()); + const variant& urlOrGeoJSON = geojsonSource->getURLOrGeoJSON(); if (urlOrGeoJSON.is()) { result.requiredResourceCount += 1; @@ -160,12 +160,12 @@ void OfflineDownload::activateDownload() { parser.parse(*styleResponse.data); for (const auto& source : parser.sources) { - SourceType type = source->type; + SourceType type = source->baseImpl->type; switch (type) { case SourceType::Vector: case SourceType::Raster: { - const style::TileSource* tileSource = static_cast(source.get()); + const style::TileSourceImpl* tileSource = static_cast(source->baseImpl.get()); const variant& urlOrTileset = tileSource->getURLOrTileset(); const uint16_t tileSize = tileSource->getTileSize(); @@ -177,7 +177,7 @@ void OfflineDownload::activateDownload() { requiredSourceURLs.insert(url); ensureResource(Resource::source(url), [=] (Response sourceResponse) { - ensureTiles(type, tileSize, style::TileSource::parseTileJSON(*sourceResponse.data, url, type, tileSize)); + ensureTiles(type, tileSize, style::TileSourceImpl::parseTileJSON(*sourceResponse.data, url, type, tileSize)); requiredSourceURLs.erase(url); if (requiredSourceURLs.empty()) { @@ -189,8 +189,8 @@ void OfflineDownload::activateDownload() { } case SourceType::GeoJSON: { - style::GeoJSONSource* geojsonSource = static_cast(source.get()); - const variant& urlOrGeoJSON = geojsonSource->getURLOrGeoJSON(); + style::GeoJSONSource::Impl* geojsonSource = static_cast(source->baseImpl.get()); + const variant& urlOrGeoJSON = geojsonSource->getURLOrGeoJSON(); if (urlOrGeoJSON.is()) { ensureResource(Resource::source(urlOrGeoJSON.get())); diff --git a/src/mbgl/annotation/annotation_manager.cpp b/src/mbgl/annotation/annotation_manager.cpp index b3edbf857d..e3c6cc56d4 100644 --- a/src/mbgl/annotation/annotation_manager.cpp +++ b/src/mbgl/annotation/annotation_manager.cpp @@ -108,7 +108,7 @@ void AnnotationManager::updateStyle(Style& style) { // Create annotation source, point layer, and point bucket if (!style.getSource(SourceID)) { std::unique_ptr source = std::make_unique(); - source->enabled = true; + source->baseImpl->enabled = true; style.addSource(std::move(source)); std::unique_ptr layer = std::make_unique(PointLayerID); diff --git a/src/mbgl/annotation/annotation_source.cpp b/src/mbgl/annotation/annotation_source.cpp index 2c87c4f207..61fc4ca2e4 100644 --- a/src/mbgl/annotation/annotation_source.cpp +++ b/src/mbgl/annotation/annotation_source.cpp @@ -4,21 +4,27 @@ namespace mbgl { +using namespace style; + AnnotationSource::AnnotationSource() - : Source(SourceType::Annotations, AnnotationManager::SourceID) { + : Source(SourceType::Annotations, std::make_unique(*this)) { +} + +AnnotationSource::Impl::Impl(Source& base_) + : Source::Impl(SourceType::Annotations, AnnotationManager::SourceID, base_) { } -Range AnnotationSource::getZoomRange() { +Range AnnotationSource::Impl::getZoomRange() { return { 0, 22 }; } -void AnnotationSource::load(FileSource&) { +void AnnotationSource::Impl::load(FileSource&) { loaded = true; } -std::unique_ptr AnnotationSource::createTile(const OverscaledTileID& tileID, - const style::UpdateParameters& parameters) { - return std::make_unique(tileID, id, parameters); +std::unique_ptr AnnotationSource::Impl::createTile(const OverscaledTileID& tileID, + const style::UpdateParameters& parameters) { + return std::make_unique(tileID, parameters); } } // namespace mbgl diff --git a/src/mbgl/annotation/annotation_source.hpp b/src/mbgl/annotation/annotation_source.hpp index e8d3b43e09..db9221788f 100644 --- a/src/mbgl/annotation/annotation_source.hpp +++ b/src/mbgl/annotation/annotation_source.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include namespace mbgl { @@ -8,6 +9,13 @@ class AnnotationSource : public style::Source { public: AnnotationSource(); + class Impl; +}; + +class AnnotationSource::Impl : public style::Source::Impl { +public: + Impl(Source&); + void load(FileSource&) final; private: diff --git a/src/mbgl/annotation/annotation_tile.cpp b/src/mbgl/annotation/annotation_tile.cpp index 79e281acce..91b7f7ddc1 100644 --- a/src/mbgl/annotation/annotation_tile.cpp +++ b/src/mbgl/annotation/annotation_tile.cpp @@ -9,9 +9,8 @@ namespace mbgl { AnnotationTile::AnnotationTile(const OverscaledTileID& overscaledTileID, - std::string sourceID, const style::UpdateParameters& parameters) - : GeometryTile(overscaledTileID, sourceID, parameters.style, parameters.mode), + : GeometryTile(overscaledTileID, AnnotationManager::SourceID, parameters.style, parameters.mode), annotationManager(parameters.annotationManager) { annotationManager.addTile(*this); } diff --git a/src/mbgl/annotation/annotation_tile.hpp b/src/mbgl/annotation/annotation_tile.hpp index 290df3537e..3e7c2c447f 100644 --- a/src/mbgl/annotation/annotation_tile.hpp +++ b/src/mbgl/annotation/annotation_tile.hpp @@ -14,8 +14,7 @@ class UpdateParameters; class AnnotationTile : public GeometryTile { public: AnnotationTile(const OverscaledTileID&, - std::string sourceID, - const style::UpdateParameters&); + const style::UpdateParameters&); ~AnnotationTile() override; void setNecessity(Necessity) final; diff --git a/src/mbgl/renderer/painter.cpp b/src/mbgl/renderer/painter.cpp index eee568ec92..ca536f6671 100644 --- a/src/mbgl/renderer/painter.cpp +++ b/src/mbgl/renderer/painter.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -161,7 +162,7 @@ void Painter::render(const Style& style, const FrameData& frame_, SpriteAtlas& a // Update all clipping IDs. algorithm::ClipIDGenerator generator; for (const auto& source : sources) { - source->startRender(generator, projMatrix, state); + source->baseImpl->startRender(generator, projMatrix, state); } drawClippingMasks(generator.getStencils()); @@ -202,7 +203,7 @@ void Painter::render(const Style& style, const FrameData& frame_, SpriteAtlas& a // When only rendering layers via the stylesheet, it's possible that we don't // ever visit a tile during rendering. for (const auto& source : sources) { - source->finishRender(*this); + source->baseImpl->finishRender(*this); } } diff --git a/src/mbgl/style/parser.cpp b/src/mbgl/style/parser.cpp index 09689f22f1..34f01c14c1 100644 --- a/src/mbgl/style/parser.cpp +++ b/src/mbgl/style/parser.cpp @@ -1,7 +1,7 @@ #include -#include -#include -#include +#include +#include +#include #include #include #include @@ -99,16 +99,16 @@ void Parser::parseSources(const JSValue& value) { switch (*type) { case SourceType::Raster: { - source = RasterSource::parse(id, sourceVal); + source = RasterSource::Impl::parse(id, sourceVal); break; } case SourceType::Vector: - source = VectorSource::parse(id, sourceVal); + source = VectorSource::Impl::parse(id, sourceVal); break; case SourceType::GeoJSON: - source = GeoJSONSource::parse(id, sourceVal); + source = GeoJSONSource::Impl::parse(id, sourceVal); break; default: diff --git a/src/mbgl/style/source.cpp b/src/mbgl/style/source.cpp deleted file mode 100644 index 5f4949c94c..0000000000 --- a/src/mbgl/style/source.cpp +++ /dev/null @@ -1,279 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -#include - -namespace mbgl { -namespace style { - -static SourceObserver nullObserver; - -Source::Source(SourceType type_, std::string id_) - : type(type_), - id(std::move(id_)), - observer(&nullObserver) { -} - -Source::~Source() = default; - -bool Source::isLoaded() const { - if (!loaded) return false; - - for (const auto& pair : tiles) { - if (!pair.second->isComplete()) { - return false; - } - } - - return true; -} - -void Source::invalidateTiles() { - tiles.clear(); - renderTiles.clear(); - cache.clear(); -} - -void Source::startRender(algorithm::ClipIDGenerator& generator, - const mat4& projMatrix, - const TransformState& transform) { - if (type == SourceType::Vector || - type == SourceType::GeoJSON || - type == SourceType::Annotations) { - generator.update(renderTiles); - } - - for (auto& pair : renderTiles) { - auto& tile = pair.second; - transform.matrixFor(tile.matrix, tile.id); - matrix::multiply(tile.matrix, projMatrix, tile.matrix); - } -} - -void Source::finishRender(Painter& painter) { - for (auto& pair : renderTiles) { - auto& tile = pair.second; - painter.renderTileDebug(tile); - } -} - -const std::map& Source::getRenderTiles() const { - return renderTiles; -} - -Tile* Source::getTile(const OverscaledTileID& overscaledTileID) const { - auto it = tiles.find(overscaledTileID); - if (it != tiles.end()) { - return it->second.get(); - } else { - return nullptr; - } -} - -bool Source::update(const UpdateParameters& parameters) { - bool allTilesUpdated = true; - - if (!loaded || parameters.animationTime <= updated) { - return allTilesUpdated; - } - - const uint16_t tileSize = getTileSize(); - const Range zoomRange = getZoomRange(); - - // Determine the overzooming/underzooming amounts and required tiles. - int32_t overscaledZoom = util::coveringZoomLevel(parameters.transformState.getZoom(), type, tileSize); - int32_t dataTileZoom = overscaledZoom; - - std::vector idealTiles; - if (overscaledZoom >= zoomRange.min) { - int32_t idealZoom = std::min(zoomRange.max, overscaledZoom); - - // Make sure we're not reparsing overzoomed raster tiles. - if (type == SourceType::Raster) { - dataTileZoom = idealZoom; - } - - idealTiles = util::tileCover(parameters.transformState, idealZoom); - } - - // Stores a list of all the data tiles that we're definitely going to retain. There are two - // kinds of tiles we need: the ideal tiles determined by the tile cover. They may not yet be in - // use because they're still loading. In addition to that, we also need to retain all tiles that - // we're actively using, e.g. as a replacement for tile that aren't loaded yet. - std::set retain; - - auto retainTileFn = [&retain](Tile& tile, bool required) -> void { - retain.emplace(tile.id); - tile.setNecessity(required ? Tile::Necessity::Required - : Tile::Necessity::Optional); - }; - auto getTileFn = [this](const OverscaledTileID& dataTileID) -> Tile* { - return getTile(dataTileID); - }; - auto createTileFn = [this, ¶meters](const OverscaledTileID& dataTileID) -> Tile* { - std::unique_ptr data = cache.get(dataTileID); - if (!data) { - data = createTile(dataTileID, parameters); - if (data) { - data->setObserver(this); - } - } - if (data) { - return tiles.emplace(dataTileID, std::move(data)).first->second.get(); - } else { - return nullptr; - } - }; - auto renderTileFn = [this](const UnwrappedTileID& renderTileID, Tile& tile) { - renderTiles.emplace(renderTileID, RenderTile{ renderTileID, tile }); - }; - - renderTiles.clear(); - algorithm::updateRenderables(getTileFn, createTileFn, retainTileFn, renderTileFn, - idealTiles, zoomRange, dataTileZoom); - - if (type != SourceType::Raster && type != SourceType::Annotations && cache.getSize() == 0) { - size_t conservativeCacheSize = - ((float)parameters.transformState.getWidth() / util::tileSize) * - ((float)parameters.transformState.getHeight() / util::tileSize) * - (parameters.transformState.getMaxZoom() - parameters.transformState.getMinZoom() + 1) * - 0.5; - cache.setSize(conservativeCacheSize); - } - - // Remove stale data tiles from the active set of tiles. - // This goes through the (sorted!) tiles and retain set in lockstep and removes items from - // tiles that don't have the corresponding key in the retain set. - auto dataIt = tiles.begin(); - auto retainIt = retain.begin(); - while (dataIt != tiles.end()) { - if (retainIt == retain.end() || dataIt->first < *retainIt) { - dataIt->second->setNecessity(Tile::Necessity::Optional); - cache.add(dataIt->first, std::move(dataIt->second)); - tiles.erase(dataIt++); - } else { - if (!(*retainIt < dataIt->first)) { - ++dataIt; - } - ++retainIt; - } - } - - const PlacementConfig newConfig{ parameters.transformState.getAngle(), - parameters.transformState.getPitch(), - parameters.debugOptions & MapDebugOptions::Collision }; - for (auto& pair : tiles) { - auto tile = pair.second.get(); - if (parameters.shouldReparsePartialTiles && tile->isIncomplete()) { - if (!tile->parsePending()) { - allTilesUpdated = false; - } - } else { - tile->redoPlacement(newConfig); - } - } - - updated = parameters.animationTime; - - return allTilesUpdated; -} - -static Point coordinateToTilePoint(const UnwrappedTileID& tileID, const Point& p) { - auto zoomedCoord = TileCoordinate { p, 0 }.zoomTo(tileID.canonical.z); - return { - int16_t(util::clamp((zoomedCoord.p.x - tileID.canonical.x - tileID.wrap * std::pow(2, tileID.canonical.z)) * util::EXTENT, - std::numeric_limits::min(), - std::numeric_limits::max())), - int16_t(util::clamp((zoomedCoord.p.y - tileID.canonical.y) * util::EXTENT, - std::numeric_limits::min(), - std::numeric_limits::max())) - }; -} - -std::unordered_map> Source::queryRenderedFeatures(const QueryParameters& parameters) const { - LineString queryGeometry; - - for (const auto& p : parameters.geometry) { - queryGeometry.push_back(TileCoordinate::fromScreenCoordinate( - parameters.transformState, 0, { p.x, parameters.transformState.getHeight() - p.y }).p); - } - - mapbox::geometry::box box = mapbox::geometry::envelope(queryGeometry); - - std::unordered_map> result; - - for (const auto& tilePtr : renderTiles) { - const RenderTile& tile = tilePtr.second; - - Point tileSpaceBoundsMin = coordinateToTilePoint(tile.id, box.min); - Point tileSpaceBoundsMax = coordinateToTilePoint(tile.id, box.max); - - if (tileSpaceBoundsMin.x >= util::EXTENT || tileSpaceBoundsMin.y >= util::EXTENT || - tileSpaceBoundsMax.x < 0 || tileSpaceBoundsMax.y < 0) continue; - - GeometryCoordinates tileSpaceQueryGeometry; - - for (const auto& c : queryGeometry) { - tileSpaceQueryGeometry.push_back(coordinateToTilePoint(tile.id, c)); - } - - tile.tile.queryRenderedFeatures(result, - tileSpaceQueryGeometry, - parameters.transformState, - parameters.layerIDs); - } - - return result; -} - -void Source::setCacheSize(size_t size) { - cache.setSize(size); -} - -void Source::onLowMemory() { - cache.clear(); -} - -void Source::setObserver(SourceObserver* observer_) { - observer = observer_; -} - -void Source::onTileLoaded(Tile& tile, bool isNewTile) { - observer->onTileLoaded(*this, tile.id, isNewTile); -} - -void Source::onTileError(Tile& tile, std::exception_ptr error) { - observer->onTileError(*this, tile.id, error); -} - -void Source::onNeedsRepaint() { - observer->onNeedsRepaint(); -} - -void Source::dumpDebugLogs() const { - Log::Info(Event::General, "Source::id: %s", id.c_str()); - Log::Info(Event::General, "Source::loaded: %d", loaded); - - for (const auto& pair : tiles) { - auto& tile = pair.second; - tile->dumpDebugLogs(); - } -} - -} // namespace style -} // namespace mbgl diff --git a/src/mbgl/style/source.hpp b/src/mbgl/style/source.hpp deleted file mode 100644 index 5c1a813562..0000000000 --- a/src/mbgl/style/source.hpp +++ /dev/null @@ -1,98 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include - -namespace mbgl { - -class Painter; -class FileSource; -class TransformState; -class RenderTile; - -namespace algorithm { -class ClipIDGenerator; -} // namespace algorithm - -namespace style { - -class UpdateParameters; -class QueryParameters; -class SourceObserver; - -class Source : public TileObserver, private util::noncopyable { -public: - Source(SourceType, std::string id); - ~Source() override; - - virtual void load(FileSource&) = 0; - bool isLoaded() const; - - // Request or parse all the tiles relevant for the "TransformState". This method - // will return true if all the tiles were scheduled for updating of false if - // they were not. shouldReparsePartialTiles must be set to "true" if there is - // new data available that a tile in the "partial" state might be interested at. - bool update(const UpdateParameters&); - - void startRender(algorithm::ClipIDGenerator&, - const mat4& projMatrix, - const TransformState&); - void finishRender(Painter&); - - const std::map& getRenderTiles() const; - - Tile* getTile(const OverscaledTileID&) const; - - std::unordered_map> - queryRenderedFeatures(const QueryParameters&) const; - - void setCacheSize(size_t); - void onLowMemory(); - - void setObserver(SourceObserver*); - void dumpDebugLogs() const; - - const SourceType type; - const std::string id; - - bool loaded = false; - bool enabled = false; - -protected: - void invalidateTiles(); - - SourceObserver* observer = nullptr; - -private: - // TileObserver implementation. - void onTileLoaded(Tile&, bool isNewTile) override; - void onTileError(Tile&, std::exception_ptr) override; - void onNeedsRepaint() override; - - virtual uint16_t getTileSize() const = 0; - virtual Range getZoomRange() = 0; - virtual std::unique_ptr createTile(const OverscaledTileID&, const UpdateParameters&) = 0; - - // Stores the time when this source was most recently updated. - TimePoint updated = TimePoint::min(); - - std::map> tiles; - std::map renderTiles; - TileCache cache; -}; - -} // namespace style -} // namespace mbgl diff --git a/src/mbgl/style/source_impl.cpp b/src/mbgl/style/source_impl.cpp new file mode 100644 index 0000000000..4223f2b50d --- /dev/null +++ b/src/mbgl/style/source_impl.cpp @@ -0,0 +1,280 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include + +namespace mbgl { +namespace style { + +static SourceObserver nullObserver; + +Source::Impl::Impl(SourceType type_, std::string id_, Source& base_) + : type(type_), + id(std::move(id_)), + base(base_), + observer(&nullObserver) { +} + +Source::Impl::~Impl() = default; + +bool Source::Impl::isLoaded() const { + if (!loaded) return false; + + for (const auto& pair : tiles) { + if (!pair.second->isComplete()) { + return false; + } + } + + return true; +} + +void Source::Impl::invalidateTiles() { + tiles.clear(); + renderTiles.clear(); + cache.clear(); +} + +void Source::Impl::startRender(algorithm::ClipIDGenerator& generator, + const mat4& projMatrix, + const TransformState& transform) { + if (type == SourceType::Vector || + type == SourceType::GeoJSON || + type == SourceType::Annotations) { + generator.update(renderTiles); + } + + for (auto& pair : renderTiles) { + auto& tile = pair.second; + transform.matrixFor(tile.matrix, tile.id); + matrix::multiply(tile.matrix, projMatrix, tile.matrix); + } +} + +void Source::Impl::finishRender(Painter& painter) { + for (auto& pair : renderTiles) { + auto& tile = pair.second; + painter.renderTileDebug(tile); + } +} + +const std::map& Source::Impl::getRenderTiles() const { + return renderTiles; +} + +Tile* Source::Impl::getTile(const OverscaledTileID& overscaledTileID) const { + auto it = tiles.find(overscaledTileID); + if (it != tiles.end()) { + return it->second.get(); + } else { + return nullptr; + } +} + +bool Source::Impl::update(const UpdateParameters& parameters) { + bool allTilesUpdated = true; + + if (!loaded || parameters.animationTime <= updated) { + return allTilesUpdated; + } + + const uint16_t tileSize = getTileSize(); + const Range zoomRange = getZoomRange(); + + // Determine the overzooming/underzooming amounts and required tiles. + int32_t overscaledZoom = util::coveringZoomLevel(parameters.transformState.getZoom(), type, tileSize); + int32_t dataTileZoom = overscaledZoom; + + std::vector idealTiles; + if (overscaledZoom >= zoomRange.min) { + int32_t idealZoom = std::min(zoomRange.max, overscaledZoom); + + // Make sure we're not reparsing overzoomed raster tiles. + if (type == SourceType::Raster) { + dataTileZoom = idealZoom; + } + + idealTiles = util::tileCover(parameters.transformState, idealZoom); + } + + // Stores a list of all the data tiles that we're definitely going to retain. There are two + // kinds of tiles we need: the ideal tiles determined by the tile cover. They may not yet be in + // use because they're still loading. In addition to that, we also need to retain all tiles that + // we're actively using, e.g. as a replacement for tile that aren't loaded yet. + std::set retain; + + auto retainTileFn = [&retain](Tile& tile, bool required) -> void { + retain.emplace(tile.id); + tile.setNecessity(required ? Tile::Necessity::Required + : Tile::Necessity::Optional); + }; + auto getTileFn = [this](const OverscaledTileID& dataTileID) -> Tile* { + return getTile(dataTileID); + }; + auto createTileFn = [this, ¶meters](const OverscaledTileID& dataTileID) -> Tile* { + std::unique_ptr data = cache.get(dataTileID); + if (!data) { + data = createTile(dataTileID, parameters); + if (data) { + data->setObserver(this); + } + } + if (data) { + return tiles.emplace(dataTileID, std::move(data)).first->second.get(); + } else { + return nullptr; + } + }; + auto renderTileFn = [this](const UnwrappedTileID& renderTileID, Tile& tile) { + renderTiles.emplace(renderTileID, RenderTile{ renderTileID, tile }); + }; + + renderTiles.clear(); + algorithm::updateRenderables(getTileFn, createTileFn, retainTileFn, renderTileFn, + idealTiles, zoomRange, dataTileZoom); + + if (type != SourceType::Raster && type != SourceType::Annotations && cache.getSize() == 0) { + size_t conservativeCacheSize = + ((float)parameters.transformState.getWidth() / util::tileSize) * + ((float)parameters.transformState.getHeight() / util::tileSize) * + (parameters.transformState.getMaxZoom() - parameters.transformState.getMinZoom() + 1) * + 0.5; + cache.setSize(conservativeCacheSize); + } + + // Remove stale data tiles from the active set of tiles. + // This goes through the (sorted!) tiles and retain set in lockstep and removes items from + // tiles that don't have the corresponding key in the retain set. + auto dataIt = tiles.begin(); + auto retainIt = retain.begin(); + while (dataIt != tiles.end()) { + if (retainIt == retain.end() || dataIt->first < *retainIt) { + dataIt->second->setNecessity(Tile::Necessity::Optional); + cache.add(dataIt->first, std::move(dataIt->second)); + tiles.erase(dataIt++); + } else { + if (!(*retainIt < dataIt->first)) { + ++dataIt; + } + ++retainIt; + } + } + + const PlacementConfig newConfig{ parameters.transformState.getAngle(), + parameters.transformState.getPitch(), + parameters.debugOptions & MapDebugOptions::Collision }; + for (auto& pair : tiles) { + auto tile = pair.second.get(); + if (parameters.shouldReparsePartialTiles && tile->isIncomplete()) { + if (!tile->parsePending()) { + allTilesUpdated = false; + } + } else { + tile->redoPlacement(newConfig); + } + } + + updated = parameters.animationTime; + + return allTilesUpdated; +} + +static Point coordinateToTilePoint(const UnwrappedTileID& tileID, const Point& p) { + auto zoomedCoord = TileCoordinate { p, 0 }.zoomTo(tileID.canonical.z); + return { + int16_t(util::clamp((zoomedCoord.p.x - tileID.canonical.x - tileID.wrap * std::pow(2, tileID.canonical.z)) * util::EXTENT, + std::numeric_limits::min(), + std::numeric_limits::max())), + int16_t(util::clamp((zoomedCoord.p.y - tileID.canonical.y) * util::EXTENT, + std::numeric_limits::min(), + std::numeric_limits::max())) + }; +} + +std::unordered_map> Source::Impl::queryRenderedFeatures(const QueryParameters& parameters) const { + LineString queryGeometry; + + for (const auto& p : parameters.geometry) { + queryGeometry.push_back(TileCoordinate::fromScreenCoordinate( + parameters.transformState, 0, { p.x, parameters.transformState.getHeight() - p.y }).p); + } + + mapbox::geometry::box box = mapbox::geometry::envelope(queryGeometry); + + std::unordered_map> result; + + for (const auto& tilePtr : renderTiles) { + const RenderTile& tile = tilePtr.second; + + Point tileSpaceBoundsMin = coordinateToTilePoint(tile.id, box.min); + Point tileSpaceBoundsMax = coordinateToTilePoint(tile.id, box.max); + + if (tileSpaceBoundsMin.x >= util::EXTENT || tileSpaceBoundsMin.y >= util::EXTENT || + tileSpaceBoundsMax.x < 0 || tileSpaceBoundsMax.y < 0) continue; + + GeometryCoordinates tileSpaceQueryGeometry; + + for (const auto& c : queryGeometry) { + tileSpaceQueryGeometry.push_back(coordinateToTilePoint(tile.id, c)); + } + + tile.tile.queryRenderedFeatures(result, + tileSpaceQueryGeometry, + parameters.transformState, + parameters.layerIDs); + } + + return result; +} + +void Source::Impl::setCacheSize(size_t size) { + cache.setSize(size); +} + +void Source::Impl::onLowMemory() { + cache.clear(); +} + +void Source::Impl::setObserver(SourceObserver* observer_) { + observer = observer_; +} + +void Source::Impl::onTileLoaded(Tile& tile, bool isNewTile) { + observer->onTileLoaded(base, tile.id, isNewTile); +} + +void Source::Impl::onTileError(Tile& tile, std::exception_ptr error) { + observer->onTileError(base, tile.id, error); +} + +void Source::Impl::onNeedsRepaint() { + observer->onNeedsRepaint(); +} + +void Source::Impl::dumpDebugLogs() const { + Log::Info(Event::General, "Source::id: %s", base.getID().c_str()); + Log::Info(Event::General, "Source::loaded: %d", loaded); + + for (const auto& pair : tiles) { + auto& tile = pair.second; + tile->dumpDebugLogs(); + } +} + +} // namespace style +} // namespace mbgl diff --git a/src/mbgl/style/source_impl.hpp b/src/mbgl/style/source_impl.hpp new file mode 100644 index 0000000000..64651d7e3f --- /dev/null +++ b/src/mbgl/style/source_impl.hpp @@ -0,0 +1,101 @@ +#pragma once + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace mbgl { + +class Painter; +class FileSource; +class TransformState; +class RenderTile; + +namespace algorithm { +class ClipIDGenerator; +} // namespace algorithm + +namespace style { + +class UpdateParameters; +class QueryParameters; +class SourceObserver; + +class Source::Impl : public TileObserver, private util::noncopyable { +public: + Impl(SourceType, std::string id, Source&); + ~Impl() override; + + virtual void load(FileSource&) = 0; + bool isLoaded() const; + + // Request or parse all the tiles relevant for the "TransformState". This method + // will return true if all the tiles were scheduled for updating of false if + // they were not. shouldReparsePartialTiles must be set to "true" if there is + // new data available that a tile in the "partial" state might be interested at. + bool update(const UpdateParameters&); + + void startRender(algorithm::ClipIDGenerator&, + const mat4& projMatrix, + const TransformState&); + void finishRender(Painter&); + + const std::map& getRenderTiles() const; + + Tile* getTile(const OverscaledTileID&) const; + + std::unordered_map> + queryRenderedFeatures(const QueryParameters&) const; + + void setCacheSize(size_t); + void onLowMemory(); + + void setObserver(SourceObserver*); + void dumpDebugLogs() const; + + const SourceType type; + const std::string id; + + bool loaded = false; + bool enabled = false; + +protected: + void invalidateTiles(); + + Source& base; + SourceObserver* observer = nullptr; + +private: + // TileObserver implementation. + void onTileLoaded(Tile&, bool isNewTile) override; + void onTileError(Tile&, std::exception_ptr) override; + void onNeedsRepaint() override; + + virtual uint16_t getTileSize() const = 0; + virtual Range getZoomRange() = 0; + virtual std::unique_ptr createTile(const OverscaledTileID&, const UpdateParameters&) = 0; + + // Stores the time when this source was most recently updated. + TimePoint updated = TimePoint::min(); + + std::map> tiles; + std::map renderTiles; + TileCache cache; +}; + +} // namespace style +} // namespace mbgl diff --git a/src/mbgl/style/sources/geojson_source.cpp b/src/mbgl/style/sources/geojson_source.cpp deleted file mode 100644 index e06b00ec99..0000000000 --- a/src/mbgl/style/sources/geojson_source.cpp +++ /dev/null @@ -1,113 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - -#include - -namespace mbgl { -namespace style { - -std::unique_ptr GeoJSONSource::parseGeoJSON(const JSValue& value) { - using namespace mapbox::geojsonvt; - - Options options; - options.buffer = util::EXTENT / util::tileSize * 128; - options.extent = util::EXTENT; - - try { - return std::make_unique(Convert::convert(value, 0), options); - } catch (const std::exception& ex) { - Log::Error(Event::ParseStyle, "Failed to parse GeoJSON data: %s", ex.what()); - // Create an empty GeoJSON VT object to make sure we're not infinitely waiting for - // tiles to load. - return std::make_unique(std::vector{}, options); - } -} - -std::unique_ptr GeoJSONSource::parse(const std::string& id, const JSValue& value) { - // We should probably split this up to have URLs in the url property, and actual data - // in the data property. Until then, we're going to detect the content based on the - // object type. - if (!value.HasMember("data")) { - Log::Error(Event::ParseStyle, "GeoJSON source must have a data value"); - return nullptr; - } - - const JSValue& dataVal = value["data"]; - if (dataVal.IsString()) { - return std::make_unique(id, std::string(dataVal.GetString(), dataVal.GetStringLength())); - } else if (dataVal.IsObject()) { - return std::make_unique(id, parseGeoJSON(dataVal)); - } else { - Log::Error(Event::ParseStyle, "GeoJSON data must be a URL or an object"); - return nullptr; - } -} - -GeoJSONSource::GeoJSONSource(std::string id_, variant urlOrGeoJSON_) - : Source(SourceType::GeoJSON, std::move(id_)), - urlOrGeoJSON(std::move(urlOrGeoJSON_)) { -} - -GeoJSONSource::~GeoJSONSource() = default; - -void GeoJSONSource::load(FileSource& fileSource) { - if (urlOrGeoJSON.is()) { - loaded = true; - return; - } - - if (req) { - return; - } - - const std::string& url = urlOrGeoJSON.get(); - req = fileSource.request(Resource::source(url), [this](Response res) { - if (res.error) { - observer->onSourceError(*this, std::make_exception_ptr(std::runtime_error(res.error->message))); - } else if (res.notModified) { - return; - } else if (res.noContent) { - observer->onSourceError(*this, std::make_exception_ptr(std::runtime_error("unexpectedly empty GeoJSON"))); - } else { - rapidjson::GenericDocument, rapidjson::CrtAllocator> d; - d.Parse<0>(res.data->c_str()); - - if (d.HasParseError()) { - std::stringstream message; - message << d.GetErrorOffset() << " - " << rapidjson::GetParseError_En(d.GetParseError()); - observer->onSourceError(*this, std::make_exception_ptr(std::runtime_error(message.str()))); - return; - } - - invalidateTiles(); - - urlOrGeoJSON = parseGeoJSON(d); - loaded = true; - - observer->onSourceLoaded(*this); - } - }); -} - -Range GeoJSONSource::getZoomRange() { - assert(loaded); - return { 0, urlOrGeoJSON.get()->options.maxZoom }; -} - -std::unique_ptr GeoJSONSource::createTile(const OverscaledTileID& tileID, - const UpdateParameters& parameters) { - assert(loaded); - return std::make_unique(tileID, id, parameters, *urlOrGeoJSON.get()); -} - -} // namespace style -} // namespace mbgl diff --git a/src/mbgl/style/sources/geojson_source.hpp b/src/mbgl/style/sources/geojson_source.hpp deleted file mode 100644 index 490dae48b8..0000000000 --- a/src/mbgl/style/sources/geojson_source.hpp +++ /dev/null @@ -1,48 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace mapbox { -namespace geojsonvt { -class GeoJSONVT; -} // namespace geojsonvt -} // namespace mapbox - -namespace mbgl { - -class AsyncRequest; - -namespace style { - -class GeoJSONSource : public Source { -public: - using GeoJSON = std::unique_ptr; - - static std::unique_ptr parse(const std::string& id, const JSValue&); - static GeoJSON parseGeoJSON(const JSValue&); - - GeoJSONSource(std::string id, variant urlOrGeoJSON); - ~GeoJSONSource() final; - - void load(FileSource&) final; - - uint16_t getTileSize() const final { - return util::tileSize; - } - - const variant& getURLOrGeoJSON() const { - return urlOrGeoJSON; - } - -private: - Range getZoomRange() final; - std::unique_ptr createTile(const OverscaledTileID&, const UpdateParameters&) final; - - variant urlOrGeoJSON; - std::unique_ptr req; -}; - -} // namespace style -} // namespace mbgl diff --git a/src/mbgl/style/sources/geojson_source_impl.cpp b/src/mbgl/style/sources/geojson_source_impl.cpp new file mode 100644 index 0000000000..f58e0fc62b --- /dev/null +++ b/src/mbgl/style/sources/geojson_source_impl.cpp @@ -0,0 +1,117 @@ +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include + +namespace mbgl { +namespace style { + +std::unique_ptr GeoJSONSource::Impl::parseGeoJSON(const JSValue& value) { + using namespace mapbox::geojsonvt; + + Options options; + options.buffer = util::EXTENT / util::tileSize * 128; + options.extent = util::EXTENT; + + try { + return std::make_unique(Convert::convert(value, 0), options); + } catch (const std::exception& ex) { + Log::Error(Event::ParseStyle, "Failed to parse GeoJSON data: %s", ex.what()); + // Create an empty GeoJSON VT object to make sure we're not infinitely waiting for + // tiles to load. + return std::make_unique(std::vector{}, options); + } +} + +std::unique_ptr GeoJSONSource::Impl::parse(const std::string& id, const JSValue& value) { + // We should probably split this up to have URLs in the url property, and actual data + // in the data property. Until then, we're going to detect the content based on the + // object type. + if (!value.HasMember("data")) { + Log::Error(Event::ParseStyle, "GeoJSON source must have a data value"); + return nullptr; + } + + const JSValue& dataVal = value["data"]; + if (dataVal.IsString()) { + return std::make_unique([&] (Source& base) { + return std::make_unique(id, base, std::string(dataVal.GetString(), dataVal.GetStringLength())); + }); + } else if (dataVal.IsObject()) { + return std::make_unique([&] (Source& base) { + return std::make_unique(id, base, parseGeoJSON(dataVal)); + }); + } else { + Log::Error(Event::ParseStyle, "GeoJSON data must be a URL or an object"); + return nullptr; + } +} + +GeoJSONSource::Impl::Impl(std::string id_, Source& base_, variant urlOrGeoJSON_) + : Source::Impl(SourceType::GeoJSON, std::move(id_), base_), + urlOrGeoJSON(std::move(urlOrGeoJSON_)) { +} + +GeoJSONSource::Impl::~Impl() = default; + +void GeoJSONSource::Impl::load(FileSource& fileSource) { + if (urlOrGeoJSON.is()) { + loaded = true; + return; + } + + if (req) { + return; + } + + const std::string& url = urlOrGeoJSON.get(); + req = fileSource.request(Resource::source(url), [this](Response res) { + if (res.error) { + observer->onSourceError(base, std::make_exception_ptr(std::runtime_error(res.error->message))); + } else if (res.notModified) { + return; + } else if (res.noContent) { + observer->onSourceError(base, std::make_exception_ptr(std::runtime_error("unexpectedly empty GeoJSON"))); + } else { + rapidjson::GenericDocument, rapidjson::CrtAllocator> d; + d.Parse<0>(res.data->c_str()); + + if (d.HasParseError()) { + std::stringstream message; + message << d.GetErrorOffset() << " - " << rapidjson::GetParseError_En(d.GetParseError()); + observer->onSourceError(base, std::make_exception_ptr(std::runtime_error(message.str()))); + return; + } + + invalidateTiles(); + + urlOrGeoJSON = parseGeoJSON(d); + loaded = true; + + observer->onSourceLoaded(base); + } + }); +} + +Range GeoJSONSource::Impl::getZoomRange() { + assert(loaded); + return { 0, urlOrGeoJSON.get()->options.maxZoom }; +} + +std::unique_ptr GeoJSONSource::Impl::createTile(const OverscaledTileID& tileID, + const UpdateParameters& parameters) { + assert(loaded); + return std::make_unique(tileID, base.getID(), parameters, *urlOrGeoJSON.get()); +} + +} // namespace style +} // namespace mbgl diff --git a/src/mbgl/style/sources/geojson_source_impl.hpp b/src/mbgl/style/sources/geojson_source_impl.hpp new file mode 100644 index 0000000000..350045b27c --- /dev/null +++ b/src/mbgl/style/sources/geojson_source_impl.hpp @@ -0,0 +1,50 @@ +#pragma once + +#include +#include +#include +#include + +namespace mapbox { +namespace geojsonvt { +class GeoJSONVT; +} // namespace geojsonvt +} // namespace mapbox + +namespace mbgl { + +class AsyncRequest; + +namespace style { + +class GeoJSONSource::Impl : public Source::Impl { +public: + using GeoJSON = std::unique_ptr; + + static std::unique_ptr parse(const std::string& id, const JSValue&); + static GeoJSON parseGeoJSON(const JSValue&); + + Impl(std::string id, Source&, + variant urlOrGeoJSON); + ~Impl() final; + + void load(FileSource&) final; + + uint16_t getTileSize() const final { + return util::tileSize; + } + + const variant& getURLOrGeoJSON() const { + return urlOrGeoJSON; + } + +private: + Range getZoomRange() final; + std::unique_ptr createTile(const OverscaledTileID&, const UpdateParameters&) final; + + variant urlOrGeoJSON; + std::unique_ptr req; +}; + +} // namespace style +} // namespace mbgl diff --git a/src/mbgl/style/sources/raster_source.cpp b/src/mbgl/style/sources/raster_source.cpp deleted file mode 100644 index 9f873a3065..0000000000 --- a/src/mbgl/style/sources/raster_source.cpp +++ /dev/null @@ -1,40 +0,0 @@ -#include -#include -#include - -namespace mbgl { -namespace style { - -std::unique_ptr RasterSource::parse(std::string id, const JSValue& value) { - optional> urlOrTileset = TileSource::parseURLOrTileset(value); - if (!urlOrTileset) { - return nullptr; - } - - uint16_t tileSize = util::tileSize; - if (value.HasMember("tileSize")) { - const JSValue& tileSizeVal = value["tileSize"]; - if (tileSizeVal.IsNumber() && tileSizeVal.GetUint64() <= std::numeric_limits::max()) { - tileSize = tileSizeVal.GetUint64(); - } else { - Log::Error(Event::ParseStyle, "invalid tileSize"); - return nullptr; - } - } - - return std::make_unique(std::move(id), std::move(*urlOrTileset), tileSize); -} - -RasterSource::RasterSource(std::string id_, - variant urlOrTileset_, - uint16_t tileSize_) - : TileSource(SourceType::Raster, std::move(id_), std::move(urlOrTileset_), tileSize_) { -} - -std::unique_ptr RasterSource::createTile(const OverscaledTileID& tileID, - const UpdateParameters& parameters) { - return std::make_unique(tileID, parameters, tileset); -} - -} // namespace style -} // namespace mbgl diff --git a/src/mbgl/style/sources/raster_source.hpp b/src/mbgl/style/sources/raster_source.hpp deleted file mode 100644 index 27b2276df5..0000000000 --- a/src/mbgl/style/sources/raster_source.hpp +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include - -namespace mbgl { -namespace style { - -class RasterSource : public TileSource { -public: - static std::unique_ptr parse(std::string id, const JSValue&); - - RasterSource(std::string id, variant, uint16_t tileSize); - -private: - std::unique_ptr createTile(const OverscaledTileID&, const UpdateParameters&) final; -}; - -} // namespace style -} // namespace mbgl diff --git a/src/mbgl/style/sources/raster_source_impl.cpp b/src/mbgl/style/sources/raster_source_impl.cpp new file mode 100644 index 0000000000..a6e19b4757 --- /dev/null +++ b/src/mbgl/style/sources/raster_source_impl.cpp @@ -0,0 +1,40 @@ +#include +#include +#include + +namespace mbgl { +namespace style { + +std::unique_ptr RasterSource::Impl::parse(std::string id, const JSValue& value) { + optional> urlOrTileset = TileSourceImpl::parseURLOrTileset(value); + if (!urlOrTileset) { + return nullptr; + } + + uint16_t tileSize = util::tileSize; + if (value.HasMember("tileSize")) { + const JSValue& tileSizeVal = value["tileSize"]; + if (tileSizeVal.IsNumber() && tileSizeVal.GetUint64() <= std::numeric_limits::max()) { + tileSize = tileSizeVal.GetUint64(); + } else { + Log::Error(Event::ParseStyle, "invalid tileSize"); + return nullptr; + } + } + + return std::make_unique(std::move(id), std::move(*urlOrTileset), tileSize); +} + +RasterSource::Impl::Impl(std::string id_, Source& base_, + variant urlOrTileset_, + uint16_t tileSize_) + : TileSourceImpl(SourceType::Raster, std::move(id_), base_, std::move(urlOrTileset_), tileSize_) { +} + +std::unique_ptr RasterSource::Impl::createTile(const OverscaledTileID& tileID, + const UpdateParameters& parameters) { + return std::make_unique(tileID, parameters, tileset); +} + +} // namespace style +} // namespace mbgl diff --git a/src/mbgl/style/sources/raster_source_impl.hpp b/src/mbgl/style/sources/raster_source_impl.hpp new file mode 100644 index 0000000000..2222b13082 --- /dev/null +++ b/src/mbgl/style/sources/raster_source_impl.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include +#include + +namespace mbgl { +namespace style { + +class RasterSource::Impl : public TileSourceImpl { +public: + static std::unique_ptr parse(std::string id, const JSValue&); + + Impl(std::string id, Source&, variant, uint16_t tileSize); + +private: + std::unique_ptr createTile(const OverscaledTileID&, const UpdateParameters&) final; +}; + +} // namespace style +} // namespace mbgl diff --git a/src/mbgl/style/sources/vector_source.cpp b/src/mbgl/style/sources/vector_source.cpp deleted file mode 100644 index 3f8f840b38..0000000000 --- a/src/mbgl/style/sources/vector_source.cpp +++ /dev/null @@ -1,25 +0,0 @@ -#include -#include - -namespace mbgl { -namespace style { - -std::unique_ptr VectorSource::parse(std::string id, const JSValue& value) { - optional> urlOrTileset = TileSource::parseURLOrTileset(value); - if (!urlOrTileset) { - return nullptr; - } - return std::make_unique(std::move(id), std::move(*urlOrTileset)); -} - -VectorSource::VectorSource(std::string id_, variant urlOrTileset_) - : TileSource(SourceType::Vector, std::move(id_), std::move(urlOrTileset_), util::tileSize) { -} - -std::unique_ptr VectorSource::createTile(const OverscaledTileID& tileID, - const UpdateParameters& parameters) { - return std::make_unique(tileID, id, parameters, tileset); -} - -} // namespace style -} // namespace mbgl diff --git a/src/mbgl/style/sources/vector_source.hpp b/src/mbgl/style/sources/vector_source.hpp deleted file mode 100644 index ced7d80471..0000000000 --- a/src/mbgl/style/sources/vector_source.hpp +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include - -namespace mbgl { -namespace style { - -class VectorSource : public TileSource { -public: - static std::unique_ptr parse(std::string id, const JSValue&); - - VectorSource(std::string id, variant); - -private: - std::unique_ptr createTile(const OverscaledTileID&, const UpdateParameters&) final; -}; - -} // namespace style -} // namespace mbgl diff --git a/src/mbgl/style/sources/vector_source_impl.cpp b/src/mbgl/style/sources/vector_source_impl.cpp new file mode 100644 index 0000000000..28e14f3e16 --- /dev/null +++ b/src/mbgl/style/sources/vector_source_impl.cpp @@ -0,0 +1,25 @@ +#include +#include + +namespace mbgl { +namespace style { + +std::unique_ptr VectorSource::Impl::parse(std::string id, const JSValue& value) { + optional> urlOrTileset = TileSourceImpl::parseURLOrTileset(value); + if (!urlOrTileset) { + return nullptr; + } + return std::make_unique(std::move(id), std::move(*urlOrTileset)); +} + +VectorSource::Impl::Impl(std::string id_, Source& base_, variant urlOrTileset_) + : TileSourceImpl(SourceType::Vector, std::move(id_), base_, std::move(urlOrTileset_), util::tileSize) { +} + +std::unique_ptr VectorSource::Impl::createTile(const OverscaledTileID& tileID, + const UpdateParameters& parameters) { + return std::make_unique(tileID, base.getID(), parameters, tileset); +} + +} // namespace style +} // namespace mbgl diff --git a/src/mbgl/style/sources/vector_source_impl.hpp b/src/mbgl/style/sources/vector_source_impl.hpp new file mode 100644 index 0000000000..4a6703e5c0 --- /dev/null +++ b/src/mbgl/style/sources/vector_source_impl.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include +#include + +namespace mbgl { +namespace style { + +class VectorSource::Impl : public TileSourceImpl { +public: + static std::unique_ptr parse(std::string id, const JSValue&); + + Impl(std::string id, Source&, variant); + +private: + std::unique_ptr createTile(const OverscaledTileID&, const UpdateParameters&) final; +}; + +} // namespace style +} // namespace mbgl diff --git a/src/mbgl/style/style.cpp b/src/mbgl/style/style.cpp index af19c41393..a75133f82d 100644 --- a/src/mbgl/style/style.cpp +++ b/src/mbgl/style/style.cpp @@ -1,6 +1,6 @@ #include #include -#include +#include #include #include #include @@ -47,7 +47,7 @@ Style::Style(FileSource& fileSource_, float pixelRatio) Style::~Style() { for (const auto& source : sources) { - source->setObserver(nullptr); + source->baseImpl->setObserver(nullptr); } glyphStore->setObserver(nullptr); @@ -107,7 +107,7 @@ void Style::setJSON(const std::string& json) { } void Style::addSource(std::unique_ptr source) { - source->setObserver(this); + source->baseImpl->setObserver(this); sources.emplace_back(std::move(source)); } @@ -156,7 +156,7 @@ void Style::update(const UpdateParameters& parameters) { bool allTilesUpdated = true; for (const auto& source : sources) { - if (!source->update(parameters)) { + if (!source->baseImpl->update(parameters)) { allTilesUpdated = false; } } @@ -195,7 +195,7 @@ void Style::cascade(const TimePoint& timePoint, MapMode mode) { void Style::recalculate(float z, const TimePoint& timePoint, MapMode mode) { for (const auto& source : sources) { - source->enabled = false; + source->baseImpl->enabled = false; } zoomHistory.update(z, timePoint); @@ -213,9 +213,9 @@ void Style::recalculate(float z, const TimePoint& timePoint, MapMode mode) { Source* source = getSource(layer->baseImpl->source); if (source && layer->baseImpl->needsRendering()) { - source->enabled = true; - if (!source->loaded) { - source->load(fileSource); + source->baseImpl->enabled = true; + if (!source->baseImpl->loaded) { + source->baseImpl->load(fileSource); } } } @@ -223,7 +223,7 @@ void Style::recalculate(float z, const TimePoint& timePoint, MapMode mode) { Source* Style::getSource(const std::string& id) const { const auto it = std::find_if(sources.begin(), sources.end(), [&](const auto& source) { - return source->id == id; + return source->getID() == id; }); return it != sources.end() ? it->get() : nullptr; @@ -239,7 +239,7 @@ bool Style::isLoaded() const { } for (const auto& source: sources) { - if (source->enabled && !source->isLoaded()) return false; + if (source->baseImpl->enabled && !source->baseImpl->isLoaded()) return false; } if (!spriteStore->isLoaded()) { @@ -253,7 +253,7 @@ RenderData Style::getRenderData() const { RenderData result; for (const auto& source : sources) { - if (source->enabled) { + if (source->baseImpl->enabled) { result.sources.insert(source.get()); } } @@ -285,7 +285,7 @@ RenderData Style::getRenderData() const { continue; } - for (auto& pair : source->getRenderTiles()) { + for (auto& pair : source->baseImpl->getRenderTiles()) { auto& tile = pair.second; if (!tile.tile.isRenderable()) { continue; @@ -324,7 +324,7 @@ std::vector Style::queryRenderedFeatures(const QueryParameters& paramet std::unordered_map> resultsByLayer; for (const auto& source : sources) { - auto sourceResults = source->queryRenderedFeatures(parameters); + auto sourceResults = source->baseImpl->queryRenderedFeatures(parameters); std::move(sourceResults.begin(), sourceResults.end(), std::inserter(resultsByLayer, resultsByLayer.begin())); } @@ -352,13 +352,13 @@ float Style::getQueryRadius() const { void Style::setSourceTileCacheSize(size_t size) { for (const auto& source : sources) { - source->setCacheSize(size); + source->baseImpl->setCacheSize(size); } } void Style::onLowMemory() { for (const auto& source : sources) { - source->onLowMemory(); + source->baseImpl->onLowMemory(); } } @@ -388,7 +388,7 @@ void Style::onSourceLoaded(Source& source) { void Style::onSourceError(Source& source, std::exception_ptr error) { lastError = error; Log::Error(Event::Style, "Failed to load source %s: %s", - source.id.c_str(), util::toString(error).c_str()); + source.getID().c_str(), util::toString(error).c_str()); observer->onSourceError(source, error); observer->onResourceError(error); } @@ -405,7 +405,7 @@ void Style::onTileLoaded(Source& source, const OverscaledTileID& tileID, bool is void Style::onTileError(Source& 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.id.c_str(), util::toString(error).c_str()); + util::toString(tileID).c_str(), source.getID().c_str(), util::toString(error).c_str()); observer->onTileError(source, tileID, error); observer->onResourceError(error); } @@ -429,7 +429,7 @@ void Style::onSpriteError(std::exception_ptr error) { void Style::dumpDebugLogs() const { for (const auto& source : sources) { - source->dumpDebugLogs(); + source->baseImpl->dumpDebugLogs(); } spriteStore->dumpDebugLogs(); diff --git a/src/mbgl/style/tile_source.cpp b/src/mbgl/style/tile_source.cpp deleted file mode 100644 index 0824693331..0000000000 --- a/src/mbgl/style/tile_source.cpp +++ /dev/null @@ -1,197 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - -namespace mbgl { -namespace style { - -namespace { - -void parseTileJSONMember(const JSValue& value, std::vector& target, const char* name) { - if (!value.HasMember(name)) { - return; - } - - const JSValue& property = value[name]; - if (!property.IsArray()) { - return; - } - - for (rapidjson::SizeType i = 0; i < property.Size(); i++) { - if (!property[i].IsString()) { - return; - } - } - - for (rapidjson::SizeType i = 0; i < property.Size(); i++) { - target.emplace_back(std::string(property[i].GetString(), property[i].GetStringLength())); - } -} - -void parseTileJSONMember(const JSValue& value, std::string& target, const char* name) { - if (!value.HasMember(name)) { - return; - } - - const JSValue& property = value[name]; - if (!property.IsString()) { - return; - } - - target = { property.GetString(), property.GetStringLength() }; -} - -void parseTileJSONMember(const JSValue& value, uint8_t& target, const char* name) { - if (!value.HasMember(name)) { - return; - } - - const JSValue& property = value[name]; - if (!property.IsUint()) { - return; - } - - unsigned int uint = property.GetUint(); - if (uint > std::numeric_limits::max()) { - return; - } - - target = uint; -} - -Tileset parseTileJSON(const JSValue& value) { - Tileset result; - - parseTileJSONMember(value, result.tiles, "tiles"); - parseTileJSONMember(value, result.zoomRange.min, "minzoom"); - parseTileJSONMember(value, result.zoomRange.max, "maxzoom"); - parseTileJSONMember(value, result.attribution, "attribution"); - - return result; -} - -} // end namespace - -Tileset TileSource::parseTileJSON(const std::string& json, const std::string& sourceURL, SourceType type, uint16_t tileSize) { - rapidjson::GenericDocument, rapidjson::CrtAllocator> document; - document.Parse<0>(json.c_str()); - - if (document.HasParseError()) { - std::stringstream message; - message << document.GetErrorOffset() << " - " << rapidjson::GetParseError_En(document.GetParseError()); - throw std::runtime_error(message.str()); - } - - Tileset result = mbgl::style::parseTileJSON(document); - - // TODO: Remove this hack by delivering proper URLs in the TileJSON to begin with. - if (util::mapbox::isMapboxURL(sourceURL)) { - for (auto& url : result.tiles) { - url = util::mapbox::canonicalizeTileURL(url, type, tileSize); - } - } - - return result; -} - -optional> TileSource::parseURLOrTileset(const JSValue& value) { - if (!value.HasMember("url")) { - return { mbgl::style::parseTileJSON(value) }; - } - - const JSValue& urlVal = value["url"]; - if (!urlVal.IsString()) { - Log::Error(Event::ParseStyle, "source url must be a string"); - return {}; - } - - return { std::string(urlVal.GetString(), urlVal.GetStringLength()) }; -} - -TileSource::TileSource(SourceType type_, - std::string id_, - variant urlOrTileset_, - uint16_t tileSize_) - : Source(type_, std::move(id_)), - urlOrTileset(std::move(urlOrTileset_)), - tileSize(tileSize_) { -} - -TileSource::~TileSource() = default; - -void TileSource::load(FileSource& fileSource) { - if (urlOrTileset.is()) { - tileset = urlOrTileset.get(); - loaded = true; - return; - } - - if (req) { - return; - } - - const std::string& url = urlOrTileset.get(); - req = fileSource.request(Resource::source(url), [this, url](Response res) { - if (res.error) { - observer->onSourceError(*this, std::make_exception_ptr(std::runtime_error(res.error->message))); - } else if (res.notModified) { - return; - } else if (res.noContent) { - observer->onSourceError(*this, std::make_exception_ptr(std::runtime_error("unexpectedly empty TileJSON"))); - } else { - Tileset newTileset; - - // Create a new copy of the Tileset object that holds the base values we've parsed - // from the stylesheet. Then merge in the values parsed from the TileJSON we retrieved - // via the URL. - try { - newTileset = parseTileJSON(*res.data, url, type, tileSize); - } catch (...) { - observer->onSourceError(*this, std::current_exception()); - return; - } - - // Check whether previous information specifies different tile - if (tileset.tiles != newTileset.tiles) { - // Tile URLs changed: force tiles to be reloaded. - invalidateTiles(); - - // Tile size changed: We need to recalculate the tiles we need to load because we - // might have to load tiles for a different zoom level - // This is done automatically when we trigger the onSourceLoaded observer below. - - // Min/Max zoom changed: We need to recalculate what tiles to load, if we have tiles - // loaded that are outside the new zoom range - // This is done automatically when we trigger the onSourceLoaded observer below. - - // Attribution changed: We need to notify the embedding application that this - // changed. See https://github.com/mapbox/mapbox-gl-native/issues/2723 - // This is not yet implemented. - - // Center/bounds changed: We're not using these values currently - } - - tileset = newTileset; - loaded = true; - - observer->onSourceLoaded(*this); - } - }); -} - -Range TileSource::getZoomRange() { - assert(loaded); - return tileset.zoomRange; -} - -} // namespace style -} // namespace mbgl diff --git a/src/mbgl/style/tile_source.hpp b/src/mbgl/style/tile_source.hpp deleted file mode 100644 index 66d956a6ef..0000000000 --- a/src/mbgl/style/tile_source.hpp +++ /dev/null @@ -1,52 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -namespace mbgl { - -class AsyncRequest; - -namespace style { - -/* - Shared implementation for VectorSource and RasterSource. Should eventually - be refactored to use composition rather than inheritance. -*/ -class TileSource : public Source { -public: - // A tile source can either specify a URL to TileJSON, or inline TileJSON. - static optional> parseURLOrTileset(const JSValue&); - static Tileset parseTileJSON(const std::string& json, const std::string& sourceURL, SourceType, uint16_t tileSize); - - TileSource(SourceType, - std::string id, - variant urlOrTileset, - uint16_t tileSize); - ~TileSource() override; - - void load(FileSource&) final; - - uint16_t getTileSize() const final { - return tileSize; - } - - const variant& getURLOrTileset() const { - return urlOrTileset; - } - -protected: - Range getZoomRange() final; - - const variant urlOrTileset; - const uint16_t tileSize; - - Tileset tileset; - std::unique_ptr req; -}; - -} // namespace style -} // namespace mbgl diff --git a/src/mbgl/style/tile_source_impl.cpp b/src/mbgl/style/tile_source_impl.cpp new file mode 100644 index 0000000000..634ef39808 --- /dev/null +++ b/src/mbgl/style/tile_source_impl.cpp @@ -0,0 +1,196 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +namespace mbgl { +namespace style { + +namespace { + +void parseTileJSONMember(const JSValue& value, std::vector& target, const char* name) { + if (!value.HasMember(name)) { + return; + } + + const JSValue& property = value[name]; + if (!property.IsArray()) { + return; + } + + for (rapidjson::SizeType i = 0; i < property.Size(); i++) { + if (!property[i].IsString()) { + return; + } + } + + for (rapidjson::SizeType i = 0; i < property.Size(); i++) { + target.emplace_back(std::string(property[i].GetString(), property[i].GetStringLength())); + } +} + +void parseTileJSONMember(const JSValue& value, std::string& target, const char* name) { + if (!value.HasMember(name)) { + return; + } + + const JSValue& property = value[name]; + if (!property.IsString()) { + return; + } + + target = { property.GetString(), property.GetStringLength() }; +} + +void parseTileJSONMember(const JSValue& value, uint8_t& target, const char* name) { + if (!value.HasMember(name)) { + return; + } + + const JSValue& property = value[name]; + if (!property.IsUint()) { + return; + } + + unsigned int uint = property.GetUint(); + if (uint > std::numeric_limits::max()) { + return; + } + + target = uint; +} + +Tileset parseTileJSON(const JSValue& value) { + Tileset result; + + parseTileJSONMember(value, result.tiles, "tiles"); + parseTileJSONMember(value, result.zoomRange.min, "minzoom"); + parseTileJSONMember(value, result.zoomRange.max, "maxzoom"); + parseTileJSONMember(value, result.attribution, "attribution"); + + return result; +} + +} // end namespace + +Tileset TileSourceImpl::parseTileJSON(const std::string& json, const std::string& sourceURL, SourceType type, uint16_t tileSize) { + rapidjson::GenericDocument, rapidjson::CrtAllocator> document; + document.Parse<0>(json.c_str()); + + if (document.HasParseError()) { + std::stringstream message; + message << document.GetErrorOffset() << " - " << rapidjson::GetParseError_En(document.GetParseError()); + throw std::runtime_error(message.str()); + } + + Tileset result = mbgl::style::parseTileJSON(document); + + // TODO: Remove this hack by delivering proper URLs in the TileJSON to begin with. + if (util::mapbox::isMapboxURL(sourceURL)) { + for (auto& url : result.tiles) { + url = util::mapbox::canonicalizeTileURL(url, type, tileSize); + } + } + + return result; +} + +optional> TileSourceImpl::parseURLOrTileset(const JSValue& value) { + if (!value.HasMember("url")) { + return { mbgl::style::parseTileJSON(value) }; + } + + const JSValue& urlVal = value["url"]; + if (!urlVal.IsString()) { + Log::Error(Event::ParseStyle, "source url must be a string"); + return {}; + } + + return { std::string(urlVal.GetString(), urlVal.GetStringLength()) }; +} + +TileSourceImpl::TileSourceImpl(SourceType type_, std::string id_, Source& base_, + variant urlOrTileset_, + uint16_t tileSize_) + : Impl(type_, std::move(id_), base_), + urlOrTileset(std::move(urlOrTileset_)), + tileSize(tileSize_) { +} + +TileSourceImpl::~TileSourceImpl() = default; + +void TileSourceImpl::load(FileSource& fileSource) { + if (urlOrTileset.is()) { + tileset = urlOrTileset.get(); + loaded = true; + return; + } + + if (req) { + return; + } + + const std::string& url = urlOrTileset.get(); + req = fileSource.request(Resource::source(url), [this, url](Response res) { + if (res.error) { + observer->onSourceError(base, std::make_exception_ptr(std::runtime_error(res.error->message))); + } else if (res.notModified) { + return; + } else if (res.noContent) { + observer->onSourceError(base, std::make_exception_ptr(std::runtime_error("unexpectedly empty TileJSON"))); + } else { + Tileset newTileset; + + // Create a new copy of the Tileset object that holds the base values we've parsed + // from the stylesheet. Then merge in the values parsed from the TileJSON we retrieved + // via the URL. + try { + newTileset = parseTileJSON(*res.data, url, type, tileSize); + } catch (...) { + observer->onSourceError(base, std::current_exception()); + return; + } + + // Check whether previous information specifies different tile + if (tileset.tiles != newTileset.tiles) { + // Tile URLs changed: force tiles to be reloaded. + invalidateTiles(); + + // Tile size changed: We need to recalculate the tiles we need to load because we + // might have to load tiles for a different zoom level + // This is done automatically when we trigger the onSourceLoaded observer below. + + // Min/Max zoom changed: We need to recalculate what tiles to load, if we have tiles + // loaded that are outside the new zoom range + // This is done automatically when we trigger the onSourceLoaded observer below. + + // Attribution changed: We need to notify the embedding application that this + // changed. See https://github.com/mapbox/mapbox-gl-native/issues/2723 + // This is not yet implemented. + + // Center/bounds changed: We're not using these values currently + } + + tileset = newTileset; + loaded = true; + + observer->onSourceLoaded(base); + } + }); +} + +Range TileSourceImpl::getZoomRange() { + assert(loaded); + return tileset.zoomRange; +} + +} // namespace style +} // namespace mbgl diff --git a/src/mbgl/style/tile_source_impl.hpp b/src/mbgl/style/tile_source_impl.hpp new file mode 100644 index 0000000000..1ceb1188db --- /dev/null +++ b/src/mbgl/style/tile_source_impl.hpp @@ -0,0 +1,51 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace mbgl { + +class AsyncRequest; + +namespace style { + +/* + Shared implementation for VectorSource and RasterSource. Should eventually + be refactored to use composition rather than inheritance. +*/ +class TileSourceImpl : public Source::Impl { +public: + // A tile source can either specify a URL to TileJSON, or inline TileJSON. + static optional> parseURLOrTileset(const JSValue&); + static Tileset parseTileJSON(const std::string& json, const std::string& sourceURL, SourceType, uint16_t tileSize); + + TileSourceImpl(SourceType, std::string id, Source&, + variant urlOrTileset, + uint16_t tileSize); + ~TileSourceImpl() override; + + void load(FileSource&) final; + + uint16_t getTileSize() const final { + return tileSize; + } + + const variant& getURLOrTileset() const { + return urlOrTileset; + } + +protected: + Range getZoomRange() final; + + const variant urlOrTileset; + const uint16_t tileSize; + + Tileset tileset; + std::unique_ptr req; +}; + +} // namespace style +} // namespace mbgl diff --git a/src/mbgl/util/tileset.hpp b/src/mbgl/util/tileset.hpp deleted file mode 100644 index 8a7fbe9b73..0000000000 --- a/src/mbgl/util/tileset.hpp +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include - -#include -#include -#include - -namespace mbgl { - -class Tileset { -public: - std::vector tiles; - Range zoomRange { 0, 22 }; - std::string attribution; - - // TileJSON also includes center, zoom, and bounds, but they are not used by mbgl. -}; - -} // namespace mbgl diff --git a/test/style/source.cpp b/test/style/source.cpp index 9de208f343..6afa5073d4 100644 --- a/test/style/source.cpp +++ b/test/style/source.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -82,14 +83,14 @@ TEST(Source, LoadingFail) { }; test.observer.sourceError = [&] (Source& source, std::exception_ptr error) { - EXPECT_EQ("source", source.id); + EXPECT_EQ("source", source.getID()); EXPECT_EQ("Failed by the test case", util::toString(error)); test.end(); }; VectorSource source("source", "url"); - source.setObserver(&test.observer); - source.load(test.fileSource); + source.baseImpl->setObserver(&test.observer); + source.baseImpl->load(test.fileSource); test.run(); } @@ -105,14 +106,14 @@ TEST(Source, LoadingCorrupt) { }; test.observer.sourceError = [&] (Source& source, std::exception_ptr error) { - EXPECT_EQ("source", source.id); + EXPECT_EQ("source", source.getID()); EXPECT_EQ("0 - Invalid value.", util::toString(error)); test.end(); }; VectorSource source("source", "url"); - source.setObserver(&test.observer); - source.load(test.fileSource); + source.baseImpl->setObserver(&test.observer); + source.baseImpl->load(test.fileSource); test.run(); } @@ -127,7 +128,7 @@ TEST(Source, RasterTileEmpty) { }; test.observer.tileLoaded = [&] (Source& source, const OverscaledTileID&, bool) { - EXPECT_EQ("source", source.id); + EXPECT_EQ("source", source.getID()); test.end(); }; @@ -139,9 +140,9 @@ TEST(Source, RasterTileEmpty) { tileset.tiles = { "tiles" }; RasterSource source("source", tileset, 512); - source.setObserver(&test.observer); - source.load(test.fileSource); - source.update(test.updateParameters); + source.baseImpl->setObserver(&test.observer); + source.baseImpl->load(test.fileSource); + source.baseImpl->update(test.updateParameters); test.run(); } @@ -156,7 +157,7 @@ TEST(Source, VectorTileEmpty) { }; test.observer.tileLoaded = [&] (Source& source, const OverscaledTileID&, bool) { - EXPECT_EQ("source", source.id); + EXPECT_EQ("source", source.getID()); test.end(); }; @@ -168,9 +169,9 @@ TEST(Source, VectorTileEmpty) { tileset.tiles = { "tiles" }; VectorSource source("source", tileset); - source.setObserver(&test.observer); - source.load(test.fileSource); - source.update(test.updateParameters); + source.baseImpl->setObserver(&test.observer); + source.baseImpl->load(test.fileSource); + source.baseImpl->update(test.updateParameters); test.run(); } @@ -187,7 +188,7 @@ TEST(Source, RasterTileFail) { }; test.observer.tileError = [&] (Source& source, const OverscaledTileID& tileID, std::exception_ptr error) { - EXPECT_EQ(SourceType::Raster, source.type); + EXPECT_EQ(SourceType::Raster, source.baseImpl->type); EXPECT_EQ(OverscaledTileID(0, 0, 0), tileID); EXPECT_EQ("Failed by the test case", util::toString(error)); test.end(); @@ -197,9 +198,9 @@ TEST(Source, RasterTileFail) { tileset.tiles = { "tiles" }; RasterSource source("source", tileset, 512); - source.setObserver(&test.observer); - source.load(test.fileSource); - source.update(test.updateParameters); + source.baseImpl->setObserver(&test.observer); + source.baseImpl->load(test.fileSource); + source.baseImpl->update(test.updateParameters); test.run(); } @@ -216,7 +217,7 @@ TEST(Source, VectorTileFail) { }; test.observer.tileError = [&] (Source& source, const OverscaledTileID& tileID, std::exception_ptr error) { - EXPECT_EQ(SourceType::Vector, source.type); + EXPECT_EQ(SourceType::Vector, source.baseImpl->type); EXPECT_EQ(OverscaledTileID(0, 0, 0), tileID); EXPECT_EQ("Failed by the test case", util::toString(error)); test.end(); @@ -226,9 +227,9 @@ TEST(Source, VectorTileFail) { tileset.tiles = { "tiles" }; VectorSource source("source", tileset); - source.setObserver(&test.observer); - source.load(test.fileSource); - source.update(test.updateParameters); + source.baseImpl->setObserver(&test.observer); + source.baseImpl->load(test.fileSource); + source.baseImpl->update(test.updateParameters); test.run(); } @@ -243,7 +244,7 @@ TEST(Source, RasterTileCorrupt) { }; test.observer.tileError = [&] (Source& source, const OverscaledTileID& tileID, std::exception_ptr error) { - EXPECT_EQ(source.type, SourceType::Raster); + EXPECT_EQ(source.baseImpl->type, SourceType::Raster); EXPECT_EQ(OverscaledTileID(0, 0, 0), tileID); EXPECT_TRUE(bool(error)); // Not asserting on platform-specific error text. @@ -254,9 +255,9 @@ TEST(Source, RasterTileCorrupt) { tileset.tiles = { "tiles" }; RasterSource source("source", tileset, 512); - source.setObserver(&test.observer); - source.load(test.fileSource); - source.update(test.updateParameters); + source.baseImpl->setObserver(&test.observer); + source.baseImpl->load(test.fileSource); + source.baseImpl->update(test.updateParameters); test.run(); } @@ -271,7 +272,7 @@ TEST(Source, VectorTileCorrupt) { }; test.observer.tileError = [&] (Source& source, const OverscaledTileID& tileID, std::exception_ptr error) { - EXPECT_EQ(source.type, SourceType::Vector); + EXPECT_EQ(source.baseImpl->type, SourceType::Vector); EXPECT_EQ(OverscaledTileID(0, 0, 0), tileID); EXPECT_EQ(util::toString(error), "unknown pbf field type exception"); test.end(); @@ -286,9 +287,9 @@ TEST(Source, VectorTileCorrupt) { tileset.tiles = { "tiles" }; VectorSource source("source", tileset); - source.setObserver(&test.observer); - source.load(test.fileSource); - source.update(test.updateParameters); + source.baseImpl->setObserver(&test.observer); + source.baseImpl->load(test.fileSource); + source.baseImpl->update(test.updateParameters); test.run(); } @@ -313,9 +314,9 @@ TEST(Source, RasterTileCancel) { tileset.tiles = { "tiles" }; RasterSource source("source", tileset, 512); - source.setObserver(&test.observer); - source.load(test.fileSource); - source.update(test.updateParameters); + source.baseImpl->setObserver(&test.observer); + source.baseImpl->load(test.fileSource); + source.baseImpl->update(test.updateParameters); test.run(); } @@ -340,9 +341,9 @@ TEST(Source, VectorTileCancel) { tileset.tiles = { "tiles" }; VectorSource source("source", tileset); - source.setObserver(&test.observer); - source.load(test.fileSource); - source.update(test.updateParameters); + source.baseImpl->setObserver(&test.observer); + source.baseImpl->load(test.fileSource); + source.baseImpl->update(test.updateParameters); test.run(); } diff --git a/test/style/style.cpp b/test/style/style.cpp index d530d9cbdd..1681bac1c9 100644 --- a/test/style/style.cpp +++ b/test/style/style.cpp @@ -2,7 +2,7 @@ #include #include -#include +#include #include using namespace mbgl; @@ -22,11 +22,11 @@ TEST(Style, UnusedSource) { Source *usedSource = style.getSource("usedsource"); EXPECT_TRUE(usedSource); - EXPECT_TRUE(usedSource->isLoaded()); + EXPECT_TRUE(usedSource->baseImpl->isLoaded()); Source *unusedSource = style.getSource("unusedsource"); EXPECT_TRUE(unusedSource); - EXPECT_FALSE(unusedSource->isLoaded()); + EXPECT_FALSE(unusedSource->baseImpl->isLoaded()); } TEST(Style, UnusedSourceActiveViaClassUpdate) { @@ -46,7 +46,7 @@ TEST(Style, UnusedSourceActiveViaClassUpdate) { Source *unusedSource = style.getSource("unusedsource"); EXPECT_TRUE(unusedSource); - EXPECT_TRUE(unusedSource->isLoaded()); + EXPECT_TRUE(unusedSource->baseImpl->isLoaded()); // Style classes should be cleared upon new style load. style.setJSON(util::read_file("test/fixtures/resources/style-unused-sources.json")); @@ -59,5 +59,5 @@ TEST(Style, UnusedSourceActiveViaClassUpdate) { unusedSource = style.getSource("unusedsource"); EXPECT_TRUE(unusedSource); - EXPECT_FALSE(unusedSource->isLoaded()); + EXPECT_FALSE(unusedSource->baseImpl->isLoaded()); } diff --git a/test/style/tile_source.cpp b/test/style/tile_source.cpp index e0709e3be1..35d037a049 100644 --- a/test/style/tile_source.cpp +++ b/test/style/tile_source.cpp @@ -1,13 +1,13 @@ #include -#include +#include #include using namespace mbgl; using namespace mbgl::style; -TEST(TileSource, ParseTileJSONRaster) { - auto result = TileSource::parseTileJSON( +TEST(TileSourceImpl, ParseTileJSONRaster) { + auto result = TileSourceImpl::parseTileJSON( util::read_file("test/fixtures/style_parser/tilejson.raster.json"), "mapbox://mapbox.satellite", SourceType::Raster, @@ -23,8 +23,8 @@ TEST(TileSource, ParseTileJSONRaster) { #endif } -TEST(TileSource, ParseTileJSONVector) { - auto result = TileSource::parseTileJSON( +TEST(TileSourceImpl, ParseTileJSONVector) { + auto result = TileSourceImpl::parseTileJSON( util::read_file("test/fixtures/style_parser/tilejson.vector.json"), "mapbox://mapbox.streets", SourceType::Vector, -- cgit v1.2.1