[core] Virtualize Source::load

This commit is contained in:
John Firebaugh
2016-06-13 13:35:24 -07:00
parent e6ba10ceed
commit 2a991c6846
8 changed files with 132 additions and 99 deletions

View File

@@ -8,6 +8,10 @@ AnnotationSource::AnnotationSource()
: Source(SourceType::Annotations, AnnotationManager::SourceID, "", util::tileSize, std::make_unique<Tileset>()) {
}
void AnnotationSource::load(FileSource&) {
loaded = true;
}
std::unique_ptr<Tile> AnnotationSource::createTile(const OverscaledTileID& tileID,
const style::UpdateParameters& parameters) {
return std::make_unique<AnnotationTile>(tileID, id, parameters);

View File

@@ -8,6 +8,8 @@ class AnnotationSource : public style::Source {
public:
AnnotationSource();
void load(FileSource&) final;
private:
std::unique_ptr<Tile> createTile(const OverscaledTileID&, const style::UpdateParameters&) final;
};

View File

@@ -7,7 +7,6 @@
#include <mbgl/util/constants.hpp>
#include <mbgl/storage/resource.hpp>
#include <mbgl/storage/response.hpp>
#include <mbgl/storage/file_source.hpp>
#include <mbgl/style/layer.hpp>
#include <mbgl/style/update_parameters.hpp>
#include <mbgl/style/query_parameters.hpp>
@@ -20,7 +19,6 @@
#include <mbgl/util/tile_cover.hpp>
#include <mbgl/util/enum.hpp>
#include <mbgl/style/parser.hpp>
#include <mbgl/gl/debugging.hpp>
#include <mbgl/algorithm/update_renderables.hpp>
@@ -29,10 +27,7 @@
#include <mapbox/geojsonvt/convert.hpp>
#include <mapbox/geometry/envelope.hpp>
#include <rapidjson/error/en.h>
#include <algorithm>
#include <sstream>
namespace mbgl {
namespace style {
@@ -70,95 +65,10 @@ bool Source::isLoading() const {
return !loaded && req.operator bool();
}
void Source::load(FileSource& fileSource) {
if (url.empty()) {
// In case there is no URL set, we assume that we already have all of the data because the
// TileJSON was specified inline in the stylesheet.
loaded = true;
return;
}
if (req) {
// We don't have a Tileset object yet, but there's already a request underway to load
// the data.
return;
}
// URL may either be a TileJSON file, or a GeoJSON file.
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 source")));
} else {
bool reloadTiles = false;
if (type == SourceType::Vector || type == SourceType::Raster) {
std::unique_ptr<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 = style::parseTileJSON(*res.data, url, type, tileSize);
} catch (...) {
observer->onSourceError(*this, std::current_exception());
return;
}
// Check whether previous information specifies different tile
if (tileset && tileset->tiles != newTileset->tiles) {
reloadTiles = true;
// 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 = std::move(newTileset);
} else if (type == SourceType::GeoJSON) {
std::unique_ptr<Tileset> newTileset = std::make_unique<Tileset>();
rapidjson::GenericDocument<rapidjson::UTF8<>, 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;
}
geojsonvt = style::parseGeoJSON(d);
reloadTiles = true;
newTileset->maxZoom = geojsonvt->options.maxZoom;
tileset = std::move(newTileset);
}
if (reloadTiles) {
// Tile information changed because we got new GeoJSON data, or a new tile URL.
tiles.clear();
renderTiles.clear();
cache.clear();
}
loaded = true;
observer->onSourceLoaded(*this);
}
});
void Source::invalidateTiles() {
tiles.clear();
renderTiles.clear();
cache.clear();
}
void Source::updateMatrices(const mat4 &projMatrix, const TransformState &transform) {

View File

@@ -48,7 +48,7 @@ public:
~Source() override;
bool loaded = false;
void load(FileSource&);
virtual void load(FileSource&) = 0;
bool isLoading() const;
bool isLoaded() const;
@@ -96,8 +96,13 @@ private:
virtual std::unique_ptr<Tile> createTile(const OverscaledTileID&, const UpdateParameters&) = 0;
protected:
void invalidateTiles();
std::unique_ptr<const Tileset> tileset;
std::unique_ptr<mapbox::geojsonvt::GeoJSONVT> geojsonvt;
std::unique_ptr<AsyncRequest> req;
SourceObserver* observer = nullptr;
private:
// Stores the time when this source was most recently updated.
@@ -106,10 +111,6 @@ private:
std::map<OverscaledTileID, std::unique_ptr<Tile>> tiles;
std::map<UnwrappedTileID, RenderTile> renderTiles;
TileCache cache;
std::unique_ptr<AsyncRequest> req;
SourceObserver* observer = nullptr;
};
} // namespace style

