diff options
author | Ivo van Dongen <info@ivovandongen.nl> | 2018-03-23 15:44:21 +0200 |
---|---|---|
committer | Ivo van Dongen <ivovandongen@users.noreply.github.com> | 2018-08-20 22:49:01 +0300 |
commit | fae099933b23a36176dcc8c4a91c37816fa9b7fe (patch) | |
tree | c1a57fb87405cd341c2dc8e5b5bffc9b4b08cc7f /platform/default | |
parent | cf0313f21919b0df4840d3085d9db3fe4b6bbbec (diff) | |
download | qtlocation-mapboxgl-fae099933b23a36176dcc8c4a91c37816fa9b7fe.tar.gz |
[core] offline region definition - add support for arbitrary geometries
Diffstat (limited to 'platform/default')
-rw-r--r-- | platform/default/mbgl/storage/offline.cpp | 133 | ||||
-rw-r--r-- | platform/default/mbgl/storage/offline_download.cpp | 85 |
2 files changed, 149 insertions, 69 deletions
diff --git a/platform/default/mbgl/storage/offline.cpp b/platform/default/mbgl/storage/offline.cpp index 598a0b182b..e1ec0acb31 100644 --- a/platform/default/mbgl/storage/offline.cpp +++ b/platform/default/mbgl/storage/offline.cpp @@ -1,8 +1,10 @@ #include <mbgl/storage/offline.hpp> -#include <mbgl/util/tile_cover.hpp> #include <mbgl/util/tileset.hpp> #include <mbgl/util/projection.hpp> +#include <mapbox/geojson.hpp> +#include <mapbox/geojson/rapidjson.hpp> + #include <rapidjson/document.h> #include <rapidjson/stringbuffer.h> #include <rapidjson/writer.h> @@ -11,6 +13,8 @@ namespace mbgl { +// OfflineTilePyramidRegionDefinition + OfflineTilePyramidRegionDefinition::OfflineTilePyramidRegionDefinition( std::string styleURL_, LatLngBounds bounds_, double minZoom_, double maxZoom_, float pixelRatio_) : styleURL(std::move(styleURL_)), @@ -24,87 +28,100 @@ OfflineTilePyramidRegionDefinition::OfflineTilePyramidRegionDefinition( } } -std::vector<CanonicalTileID> OfflineTilePyramidRegionDefinition::tileCover(style::SourceType type, uint16_t tileSize, const Range<uint8_t>& zoomRange) const { - const Range<uint8_t> clampedZoomRange = coveringZoomRange(type, tileSize, zoomRange); - std::vector<CanonicalTileID> result; - - for (uint8_t z = clampedZoomRange.min; z <= clampedZoomRange.max; z++) { - for (const auto& tile : util::tileCover(bounds, z)) { - result.emplace_back(tile.canonical); - } - } - - return result; -} +// OfflineGeometryRegionDefinition -uint64_t OfflineTilePyramidRegionDefinition::tileCount(style::SourceType type, uint16_t tileSize, const Range<uint8_t>& zoomRange) const { - - const Range<uint8_t> clampedZoomRange = coveringZoomRange(type, tileSize, zoomRange); - unsigned long result = 0;; - for (uint8_t z = clampedZoomRange.min; z <= clampedZoomRange.max; z++) { - result += util::tileCount(bounds, z); +OfflineGeometryRegionDefinition::OfflineGeometryRegionDefinition(std::string styleURL_, Geometry<double> geometry_, double minZoom_, double maxZoom_, float pixelRatio_) + : styleURL(styleURL_) + , geometry(std::move(geometry_)) + , minZoom(minZoom_) + , maxZoom(maxZoom_) + , pixelRatio(pixelRatio_) { + if (minZoom < 0 || maxZoom < 0 || maxZoom < minZoom || pixelRatio < 0 || + !std::isfinite(minZoom) || std::isnan(maxZoom) || !std::isfinite(pixelRatio)) { + throw std::invalid_argument("Invalid offline region definition"); } - - return result; -} - -Range<uint8_t> OfflineTilePyramidRegionDefinition::coveringZoomRange(style::SourceType type, uint16_t tileSize, const Range<uint8_t>& zoomRange) const { - double minZ = std::max<double>(util::coveringZoomLevel(minZoom, type, tileSize), zoomRange.min); - double maxZ = std::min<double>(util::coveringZoomLevel(maxZoom, type, tileSize), zoomRange.max); - - assert(minZ >= 0); - assert(maxZ >= 0); - assert(minZ < std::numeric_limits<uint8_t>::max()); - assert(maxZ < std::numeric_limits<uint8_t>::max()); - return { static_cast<uint8_t>(minZ), static_cast<uint8_t>(maxZ) }; } OfflineRegionDefinition decodeOfflineRegionDefinition(const std::string& region) { rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator> doc; doc.Parse<0>(region.c_str()); - if (doc.HasParseError() || - !doc.HasMember("style_url") || !doc["style_url"].IsString() || - !doc.HasMember("bounds") || !doc["bounds"].IsArray() || doc["bounds"].Size() != 4 || - !doc["bounds"][0].IsDouble() || !doc["bounds"][1].IsDouble() || - !doc["bounds"][2].IsDouble() || !doc["bounds"][3].IsDouble() || - !doc.HasMember("min_zoom") || !doc["min_zoom"].IsDouble() || - (doc.HasMember("max_zoom") && !doc["max_zoom"].IsDouble()) || - !doc.HasMember("pixel_ratio") || !doc["pixel_ratio"].IsDouble()) { + // validation + + auto hasValidBounds = [&] { + return doc.HasMember("bounds") && doc["bounds"].IsArray() && doc["bounds"].Size() == 4 + && doc["bounds"][0].IsDouble() && doc["bounds"][1].IsDouble() + && doc["bounds"][2].IsDouble() && doc["bounds"][3].IsDouble(); + }; + + auto hasValidGeometry = [&] { + return doc.HasMember("geometry") && doc["geometry"].IsObject(); + }; + + if (doc.HasParseError() + || !doc.HasMember("style_url") || !doc["style_url"].IsString() + || !(hasValidBounds() || hasValidGeometry()) + || !doc.HasMember("min_zoom") || !doc["min_zoom"].IsDouble() + || (doc.HasMember("max_zoom") && !doc["max_zoom"].IsDouble()) + || !doc.HasMember("pixel_ratio") || !doc["pixel_ratio"].IsDouble()) { throw std::runtime_error("Malformed offline region definition"); } + // Common properties + std::string styleURL { doc["style_url"].GetString(), doc["style_url"].GetStringLength() }; - LatLngBounds bounds = LatLngBounds::hull( - LatLng(doc["bounds"][0].GetDouble(), doc["bounds"][1].GetDouble()), - LatLng(doc["bounds"][2].GetDouble(), doc["bounds"][3].GetDouble())); double minZoom = doc["min_zoom"].GetDouble(); double maxZoom = doc.HasMember("max_zoom") ? doc["max_zoom"].GetDouble() : INFINITY; float pixelRatio = doc["pixel_ratio"].GetDouble(); + + if (doc.HasMember("bounds")) { + return OfflineTilePyramidRegionDefinition{ + styleURL, + LatLngBounds::hull( + LatLng(doc["bounds"][0].GetDouble(), doc["bounds"][1].GetDouble()), + LatLng(doc["bounds"][2].GetDouble(), doc["bounds"][3].GetDouble())), + minZoom, maxZoom, pixelRatio }; + } else { + return OfflineGeometryRegionDefinition{ + styleURL, + mapbox::geojson::convert<Geometry<double>>(doc["geometry"].GetObject()), + minZoom, maxZoom, pixelRatio }; + }; - return { styleURL, bounds, minZoom, maxZoom, pixelRatio }; } std::string encodeOfflineRegionDefinition(const OfflineRegionDefinition& region) { rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator> doc; doc.SetObject(); - doc.AddMember("style_url", rapidjson::StringRef(region.styleURL.data(), region.styleURL.length()), doc.GetAllocator()); + // Encode common properties + region.match([&](auto& _region) { + doc.AddMember("style_url", rapidjson::StringRef(_region.styleURL.data(), _region.styleURL.length()), doc.GetAllocator()); + doc.AddMember("min_zoom", _region.minZoom, doc.GetAllocator()); + if (std::isfinite(_region.maxZoom)) { + doc.AddMember("max_zoom", _region.maxZoom, doc.GetAllocator()); + } - rapidjson::GenericValue<rapidjson::UTF8<>, rapidjson::CrtAllocator> bounds(rapidjson::kArrayType); - bounds.PushBack(region.bounds.south(), doc.GetAllocator()); - bounds.PushBack(region.bounds.west(), doc.GetAllocator()); - bounds.PushBack(region.bounds.north(), doc.GetAllocator()); - bounds.PushBack(region.bounds.east(), doc.GetAllocator()); - doc.AddMember("bounds", bounds, doc.GetAllocator()); + doc.AddMember("pixel_ratio", _region.pixelRatio, doc.GetAllocator()); + }); - doc.AddMember("min_zoom", region.minZoom, doc.GetAllocator()); - if (std::isfinite(region.maxZoom)) { - doc.AddMember("max_zoom", region.maxZoom, doc.GetAllocator()); - } + // Encode specific properties + region.match( + [&] (const OfflineTilePyramidRegionDefinition& _region) { + rapidjson::GenericValue<rapidjson::UTF8<>, rapidjson::CrtAllocator> bounds(rapidjson::kArrayType); + bounds.PushBack(_region.bounds.south(), doc.GetAllocator()); + bounds.PushBack(_region.bounds.west(), doc.GetAllocator()); + bounds.PushBack(_region.bounds.north(), doc.GetAllocator()); + bounds.PushBack(_region.bounds.east(), doc.GetAllocator()); + doc.AddMember("bounds", bounds, doc.GetAllocator()); + + }, + [&] (const OfflineGeometryRegionDefinition& _region) { + doc.AddMember("geometry", mapbox::geojson::convert(_region.geometry, doc.GetAllocator()), doc.GetAllocator()); - doc.AddMember("pixel_ratio", region.pixelRatio, doc.GetAllocator()); + } + ); rapidjson::StringBuffer buffer; rapidjson::Writer<rapidjson::StringBuffer> writer(buffer); @@ -113,6 +130,9 @@ std::string encodeOfflineRegionDefinition(const OfflineRegionDefinition& region) return buffer.GetString(); } + +// OfflineRegion + OfflineRegion::OfflineRegion(int64_t id_, OfflineRegionDefinition definition_, OfflineRegionMetadata metadata_) @@ -135,5 +155,4 @@ const OfflineRegionMetadata& OfflineRegion::getMetadata() const { int64_t OfflineRegion::getID() const { return id; } - } // namespace mbgl diff --git a/platform/default/mbgl/storage/offline_download.cpp b/platform/default/mbgl/storage/offline_download.cpp index 179d2d5f57..118f3aad88 100644 --- a/platform/default/mbgl/storage/offline_download.cpp +++ b/platform/default/mbgl/storage/offline_download.cpp @@ -24,6 +24,63 @@ namespace mbgl { using namespace style; +// Generic functions + +template <class RegionDefinition> +Range<uint8_t> coveringZoomRange(const RegionDefinition& definition, + style::SourceType type, uint16_t tileSize, const Range<uint8_t>& zoomRange) { + double minZ = std::max<double>(util::coveringZoomLevel(definition.minZoom, type, tileSize), zoomRange.min); + double maxZ = std::min<double>(util::coveringZoomLevel(definition.maxZoom, type, tileSize), zoomRange.max); + + assert(minZ >= 0); + assert(maxZ >= 0); + assert(minZ < std::numeric_limits<uint8_t>::max()); + assert(maxZ < std::numeric_limits<uint8_t>::max()); + return { static_cast<uint8_t>(minZ), static_cast<uint8_t>(maxZ) }; +} + +template <class Geometry, class Fn> +void tileCover(const Geometry& geometry, uint8_t z, Fn&& fn) { + util::TileCover cover(geometry, z); + while (cover.hasNext()) { + fn(cover.next()->canonical); + } +} + + +template <class Fn> +void tileCover(const OfflineRegionDefinition& definition, style::SourceType type, + uint16_t tileSize, const Range<uint8_t>& zoomRange, Fn&& fn) { + const Range<uint8_t> clampedZoomRange = + definition.match([&](auto& reg) { return coveringZoomRange(reg, type, tileSize, zoomRange); }); + + for (uint8_t z = clampedZoomRange.min; z <= clampedZoomRange.max; z++) { + definition.match( + [&](const OfflineTilePyramidRegionDefinition& reg){ tileCover(reg.bounds, z, fn); }, + [&](const OfflineGeometryRegionDefinition& reg){ tileCover(reg.geometry, z, fn); } + ); + } +} + +uint64_t tileCount(const OfflineRegionDefinition& definition, style::SourceType type, + uint16_t tileSize, const Range<uint8_t>& zoomRange) { + + const Range<uint8_t> clampedZoomRange = + definition.match([&](auto& reg) { return coveringZoomRange(reg, type, tileSize, zoomRange); }); + + unsigned long result = 0;; + for (uint8_t z = clampedZoomRange.min; z <= clampedZoomRange.max; z++) { + result += definition.match( + [&](const OfflineTilePyramidRegionDefinition& reg){ return util::tileCount(reg.bounds, z); }, + [&](const OfflineGeometryRegionDefinition& reg){ return util::tileCount(reg.geometry, z); } + ); + } + + return result; +} + +// OfflineDownload + OfflineDownload::OfflineDownload(int64_t id_, OfflineRegionDefinition&& definition_, OfflineDatabase& offlineDatabase_, @@ -70,7 +127,8 @@ OfflineRegionStatus OfflineDownload::getStatus() const { } result->requiredResourceCount++; - optional<Response> styleResponse = offlineDatabase.get(Resource::style(definition.styleURL)); + optional<Response> styleResponse = + offlineDatabase.get(Resource::style(definition.match([](auto& reg){ return reg.styleURL; }))); if (!styleResponse) { return *result; } @@ -86,7 +144,7 @@ OfflineRegionStatus OfflineDownload::getStatus() const { auto handleTiledSource = [&] (const variant<std::string, Tileset>& urlOrTileset, const uint16_t tileSize) { if (urlOrTileset.is<Tileset>()) { result->requiredResourceCount += - definition.tileCount(type, tileSize, urlOrTileset.get<Tileset>().zoomRange); + tileCount(definition, type, tileSize, urlOrTileset.get<Tileset>().zoomRange); } else { result->requiredResourceCount += 1; const auto& url = urlOrTileset.get<std::string>(); @@ -96,7 +154,7 @@ OfflineRegionStatus OfflineDownload::getStatus() const { optional<Tileset> tileset = style::conversion::convertJSON<Tileset>(*sourceResponse->data, error); if (tileset) { result->requiredResourceCount += - definition.tileCount(type, tileSize, (*tileset).zoomRange); + tileCount(definition, type, tileSize, (*tileset).zoomRange); } } else { result->requiredResourceCountIsPrecise = false; @@ -116,7 +174,7 @@ OfflineRegionStatus OfflineDownload::getStatus() const { handleTiledSource(rasterSource.getURLOrTileset(), rasterSource.getTileSize()); break; } - + case SourceType::RasterDEM: { const auto& rasterDEMSource = *source->as<RasterDEMSource>(); handleTiledSource(rasterDEMSource.getURLOrTileset(), rasterDEMSource.getTileSize()); @@ -161,7 +219,8 @@ void OfflineDownload::activateDownload() { status = OfflineRegionStatus(); status.downloadState = OfflineRegionDownloadState::Active; status.requiredResourceCount++; - ensureResource(Resource::style(definition.styleURL), [&](Response styleResponse) { + ensureResource(Resource::style(definition.match([](auto& reg){ return reg.styleURL; })), + [&](Response styleResponse) { status.requiredResourceCountIsPrecise = true; style::Parser parser; @@ -207,7 +266,7 @@ void OfflineDownload::activateDownload() { handleTiledSource(rasterSource.getURLOrTileset(), rasterSource.getTileSize()); break; } - + case SourceType::RasterDEM: { const auto& rasterDEMSource = *source->as<RasterDEMSource>(); handleTiledSource(rasterDEMSource.getURLOrTileset(), rasterDEMSource.getTileSize()); @@ -247,8 +306,9 @@ void OfflineDownload::activateDownload() { } if (!parser.spriteURL.empty()) { - queueResource(Resource::spriteImage(parser.spriteURL, definition.pixelRatio)); - queueResource(Resource::spriteJSON(parser.spriteURL, definition.pixelRatio)); + auto pixelRatio = definition.match([](auto& reg){ return reg.pixelRatio; }); + queueResource(Resource::spriteImage(parser.spriteURL, pixelRatio)); + queueResource(Resource::spriteJSON(parser.spriteURL, pixelRatio)); } continueDownload(); @@ -296,11 +356,12 @@ void OfflineDownload::queueResource(Resource resource) { } void OfflineDownload::queueTiles(SourceType type, uint16_t tileSize, const Tileset& tileset) { - for (const auto& tile : definition.tileCover(type, tileSize, tileset.zoomRange)) { + tileCover(definition, type, tileSize, tileset.zoomRange, [&](const auto& tile) { status.requiredResourceCount++; - resourcesRemaining.push_back( - Resource::tile(tileset.tiles[0], definition.pixelRatio, tile.x, tile.y, tile.z, tileset.scheme)); - } + resourcesRemaining.push_back(Resource::tile( + tileset.tiles[0], definition.match([](auto& def) { return def.pixelRatio; }), tile.x, + tile.y, tile.z, tileset.scheme)); + }); } void OfflineDownload::ensureResource(const Resource& resource, |