diff options
-rw-r--r-- | include/mbgl/storage/offline.hpp | 4 | ||||
-rw-r--r-- | include/mbgl/util/projection.hpp | 22 | ||||
-rw-r--r-- | platform/default/mbgl/storage/offline.cpp | 33 | ||||
-rw-r--r-- | platform/default/mbgl/storage/offline_download.cpp | 4 | ||||
-rw-r--r-- | src/mbgl/util/tile_cover.cpp | 21 | ||||
-rw-r--r-- | src/mbgl/util/tile_cover.hpp | 3 | ||||
-rw-r--r-- | test/storage/offline.test.cpp | 8 | ||||
-rw-r--r-- | test/util/tile_cover.test.cpp | 9 |
8 files changed, 89 insertions, 15 deletions
diff --git a/include/mbgl/storage/offline.hpp b/include/mbgl/storage/offline.hpp index 818cfe2ba5..afb2fa1e81 100644 --- a/include/mbgl/storage/offline.hpp +++ b/include/mbgl/storage/offline.hpp @@ -31,12 +31,14 @@ public: /* Private */ std::vector<CanonicalTileID> tileCover(SourceType, uint16_t tileSize, const Range<uint8_t>& zoomRange) const; - + unsigned long tileCount(SourceType, uint16_t tileSize, const Range<uint8_t>& zoomRange) const; const std::string styleURL; const LatLngBounds bounds; const double minZoom; const double maxZoom; const float pixelRatio; +private: + Range<uint8_t> coveringZoomRange(SourceType, uint16_t tileSize, const Range<uint8_t>& zoomRange) const; }; /* diff --git a/include/mbgl/util/projection.hpp b/include/mbgl/util/projection.hpp index 3cc1146513..f64502c5bc 100644 --- a/include/mbgl/util/projection.hpp +++ b/include/mbgl/util/projection.hpp @@ -75,10 +75,7 @@ public: } static Point<double> project(const LatLng& latLng, double scale) { - return Point<double> { - util::LONGITUDE_MAX + latLng.longitude(), - util::LONGITUDE_MAX - util::RAD2DEG * std::log(std::tan(M_PI / 4 + latLng.latitude() * M_PI / util::DEGREES_MAX)) - } * worldSize(scale) / util::DEGREES_MAX; + return project_(latLng, worldSize(scale)); } static LatLng unproject(const Point<double>& p, double scale, LatLng::WrapMode wrapMode = LatLng::Unwrapped) { @@ -89,6 +86,23 @@ public: wrapMode }; } + + // Project lat, lon to point in a zoom-dependent world size + static Point<double> project(const LatLng& point, uint8_t zoom, uint16_t tileSize) { + const double t2z = tileSize * std::pow(2, zoom); + Point<double> pt = project_(point, t2z); + // Flip y coordinate + auto x = std::round(std::min(pt.x, t2z)); + auto y = std::round(std::min(t2z - pt.y, t2z)); + return { x, y }; + } +private: + static Point<double> project_(const LatLng& latLng, double worldSize) { + return Point<double> { + util::LONGITUDE_MAX + latLng.longitude(), + util::LONGITUDE_MAX - util::RAD2DEG * std::log(std::tan(M_PI / 4 + latLng.latitude() * M_PI / util::DEGREES_MAX)) + } * worldSize / util::DEGREES_MAX; + } }; } // namespace mbgl diff --git a/platform/default/mbgl/storage/offline.cpp b/platform/default/mbgl/storage/offline.cpp index fd2d47819b..644684c8a6 100644 --- a/platform/default/mbgl/storage/offline.cpp +++ b/platform/default/mbgl/storage/offline.cpp @@ -1,6 +1,7 @@ #include <mbgl/storage/offline.hpp> #include <mbgl/util/tile_cover.hpp> #include <mbgl/util/tileset.hpp> +#include <mbgl/util/projection.hpp> #include <rapidjson/document.h> #include <rapidjson/stringbuffer.h> @@ -24,17 +25,11 @@ OfflineTilePyramidRegionDefinition::OfflineTilePyramidRegionDefinition( } std::vector<CanonicalTileID> OfflineTilePyramidRegionDefinition::tileCover(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()); + const Range<uint8_t> clampedZoomRange = coveringZoomRange(type, tileSize, zoomRange); std::vector<CanonicalTileID> result; - for (uint8_t z = minZ; z <= maxZ; z++) { + for (uint8_t z = clampedZoomRange.min; z <= clampedZoomRange.max; z++) { for (const auto& tile : util::tileCover(bounds, z)) { result.emplace_back(tile.canonical); } @@ -43,6 +38,28 @@ std::vector<CanonicalTileID> OfflineTilePyramidRegionDefinition::tileCover(Sourc return result; } +unsigned long OfflineTilePyramidRegionDefinition::tileCount(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, tileSize); + } + + return result; +} + +Range<uint8_t> OfflineTilePyramidRegionDefinition::coveringZoomRange(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()); diff --git a/platform/default/mbgl/storage/offline_download.cpp b/platform/default/mbgl/storage/offline_download.cpp index 7f0001f64b..ff61114888 100644 --- a/platform/default/mbgl/storage/offline_download.cpp +++ b/platform/default/mbgl/storage/offline_download.cpp @@ -80,7 +80,7 @@ OfflineRegionStatus OfflineDownload::getStatus() const { auto handleTiledSource = [&] (const variant<std::string, Tileset>& urlOrTileset, const uint16_t tileSize) { if (urlOrTileset.is<Tileset>()) { result.requiredResourceCount += - definition.tileCover(type, tileSize, urlOrTileset.get<Tileset>().zoomRange).size(); + definition.tileCount(type, tileSize, urlOrTileset.get<Tileset>().zoomRange); } else { result.requiredResourceCount += 1; const auto& url = urlOrTileset.get<std::string>(); @@ -90,7 +90,7 @@ OfflineRegionStatus OfflineDownload::getStatus() const { optional<Tileset> tileset = style::conversion::convertJSON<Tileset>(*sourceResponse->data, error); if (tileset) { result.requiredResourceCount += - definition.tileCover(type, tileSize, (*tileset).zoomRange).size(); + definition.tileCount(type, tileSize, (*tileset).zoomRange); } } else { result.requiredResourceCountIsPrecise = false; diff --git a/src/mbgl/util/tile_cover.cpp b/src/mbgl/util/tile_cover.cpp index b53e91162c..c06634c9b2 100644 --- a/src/mbgl/util/tile_cover.cpp +++ b/src/mbgl/util/tile_cover.cpp @@ -169,5 +169,26 @@ std::vector<UnwrappedTileID> tileCover(const TransformState& state, int32_t z) { z); } +// Taken from https://github.com/mapbox/sphericalmercator#xyzbbox-zoom-tms_style-srs +// Computes the projected tiles for the lower left and uppoer right points of the bounds +// and uses that to compute the tile cover count +unsigned long tileCount(const LatLngBounds& bounds, uint8_t zoom, uint16_t tileSize_){ + + auto sw = Projection::project(bounds.southwest().wrapped(), zoom, tileSize_); + auto ne = Projection::project(bounds.northeast().wrapped(), zoom, tileSize_); + + auto x1 = floor(sw.x/ tileSize_); + auto x2 = floor((ne.x - 1) / tileSize_); + auto y1 = floor(sw.y/ tileSize_); + auto y2 = floor((ne.y - 1) / tileSize_); + + auto minX = std::fmax(std::min(x1, x2), 0); + auto maxX = std::max(x1, x2); + auto minY = (std::pow(2, zoom) - 1) - std::max(y1, y2); + auto maxY = (std::pow(2, zoom) - 1) - std::fmax(std::min(y1, y2), 0); + + return (maxX - minX + 1) * (maxY - minY + 1); +} + } // namespace util } // namespace mbgl diff --git a/src/mbgl/util/tile_cover.hpp b/src/mbgl/util/tile_cover.hpp index 2d32d8bf41..405e6a48e6 100644 --- a/src/mbgl/util/tile_cover.hpp +++ b/src/mbgl/util/tile_cover.hpp @@ -18,5 +18,8 @@ int32_t coveringZoomLevel(double z, SourceType type, uint16_t tileSize); std::vector<UnwrappedTileID> tileCover(const TransformState&, int32_t z); std::vector<UnwrappedTileID> tileCover(const LatLngBounds&, int32_t z); +// Compute only the count of tiles needed for tileCover +unsigned long tileCount(const LatLngBounds&, uint8_t z, uint16_t tileSize); + } // namespace util } // namespace mbgl diff --git a/test/storage/offline.test.cpp b/test/storage/offline.test.cpp index 0faaabc298..e7ebe5199f 100644 --- a/test/storage/offline.test.cpp +++ b/test/storage/offline.test.cpp @@ -52,3 +52,11 @@ TEST(OfflineTilePyramidRegionDefinition, TileCoverWrapped) { EXPECT_EQ((std::vector<CanonicalTileID>{ { 0, 0, 0 } }), region.tileCover(SourceType::Vector, 512, { 0, 22 })); } + +TEST(OfflineTilePyramidRegionDefinition, TileCount) { + OfflineTilePyramidRegionDefinition region("", sanFranciscoWrapped, 0, 22, 1.0); + + //These numbers match the count from tileCover().size(). + EXPECT_EQ(38424u, region.tileCount(SourceType::Vector, 512, { 10, 18 })); + EXPECT_EQ(9675240u, region.tileCount(SourceType::Vector, 512, { 3, 22 })); +} diff --git a/test/util/tile_cover.test.cpp b/test/util/tile_cover.test.cpp index c746e6dab5..933c18b5ea 100644 --- a/test/util/tile_cover.test.cpp +++ b/test/util/tile_cover.test.cpp @@ -84,3 +84,12 @@ TEST(TileCover, SanFranciscoZ0Wrapped) { EXPECT_EQ((std::vector<UnwrappedTileID>{ { 0, 1, 0 } }), util::tileCover(sanFranciscoWrapped, 0)); } + +TEST(TileCount, SanFranciscoZ10) { + EXPECT_EQ(4u, util::tileCount(sanFrancisco, 10, util::tileSize)); +} + +TEST(TileCount, SanFranciscoZ22) { + EXPECT_EQ(7254450u, util::tileCount(sanFrancisco, 22, util::tileSize)); +} + |