Implement distance-based tile LOD (#4057)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Bart Louwers <bart@emeel.net>
@@ -25,8 +25,9 @@ static void TileCoverPitchedViewport(benchmark::State& state) {
|
|||||||
transform.jumpTo(CameraOptions().withCenter(LatLng{0.1, -0.1}).withZoom(8.0).withBearing(5.0).withPitch(40.0));
|
transform.jumpTo(CameraOptions().withCenter(LatLng{0.1, -0.1}).withZoom(8.0).withBearing(5.0).withPitch(40.0));
|
||||||
|
|
||||||
std::size_t length = 0;
|
std::size_t length = 0;
|
||||||
|
const Range<uint8_t> zoomRange(0, 14);
|
||||||
while (state.KeepRunning()) {
|
while (state.KeepRunning()) {
|
||||||
auto tiles = util::tileCover({transform.getState()}, 8);
|
auto tiles = util::tileCover({transform.getState()}, 8, zoomRange);
|
||||||
length += tiles.size();
|
length += tiles.size();
|
||||||
}
|
}
|
||||||
benchmark::DoNotOptimize(length);
|
benchmark::DoNotOptimize(length);
|
||||||
|
|||||||
@@ -69,6 +69,17 @@ Now download the `actual.png` in `metrics/integration/render-tests/background-co
|
|||||||
|
|
||||||
Of we don't want to commit this change. But know you can add and debug (Android) render tests.
|
Of we don't want to commit this change. But know you can add and debug (Android) render tests.
|
||||||
|
|
||||||
|
### Adding Alternate Expected Images
|
||||||
|
|
||||||
|
You can add alternate expected images by coping the `actual.png` to a file that starts with `expected`. You can use the following command:
|
||||||
|
|
||||||
|
```
|
||||||
|
render_test_name=tile-lod/zoom-shift
|
||||||
|
adb exec-out run-as org.maplibre.render_test_runner cat files/metrics/integration/render-tests/$render_test_name/actual.png > metrics/integration/render-tests/$render_test_name/expected-android.png
|
||||||
|
```
|
||||||
|
|
||||||
|
Which will pull `tile-lod/zoom-shift/actual.png` and save it as `tile-lod/zoom-shift/expected-android.png`.
|
||||||
|
|
||||||
## Instrumentation Tests
|
## Instrumentation Tests
|
||||||
|
|
||||||
To run the instrumentation tests, choose the "Instrumentation Tests" run configuration.
|
To run the instrumentation tests, choose the "Instrumentation Tests" run configuration.
|
||||||
|
|||||||
@@ -186,6 +186,14 @@ public:
|
|||||||
/// A negative values typically improves performance but reduces quality.
|
/// A negative values typically improves performance but reduces quality.
|
||||||
/// For instance, a value of -1 reduces the zoom level by 1 and this
|
/// For instance, a value of -1 reduces the zoom level by 1 and this
|
||||||
/// reduces the number of tiles by a factor of 4 for the same camera view.
|
/// reduces the number of tiles by a factor of 4 for the same camera view.
|
||||||
|
/// - `TileLodMode` selects the tile cover algorithm. The possible algorithms
|
||||||
|
/// are "default" and "distance". When "distance" is selected, the tile LOD is
|
||||||
|
/// based on the distance from camera to tile. This algorithm causes tiles
|
||||||
|
/// closer to the camera to be rendered at higher LOD, in contrast with the
|
||||||
|
/// default algorithm, which renders the highest LOD at the center of the
|
||||||
|
/// screen. The distance based algorithm observes `TileLodScale` and
|
||||||
|
/// `TileLodPitchThreshold` and ignores `TileLodMinRadius` and
|
||||||
|
/// `TileLodZoomShift`.
|
||||||
void setTileLodMinRadius(double radius);
|
void setTileLodMinRadius(double radius);
|
||||||
double getTileLodMinRadius() const;
|
double getTileLodMinRadius() const;
|
||||||
void setTileLodScale(double scale);
|
void setTileLodScale(double scale);
|
||||||
@@ -194,6 +202,8 @@ public:
|
|||||||
double getTileLodPitchThreshold() const;
|
double getTileLodPitchThreshold() const;
|
||||||
void setTileLodZoomShift(double shift);
|
void setTileLodZoomShift(double shift);
|
||||||
double getTileLodZoomShift() const;
|
double getTileLodZoomShift() const;
|
||||||
|
void setTileLodMode(TileLodMode mode);
|
||||||
|
TileLodMode getTileLodMode() const;
|
||||||
|
|
||||||
ClientOptions getClientOptions() const;
|
ClientOptions getClientOptions() const;
|
||||||
|
|
||||||
|
|||||||
@@ -35,6 +35,11 @@ enum class ViewportMode : EnumType {
|
|||||||
FlippedY,
|
FlippedY,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class TileLodMode : uint8_t {
|
||||||
|
Default, ///< Default TileLOD algorithm
|
||||||
|
Distance ///< Distance-based TileLOD algorithm
|
||||||
|
};
|
||||||
|
|
||||||
enum class MapDebugOptions : EnumType {
|
enum class MapDebugOptions : EnumType {
|
||||||
NoDebug = 0,
|
NoDebug = 0,
|
||||||
TileBorders = 1 << 1,
|
TileBorders = 1 << 1,
|
||||||
|
|||||||
|
After Width: | Height: | Size: 39 KiB |
BIN
metrics/integration/render-tests/tile-lod/default/expected.png
Normal file
|
After Width: | Height: | Size: 40 KiB |
38
metrics/integration/render-tests/tile-lod/default/style.json
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"version": 8,
|
||||||
|
"metadata": {
|
||||||
|
"test": {
|
||||||
|
"maxPitch": 85,
|
||||||
|
"operations": [
|
||||||
|
["wait"]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"center": [
|
||||||
|
13.418056,
|
||||||
|
52.499167
|
||||||
|
],
|
||||||
|
"bearing": 30,
|
||||||
|
"pitch": 72,
|
||||||
|
"zoom": 16,
|
||||||
|
"sources": {
|
||||||
|
"satellite": {
|
||||||
|
"type": "raster",
|
||||||
|
"tiles": [
|
||||||
|
"https://demotiles.maplibre.org/debug-tiles/number/{z}.png"
|
||||||
|
],
|
||||||
|
"maxzoom": 22,
|
||||||
|
"tileSize": 256
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"layers": [
|
||||||
|
{
|
||||||
|
"id": "raster",
|
||||||
|
"type": "raster",
|
||||||
|
"source": "satellite",
|
||||||
|
"paint": {
|
||||||
|
"raster-fade-duration": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 41 KiB |
@@ -0,0 +1,40 @@
|
|||||||
|
{
|
||||||
|
"version": 8,
|
||||||
|
"metadata": {
|
||||||
|
"test": {
|
||||||
|
"maxPitch": 85,
|
||||||
|
"operations": [
|
||||||
|
["setTileLodMode", "distance"],
|
||||||
|
["setTileLodPitchThreshold", 62],
|
||||||
|
["wait"]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"center": [
|
||||||
|
13.418056,
|
||||||
|
52.499167
|
||||||
|
],
|
||||||
|
"bearing": 30,
|
||||||
|
"pitch": 61,
|
||||||
|
"zoom": 16,
|
||||||
|
"sources": {
|
||||||
|
"satellite": {
|
||||||
|
"type": "raster",
|
||||||
|
"tiles": [
|
||||||
|
"https://demotiles.maplibre.org/debug-tiles/number/{z}.png"
|
||||||
|
],
|
||||||
|
"maxzoom": 22,
|
||||||
|
"tileSize": 256
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"layers": [
|
||||||
|
{
|
||||||
|
"id": "raster",
|
||||||
|
"type": "raster",
|
||||||
|
"source": "satellite",
|
||||||
|
"paint": {
|
||||||
|
"raster-fade-duration": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 37 KiB |
@@ -0,0 +1,40 @@
|
|||||||
|
{
|
||||||
|
"version": 8,
|
||||||
|
"metadata": {
|
||||||
|
"test": {
|
||||||
|
"maxPitch": 85,
|
||||||
|
"operations": [
|
||||||
|
["setTileLodMode", "distance"],
|
||||||
|
["setTileLodScale", 3],
|
||||||
|
["wait"]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"center": [
|
||||||
|
13.418056,
|
||||||
|
52.499167
|
||||||
|
],
|
||||||
|
"bearing": 30,
|
||||||
|
"pitch": 72,
|
||||||
|
"zoom": 16,
|
||||||
|
"sources": {
|
||||||
|
"satellite": {
|
||||||
|
"type": "raster",
|
||||||
|
"tiles": [
|
||||||
|
"https://demotiles.maplibre.org/debug-tiles/number/{z}.png"
|
||||||
|
],
|
||||||
|
"maxzoom": 22,
|
||||||
|
"tileSize": 256
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"layers": [
|
||||||
|
{
|
||||||
|
"id": "raster",
|
||||||
|
"type": "raster",
|
||||||
|
"source": "satellite",
|
||||||
|
"paint": {
|
||||||
|
"raster-fade-duration": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 45 KiB |
|
After Width: | Height: | Size: 45 KiB |
@@ -0,0 +1,39 @@
|
|||||||
|
{
|
||||||
|
"version": 8,
|
||||||
|
"metadata": {
|
||||||
|
"test": {
|
||||||
|
"maxPitch": 85,
|
||||||
|
"operations": [
|
||||||
|
["setTileLodMode", "distance"],
|
||||||
|
["wait"]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"center": [
|
||||||
|
13.418056,
|
||||||
|
52.499167
|
||||||
|
],
|
||||||
|
"bearing": 30,
|
||||||
|
"pitch": 72,
|
||||||
|
"zoom": 16,
|
||||||
|
"sources": {
|
||||||
|
"satellite": {
|
||||||
|
"type": "raster",
|
||||||
|
"tiles": [
|
||||||
|
"https://demotiles.maplibre.org/debug-tiles/number/{z}.png"
|
||||||
|
],
|
||||||
|
"maxzoom": 22,
|
||||||
|
"tileSize": 256
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"layers": [
|
||||||
|
{
|
||||||
|
"id": "raster",
|
||||||
|
"type": "raster",
|
||||||
|
"source": "satellite",
|
||||||
|
"paint": {
|
||||||
|
"raster-fade-duration": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 39 KiB |
|
After Width: | Height: | Size: 40 KiB |
@@ -0,0 +1,39 @@
|
|||||||
|
{
|
||||||
|
"version": 8,
|
||||||
|
"metadata": {
|
||||||
|
"test": {
|
||||||
|
"maxPitch": 85,
|
||||||
|
"operations": [
|
||||||
|
["setTileLodMinRadius", 1],
|
||||||
|
["wait"]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"center": [
|
||||||
|
13.418056,
|
||||||
|
52.499167
|
||||||
|
],
|
||||||
|
"bearing": 30,
|
||||||
|
"pitch": 72,
|
||||||
|
"zoom": 16,
|
||||||
|
"sources": {
|
||||||
|
"satellite": {
|
||||||
|
"type": "raster",
|
||||||
|
"tiles": [
|
||||||
|
"https://demotiles.maplibre.org/debug-tiles/number/{z}.png"
|
||||||
|
],
|
||||||
|
"maxzoom": 22,
|
||||||
|
"tileSize": 256
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"layers": [
|
||||||
|
{
|
||||||
|
"id": "raster",
|
||||||
|
"type": "raster",
|
||||||
|
"source": "satellite",
|
||||||
|
"paint": {
|
||||||
|
"raster-fade-duration": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 41 KiB |
@@ -0,0 +1,39 @@
|
|||||||
|
{
|
||||||
|
"version": 8,
|
||||||
|
"metadata": {
|
||||||
|
"test": {
|
||||||
|
"maxPitch": 85,
|
||||||
|
"operations": [
|
||||||
|
["setTileLodPitchThreshold", 62],
|
||||||
|
["wait"]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"center": [
|
||||||
|
13.418056,
|
||||||
|
52.499167
|
||||||
|
],
|
||||||
|
"bearing": 30,
|
||||||
|
"pitch": 61,
|
||||||
|
"zoom": 16,
|
||||||
|
"sources": {
|
||||||
|
"satellite": {
|
||||||
|
"type": "raster",
|
||||||
|
"tiles": [
|
||||||
|
"https://demotiles.maplibre.org/debug-tiles/number/{z}.png"
|
||||||
|
],
|
||||||
|
"maxzoom": 22,
|
||||||
|
"tileSize": 256
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"layers": [
|
||||||
|
{
|
||||||
|
"id": "raster",
|
||||||
|
"type": "raster",
|
||||||
|
"source": "satellite",
|
||||||
|
"paint": {
|
||||||
|
"raster-fade-duration": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 37 KiB |
BIN
metrics/integration/render-tests/tile-lod/scale/expected.png
Normal file
|
After Width: | Height: | Size: 37 KiB |
39
metrics/integration/render-tests/tile-lod/scale/style.json
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
{
|
||||||
|
"version": 8,
|
||||||
|
"metadata": {
|
||||||
|
"test": {
|
||||||
|
"maxPitch": 85,
|
||||||
|
"operations": [
|
||||||
|
["setTileLodScale", 3],
|
||||||
|
["wait"]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"center": [
|
||||||
|
13.418056,
|
||||||
|
52.499167
|
||||||
|
],
|
||||||
|
"bearing": 30,
|
||||||
|
"pitch": 72,
|
||||||
|
"zoom": 16,
|
||||||
|
"sources": {
|
||||||
|
"satellite": {
|
||||||
|
"type": "raster",
|
||||||
|
"tiles": [
|
||||||
|
"https://demotiles.maplibre.org/debug-tiles/number/{z}.png"
|
||||||
|
],
|
||||||
|
"maxzoom": 22,
|
||||||
|
"tileSize": 256
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"layers": [
|
||||||
|
{
|
||||||
|
"id": "raster",
|
||||||
|
"type": "raster",
|
||||||
|
"source": "satellite",
|
||||||
|
"paint": {
|
||||||
|
"raster-fade-duration": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 53 KiB |
|
After Width: | Height: | Size: 53 KiB |
@@ -0,0 +1,39 @@
|
|||||||
|
{
|
||||||
|
"version": 8,
|
||||||
|
"metadata": {
|
||||||
|
"test": {
|
||||||
|
"maxPitch": 85,
|
||||||
|
"operations": [
|
||||||
|
["setTileLodZoomShift", 2],
|
||||||
|
["wait"]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"center": [
|
||||||
|
13.418056,
|
||||||
|
52.499167
|
||||||
|
],
|
||||||
|
"bearing": 30,
|
||||||
|
"pitch": 72,
|
||||||
|
"zoom": 16,
|
||||||
|
"sources": {
|
||||||
|
"satellite": {
|
||||||
|
"type": "raster",
|
||||||
|
"tiles": [
|
||||||
|
"https://demotiles.maplibre.org/debug-tiles/number/{z}.png"
|
||||||
|
],
|
||||||
|
"maxzoom": 22,
|
||||||
|
"tileSize": 256
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"layers": [
|
||||||
|
{
|
||||||
|
"id": "raster",
|
||||||
|
"type": "raster",
|
||||||
|
"source": "satellite",
|
||||||
|
"paint": {
|
||||||
|
"raster-fade-duration": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -100,60 +100,71 @@ public:
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
enum class TileLodMode {
|
enum class TileLodProfile {
|
||||||
Default, // Default Tile LOD parameters
|
Default, // Default Tile LOD parameters
|
||||||
NoLod, // Disable LOD
|
NoLod, // Disable LOD
|
||||||
Reduced, // Reduce LOD away from camera
|
Reduced, // Reduce LOD away from camera
|
||||||
Aggressive, // Aggressively reduce LOD away from camera at the detriment of quality
|
Aggressive, // Aggressively reduce LOD away from camera at the detriment of quality
|
||||||
|
DistanceBased, // Use distance-based algorithm
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr TileLodMode nextTileLodMode(TileLodMode current) {
|
constexpr TileLodProfile nextTileLodProfile(TileLodProfile current) {
|
||||||
switch (current) {
|
switch (current) {
|
||||||
case TileLodMode::Default:
|
case TileLodProfile::Default:
|
||||||
return TileLodMode::NoLod;
|
return TileLodProfile::NoLod;
|
||||||
case TileLodMode::NoLod:
|
case TileLodProfile::NoLod:
|
||||||
return TileLodMode::Reduced;
|
return TileLodProfile::Reduced;
|
||||||
case TileLodMode::Reduced:
|
case TileLodProfile::Reduced:
|
||||||
return TileLodMode::Aggressive;
|
return TileLodProfile::Aggressive;
|
||||||
case TileLodMode::Aggressive:
|
case TileLodProfile::Aggressive:
|
||||||
return TileLodMode::Default;
|
return TileLodProfile::DistanceBased;
|
||||||
|
case TileLodProfile::DistanceBased:
|
||||||
|
return TileLodProfile::Default;
|
||||||
default:
|
default:
|
||||||
return TileLodMode::Default;
|
return TileLodProfile::Default;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void cycleTileLodMode(mbgl::Map &map) {
|
void cycleTileLodProfile(mbgl::Map &map) {
|
||||||
// TileLodMode::Default parameters
|
// TileLodProfile::Default parameters
|
||||||
static const auto defaultRadius = map.getTileLodMinRadius();
|
static const auto defaultRadius = map.getTileLodMinRadius();
|
||||||
static const auto defaultScale = map.getTileLodScale();
|
static const auto defaultScale = map.getTileLodScale();
|
||||||
static const auto defaultTilePitchThreshold = map.getTileLodPitchThreshold();
|
static const auto defaultTilePitchThreshold = map.getTileLodPitchThreshold();
|
||||||
|
static const auto defaultTileLodMode = map.getTileLodMode();
|
||||||
|
|
||||||
static TileLodMode mode = TileLodMode::Default;
|
static TileLodProfile profile = TileLodProfile::Default;
|
||||||
mode = nextTileLodMode(mode);
|
profile = nextTileLodProfile(profile);
|
||||||
|
|
||||||
switch (mode) {
|
switch (profile) {
|
||||||
case TileLodMode::Default:
|
case TileLodProfile::Default:
|
||||||
map.setTileLodMinRadius(defaultRadius);
|
map.setTileLodMinRadius(defaultRadius);
|
||||||
map.setTileLodScale(defaultScale);
|
map.setTileLodScale(defaultScale);
|
||||||
map.setTileLodPitchThreshold(defaultTilePitchThreshold);
|
map.setTileLodPitchThreshold(defaultTilePitchThreshold);
|
||||||
mbgl::Log::Info(mbgl::Event::General, "Tile LOD mode: default");
|
map.setTileLodMode(defaultTileLodMode);
|
||||||
|
mbgl::Log::Info(mbgl::Event::General, "Tile LOD profile: default");
|
||||||
break;
|
break;
|
||||||
case TileLodMode::NoLod:
|
case TileLodProfile::NoLod:
|
||||||
// When LOD is off we set a maximum PitchThreshold
|
// When LOD is off we set a maximum PitchThreshold
|
||||||
map.setTileLodPitchThreshold(std::numbers::pi);
|
map.setTileLodPitchThreshold(std::numbers::pi);
|
||||||
mbgl::Log::Info(mbgl::Event::General, "Tile LOD mode: disabled");
|
mbgl::Log::Info(mbgl::Event::General, "Tile LOD profile: disabled");
|
||||||
break;
|
break;
|
||||||
case TileLodMode::Reduced:
|
case TileLodProfile::Reduced:
|
||||||
map.setTileLodMinRadius(2);
|
map.setTileLodMinRadius(2);
|
||||||
map.setTileLodScale(1.5);
|
map.setTileLodScale(1.5);
|
||||||
map.setTileLodPitchThreshold(std::numbers::pi / 4);
|
map.setTileLodPitchThreshold(std::numbers::pi / 4);
|
||||||
mbgl::Log::Info(mbgl::Event::General, "Tile LOD mode: reduced");
|
mbgl::Log::Info(mbgl::Event::General, "Tile LOD profile: reduced");
|
||||||
break;
|
break;
|
||||||
case TileLodMode::Aggressive:
|
case TileLodProfile::Aggressive:
|
||||||
map.setTileLodMinRadius(1);
|
map.setTileLodMinRadius(1);
|
||||||
map.setTileLodScale(2);
|
map.setTileLodScale(2);
|
||||||
map.setTileLodPitchThreshold(0);
|
map.setTileLodPitchThreshold(0);
|
||||||
mbgl::Log::Info(mbgl::Event::General, "Tile LOD mode: aggressive");
|
mbgl::Log::Info(mbgl::Event::General, "Tile LOD profile: aggressive");
|
||||||
|
break;
|
||||||
|
case TileLodProfile::DistanceBased:
|
||||||
|
map.setTileLodScale(1);
|
||||||
|
map.setTileLodPitchThreshold(0);
|
||||||
|
map.setTileLodMode(mbgl::TileLodMode::Distance);
|
||||||
|
mbgl::Log::Info(mbgl::Event::General, "Tile LOD profile: distance-based");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
map.triggerRepaint();
|
map.triggerRepaint();
|
||||||
@@ -651,7 +662,7 @@ void GLFWView::onKey(GLFWwindow *window, int key, int /*scancode*/, int action,
|
|||||||
view->invalidate();
|
view->invalidate();
|
||||||
} break;
|
} break;
|
||||||
case GLFW_KEY_F6: {
|
case GLFW_KEY_F6: {
|
||||||
cycleTileLodMode(*view->map);
|
cycleTileLodProfile(*view->map);
|
||||||
} break;
|
} break;
|
||||||
case GLFW_KEY_F7: {
|
case GLFW_KEY_F7: {
|
||||||
tileLodZoomShift(*view->map, false);
|
tileLodZoomShift(*view->map, false);
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
#include <mbgl/style/style.hpp>
|
#include <mbgl/style/style.hpp>
|
||||||
#include <mbgl/util/compression.hpp>
|
#include <mbgl/util/compression.hpp>
|
||||||
#include <mbgl/util/io.hpp>
|
#include <mbgl/util/io.hpp>
|
||||||
|
#include <mbgl/util/enum.hpp>
|
||||||
#include <mbgl/util/logging.hpp>
|
#include <mbgl/util/logging.hpp>
|
||||||
#include <mbgl/util/rapidjson.hpp>
|
#include <mbgl/util/rapidjson.hpp>
|
||||||
#include <mbgl/util/run_loop.hpp>
|
#include <mbgl/util/run_loop.hpp>
|
||||||
@@ -612,6 +613,10 @@ TestMetadata parseTestMetadata(const TestPaths& paths) {
|
|||||||
return metadata;
|
return metadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace mbgl {
|
||||||
|
MBGL_DEFINE_ENUM(TileLodMode, {{TileLodMode::Default, "default"}, {TileLodMode::Distance, "distance"}});
|
||||||
|
}
|
||||||
|
|
||||||
namespace TestOperationNames {
|
namespace TestOperationNames {
|
||||||
const std::string waitOp("wait");
|
const std::string waitOp("wait");
|
||||||
const std::string sleepOp("sleep");
|
const std::string sleepOp("sleep");
|
||||||
@@ -648,6 +653,11 @@ const std::string panGestureOp("panGesture");
|
|||||||
const std::string gfxProbeOp("probeGFX");
|
const std::string gfxProbeOp("probeGFX");
|
||||||
const std::string gfxProbeStartOp("probeGFXStart");
|
const std::string gfxProbeStartOp("probeGFXStart");
|
||||||
const std::string gfxProbeEndOp("probeGFXEnd");
|
const std::string gfxProbeEndOp("probeGFXEnd");
|
||||||
|
const std::string setTileLodMinRadiusOp("setTileLodMinRadius");
|
||||||
|
const std::string setTileLodScaleOp("setTileLodScale");
|
||||||
|
const std::string setTileLodPitchThresholdOp("setTileLodPitchThreshold");
|
||||||
|
const std::string setTileLodZoomShiftOp("setTileLodZoomShift");
|
||||||
|
const std::string setTileLodModeOp("setTileLodMode");
|
||||||
} // namespace TestOperationNames
|
} // namespace TestOperationNames
|
||||||
|
|
||||||
using namespace TestOperationNames;
|
using namespace TestOperationNames;
|
||||||
@@ -1321,6 +1331,53 @@ TestOperations parseTestOperations(TestMetadata& metadata) {
|
|||||||
ctx.getMetadata().metrics.gfx.insert({mark, metricProbe});
|
ctx.getMetadata().metrics.gfx.insert({mark, metricProbe});
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
} else if (operationArray[0].GetString() == setTileLodMinRadiusOp) {
|
||||||
|
// setTileLodMinRadius
|
||||||
|
assert(operationArray.Size() == 2u);
|
||||||
|
assert(operationArray[1].IsNumber());
|
||||||
|
double minRadius = operationArray[1].GetDouble();
|
||||||
|
result.emplace_back([minRadius](TestContext& ctx) {
|
||||||
|
ctx.getMap().setTileLodMinRadius(minRadius);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
} else if (operationArray[0].GetString() == setTileLodScaleOp) {
|
||||||
|
// setTileLodScale
|
||||||
|
assert(operationArray.Size() == 2u);
|
||||||
|
assert(operationArray[1].IsNumber());
|
||||||
|
double scale = operationArray[1].GetDouble();
|
||||||
|
result.emplace_back([scale](TestContext& ctx) {
|
||||||
|
ctx.getMap().setTileLodScale(scale);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
} else if (operationArray[0].GetString() == setTileLodPitchThresholdOp) {
|
||||||
|
// setTileLodPitchThreshold
|
||||||
|
assert(operationArray.Size() == 2u);
|
||||||
|
assert(operationArray[1].IsNumber());
|
||||||
|
double pitchThreshold = operationArray[1].GetDouble();
|
||||||
|
result.emplace_back([pitchThreshold](TestContext& ctx) {
|
||||||
|
ctx.getMap().setTileLodPitchThreshold(pitchThreshold);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
} else if (operationArray[0].GetString() == setTileLodZoomShiftOp) {
|
||||||
|
// setTileLodZoomShift
|
||||||
|
assert(operationArray.Size() == 2u);
|
||||||
|
assert(operationArray[1].IsNumber());
|
||||||
|
double zoomShift = operationArray[1].GetDouble();
|
||||||
|
result.emplace_back([zoomShift](TestContext& ctx) {
|
||||||
|
ctx.getMap().setTileLodZoomShift(zoomShift);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
} else if (operationArray[0].GetString() == setTileLodModeOp) {
|
||||||
|
// setTileLodMode
|
||||||
|
assert(operationArray.Size() == 2u);
|
||||||
|
assert(operationArray[1].IsString());
|
||||||
|
mbgl::TileLodMode mode = mbgl::Enum<mbgl::TileLodMode>::toEnum(operationArray[1].GetString()).value();
|
||||||
|
result.emplace_back([mode](TestContext& ctx) {
|
||||||
|
ctx.getMap().setTileLodMode(mode);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
metadata.errorMessage = std::string("Unsupported operation: ") + operationArray[0].GetString();
|
metadata.errorMessage = std::string("Unsupported operation: ") + operationArray[0].GetString();
|
||||||
return {};
|
return {};
|
||||||
|
|||||||
@@ -67,4 +67,9 @@ extern const std::string panGestureOp;
|
|||||||
extern const std::string gfxProbeOp;
|
extern const std::string gfxProbeOp;
|
||||||
extern const std::string gfxProbeStartOp;
|
extern const std::string gfxProbeStartOp;
|
||||||
extern const std::string gfxProbeEndOp;
|
extern const std::string gfxProbeEndOp;
|
||||||
|
extern const std::string setTileLodMinRadiusOp;
|
||||||
|
extern const std::string setTileLodScaleOp;
|
||||||
|
extern const std::string setTileLodPitchThresholdOp;
|
||||||
|
extern const std::string setTileLodZoomShiftOp;
|
||||||
|
extern const std::string setTileLodModeOp;
|
||||||
} // namespace TestOperationNames
|
} // namespace TestOperationNames
|
||||||
|
|||||||
@@ -588,6 +588,14 @@ double Map::getTileLodZoomShift() const {
|
|||||||
return impl->tileLodZoomShift;
|
return impl->tileLodZoomShift;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Map::setTileLodMode(TileLodMode mode) {
|
||||||
|
impl->tileLodMode = mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
TileLodMode Map::getTileLodMode() const {
|
||||||
|
return impl->tileLodMode;
|
||||||
|
}
|
||||||
|
|
||||||
ClientOptions Map::getClientOptions() const {
|
ClientOptions Map::getClientOptions() const {
|
||||||
return impl->fileSource ? impl->fileSource->getClientOptions() : ClientOptions();
|
return impl->fileSource ? impl->fileSource->getClientOptions() : ClientOptions();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -135,7 +135,8 @@ void Map::Impl::onUpdate() {
|
|||||||
.tileLodMinRadius = tileLodMinRadius,
|
.tileLodMinRadius = tileLodMinRadius,
|
||||||
.tileLodScale = tileLodScale,
|
.tileLodScale = tileLodScale,
|
||||||
.tileLodPitchThreshold = tileLodPitchThreshold,
|
.tileLodPitchThreshold = tileLodPitchThreshold,
|
||||||
.tileLodZoomShift = tileLodZoomShift};
|
.tileLodZoomShift = tileLodZoomShift,
|
||||||
|
.tileLodMode = tileLodMode};
|
||||||
|
|
||||||
rendererFrontend.update(std::make_shared<UpdateParameters>(std::move(params)));
|
rendererFrontend.update(std::make_shared<UpdateParameters>(std::move(params)));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -111,6 +111,7 @@ public:
|
|||||||
double tileLodScale = 1;
|
double tileLodScale = 1;
|
||||||
double tileLodPitchThreshold = (60.0 / 180.0) * std::numbers::pi;
|
double tileLodPitchThreshold = (60.0 / 180.0) * std::numbers::pi;
|
||||||
double tileLodZoomShift = 0;
|
double tileLodZoomShift = 0;
|
||||||
|
TileLodMode tileLodMode = TileLodMode::Default;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Forward declaration of this method is required for the MapProjection class
|
// Forward declaration of this method is required for the MapProjection class
|
||||||
|
|||||||
@@ -120,11 +120,14 @@ void RenderBackgroundLayer::update(gfx::ShaderRegistry& shaders,
|
|||||||
[[maybe_unused]] UniqueChangeRequestVec& changes) {
|
[[maybe_unused]] UniqueChangeRequestVec& changes) {
|
||||||
assert(updateParameters);
|
assert(updateParameters);
|
||||||
const auto zoom = state.getIntegerZoom();
|
const auto zoom = state.getIntegerZoom();
|
||||||
|
Range<uint8_t> zoomRange(0, zoom);
|
||||||
const auto tileCover = util::tileCover({.transformState = state,
|
const auto tileCover = util::tileCover({.transformState = state,
|
||||||
.tileLodMinRadius = updateParameters->tileLodMinRadius,
|
.tileLodMinRadius = updateParameters->tileLodMinRadius,
|
||||||
.tileLodScale = updateParameters->tileLodScale,
|
.tileLodScale = updateParameters->tileLodScale,
|
||||||
.tileLodPitchThreshold = updateParameters->tileLodPitchThreshold},
|
.tileLodPitchThreshold = updateParameters->tileLodPitchThreshold,
|
||||||
zoom);
|
.tileLodMode = updateParameters->tileLodMode},
|
||||||
|
zoom,
|
||||||
|
zoomRange);
|
||||||
|
|
||||||
// renderTiles is always empty, we use tileCover instead
|
// renderTiles is always empty, we use tileCover instead
|
||||||
if (tileCover.empty()) {
|
if (tileCover.empty()) {
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ PaintParameters::PaintParameters(gfx::Context& context_,
|
|||||||
double tileLodMinRadius_,
|
double tileLodMinRadius_,
|
||||||
double tileLodScale_,
|
double tileLodScale_,
|
||||||
double tileLodPitchThreshold_,
|
double tileLodPitchThreshold_,
|
||||||
|
TileLodMode tileLodMode_,
|
||||||
const gfx::ScissorRect& scissorRect_)
|
const gfx::ScissorRect& scissorRect_)
|
||||||
: context(context_),
|
: context(context_),
|
||||||
backend(backend_),
|
backend(backend_),
|
||||||
@@ -83,6 +84,7 @@ PaintParameters::PaintParameters(gfx::Context& context_,
|
|||||||
tileLodMinRadius(tileLodMinRadius_),
|
tileLodMinRadius(tileLodMinRadius_),
|
||||||
tileLodScale(tileLodScale_),
|
tileLodScale(tileLodScale_),
|
||||||
tileLodPitchThreshold(tileLodPitchThreshold_),
|
tileLodPitchThreshold(tileLodPitchThreshold_),
|
||||||
|
tileLodMode(tileLodMode_),
|
||||||
scissorRect(scissorRect_) {
|
scissorRect(scissorRect_) {
|
||||||
pixelsToGLUnits = {{2.0f / state.getSize().width, -2.0f / state.getSize().height}};
|
pixelsToGLUnits = {{2.0f / state.getSize().width, -2.0f / state.getSize().height}};
|
||||||
|
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ public:
|
|||||||
double tileLodMinRadius,
|
double tileLodMinRadius,
|
||||||
double tileLodScale,
|
double tileLodScale,
|
||||||
double tileLodPitchThreshold,
|
double tileLodPitchThreshold,
|
||||||
|
TileLodMode tileLodMode,
|
||||||
const gfx::ScissorRect&);
|
const gfx::ScissorRect&);
|
||||||
~PaintParameters();
|
~PaintParameters();
|
||||||
|
|
||||||
@@ -139,6 +140,7 @@ public:
|
|||||||
double tileLodMinRadius;
|
double tileLodMinRadius;
|
||||||
double tileLodScale;
|
double tileLodScale;
|
||||||
double tileLodPitchThreshold;
|
double tileLodPitchThreshold;
|
||||||
|
TileLodMode tileLodMode;
|
||||||
|
|
||||||
gfx::ScissorRect scissorRect;
|
gfx::ScissorRect scissorRect;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -200,6 +200,7 @@ std::unique_ptr<RenderTree> RenderOrchestrator::createRenderTree(
|
|||||||
.tileLodScale = updateParameters->tileLodScale,
|
.tileLodScale = updateParameters->tileLodScale,
|
||||||
.tileLodPitchThreshold = updateParameters->tileLodPitchThreshold,
|
.tileLodPitchThreshold = updateParameters->tileLodPitchThreshold,
|
||||||
.tileLodZoomShift = updateParameters->tileLodZoomShift,
|
.tileLodZoomShift = updateParameters->tileLodZoomShift,
|
||||||
|
.tileLodMode = updateParameters->tileLodMode,
|
||||||
.dynamicTextureAtlas = dynamicTextureAtlas};
|
.dynamicTextureAtlas = dynamicTextureAtlas};
|
||||||
|
|
||||||
glyphManager->setURL(updateParameters->glyphURL);
|
glyphManager->setURL(updateParameters->glyphURL);
|
||||||
|
|||||||
@@ -215,6 +215,7 @@ void Renderer::Impl::render(const RenderTree& renderTree, const std::shared_ptr<
|
|||||||
updateParameters->tileLodMinRadius,
|
updateParameters->tileLodMinRadius,
|
||||||
updateParameters->tileLodScale,
|
updateParameters->tileLodScale,
|
||||||
updateParameters->tileLodPitchThreshold,
|
updateParameters->tileLodPitchThreshold,
|
||||||
|
updateParameters->tileLodMode,
|
||||||
scissorRect,
|
scissorRect,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -133,11 +133,14 @@ void RenderImageSource::update(Immutable<style::Source::Impl> baseImpl_,
|
|||||||
|
|
||||||
bool hasVisibleTile = false;
|
bool hasVisibleTile = false;
|
||||||
// Add additional wrapped tile ids if neccessary
|
// Add additional wrapped tile ids if neccessary
|
||||||
|
Range<uint8_t> zoomRange(0, zoom);
|
||||||
auto idealTiles = util::tileCover({.transformState = transformState,
|
auto idealTiles = util::tileCover({.transformState = transformState,
|
||||||
.tileLodMinRadius = parameters.tileLodMinRadius,
|
.tileLodMinRadius = parameters.tileLodMinRadius,
|
||||||
.tileLodScale = parameters.tileLodScale,
|
.tileLodScale = parameters.tileLodScale,
|
||||||
.tileLodPitchThreshold = parameters.tileLodPitchThreshold},
|
.tileLodPitchThreshold = parameters.tileLodPitchThreshold,
|
||||||
static_cast<uint8_t>(transformState.getZoom()));
|
.tileLodMode = parameters.tileLodMode},
|
||||||
|
static_cast<uint8_t>(transformState.getZoom()),
|
||||||
|
zoomRange);
|
||||||
for (auto tile : idealTiles) {
|
for (auto tile : idealTiles) {
|
||||||
if (tile.wrap != 0 && tileCover[0].canonical.isChildOf(tile.canonical)) {
|
if (tile.wrap != 0 && tileCover[0].canonical.isChildOf(tile.canonical)) {
|
||||||
tileIds.emplace_back(tile.wrap, tileCover[0].canonical);
|
tileIds.emplace_back(tile.wrap, tileCover[0].canonical);
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ public:
|
|||||||
double tileLodScale = 1;
|
double tileLodScale = 1;
|
||||||
double tileLodPitchThreshold = (60.0 / 180.0) * std::numbers::pi;
|
double tileLodPitchThreshold = (60.0 / 180.0) * std::numbers::pi;
|
||||||
double tileLodZoomShift = 0;
|
double tileLodZoomShift = 0;
|
||||||
|
TileLodMode tileLodMode = TileLodMode::Default;
|
||||||
gfx::DynamicTextureAtlasPtr dynamicTextureAtlas;
|
gfx::DynamicTextureAtlasPtr dynamicTextureAtlas;
|
||||||
bool isUpdateSynchronous = false;
|
bool isUpdateSynchronous = false;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -113,7 +113,8 @@ void TilePyramid::update(const std::vector<Immutable<style::LayerProperties>>& l
|
|||||||
util::TileCoverParameters tileCoverParameters = {.transformState = parameters.transformState,
|
util::TileCoverParameters tileCoverParameters = {.transformState = parameters.transformState,
|
||||||
.tileLodMinRadius = parameters.tileLodMinRadius,
|
.tileLodMinRadius = parameters.tileLodMinRadius,
|
||||||
.tileLodScale = parameters.tileLodScale,
|
.tileLodScale = parameters.tileLodScale,
|
||||||
.tileLodPitchThreshold = parameters.tileLodPitchThreshold};
|
.tileLodPitchThreshold = parameters.tileLodPitchThreshold,
|
||||||
|
.tileLodMode = parameters.tileLodMode};
|
||||||
|
|
||||||
if (std::cmp_greater_equal(overscaledZoom, zoomRange.min)) {
|
if (std::cmp_greater_equal(overscaledZoom, zoomRange.min)) {
|
||||||
int32_t idealZoom = std::min<int32_t>(zoomRange.max, overscaledZoom);
|
int32_t idealZoom = std::min<int32_t>(zoomRange.max, overscaledZoom);
|
||||||
@@ -135,11 +136,11 @@ void TilePyramid::update(const std::vector<Immutable<style::LayerProperties>>& l
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (panZoom < idealZoom) {
|
if (panZoom < idealZoom) {
|
||||||
panTiles = util::tileCover(tileCoverParameters, panZoom);
|
panTiles = util::tileCover(tileCoverParameters, panZoom, zoomRange);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
idealTiles = util::tileCover(tileCoverParameters, idealZoom, tileZoom);
|
idealTiles = util::tileCover(tileCoverParameters, idealZoom, zoomRange, tileZoom);
|
||||||
if (parameters.mode == MapMode::Tile && type != SourceType::Raster && type != SourceType::RasterDEM &&
|
if (parameters.mode == MapMode::Tile && type != SourceType::Raster && type != SourceType::RasterDEM &&
|
||||||
idealTiles.size() > 1) {
|
idealTiles.size() > 1) {
|
||||||
mbgl::Log::Warning(mbgl::Event::General,
|
mbgl::Log::Warning(mbgl::Event::General,
|
||||||
@@ -176,8 +177,10 @@ void TilePyramid::update(const std::vector<Immutable<style::LayerProperties>>& l
|
|||||||
// levels. Child tiles are used from the cache, but not created.
|
// levels. Child tiles are used from the cache, but not created.
|
||||||
std::optional<util::TileRange> tileRange = std::nullopt;
|
std::optional<util::TileRange> tileRange = std::nullopt;
|
||||||
if (bounds) {
|
if (bounds) {
|
||||||
tileRange = util::TileRange::fromLatLngBounds(
|
int32_t maxZoom = (parameters.tileLodMode == TileLodMode::Distance)
|
||||||
*bounds, zoomRange.min, std::min(tileZoom, static_cast<int32_t>(zoomRange.max)));
|
? zoomRange.max
|
||||||
|
: std::min(tileZoom, static_cast<int32_t>(zoomRange.max));
|
||||||
|
tileRange = util::TileRange::fromLatLngBounds(*bounds, zoomRange.min, maxZoom);
|
||||||
}
|
}
|
||||||
auto createTileFn = [&](const OverscaledTileID& tileID) -> Tile* {
|
auto createTileFn = [&](const OverscaledTileID& tileID) -> Tile* {
|
||||||
if (tileRange && !tileRange->contains(tileID.canonical)) {
|
if (tileRange && !tileRange->contains(tileID.canonical)) {
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ public:
|
|||||||
double tileLodScale = 1;
|
double tileLodScale = 1;
|
||||||
double tileLodPitchThreshold = (60.0 / 180.0) * std::numbers::pi;
|
double tileLodPitchThreshold = (60.0 / 180.0) * std::numbers::pi;
|
||||||
double tileLodZoomShift = 0;
|
double tileLodZoomShift = 0;
|
||||||
|
TileLodMode tileLodMode = TileLodMode::Default;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace mbgl
|
} // namespace mbgl
|
||||||
|
|||||||
@@ -157,6 +157,7 @@ int32_t coveringZoomLevel(double zoom, style::SourceType type, uint16_t size) no
|
|||||||
|
|
||||||
std::vector<OverscaledTileID> tileCover(const TileCoverParameters& state,
|
std::vector<OverscaledTileID> tileCover(const TileCoverParameters& state,
|
||||||
uint8_t z,
|
uint8_t z,
|
||||||
|
const Range<uint8_t> zoomRange,
|
||||||
const std::optional<uint8_t>& overscaledZ) {
|
const std::optional<uint8_t>& overscaledZ) {
|
||||||
struct Node {
|
struct Node {
|
||||||
AABB aabb;
|
AABB aabb;
|
||||||
@@ -171,12 +172,28 @@ std::vector<OverscaledTileID> tileCover(const TileCoverParameters& state,
|
|||||||
double sqrDist;
|
double sqrDist;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
auto childrenOf = [](const Node& node) -> std::vector<Node> {
|
||||||
|
std::vector<Node> children(4);
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
const uint32_t childX = (node.x << 1) + (i % 2);
|
||||||
|
const uint32_t childY = (node.y << 1) + (i >> 1);
|
||||||
|
|
||||||
|
children[i] = node;
|
||||||
|
children[i].aabb = node.aabb.quadrant(i);
|
||||||
|
children[i].zoom = node.zoom + 1;
|
||||||
|
children[i].x = childX;
|
||||||
|
children[i].y = childY;
|
||||||
|
}
|
||||||
|
return children;
|
||||||
|
};
|
||||||
|
|
||||||
const auto& transform = state.transformState;
|
const auto& transform = state.transformState;
|
||||||
const double numTiles = std::pow(2.0, z);
|
const double numTiles = std::pow(2.0, z);
|
||||||
const double worldSize = Projection::worldSize(transform.getScale());
|
const double worldSize = Projection::worldSize(transform.getScale());
|
||||||
const uint8_t minZoom = transform.getPitch() <= state.tileLodPitchThreshold ? z : 0;
|
const bool allowVariableZoom = transform.getPitch() > state.tileLodPitchThreshold;
|
||||||
const uint8_t maxZoom = z;
|
const uint8_t minZoom = allowVariableZoom ? zoomRange.min : z;
|
||||||
const uint8_t overscaledZoom = std::max(overscaledZ.value_or(z), z);
|
const uint8_t maxZoom = ((state.tileLodMode == TileLodMode::Distance) && allowVariableZoom) ? zoomRange.max : z;
|
||||||
|
const uint8_t overscaledZoom = std::max(overscaledZ.value_or(z), maxZoom);
|
||||||
const bool flippedY = transform.getViewportMode() == ViewportMode::FlippedY;
|
const bool flippedY = transform.getViewportMode() == ViewportMode::FlippedY;
|
||||||
|
|
||||||
const auto centerPoint = TileCoordinate::fromScreenCoordinate(
|
const auto centerPoint = TileCoordinate::fromScreenCoordinate(
|
||||||
@@ -185,6 +202,12 @@ std::vector<OverscaledTileID> tileCover(const TileCoverParameters& state,
|
|||||||
|
|
||||||
const vec3 centerCoord = {{centerPoint.x, centerPoint.y, 0.0}};
|
const vec3 centerCoord = {{centerPoint.x, centerPoint.y, 0.0}};
|
||||||
|
|
||||||
|
assert(transform.getFreeCameraOptions().position);
|
||||||
|
const vec3 cameraPositionMercator = *transform.getFreeCameraOptions().position;
|
||||||
|
const double nominalScale = std::pow(2.0, z);
|
||||||
|
const vec3 cameraCoord = vec3Scale(cameraPositionMercator, nominalScale);
|
||||||
|
const double cameraToCenterDistanceMercator = vec3Length(vec3Sub(cameraCoord, centerCoord)) / worldSize;
|
||||||
|
|
||||||
const Frustum frustum = Frustum::fromInvProjMatrix(transform.getInvProjectionMatrix(), worldSize, z, flippedY);
|
const Frustum frustum = Frustum::fromInvProjMatrix(transform.getInvProjectionMatrix(), worldSize, z, flippedY);
|
||||||
|
|
||||||
// There should always be a certain number of maximum zoom level tiles
|
// There should always be a certain number of maximum zoom level tiles
|
||||||
@@ -227,48 +250,51 @@ std::vector<OverscaledTileID> tileCover(const TileCoverParameters& state,
|
|||||||
node.fullyVisible = intersection == IntersectionResult::Contains;
|
node.fullyVisible = intersection == IntersectionResult::Contains;
|
||||||
}
|
}
|
||||||
|
|
||||||
const vec3 distanceXyz = node.aabb.distanceXYZ(centerCoord);
|
bool shouldSplitTile;
|
||||||
const double* longestDim = std::max_element(distanceXyz.data(), distanceXyz.data() + distanceXyz.size());
|
if (state.tileLodMode == TileLodMode::Distance) {
|
||||||
assert(longestDim);
|
const vec3 camToTileMercator = vec3Scale(node.aabb.distanceXYZ(cameraCoord), 1.0 / worldSize);
|
||||||
|
const double distanceToTileMercator = vec3Length(camToTileMercator);
|
||||||
|
const double cosPitchToTile = std::max(0.0, camToTileMercator[2] / distanceToTileMercator);
|
||||||
|
const double pitchExponent =
|
||||||
|
0.5; // 0: constant screen width, 1/2: constant screen area, 1: constant screen height
|
||||||
|
double tileScale = std::pow(2.0, node.zoom);
|
||||||
|
shouldSplitTile = distanceToTileMercator * tileScale < std::pow(cosPitchToTile, pitchExponent) *
|
||||||
|
cameraToCenterDistanceMercator /
|
||||||
|
state.tileLodScale * nominalScale;
|
||||||
|
} else {
|
||||||
|
const vec3 distanceXyz = node.aabb.distanceXYZ(centerCoord);
|
||||||
|
const double* longestDim = std::max_element(distanceXyz.data(), distanceXyz.data() + distanceXyz.size());
|
||||||
|
assert(longestDim);
|
||||||
|
|
||||||
// We're using distance based heuristics to determine if a tile should
|
// We're using distance based heuristics to determine if a tile should
|
||||||
// be split into quadrants or not. radiusOfMaxLvlLodInTiles defines that
|
// be split into quadrants or not. radiusOfMaxLvlLodInTiles defines that
|
||||||
// there's always a certain number of maxLevel tiles next to the map
|
// there's always a certain number of maxLevel tiles next to the map
|
||||||
// center. Using the fact that a parent node in quadtree is twice the
|
// center. Using the fact that a parent node in quadtree is twice the
|
||||||
// size of its children (per dimension) we can define distance
|
// size of its children (per dimension) we can define distance
|
||||||
// thresholds for each relative level:
|
// thresholds for each relative level:
|
||||||
// f(k) = offset + 2 + 4 + 8 + 16 + ... + 2^k
|
// f(k) = offset + 2 + 4 + 8 + 16 + ... + 2^k
|
||||||
// This is the same as:
|
// This is the same as:
|
||||||
// f(k) = offset + 2^(k+1)-2
|
// f(k) = offset + 2^(k+1)-2
|
||||||
const double distToSplit = radiusOfMaxLvlLodInTiles + (1 << (maxZoom - node.zoom)) - 2;
|
const double distToSplit = radiusOfMaxLvlLodInTiles + (1 << (maxZoom - node.zoom)) - 2;
|
||||||
|
shouldSplitTile = *longestDim * state.tileLodScale < distToSplit;
|
||||||
|
}
|
||||||
|
|
||||||
// Have we reached the target depth or is the tile too far away to be any split further?
|
// Have we reached the target depth or is the tile too far away to be any split further?
|
||||||
if (node.zoom == maxZoom || (*longestDim * state.tileLodScale > distToSplit && node.zoom >= minZoom)) {
|
if (node.zoom == maxZoom || (!shouldSplitTile && node.zoom >= minZoom)) {
|
||||||
// Perform precise intersection test between the frustum and aabb.
|
// Perform precise intersection test between the frustum and aabb.
|
||||||
// This will cull < 1% false positives missed by the original test
|
// This will cull < 1% false positives missed by the original test
|
||||||
if (node.fullyVisible || frustum.intersectsPrecise(node.aabb, true) != IntersectionResult::Separate) {
|
if (node.fullyVisible || frustum.intersectsPrecise(node.aabb, true) != IntersectionResult::Separate) {
|
||||||
const OverscaledTileID id = {
|
const OverscaledTileID id = {
|
||||||
node.zoom == maxZoom ? overscaledZoom : node.zoom, node.wrap, node.zoom, node.x, node.y};
|
node.zoom == maxZoom ? overscaledZoom : node.zoom, node.wrap, node.zoom, node.x, node.y};
|
||||||
const double dx = node.wrap * numTiles + node.x + 0.5 - centerCoord[0];
|
vec3 coordToLoadFirst = (state.tileLodMode == TileLodMode::Distance) ? cameraCoord : centerCoord;
|
||||||
const double dy = node.y + 0.5 - centerCoord[1];
|
const double dx = node.wrap * numTiles + node.x + 0.5 - coordToLoadFirst[0];
|
||||||
|
const double dy = node.y + 0.5 - coordToLoadFirst[1];
|
||||||
|
|
||||||
result.push_back({id, dx * dx + dy * dy});
|
result.push_back({id, dx * dx + dy * dy});
|
||||||
}
|
}
|
||||||
continue;
|
} else {
|
||||||
}
|
std::vector<Node> children = childrenOf(node);
|
||||||
|
stack.insert(stack.end(), children.begin(), children.end());
|
||||||
for (int i = 0; i < 4; i++) {
|
|
||||||
const uint32_t childX = (node.x << 1) + (i % 2);
|
|
||||||
const uint32_t childY = (node.y << 1) + (i >> 1);
|
|
||||||
|
|
||||||
// Create child node and push to the stack for traversal
|
|
||||||
stack.emplace_back(node);
|
|
||||||
Node& child = stack.back();
|
|
||||||
|
|
||||||
child.aabb = node.aabb.quadrant(i);
|
|
||||||
child.zoom = node.zoom + 1;
|
|
||||||
child.x = childX;
|
|
||||||
child.y = childY;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,12 +38,14 @@ struct TileCoverParameters {
|
|||||||
double tileLodMinRadius = 3;
|
double tileLodMinRadius = 3;
|
||||||
double tileLodScale = 1;
|
double tileLodScale = 1;
|
||||||
double tileLodPitchThreshold = (60.0 / 180.0) * std::numbers::pi;
|
double tileLodPitchThreshold = (60.0 / 180.0) * std::numbers::pi;
|
||||||
|
TileLodMode tileLodMode = TileLodMode::Default;
|
||||||
};
|
};
|
||||||
|
|
||||||
int32_t coveringZoomLevel(double z, style::SourceType type, uint16_t tileSize) noexcept;
|
int32_t coveringZoomLevel(double z, style::SourceType type, uint16_t tileSize) noexcept;
|
||||||
|
|
||||||
std::vector<OverscaledTileID> tileCover(const TileCoverParameters& state,
|
std::vector<OverscaledTileID> tileCover(const TileCoverParameters& state,
|
||||||
uint8_t z,
|
uint8_t z,
|
||||||
|
const Range<uint8_t> zoomRange,
|
||||||
const std::optional<uint8_t>& overscaledZ = std::nullopt);
|
const std::optional<uint8_t>& overscaledZ = std::nullopt);
|
||||||
std::vector<UnwrappedTileID> tileCover(const LatLngBounds&, uint8_t z);
|
std::vector<UnwrappedTileID> tileCover(const LatLngBounds&, uint8_t z);
|
||||||
std::vector<UnwrappedTileID> tileCover(const Geometry<double>&, uint8_t z);
|
std::vector<UnwrappedTileID> tileCover(const Geometry<double>&, uint8_t z);
|
||||||
|
|||||||
@@ -10,6 +10,8 @@
|
|||||||
|
|
||||||
using namespace mbgl;
|
using namespace mbgl;
|
||||||
|
|
||||||
|
const Range<uint8_t> zoomRange(0, 14);
|
||||||
|
|
||||||
TEST(TileCover, Empty) {
|
TEST(TileCover, Empty) {
|
||||||
EXPECT_EQ((std::vector<UnwrappedTileID>{}), util::tileCover(LatLngBounds::empty(), 0));
|
EXPECT_EQ((std::vector<UnwrappedTileID>{}), util::tileCover(LatLngBounds::empty(), 0));
|
||||||
}
|
}
|
||||||
@@ -44,7 +46,12 @@ TEST(TileCover, Pitch) {
|
|||||||
.withPitch(40.0));
|
.withPitch(40.0));
|
||||||
|
|
||||||
EXPECT_EQ((std::vector<OverscaledTileID>{{2, 1, 1}, {2, 2, 1}, {2, 1, 2}, {2, 2, 2}}),
|
EXPECT_EQ((std::vector<OverscaledTileID>{{2, 1, 1}, {2, 2, 1}, {2, 1, 2}, {2, 2, 2}}),
|
||||||
util::tileCover({transform.getState()}, 2));
|
util::tileCover({transform.getState()}, 2, zoomRange));
|
||||||
|
|
||||||
|
util::TileCoverParameters params{transform.getState()};
|
||||||
|
params.tileLodMode = TileLodMode::Distance;
|
||||||
|
EXPECT_EQ((std::vector<OverscaledTileID>{{2, 1, 2}, {2, 2, 2}, {2, 1, 1}, {2, 2, 1}}),
|
||||||
|
util::tileCover(params, 2, zoomRange));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(TileCover, PitchIssue15442) {
|
TEST(TileCover, PitchIssue15442) {
|
||||||
@@ -62,7 +69,47 @@ TEST(TileCover, PitchIssue15442) {
|
|||||||
|
|
||||||
EXPECT_EQ((std::vector<OverscaledTileID>{
|
EXPECT_EQ((std::vector<OverscaledTileID>{
|
||||||
{2, 3, 1}, {2, 2, 1}, {2, 3, 0}, {2, 2, 0}, {2, 1, {2, 0, 0}}, {2, 1, {2, 1, 0}}}),
|
{2, 3, 1}, {2, 2, 1}, {2, 3, 0}, {2, 2, 0}, {2, 1, {2, 0, 0}}, {2, 1, {2, 1, 0}}}),
|
||||||
util::tileCover({transform.getState()}, 2));
|
util::tileCover({transform.getState()}, 2, zoomRange));
|
||||||
|
|
||||||
|
util::TileCoverParameters params{transform.getState()};
|
||||||
|
params.tileLodMode = TileLodMode::Distance;
|
||||||
|
EXPECT_EQ((std::vector<OverscaledTileID>{
|
||||||
|
{2, 2, 1}, {2, 3, 1}, {2, 2, 0}, {2, 3, 0}, {2, 1, {2, 0, 0}}, {2, 1, {2, 1, 0}}}),
|
||||||
|
util::tileCover(params, 2, zoomRange));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TileCover, HighPitch) {
|
||||||
|
Transform transform;
|
||||||
|
transform.resize({512, 512});
|
||||||
|
// slightly offset center so that tile order is better defined
|
||||||
|
|
||||||
|
transform.jumpTo(CameraOptions()
|
||||||
|
.withCenter(LatLng{
|
||||||
|
0.1,
|
||||||
|
-0.1,
|
||||||
|
})
|
||||||
|
.withZoom(2.0)
|
||||||
|
.withBearing(5.0)
|
||||||
|
.withPitch(60.0));
|
||||||
|
|
||||||
|
util::TileCoverParameters params{transform.getState()};
|
||||||
|
params.tileLodPitchThreshold = 0.0;
|
||||||
|
params.tileLodMinRadius = 1.0;
|
||||||
|
EXPECT_EQ(
|
||||||
|
(std::vector<OverscaledTileID>{{2, 1, 1}, {2, 2, 1}, {2, 1, 2}, {2, 2, 2}, {2, 1, 0}, {2, 2, 0}, {2, 3, 0}}),
|
||||||
|
util::tileCover(params, 2, zoomRange));
|
||||||
|
params.tileLodMode = TileLodMode::Distance;
|
||||||
|
EXPECT_EQ((std::vector<OverscaledTileID>{{2, 1, 1},
|
||||||
|
{2, 2, 1},
|
||||||
|
{3, 3, 4},
|
||||||
|
{3, 3, 5},
|
||||||
|
{2, 1, 0},
|
||||||
|
{2, 2, 0},
|
||||||
|
{3, 4, 4},
|
||||||
|
{2, 3, 0},
|
||||||
|
{3, 4, 5},
|
||||||
|
{3, 5, 4}}),
|
||||||
|
util::tileCover(params, 2, zoomRange));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(TileCover, PitchOverAllowedByContentInsets) {
|
TEST(TileCover, PitchOverAllowedByContentInsets) {
|
||||||
@@ -79,7 +126,12 @@ TEST(TileCover, PitchOverAllowedByContentInsets) {
|
|||||||
EXPECT_LE(transform.getPitch() + 0.001, util::deg2rad(60));
|
EXPECT_LE(transform.getPitch() + 0.001, util::deg2rad(60));
|
||||||
|
|
||||||
EXPECT_EQ((std::vector<OverscaledTileID>{{3, 4, 3}, {3, 3, 3}, {3, 4, 4}, {3, 3, 4}}),
|
EXPECT_EQ((std::vector<OverscaledTileID>{{3, 4, 3}, {3, 3, 3}, {3, 4, 4}, {3, 3, 4}}),
|
||||||
util::tileCover({transform.getState()}, 3));
|
util::tileCover({transform.getState()}, 3, zoomRange));
|
||||||
|
|
||||||
|
util::TileCoverParameters params{transform.getState()};
|
||||||
|
params.tileLodMode = TileLodMode::Distance;
|
||||||
|
EXPECT_EQ((std::vector<OverscaledTileID>{{3, 3, 4}, {3, 3, 3}, {3, 4, 4}, {3, 4, 3}}),
|
||||||
|
util::tileCover(params, 3, zoomRange));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(TileCover, PitchWithLargerResultSet) {
|
TEST(TileCover, PitchWithLargerResultSet) {
|
||||||
@@ -95,7 +147,7 @@ TEST(TileCover, PitchWithLargerResultSet) {
|
|||||||
.withBearing(-142.2630000003529176)
|
.withBearing(-142.2630000003529176)
|
||||||
.withPitch(60.0));
|
.withPitch(60.0));
|
||||||
|
|
||||||
auto cover = util::tileCover({.transformState = transform.getState()}, 5);
|
auto cover = util::tileCover({.transformState = transform.getState()}, 5, zoomRange);
|
||||||
// Returned vector has above 100 elements, we check first 16 as there is a
|
// Returned vector has above 100 elements, we check first 16 as there is a
|
||||||
// plan to return lower LOD for distant tiles.
|
// plan to return lower LOD for distant tiles.
|
||||||
EXPECT_EQ((std::vector<OverscaledTileID>{{5, 15, 16},
|
EXPECT_EQ((std::vector<OverscaledTileID>{{5, 15, 16},
|
||||||
@@ -147,7 +199,13 @@ TEST(TileCover, CoordinatesAreUnwrapped) {
|
|||||||
|
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
(std::vector<OverscaledTileID>{{1, 0, {1, 1, 0}}, {1, 1, {1, 0, 0}}, {1, 0, {1, 1, 1}}, {1, 1, {1, 0, 1}}}),
|
(std::vector<OverscaledTileID>{{1, 0, {1, 1, 0}}, {1, 1, {1, 0, 0}}, {1, 0, {1, 1, 1}}, {1, 1, {1, 0, 1}}}),
|
||||||
util::tileCover({transform.getState()}, 1));
|
util::tileCover({transform.getState()}, 1, zoomRange));
|
||||||
|
|
||||||
|
util::TileCoverParameters params{transform.getState()};
|
||||||
|
params.tileLodMode = TileLodMode::Distance;
|
||||||
|
EXPECT_EQ(
|
||||||
|
(std::vector<OverscaledTileID>{{1, 0, {1, 1, 0}}, {1, 1, {1, 0, 0}}, {1, 0, {1, 1, 1}}, {1, 1, {1, 0, 1}}}),
|
||||||
|
util::tileCover(params, 1, zoomRange));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(TileCover, DifferentOverscaledZ) {
|
TEST(TileCover, DifferentOverscaledZ) {
|
||||||
@@ -164,7 +222,13 @@ TEST(TileCover, DifferentOverscaledZ) {
|
|||||||
|
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
(std::vector<OverscaledTileID>{{3, 0, {2, 1, 1}}, {3, 0, {2, 2, 1}}, {3, 0, {2, 1, 2}}, {3, 0, {2, 2, 2}}}),
|
(std::vector<OverscaledTileID>{{3, 0, {2, 1, 1}}, {3, 0, {2, 2, 1}}, {3, 0, {2, 1, 2}}, {3, 0, {2, 2, 2}}}),
|
||||||
util::tileCover({transform.getState()}, 2, 3));
|
util::tileCover({transform.getState()}, 2, zoomRange, 3));
|
||||||
|
|
||||||
|
util::TileCoverParameters params{transform.getState()};
|
||||||
|
params.tileLodMode = TileLodMode::Distance;
|
||||||
|
EXPECT_EQ(
|
||||||
|
(std::vector<OverscaledTileID>{{3, 0, {2, 1, 1}}, {3, 0, {2, 2, 1}}, {3, 0, {2, 1, 2}}, {3, 0, {2, 2, 2}}}),
|
||||||
|
util::tileCover(params, 2, zoomRange, 3));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(TileCover, DifferentOverscaledZWithPitch) {
|
TEST(TileCover, DifferentOverscaledZWithPitch) {
|
||||||
@@ -176,7 +240,7 @@ TEST(TileCover, DifferentOverscaledZWithPitch) {
|
|||||||
-10.0,
|
-10.0,
|
||||||
})
|
})
|
||||||
.withZoom(3.5)
|
.withZoom(3.5)
|
||||||
.withPitch(60));
|
.withPitch(61));
|
||||||
|
|
||||||
EXPECT_EQ((std::vector<OverscaledTileID>{{5, 0, {3, 3, 3}},
|
EXPECT_EQ((std::vector<OverscaledTileID>{{5, 0, {3, 3, 3}},
|
||||||
{5, 0, {3, 4, 3}},
|
{5, 0, {3, 4, 3}},
|
||||||
@@ -185,7 +249,18 @@ TEST(TileCover, DifferentOverscaledZWithPitch) {
|
|||||||
{5, 0, {3, 3, 1}},
|
{5, 0, {3, 3, 1}},
|
||||||
{5, 0, {3, 4, 1}},
|
{5, 0, {3, 4, 1}},
|
||||||
{5, 0, {3, 2, 1}}}),
|
{5, 0, {3, 2, 1}}}),
|
||||||
util::tileCover({transform.getState()}, 3, 5));
|
util::tileCover({transform.getState()}, 3, zoomRange, 5));
|
||||||
|
|
||||||
|
util::TileCoverParameters params{transform.getState()};
|
||||||
|
params.tileLodMode = TileLodMode::Distance;
|
||||||
|
EXPECT_EQ((std::vector<OverscaledTileID>{{5, 0, {3, 3, 3}},
|
||||||
|
{5, 0, {3, 4, 3}},
|
||||||
|
{5, 0, {3, 3, 2}},
|
||||||
|
{5, 0, {3, 4, 2}},
|
||||||
|
{5, 0, {3, 3, 1}},
|
||||||
|
{5, 0, {3, 4, 1}},
|
||||||
|
{5, 0, {3, 2, 1}}}),
|
||||||
|
util::tileCover(params, 3, zoomRange, 5));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(TileCover, DifferentOverscaledZWrapped) {
|
TEST(TileCover, DifferentOverscaledZWrapped) {
|
||||||
@@ -200,7 +275,13 @@ TEST(TileCover, DifferentOverscaledZWrapped) {
|
|||||||
|
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
(std::vector<OverscaledTileID>{{2, 0, {1, 1, 0}}, {2, 1, {1, 0, 0}}, {2, 0, {1, 1, 1}}, {2, 1, {1, 0, 1}}}),
|
(std::vector<OverscaledTileID>{{2, 0, {1, 1, 0}}, {2, 1, {1, 0, 0}}, {2, 0, {1, 1, 1}}, {2, 1, {1, 0, 1}}}),
|
||||||
util::tileCover({transform.getState()}, 1, 2));
|
util::tileCover({transform.getState()}, 1, zoomRange, 2));
|
||||||
|
|
||||||
|
util::TileCoverParameters params{transform.getState()};
|
||||||
|
params.tileLodMode = TileLodMode::Distance;
|
||||||
|
EXPECT_EQ(
|
||||||
|
(std::vector<OverscaledTileID>{{2, 0, {1, 1, 0}}, {2, 1, {1, 0, 0}}, {2, 0, {1, 1, 1}}, {2, 1, {1, 0, 1}}}),
|
||||||
|
util::tileCover(params, 1, zoomRange, 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(TileCover, FlippedY) {
|
TEST(TileCover, FlippedY) {
|
||||||
@@ -215,7 +296,12 @@ TEST(TileCover, FlippedY) {
|
|||||||
.withZoom(1.0));
|
.withZoom(1.0));
|
||||||
|
|
||||||
EXPECT_EQ((std::vector<OverscaledTileID>{{1, 0, 0}, {1, 1, 0}, {1, 0, 1}, {1, 1, 1}}),
|
EXPECT_EQ((std::vector<OverscaledTileID>{{1, 0, 0}, {1, 1, 0}, {1, 0, 1}, {1, 1, 1}}),
|
||||||
util::tileCover({transform.getState()}, 1));
|
util::tileCover({transform.getState()}, 1, zoomRange));
|
||||||
|
|
||||||
|
util::TileCoverParameters params{transform.getState()};
|
||||||
|
params.tileLodMode = TileLodMode::Distance;
|
||||||
|
EXPECT_EQ((std::vector<OverscaledTileID>{{1, 0, 0}, {1, 1, 0}, {1, 0, 1}, {1, 1, 1}}),
|
||||||
|
util::tileCover(params, 1, zoomRange));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(TileCover, FlippedYPitch) {
|
TEST(TileCover, FlippedYPitch) {
|
||||||
@@ -232,7 +318,12 @@ TEST(TileCover, FlippedYPitch) {
|
|||||||
.withPitch(40.0));
|
.withPitch(40.0));
|
||||||
|
|
||||||
EXPECT_EQ((std::vector<OverscaledTileID>{{2, 1, 1}, {2, 2, 1}, {2, 1, 2}, {2, 2, 2}}),
|
EXPECT_EQ((std::vector<OverscaledTileID>{{2, 1, 1}, {2, 2, 1}, {2, 1, 2}, {2, 2, 2}}),
|
||||||
util::tileCover({transform.getState()}, 2));
|
util::tileCover({transform.getState()}, 2, zoomRange));
|
||||||
|
|
||||||
|
util::TileCoverParameters params{transform.getState()};
|
||||||
|
params.tileLodMode = TileLodMode::Distance;
|
||||||
|
EXPECT_EQ((std::vector<OverscaledTileID>{{2, 1, 2}, {2, 2, 2}, {2, 1, 1}, {2, 2, 1}}),
|
||||||
|
util::tileCover(params, 2, zoomRange));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(TileCover, FlippedYHelsinki) {
|
TEST(TileCover, FlippedYHelsinki) {
|
||||||
@@ -247,7 +338,12 @@ TEST(TileCover, FlippedYHelsinki) {
|
|||||||
.withZoom(11.447425));
|
.withZoom(11.447425));
|
||||||
|
|
||||||
EXPECT_EQ((std::vector<OverscaledTileID>{{11, 1165, 592}, {11, 1166, 592}, {11, 1165, 593}, {11, 1166, 593}}),
|
EXPECT_EQ((std::vector<OverscaledTileID>{{11, 1165, 592}, {11, 1166, 592}, {11, 1165, 593}, {11, 1166, 593}}),
|
||||||
util::tileCover({transform.getState()}, 11));
|
util::tileCover({transform.getState()}, 11, zoomRange));
|
||||||
|
|
||||||
|
util::TileCoverParameters params{transform.getState()};
|
||||||
|
params.tileLodMode = TileLodMode::Distance;
|
||||||
|
EXPECT_EQ((std::vector<OverscaledTileID>{{11, 1165, 592}, {11, 1166, 592}, {11, 1165, 593}, {11, 1166, 593}}),
|
||||||
|
util::tileCover(params, 11, zoomRange));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(TileCoverStream, Arctic) {
|
TEST(TileCoverStream, Arctic) {
|
||||||
|
|||||||