-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Tile coverage updates #12310
Tile coverage updates #12310
Changes from all commits
b218d7d
a9cd952
160c7b4
9899518
12b8482
1b795ea
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,8 @@ | ||
#include <mbgl/util/tile_cover.hpp> | ||
#include <mbgl/util/constants.hpp> | ||
#include <mbgl/util/interpolate.hpp> | ||
#include <mbgl/util/math.hpp> | ||
#include <mbgl/map/mode.hpp> | ||
#include <mbgl/map/transform_state.hpp> | ||
#include <mbgl/util/tile_cover_impl.hpp> | ||
#include <mbgl/util/tile_coordinate.hpp> | ||
|
@@ -78,13 +80,15 @@ namespace util { | |
|
||
namespace { | ||
|
||
std::vector<UnwrappedTileID> tileCover(const Point<double>& tl, | ||
const Point<double>& tr, | ||
const Point<double>& br, | ||
const Point<double>& bl, | ||
std::vector<UnwrappedTileID> tileCover(Point<double> tl, | ||
Point<double> tr, | ||
Point<double> br, | ||
Point<double> bl, | ||
const Point<double>& c, | ||
int32_t z) { | ||
const int32_t tiles = 1 << z; | ||
uint8_t z, | ||
TileCoverMode mode, | ||
double bearing = 0) { | ||
const int32_t maxTilesPerAxis = 1 << z; | ||
|
||
struct ID { | ||
int32_t x, y; | ||
|
@@ -93,22 +97,51 @@ std::vector<UnwrappedTileID> tileCover(const Point<double>& tl, | |
|
||
std::vector<ID> t; | ||
|
||
// Rotate the center according to bearing. | ||
const Point<double> rotatedCenter = util::rotate(c, bearing); | ||
|
||
// Limits the tile coverage to an axis-aligned rectangle according to the | ||
// given bearing. | ||
auto limitTileCoordinate = [&](Point<double>& point) { | ||
auto clampAxis = [](double a, double b) -> double { | ||
const double min = a >= b ? b : b - 2.0; | ||
const double max = a >= b ? b + 2.0 : b; | ||
return util::clamp(a, min, max); | ||
}; | ||
|
||
// Rotate the tile coordinate according to bearing. | ||
Point<double> rotated = util::rotate(point, bearing); | ||
rotated.x = clampAxis(rotated.x, rotatedCenter.x); | ||
rotated.y = clampAxis(rotated.y, rotatedCenter.y); | ||
|
||
// After clamping, rotate back to original value. | ||
point = util::rotate(rotated, -bearing); | ||
}; | ||
|
||
// Limit tile coverage in continuous mode. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this comment still appropriate? How is continuous mode being checked here? |
||
if (mode == TileCoverMode::Limited5x5) { | ||
limitTileCoordinate(tl); | ||
limitTileCoordinate(tr); | ||
limitTileCoordinate(br); | ||
limitTileCoordinate(bl); | ||
} | ||
|
||
auto scanLine = [&](int32_t x0, int32_t x1, int32_t y) { | ||
int32_t x; | ||
if (y >= 0 && y <= tiles) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This condition is no longer necessary? I think it is necessary at the poles. |
||
for (x = x0; x < x1; ++x) { | ||
const auto dx = x + 0.5 - c.x, dy = y + 0.5 - c.y; | ||
t.emplace_back(ID{ x, y, dx * dx + dy * dy }); | ||
} | ||
double dx, dy; | ||
for (int32_t x = x0; x < x1; ++x) { | ||
// We don't know which corner of the tile coordinate is the closest, | ||
// so add 0.5 to calculate the distance from its tile center. | ||
dx = x + 0.5 - c.x; dy = y + 0.5 - c.y; | ||
t.emplace_back(ID{ x, y, dx * dx + dy * dy }); | ||
} | ||
}; | ||
|
||
// Divide the screen up in two triangles and scan each of them: | ||
// \---+ | ||
// | \ | | ||
// +---\. | ||
scanTriangle(tl, tr, br, 0, tiles, scanLine); | ||
scanTriangle(br, bl, tl, 0, tiles, scanLine); | ||
scanTriangle(tl, tr, br, 0, maxTilesPerAxis, scanLine); | ||
scanTriangle(br, bl, tl, 0, maxTilesPerAxis, scanLine); | ||
|
||
// Sort first by distance, then by x/y. | ||
std::sort(t.begin(), t.end(), [](const ID& a, const ID& b) { | ||
|
@@ -129,7 +162,7 @@ std::vector<UnwrappedTileID> tileCover(const Point<double>& tl, | |
|
||
} // namespace | ||
|
||
int32_t coveringZoomLevel(double zoom, style::SourceType type, uint16_t size) { | ||
uint8_t coveringZoomLevel(double zoom, style::SourceType type, uint16_t size) { | ||
zoom += ::log2(util::tileSize / size); | ||
if (type == style::SourceType::Raster || type == style::SourceType::Video) { | ||
return ::round(zoom); | ||
|
@@ -138,7 +171,7 @@ int32_t coveringZoomLevel(double zoom, style::SourceType type, uint16_t size) { | |
} | ||
} | ||
|
||
std::vector<UnwrappedTileID> tileCover(const LatLngBounds& bounds_, int32_t z) { | ||
std::vector<UnwrappedTileID> tileCover(const LatLngBounds& bounds_, uint8_t z) { | ||
if (bounds_.isEmpty() || | ||
bounds_.south() > util::LATITUDE_MAX || | ||
bounds_.north() < -util::LATITUDE_MAX) { | ||
|
@@ -155,24 +188,26 @@ std::vector<UnwrappedTileID> tileCover(const LatLngBounds& bounds_, int32_t z) { | |
Projection::project(bounds.southeast(), z), | ||
Projection::project(bounds.southwest(), z), | ||
Projection::project(bounds.center(), z), | ||
z); | ||
z, TileCoverMode::Full); | ||
} | ||
|
||
std::vector<UnwrappedTileID> tileCover(const TransformState& state, int32_t z) { | ||
std::vector<UnwrappedTileID> tileCover(const TransformState& state, uint8_t z, TileCoverMode mode) { | ||
assert(state.valid()); | ||
|
||
const double w = state.getSize().width; | ||
const double h = state.getSize().height; | ||
|
||
// top-left, top-right, bottom-right, bottom-left, center | ||
return tileCover( | ||
TileCoordinate::fromScreenCoordinate(state, z, { 0, 0 }).p, | ||
TileCoordinate::fromScreenCoordinate(state, z, { w, 0 }).p, | ||
TileCoordinate::fromScreenCoordinate(state, z, { w, h }).p, | ||
TileCoordinate::fromScreenCoordinate(state, z, { 0, h }).p, | ||
TileCoordinate::fromScreenCoordinate(state, z, { w/2, h/2 }).p, | ||
z); | ||
z, mode, state.getAngle()); | ||
} | ||
|
||
std::vector<UnwrappedTileID> tileCover(const Geometry<double>& geometry, int32_t z) { | ||
std::vector<UnwrappedTileID> tileCover(const Geometry<double>& geometry, uint8_t z) { | ||
std::vector<UnwrappedTileID> result; | ||
TileCover tc(geometry, z, true); | ||
while (tc.hasNext()) { | ||
|
@@ -182,7 +217,7 @@ std::vector<UnwrappedTileID> tileCover(const Geometry<double>& geometry, int32_t | |
return result; | ||
} | ||
|
||
// Taken from https://github.com/mapbox/sphericalmercator#xyzbbox-zoom-tms_style-srs | ||
// Taken from https://github.com/mapbox/sphericalmercator#xyzbbox-z-tms_style-srs | ||
// Computes the projected tiles for the lower left and upper right points of the bounds | ||
// and uses that to compute the tile cover count | ||
uint64_t tileCount(const LatLngBounds& bounds, uint8_t zoom){ | ||
|
@@ -212,7 +247,7 @@ uint64_t tileCount(const Geometry<double>& geometry, uint8_t z) { | |
return tileCount; | ||
} | ||
|
||
TileCover::TileCover(const LatLngBounds&bounds_, int32_t z) { | ||
TileCover::TileCover(const LatLngBounds&bounds_, uint8_t z) { | ||
LatLngBounds bounds = LatLngBounds::hull( | ||
{ std::max(bounds_.south(), -util::LATITUDE_MAX), bounds_.west() }, | ||
{ std::min(bounds_.north(), util::LATITUDE_MAX), bounds_.east() }); | ||
|
@@ -232,7 +267,7 @@ TileCover::TileCover(const LatLngBounds&bounds_, int32_t z) { | |
impl = std::make_unique<TileCover::Impl>(z, p, false); | ||
} | ||
|
||
TileCover::TileCover(const Geometry<double>& geom, int32_t z, bool project/* = true*/) | ||
TileCover::TileCover(const Geometry<double>& geom, uint8_t z, bool project/* = true*/) | ||
: impl( std::make_unique<TileCover::Impl>(z, geom, project)) { | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,8 @@ | ||
#include <mbgl/util/tile_cover.hpp> | ||
#include <mbgl/util/geo.hpp> | ||
#include <mbgl/map/transform.hpp> | ||
#include <mbgl/util/tile_coordinate.hpp> | ||
#include <mbgl/util/math.hpp> | ||
|
||
#include <algorithm> | ||
#include <stdlib.h> /* srand, rand */ | ||
|
@@ -43,6 +45,22 @@ TEST(TileCover, Pitch) { | |
{ 2, 1, 2 }, { 2, 1, 1 }, { 2, 2, 2 }, { 2, 2, 1 }, { 2, 3, 2 } | ||
}), | ||
util::tileCover(transform.getState(), 2)); | ||
|
||
// 2 tiles on the left, 1 center tiler, then 2 tiles on the right. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Break this into a separate test |
||
constexpr uint32_t maximumLimitedTiles = 5 * 5; | ||
|
||
transform.resize({ 2048, 2048 }); | ||
transform.setAngle(-45.0 * M_PI / 180.0); | ||
|
||
transform.setPitch(67.5 * M_PI / 180.0); | ||
for (double zoom = 0.0; zoom < 16.0; zoom += 0.5) { | ||
uint8_t integerZoom = std::floor(zoom); | ||
transform.setZoom(zoom); | ||
auto fullTiles = util::tileCover(transform.getState(), integerZoom, util::TileCoverMode::Full); | ||
auto limitedTiles = util::tileCover(transform.getState(), integerZoom, util::TileCoverMode::Limited5x5); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would be good to also check the correctness of the returned tiles as part of the test. |
||
EXPECT_GT(fullTiles.size(), limitedTiles.size()); | ||
EXPECT_GE(maximumLimitedTiles, limitedTiles.size()); | ||
} | ||
} | ||
|
||
TEST(TileCover, WorldZ1) { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: non-uniform input arguments.
Would it be better to keep these as const references, and conditionally return a clamped point from
limitTileCoordinate
?