diff options
author | Konstantin Käfer <mail@kkaefer.com> | 2016-01-13 12:07:33 +0100 |
---|---|---|
committer | John Firebaugh <john.firebaugh@gmail.com> | 2016-01-13 13:04:59 -0800 |
commit | e8705d535d56415ff8f97db1ad6b5a77ed3fcd4a (patch) | |
tree | 7c825e483be49a7b069fc30cf79fa83bfa8b1a39 | |
parent | cef05b9017d4e54e61354a278120ff67879f2acb (diff) | |
download | qtlocation-mapboxgl-e8705d535d56415ff8f97db1ad6b5a77ed3fcd4a.tar.gz |
[core] move SourceInfo parsing to StyleParser
-rw-r--r-- | src/mbgl/annotation/annotation_manager.cpp | 2 | ||||
-rw-r--r-- | src/mbgl/map/source.cpp | 157 | ||||
-rw-r--r-- | src/mbgl/map/source.hpp | 14 | ||||
-rw-r--r-- | src/mbgl/map/source_info.hpp | 1 | ||||
-rw-r--r-- | src/mbgl/style/style_parser.cpp | 230 | ||||
-rw-r--r-- | src/mbgl/style/style_parser.hpp | 6 | ||||
-rw-r--r-- | test/fixtures/style_parser/geojson-missing-data.info.json | 2 |
7 files changed, 201 insertions, 211 deletions
diff --git a/src/mbgl/annotation/annotation_manager.cpp b/src/mbgl/annotation/annotation_manager.cpp index 41bebbbdea..4884ae446f 100644 --- a/src/mbgl/annotation/annotation_manager.cpp +++ b/src/mbgl/annotation/annotation_manager.cpp @@ -112,7 +112,7 @@ std::unique_ptr<AnnotationTile> AnnotationManager::getTile(const TileID& tileID) void AnnotationManager::updateStyle(Style& style) { // Create annotation source, point layer, and point bucket if (!style.getSource(SourceID)) { - std::unique_ptr<Source> source = std::make_unique<Source>(SourceType::Annotations, SourceID); + std::unique_ptr<Source> source = std::make_unique<Source>(SourceType::Annotations, SourceID, "", std::make_unique<SourceInfo>(), nullptr); source->enabled = true; style.addSource(std::move(source)); diff --git a/src/mbgl/map/source.cpp b/src/mbgl/map/source.cpp index f4c3f877ed..ed57ddce0b 100644 --- a/src/mbgl/map/source.cpp +++ b/src/mbgl/map/source.cpp @@ -27,6 +27,7 @@ #include <mbgl/map/vector_tile_data.hpp> #include <mbgl/map/raster_tile_data.hpp> #include <mbgl/style/style.hpp> +#include <mbgl/style/style_parser.hpp> #include <mbgl/gl/debugging.hpp> #include <mapbox/geojsonvt.hpp> @@ -39,86 +40,18 @@ namespace mbgl { -namespace { - -void parse(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 parse(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() }; +Source::Source(SourceType type_, + const std::string& id_, + const std::string& url_, + std::unique_ptr<SourceInfo>&& info_, + std::unique_ptr<mapbox::geojsonvt::GeoJSONVT>&& geojsonvt_) + : type(type_), + id(id_), + url(url_), + info(std::move(info_)), + geojsonvt(std::move(geojsonvt_)) { } -void parse(const JSValue& value, uint16_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<uint16_t>::max()) { - return; - } - - target = uint; -} - -template <size_t N> -void parse(const JSValue& value, std::array<float, N>& target, const char* name) { - if (!value.HasMember(name)) { - return; - } - - const JSValue& property = value[name]; - if (!property.IsArray() || property.Size() != N) { - 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 - -Source::Source(SourceType type_, const std::string& id_) : type(type_), id(id_) {} - Source::~Source() = default; bool Source::isLoaded() const { @@ -141,16 +74,22 @@ bool Source::isLoading() const { // The reason this isn't part of the constructor is that calling shared_from_this() in // the constructor fails. void Source::load() { - if (info.url.empty()) { + 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) return; + if (req) { + // We don't have a SourceInfo 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. FileSource* fs = util::ThreadContext::getFileSource(); - req = fs->request({ Resource::Kind::Source, info.url }, [this](Response res) { + req = fs->request({ Resource::Kind::Source, url }, [this](Response res) { if (res.stale) { // Only handle fresh responses. return; @@ -173,17 +112,22 @@ void Source::load() { } if (type == SourceType::Vector || type == SourceType::Raster) { - parseTileJSON(d); + // Create a new copy of the SourceInfo 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. + auto newInfo = StyleParser::parseTileJSON(d); // TODO: Remove this hack by delivering proper URLs in the TileJSON to begin with. - if (type == SourceType::Raster && util::mapbox::isMapboxURL(info.url)) { + if (type == SourceType::Raster && util::mapbox::isMapboxURL(url)) { // We need to insert {ratio} into raster source URLs that are loaded from mapbox:// // TileJSONs. - std::transform(info.tiles.begin(), info.tiles.end(), info.tiles.begin(), + std::transform(newInfo->tiles.begin(), newInfo->tiles.end(), newInfo->tiles.begin(), util::mapbox::normalizeRasterTileURL); } + info = std::move(newInfo); } else if (type == SourceType::GeoJSON) { - parseGeoJSON(d); + info = std::make_unique<SourceInfo>(); + geojsonvt = StyleParser::parseGeoJSON(d); } loaded = true; @@ -191,32 +135,10 @@ void Source::load() { }); } -void Source::parseTileJSON(const JSValue& value) { - parse(value, info.tiles, "tiles"); - parse(value, info.min_zoom, "minzoom"); - parse(value, info.max_zoom, "maxzoom"); - parse(value, info.attribution, "attribution"); - parse(value, info.center, "center"); - parse(value, info.bounds, "bounds"); -} - -void Source::parseGeoJSON(const JSValue& value) { - using namespace mapbox::geojsonvt; - - try { - geojsonvt = std::make_unique<GeoJSONVT>(Convert::convert(value, 0)); - } 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. - geojsonvt = std::make_unique<GeoJSONVT>(std::vector<ProjectedFeature>{}); - } -} - void Source::updateMatrices(const mat4 &projMatrix, const TransformState &transform) { for (const auto& pair : tiles) { Tile &tile = *pair.second; - transform.matrixFor(tile.matrix, tile.id, std::min(static_cast<int8_t>(info.max_zoom), tile.id.z)); + transform.matrixFor(tile.matrix, tile.id, std::min(static_cast<int8_t>(info->max_zoom), tile.id.z)); matrix::multiply(tile.matrix, projMatrix, tile.matrix); } } @@ -315,14 +237,13 @@ TileData::State Source::addTile(const TileID& tileID, const StyleUpdateParameter auto tileData = std::make_shared<RasterTileData>(normalizedID, parameters.texturePool, parameters.worker); - - tileData->request(util::templateTileURL(info.tiles.at(0), normalizedID, parameters.pixelRatio), callback); + tileData->request(util::templateTileURL(info->tiles.at(0), normalizedID, parameters.pixelRatio), callback); newTile->data = tileData; } else { std::unique_ptr<GeometryTileMonitor> monitor; if (type == SourceType::Vector) { - monitor = std::make_unique<VectorTileMonitor>(normalizedID, info.tiles.at(0)); + monitor = std::make_unique<VectorTileMonitor>(normalizedID, info->tiles.at(0)); } else if (type == SourceType::Annotations) { monitor = std::make_unique<AnnotationTileMonitor>(normalizedID, parameters.data); } else if (type == SourceType::GeoJSON) { @@ -349,7 +270,7 @@ TileData::State Source::addTile(const TileID& tileID, const StyleUpdateParameter } double Source::getZoom(const TransformState& state) const { - double offset = std::log(util::tileSize / info.tile_size) / std::log(2); + double offset = std::log(util::tileSize / info->tile_size) / std::log(2); return state.getZoom() + offset; } @@ -371,8 +292,8 @@ std::forward_list<TileID> Source::coveringTiles(const TransformState& state) con type == SourceType::Vector || type == SourceType::Annotations; - if (z < info.min_zoom) return {{}}; - if (z > info.max_zoom) z = info.max_zoom; + if (z < info->min_zoom) return {{}}; + if (z > info->max_zoom) z = info->max_zoom; // Map four viewport corners to pixel coordinates box points = state.cornersToBox(z); @@ -401,7 +322,7 @@ std::forward_list<TileID> Source::coveringTiles(const TransformState& state) con bool Source::findLoadedChildren(const TileID& tileID, int32_t maxCoveringZoom, std::forward_list<TileID>& retain) { bool complete = true; int32_t z = tileID.z; - auto ids = tileID.children(info.max_zoom); + auto ids = tileID.children(info->max_zoom); for (const auto& child_id : ids) { const TileData::State state = hasTile(child_id); if (TileData::isReadyState(state)) { @@ -429,7 +350,7 @@ bool Source::findLoadedChildren(const TileID& tileID, int32_t maxCoveringZoom, s */ void Source::findLoadedParent(const TileID& tileID, int32_t minCoveringZoom, std::forward_list<TileID>& retain) { for (int32_t z = tileID.z - 1; z >= minCoveringZoom; --z) { - const TileID parent_id = tileID.parent(z, info.max_zoom); + const TileID parent_id = tileID.parent(z, info->max_zoom); const TileData::State state = hasTile(parent_id); if (TileData::isReadyState(state)) { retain.emplace_front(parent_id); @@ -451,8 +372,8 @@ bool Source::update(const StyleUpdateParameters& parameters) { std::forward_list<TileID> required = coveringTiles(parameters.transformState); // Determine the overzooming/underzooming amounts. - int32_t minCoveringZoom = util::clamp<int32_t>(zoom - 10, info.min_zoom, info.max_zoom); - int32_t maxCoveringZoom = util::clamp<int32_t>(zoom + 1, info.min_zoom, info.max_zoom); + int32_t minCoveringZoom = util::clamp<int32_t>(zoom - 10, info->min_zoom, info->max_zoom); + int32_t maxCoveringZoom = util::clamp<int32_t>(zoom + 1, info->min_zoom, info->max_zoom); // Retain is a list of tiles that we shouldn't delete, even if they are not // the most ideal tile for the current viewport. This may include tiles like diff --git a/src/mbgl/map/source.hpp b/src/mbgl/map/source.hpp index 079ac5c2db..f3688ed9e8 100644 --- a/src/mbgl/map/source.hpp +++ b/src/mbgl/map/source.hpp @@ -39,12 +39,13 @@ public: virtual void onTileError(Source&, const TileID&, std::exception_ptr) {}; }; - Source(SourceType, const std::string& id); + Source(SourceType, + const std::string& id, + const std::string& url, + std::unique_ptr<SourceInfo>&&, + std::unique_ptr<mapbox::geojsonvt::GeoJSONVT>&&); ~Source(); - void parseTileJSON(const JSValue&); - void parseGeoJSON(const JSValue&); - bool loaded = false; void load(); bool isLoading() const; @@ -71,7 +72,7 @@ public: const SourceType type; const std::string id; - SourceInfo info; + const std::string url; bool enabled = false; private: @@ -88,6 +89,9 @@ private: double getZoom(const TransformState &state) const; +private: + std::unique_ptr<const SourceInfo> info; + std::unique_ptr<mapbox::geojsonvt::GeoJSONVT> geojsonvt; // Stores the time when this source was most recently updated. diff --git a/src/mbgl/map/source_info.hpp b/src/mbgl/map/source_info.hpp index 1b8c7a40f9..0f0ee1cad4 100644 --- a/src/mbgl/map/source_info.hpp +++ b/src/mbgl/map/source_info.hpp @@ -15,7 +15,6 @@ class TileID; class SourceInfo { public: - std::string url; std::vector<std::string> tiles; uint16_t tile_size = util::tileSize; uint16_t min_zoom = 0; diff --git a/src/mbgl/style/style_parser.cpp b/src/mbgl/style/style_parser.cpp index 6878907cd1..3a94160710 100644 --- a/src/mbgl/style/style_parser.cpp +++ b/src/mbgl/style/style_parser.cpp @@ -8,10 +8,92 @@ #include <mbgl/platform/log.hpp> +#include <mapbox/geojsonvt.hpp> +#include <mapbox/geojsonvt/convert.hpp> + #include <algorithm> namespace mbgl { + +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, uint16_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<uint16_t>::max()) { + return; + } + + target = uint; +} + +template <size_t N> +void parseTileJSONMember(const JSValue& value, std::array<float, N>& target, const char* name) { + if (!value.HasMember(name)) { + return; + } + + const JSValue& property = value[name]; + if (!property.IsArray() || property.Size() != N) { + 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 + StyleParser::~StyleParser() = default; void StyleParser::parse(const JSValue& document) { @@ -68,110 +150,94 @@ void StyleParser::parseSources(const JSValue& value) { } const auto type = SourceTypeClass({ typeVal.GetString(), typeVal.GetStringLength() }); - const std::string id { nameVal.GetString(), nameVal.GetStringLength() }; - std::unique_ptr<Source> source = std::make_unique<Source>(type, id); + + // 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; + + std::unique_ptr<SourceInfo> info; + std::unique_ptr<mapbox::geojsonvt::GeoJSONVT> geojsonvt; switch (type) { case SourceType::Vector: - if (!parseVectorSource(*source, sourceVal)) { - continue; - } - break; case SourceType::Raster: - if (!parseRasterSource(*source, sourceVal)) { - continue; + 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 { + info = parseTileJSON(sourceVal); } break; + case SourceType::GeoJSON: - if (!parseGeoJSONSource(*source, sourceVal)) { + // 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 (sourceVal.HasMember("data")) { + const JSValue& dataVal = sourceVal["data"]; + if (dataVal.IsString()) { + // We need to load an external GeoJSON file + url = { dataVal.GetString(), dataVal.GetStringLength() }; + } else if (dataVal.IsObject()) { + // We need to parse dataVal as a GeoJSON object + // TODO: parse GeoJSON data + geojsonvt = parseGeoJSON(dataVal); + } else { + Log::Error(Event::ParseStyle, "GeoJSON data must be a URL or an object"); + continue; + } + } else { + Log::Error(Event::ParseStyle, "GeoJSON source must have a data value"); continue; } - break; - default: - Log::Warning(Event::ParseStyle, "source type %s is not supported", type.c_str()); - } - sourcesMap.emplace(id, source.get()); - sources.emplace_back(std::move(source)); - } -} + // We always assume the default configuration for GeoJSON sources. + info = std::make_unique<SourceInfo>(); -bool StyleParser::parseVectorSource(Source& source, const JSValue& sourceVal) { - // A vector tile source either specifies the URL of a TileJSON file... - if (sourceVal.HasMember("url")) { - const JSValue& urlVal = sourceVal["url"]; + break; - if (!urlVal.IsString()) { - Log::Warning(Event::ParseStyle, "source url must be a string"); - return false; + default: + Log::Error(Event::ParseStyle, "source type '%s' is not supported", typeVal.GetString()); + continue; } - source.info.url = { urlVal.GetString(), urlVal.GetStringLength() }; + const std::string id { nameVal.GetString(), nameVal.GetStringLength() }; + std::unique_ptr<Source> source = std::make_unique<Source>(type, id, url, std::move(info), std::move(geojsonvt)); - } else { - // ...or the TileJSON directly. - source.parseTileJSON(sourceVal); + sourcesMap.emplace(id, source.get()); + sources.emplace_back(std::move(source)); } - - return true; } -bool StyleParser::parseRasterSource(Source& source, const JSValue& sourceVal) { - if (sourceVal.HasMember("tileSize")) { - const JSValue& tileSizeVal = sourceVal["tileSize"]; +std::unique_ptr<mapbox::geojsonvt::GeoJSONVT> StyleParser::parseGeoJSON(const JSValue& value) { + using namespace mapbox::geojsonvt; - if (!tileSizeVal.IsUint()) { - Log::Warning(Event::ParseStyle, "source tileSize must be an unsigned integer"); - return false; - } - - unsigned int intValue = tileSizeVal.GetUint(); - if (intValue > std::numeric_limits<uint16_t>::max()) { - Log::Warning(Event::ParseStyle, "values for tileSize that are larger than %d are not supported", std::numeric_limits<uint16_t>::max()); - return false; - } - - source.info.tile_size = intValue; + try { + return std::make_unique<GeoJSONVT>(Convert::convert(value, 0)); + } 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<GeoJSONVT>(std::vector<ProjectedFeature>{}); } - - // A raster tile source either specifies the URL of a TileJSON file... - if (sourceVal.HasMember("url")) { - const JSValue& urlVal = sourceVal["url"]; - - if (!urlVal.IsString()) { - Log::Warning(Event::ParseStyle, "source url must be a string"); - return false; - } - - source.info.url = { urlVal.GetString(), urlVal.GetStringLength() }; - - } else { - // ...or the TileJSON directly. - source.parseTileJSON(sourceVal); - } - - return true; } -bool StyleParser::parseGeoJSONSource(Source& source, const JSValue& sourceVal) { - if (!sourceVal.HasMember("data")) { - Log::Warning(Event::ParseStyle, "GeoJSON source must have a data value"); - return false; - } - - const JSValue& dataVal = sourceVal["data"]; - if (dataVal.IsString()) { - // We need to load an external GeoJSON file - source.info.url = { dataVal.GetString(), dataVal.GetStringLength() }; - } else if (dataVal.IsObject()) { - // We need to parse dataVal as a GeoJSON object - source.parseGeoJSON(dataVal); - } else { - Log::Error(Event::ParseStyle, "GeoJSON data must be a URL or an object"); - return false; - } - - return true; +std::unique_ptr<SourceInfo> StyleParser::parseTileJSON(const JSValue& value) { + auto info = std::make_unique<SourceInfo>(); + parseTileJSONMember(value, info->tiles, "tiles"); + parseTileJSONMember(value, info->tile_size, "tileSize"); + parseTileJSONMember(value, info->min_zoom, "minzoom"); + parseTileJSONMember(value, info->max_zoom, "maxzoom"); + parseTileJSONMember(value, info->attribution, "attribution"); + parseTileJSONMember(value, info->center, "center"); + parseTileJSONMember(value, info->bounds, "bounds"); + return std::move(info); } void StyleParser::parseLayers(const JSValue& value) { diff --git a/src/mbgl/style/style_parser.hpp b/src/mbgl/style/style_parser.hpp index 6242945c46..d3bb401d44 100644 --- a/src/mbgl/style/style_parser.hpp +++ b/src/mbgl/style/style_parser.hpp @@ -28,11 +28,11 @@ public: std::vector<std::unique_ptr<Source>> sources; std::vector<std::unique_ptr<StyleLayer>> layers; + static std::unique_ptr<mapbox::geojsonvt::GeoJSONVT> parseGeoJSON(const JSValue&); + static std::unique_ptr<SourceInfo> parseTileJSON(const JSValue&); + private: void parseSources(const JSValue&); - bool parseVectorSource(Source&, const JSValue&); - bool parseRasterSource(Source&, const JSValue&); - bool parseGeoJSONSource(Source&, const JSValue&); void parseLayers(const JSValue&); void parseLayer(const std::string& id, const JSValue&, std::unique_ptr<StyleLayer>&); void parseVisibility(StyleLayer&, const JSValue& value); diff --git a/test/fixtures/style_parser/geojson-missing-data.info.json b/test/fixtures/style_parser/geojson-missing-data.info.json index 594d01d19d..2c4806c3cf 100644 --- a/test/fixtures/style_parser/geojson-missing-data.info.json +++ b/test/fixtures/style_parser/geojson-missing-data.info.json @@ -1,7 +1,7 @@ { "default": { "log": [ - [1, "WARNING", "ParseStyle", "GeoJSON source must have a data value"] + [1, "ERROR", "ParseStyle", "GeoJSON source must have a data value"] ] } } |