Skip to content
This repository has been archived by the owner on Aug 8, 2023. It is now read-only.

[core] Rationalize URL vs Tileset handling #5358

Merged
merged 3 commits into from
Jun 15, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 20 additions & 11 deletions platform/default/mbgl/storage/offline_download.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,14 +107,17 @@ OfflineRegionStatus OfflineDownload::getStatus() const {
case SourceType::Vector:
case SourceType::Raster: {
style::TileSource* tileSource = static_cast<style::TileSource*>(source.get());
if (tileSource->getTileset()) {
result.requiredResourceCount += tileResources(source->type, tileSource->getTileSize(), *tileSource->getTileset()).size();
const variant<std::string, Tileset>& urlOrTileset = tileSource->getURLOrTileset();

if (urlOrTileset.is<Tileset>()) {
result.requiredResourceCount += tileResources(source->type, tileSource->getTileSize(), urlOrTileset.get<Tileset>()).size();
} else {
result.requiredResourceCount += 1;
optional<Response> sourceResponse = offlineDatabase.get(Resource::source(tileSource->getURL()));
const std::string& url = urlOrTileset.get<std::string>();
optional<Response> sourceResponse = offlineDatabase.get(Resource::source(url));
if (sourceResponse) {
result.requiredResourceCount += tileResources(source->type, tileSource->getTileSize(),
*style::parseTileJSON(*sourceResponse->data, tileSource->getURL(), source->type, tileSource->getTileSize())).size();
style::TileSource::parseTileJSON(*sourceResponse->data, url, source->type, tileSource->getTileSize())).size();
} else {
result.requiredResourceCountIsPrecise = false;
}
Expand All @@ -124,7 +127,9 @@ OfflineRegionStatus OfflineDownload::getStatus() const {

case SourceType::GeoJSON: {
style::GeoJSONSource* geojsonSource = static_cast<style::GeoJSONSource*>(source.get());
if (!geojsonSource->getURL().empty()) {
const variant<std::string, style::GeoJSONSource::GeoJSON>& urlOrGeoJSON = geojsonSource->getURLOrGeoJSON();

if (urlOrGeoJSON.is<std::string>()) {
result.requiredResourceCount += 1;
}
break;
Expand Down Expand Up @@ -161,16 +166,18 @@ void OfflineDownload::activateDownload() {
case SourceType::Vector:
case SourceType::Raster: {
const style::TileSource* tileSource = static_cast<style::TileSource*>(source.get());
const variant<std::string, Tileset>& urlOrTileset = tileSource->getURLOrTileset();
const uint16_t tileSize = tileSource->getTileSize();
if (tileSource->getTileset()) {
ensureTiles(type, tileSize, *tileSource->getTileset());

if (urlOrTileset.is<Tileset>()) {
ensureTiles(type, tileSize, urlOrTileset.get<Tileset>());
} else {
std::string url = tileSource->getURL();
const std::string& url = urlOrTileset.get<std::string>();
status.requiredResourceCountIsPrecise = false;
requiredSourceURLs.insert(url);

ensureResource(Resource::source(url), [=] (Response sourceResponse) {
ensureTiles(type, tileSize, *style::parseTileJSON(*sourceResponse.data, url, type, tileSize));
ensureTiles(type, tileSize, style::TileSource::parseTileJSON(*sourceResponse.data, url, type, tileSize));

requiredSourceURLs.erase(url);
if (requiredSourceURLs.empty()) {
Expand All @@ -183,8 +190,10 @@ void OfflineDownload::activateDownload() {

case SourceType::GeoJSON: {
style::GeoJSONSource* geojsonSource = static_cast<style::GeoJSONSource*>(source.get());
if (!geojsonSource->getURL().empty()) {
ensureResource(Resource::source(geojsonSource->getURL()));
const variant<std::string, style::GeoJSONSource::GeoJSON>& urlOrGeoJSON = geojsonSource->getURLOrGeoJSON();

if (urlOrGeoJSON.is<std::string>()) {
ensureResource(Resource::source(urlOrGeoJSON.get<std::string>()));
}
break;
}
Expand Down
154 changes: 4 additions & 150 deletions src/mbgl/style/parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,97 +13,18 @@
#include <mbgl/platform/log.hpp>

#include <mbgl/tile/geometry_tile_data.hpp>
#include <mbgl/util/mapbox.hpp>
#include <mbgl/util/enum.hpp>
#include <mbgl/util/tileset.hpp>

#include <rapidjson/document.h>
#include <rapidjson/error/en.h>

#include <algorithm>
#include <sstream>
#include <set>

namespace mbgl {
namespace style {

namespace {

void parseTileJSONMember(const JSValue& value, std::vector<std::string>& 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<uint8_t>::max()) {
return;
}

target = uint;
}

void parseTileJSONMember(const JSValue& value, std::array<double, 4>& target, const char* name) {
if (!value.HasMember(name)) {
return;
}

const JSValue& property = value[name];
if (!property.IsArray() || property.Size() > 4) {
return;
}

for (rapidjson::SizeType i = 0; i < property.Size(); i++) {
if (!property[i].IsNumber()) {
return;
}
}

for (rapidjson::SizeType i = 0; i < property.Size(); i++) {
target[i] = property[i].GetDouble();
}
}

} // end namespace

Parser::~Parser() = default;

void Parser::parse(const std::string& json) {
Expand Down Expand Up @@ -174,44 +95,16 @@ void Parser::parseSources(const JSValue& value) {
}

const std::string id { nameVal.GetString(), nameVal.GetStringLength() };

std::unique_ptr<Tileset> tileset;
std::unique_ptr<Source> source;

// Sources can have URLs, either because they reference an external TileJSON file, or
// because reference a GeoJSON file. They don't have to have one though when all source
// parameters are specified inline.
std::string url;
if (sourceVal.HasMember("url")) {
const JSValue& urlVal = sourceVal["url"];
if (urlVal.IsString()) {
url = { urlVal.GetString(), urlVal.GetStringLength() };
} else {
Log::Error(Event::ParseStyle, "source url must be a string");
continue;
}
} else {
tileset = parseTileJSON(sourceVal);
}

uint16_t tileSize = util::tileSize;
if (sourceVal.HasMember("tileSize")) {
const JSValue& tileSizeVal = sourceVal["tileSize"];
if (tileSizeVal.IsNumber() && tileSizeVal.GetUint64() <= std::numeric_limits<uint16_t>::max()) {
tileSize = tileSizeVal.GetUint64();
} else {
Log::Error(Event::ParseStyle, "invalid tileSize");
continue;
}
}

switch (*type) {
case SourceType::Raster:
source = std::make_unique<RasterSource>(id, url, tileSize, std::move(tileset));
case SourceType::Raster: {
source = RasterSource::parse(id, sourceVal);
break;
}

case SourceType::Vector:
source = std::make_unique<VectorSource>(id, url, std::move(tileset));
source = VectorSource::parse(id, sourceVal);
break;

case SourceType::GeoJSON:
Expand All @@ -230,45 +123,6 @@ void Parser::parseSources(const JSValue& value) {
}
}

std::unique_ptr<Tileset> parseTileJSON(const std::string& json, const std::string& sourceURL, SourceType type, uint16_t tileSize) {
rapidjson::GenericDocument<rapidjson::UTF8<>, 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());
}

std::unique_ptr<Tileset> result = 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;
}

std::unique_ptr<Tileset> parseTileJSON(const JSValue& value) {
auto tileset = std::make_unique<Tileset>();
parseTileJSONMember(value, tileset->tiles, "tiles");
parseTileJSONMember(value, tileset->zoomRange.min, "minzoom");
parseTileJSONMember(value, tileset->zoomRange.max, "maxzoom");
parseTileJSONMember(value, tileset->attribution, "attribution");

std::array<double, 4> array;
parseTileJSONMember(value, array, "center");
tileset->center = { array[0], array[1] };
tileset->zoom = array[2];
parseTileJSONMember(value, array, "bounds");
tileset->bounds = LatLngBounds::hull({ array[0], array[1] }, { array[2], array[3] });

return tileset;
}

void Parser::parseLayers(const JSValue& value) {
std::vector<std::string> ids;

Expand Down
6 changes: 0 additions & 6 deletions src/mbgl/style/parser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,8 @@
#include <forward_list>

namespace mbgl {

class Tileset;

namespace style {

std::unique_ptr<Tileset> parseTileJSON(const std::string& json, const std::string& sourceURL, SourceType, uint16_t tileSize);
std::unique_ptr<Tileset> parseTileJSON(const JSValue&);

Filter parseFilter(const JSValue&);

class Parser {
Expand Down
43 changes: 17 additions & 26 deletions src/mbgl/style/sources/geojson_source.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
namespace mbgl {
namespace style {

std::unique_ptr<mapbox::geojsonvt::GeoJSONVT> parseGeoJSON(const JSValue& value) {
std::unique_ptr<mapbox::geojsonvt::GeoJSONVT> GeoJSONSource::parseGeoJSON(const JSValue& value) {
using namespace mapbox::geojsonvt;

Options options;
Expand All @@ -32,11 +32,7 @@ std::unique_ptr<mapbox::geojsonvt::GeoJSONVT> parseGeoJSON(const JSValue& value)
}
}

std::unique_ptr<GeoJSONSource> GeoJSONSource::parse(const std::string& id,
const JSValue& value) {
std::unique_ptr<mapbox::geojsonvt::GeoJSONVT> geojsonvt;
std::string url;

std::unique_ptr<GeoJSONSource> 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.
Expand All @@ -47,36 +43,24 @@ std::unique_ptr<GeoJSONSource> GeoJSONSource::parse(const std::string& id,

const JSValue& dataVal = value["data"];
if (dataVal.IsString()) {
// We need to load an external GeoJSON file
url = { dataVal.GetString(), dataVal.GetStringLength() };
return std::make_unique<GeoJSONSource>(id, std::string(dataVal.GetString(), dataVal.GetStringLength()));
} else if (dataVal.IsObject()) {
// We need to parse dataVal as a GeoJSON object
geojsonvt = parseGeoJSON(dataVal);
return std::make_unique<GeoJSONSource>(id, parseGeoJSON(dataVal));
} else {
Log::Error(Event::ParseStyle, "GeoJSON data must be a URL or an object");
return nullptr;
}

return std::make_unique<GeoJSONSource>(id, url, std::move(geojsonvt));
}

GeoJSONSource::GeoJSONSource(std::string id_,
std::string url_,
std::unique_ptr<mapbox::geojsonvt::GeoJSONVT>&& geojsonvt_)
GeoJSONSource::GeoJSONSource(std::string id_, variant<std::string, GeoJSON> urlOrGeoJSON_)
: Source(SourceType::GeoJSON, std::move(id_)),
url(std::move(url_)),
geojsonvt(std::move(geojsonvt_)) {
urlOrGeoJSON(std::move(urlOrGeoJSON_)) {
}

GeoJSONSource::~GeoJSONSource() = default;

Range<uint8_t> GeoJSONSource::getZoomRange() {
return { 0, geojsonvt->options.maxZoom };
}

void GeoJSONSource::load(FileSource& fileSource) {
if (url.empty()) {
// If the URL is empty, the GeoJSON was specified inline in the stylesheet.
if (urlOrGeoJSON.is<GeoJSON>()) {
loaded = true;
return;
}
Expand All @@ -85,6 +69,7 @@ void GeoJSONSource::load(FileSource& fileSource) {
return;
}

const std::string& url = urlOrGeoJSON.get<std::string>();
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)));
Expand All @@ -103,19 +88,25 @@ void GeoJSONSource::load(FileSource& fileSource) {
return;
}

geojsonvt = style::parseGeoJSON(d);

invalidateTiles();

urlOrGeoJSON = parseGeoJSON(d);
loaded = true;

observer->onSourceLoaded(*this);
}
});
}

Range<uint8_t> GeoJSONSource::getZoomRange() {
assert(loaded);
return { 0, urlOrGeoJSON.get<GeoJSON>()->options.maxZoom };
}

std::unique_ptr<Tile> GeoJSONSource::createTile(const OverscaledTileID& tileID,
const UpdateParameters& parameters) {
return std::make_unique<GeoJSONTile>(tileID, id, parameters, geojsonvt.get());
assert(loaded);
return std::make_unique<GeoJSONTile>(tileID, id, parameters, *urlOrGeoJSON.get<GeoJSON>());
}

} // namespace style
Expand Down
Loading