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

Commit

Permalink
Fast tileCount with help from @mapbox/sphericalmercator module
Browse files Browse the repository at this point in the history
  • Loading branch information
Asheem Mamoowala committed Sep 9, 2017
1 parent aa4dff1 commit 50fd917
Show file tree
Hide file tree
Showing 8 changed files with 89 additions and 15 deletions.
4 changes: 3 additions & 1 deletion include/mbgl/storage/offline.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,14 @@ class OfflineTilePyramidRegionDefinition {

/* 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;
};

/*
Expand Down
22 changes: 18 additions & 4 deletions include/mbgl/util/projection.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,7 @@ class Projection {
}

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) {
Expand All @@ -89,6 +86,23 @@ class Projection {
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
33 changes: 25 additions & 8 deletions platform/default/mbgl/storage/offline.cpp
Original file line number Diff line number Diff line change
@@ -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>
Expand All @@ -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);
}
Expand All @@ -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());
Expand Down
4 changes: 2 additions & 2 deletions platform/default/mbgl/storage/offline_download.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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>();
Expand All @@ -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;
Expand Down
21 changes: 21 additions & 0 deletions src/mbgl/util/tile_cover.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
3 changes: 3 additions & 0 deletions src/mbgl/util/tile_cover.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
8 changes: 8 additions & 0 deletions test/storage/offline.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 }));
}
9 changes: 9 additions & 0 deletions test/util/tile_cover.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}

0 comments on commit 50fd917

Please sign in to comment.