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

[core] Fast tileCount with help from @mapbox/sphericalmercator module #9906

Merged
merged 1 commit into from
Sep 9, 2017
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
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) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be written in terms of the existing project function, or vice versa?

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);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we please use fixed width number types (like uint64_t)? Unfortunately, unsigned long is 32 bit on some and 64 bit on other platforms.


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