View File

@@ -1,8 +1,15 @@
#include <mbgl/style/sources/geojson_source.hpp>
#include <mbgl/style/source_observer.hpp>
#include <mbgl/style/parser.hpp>
#include <mbgl/tile/geojson_tile.hpp>
#include <mbgl/storage/file_source.hpp>
#include <mapbox/geojsonvt.hpp>
#include <rapidjson/error/en.h>
#include <sstream>
namespace mbgl {
namespace style {
@@ -15,6 +22,49 @@ GeoJSONSource::GeoJSONSource(std::string id_,
geojsonvt = std::move(geojsonvt_);
}
void GeoJSONSource::load(FileSource& fileSource) {
if (url.empty()) {
// If the URL is empty, the GeoJSON was specified inline in the stylesheet.
loaded = true;
return;
}
if (req) {
return;
}
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 {
std::unique_ptr<Tileset> newTileset = std::make_unique<Tileset>();
rapidjson::GenericDocument<rapidjson::UTF8<>, 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;
}
geojsonvt = style::parseGeoJSON(d);
newTileset->maxZoom = geojsonvt->options.maxZoom;
invalidateTiles();
tileset = std::move(newTileset);
loaded = true;
observer->onSourceLoaded(*this);
}
});
}
std::unique_ptr<Tile> GeoJSONSource::createTile(const OverscaledTileID& tileID,
const UpdateParameters& parameters) {
return std::make_unique<GeoJSONTile>(tileID, id, parameters, geojsonvt.get());

View File

@@ -13,6 +13,8 @@ public:
std::unique_ptr<Tileset>&&,
std::unique_ptr<mapbox::geojsonvt::GeoJSONVT>&&);
void load(FileSource&) final;
private:
std::unique_ptr<Tile> createTile(const OverscaledTileID&, const UpdateParameters&) final;
};

View File

@@ -1,4 +1,7 @@
#include <mbgl/style/tile_source.hpp>
#include <mbgl/style/source_observer.hpp>
#include <mbgl/style/parser.hpp>
#include <mbgl/storage/file_source.hpp>
namespace mbgl {
namespace style {
@@ -11,5 +14,64 @@ TileSource::TileSource(SourceType type_,
: Source(type_, std::move(id_), std::move(url_), tileSize_, std::move(tileset_)) {
}
void TileSource::load(FileSource& fileSource) {
if (url.empty()) {
// If the URL is empty, the TileJSON was specified inline in the stylesheet.
loaded = true;
return;
}
if (req) {
return;
}
// URL may either be a TileJSON file, or a GeoJSON file.
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 TileJSON")));
} else {
std::unique_ptr<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 = style::parseTileJSON(*res.data, url, type, tileSize);
} catch (...) {
observer->onSourceError(*this, std::current_exception());
return;
}
// Check whether previous information specifies different tile
if (tileset && 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 = std::move(newTileset);
loaded = true;
observer->onSourceLoaded(*this);
}
});
}
} // namespace style
} // namespace mbgl

View File

@@ -16,6 +16,8 @@ public:
std::string url,
uint16_t tileSize,
std::unique_ptr<Tileset>&&);
void load(FileSource&) final;
};
} // namespace style