Add Tile LOD controls (#2958)
Co-authored-by: Bart Louwers <bart@emeel.net> Co-authored-by: Adrian Cojocaru <adrian.cojocaru92@yahoo.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
@@ -26,7 +26,7 @@ static void TileCoverPitchedViewport(benchmark::State& state) {
|
||||
|
||||
std::size_t length = 0;
|
||||
while (state.KeepRunning()) {
|
||||
auto tiles = util::tileCover(transform.getState(), 8);
|
||||
auto tiles = util::tileCover({transform.getState()}, 8);
|
||||
length += tiles.size();
|
||||
}
|
||||
benchmark::DoNotOptimize(length);
|
||||
|
||||
@@ -161,6 +161,38 @@ public:
|
||||
void setFreeCameraOptions(const FreeCameraOptions& camera);
|
||||
FreeCameraOptions getFreeCameraOptions() const;
|
||||
|
||||
// Tile LOD controls
|
||||
//
|
||||
/// The number of map tile requests can be reduced by using a lower level
|
||||
/// of details (Lower zoom level) away from the camera.
|
||||
/// This can improve performance, particularly when the camera pitch is high.
|
||||
/// The LOD calculation uses a heuristic based on the distance to the camera
|
||||
/// view point. The heuristic behavior is controlled with 3 parameters:
|
||||
/// - `TileLodMinRadius` is a radius around the view point in unit of tiles
|
||||
/// in which the fine grained zoom level tiles are always used
|
||||
/// - `TileLodScale` is a scale factor for the distance to the camera view
|
||||
/// point. A value larger than 1 increases the distance to the camera view
|
||||
/// point in which case the LOD is reduced
|
||||
/// - `TileLodPitchThreshold` is the pitch angle in radians above which LOD
|
||||
/// calculation is performed.
|
||||
/// LOD calculation is always performed if `TileLodPitchThreshold` is zero.
|
||||
/// LOD calculation is never performed if `TileLodPitchThreshold` is pi.
|
||||
/// - `TileLodZoomShift` shifts the the Zoom level used for LOD calculation
|
||||
/// A value of zero (default) does not change the Zoom level
|
||||
/// A positive value increases the Zoom level and a negative value decreases
|
||||
/// the Zoom level
|
||||
/// A negative values typically improves performance but reduces quality.
|
||||
/// 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.
|
||||
void setTileLodMinRadius(double radius);
|
||||
double getTileLodMinRadius() const;
|
||||
void setTileLodScale(double scale);
|
||||
double getTileLodScale() const;
|
||||
void setTileLodPitchThreshold(double threshold);
|
||||
double getTileLodPitchThreshold() const;
|
||||
void setTileLodZoomShift(double shift);
|
||||
double getTileLodZoomShift() const;
|
||||
|
||||
ClientOptions getClientOptions() const;
|
||||
|
||||
const std::unique_ptr<util::ActionJournal>& getActionJournal();
|
||||
|
||||
@@ -1269,6 +1269,38 @@ jni::jboolean NativeMapView::getTileCacheEnabled(JNIEnv&) {
|
||||
return jni::jboolean(rendererFrontend->getTileCacheEnabled());
|
||||
}
|
||||
|
||||
void NativeMapView::setTileLodMinRadius(JNIEnv&, jni::jdouble radius) {
|
||||
map->setTileLodMinRadius(radius);
|
||||
}
|
||||
|
||||
jni::jdouble NativeMapView::getTileLodMinRadius(JNIEnv&) {
|
||||
return jni::jdouble(map->getTileLodMinRadius());
|
||||
}
|
||||
|
||||
void NativeMapView::setTileLodScale(JNIEnv&, jni::jdouble scale) {
|
||||
map->setTileLodScale(scale);
|
||||
}
|
||||
|
||||
jni::jdouble NativeMapView::getTileLodScale(JNIEnv&) {
|
||||
return jni::jdouble(map->getTileLodScale());
|
||||
}
|
||||
|
||||
void NativeMapView::setTileLodPitchThreshold(JNIEnv&, jni::jdouble threshold) {
|
||||
map->setTileLodPitchThreshold(threshold);
|
||||
}
|
||||
|
||||
jni::jdouble NativeMapView::getTileLodPitchThreshold(JNIEnv&) {
|
||||
return jni::jdouble(map->getTileLodPitchThreshold());
|
||||
}
|
||||
|
||||
void NativeMapView::setTileLodZoomShift(JNIEnv&, jni::jdouble shift) {
|
||||
map->setTileLodZoomShift(shift);
|
||||
}
|
||||
|
||||
jni::jdouble NativeMapView::getTileLodZoomShift(JNIEnv&) {
|
||||
return jni::jdouble(map->getTileLodZoomShift());
|
||||
}
|
||||
|
||||
mbgl::Map& NativeMapView::getMap() {
|
||||
return *map;
|
||||
}
|
||||
@@ -1400,6 +1432,14 @@ void NativeMapView::registerNative(jni::JNIEnv& env) {
|
||||
METHOD(&NativeMapView::getPrefetchZoomDelta, "nativeGetPrefetchZoomDelta"),
|
||||
METHOD(&NativeMapView::setTileCacheEnabled, "nativeSetTileCacheEnabled"),
|
||||
METHOD(&NativeMapView::getTileCacheEnabled, "nativeGetTileCacheEnabled"),
|
||||
METHOD(&NativeMapView::setTileLodMinRadius, "nativeSetTileLodMinRadius"),
|
||||
METHOD(&NativeMapView::getTileLodMinRadius, "nativeGetTileLodMinRadius"),
|
||||
METHOD(&NativeMapView::setTileLodScale, "nativeSetTileLodScale"),
|
||||
METHOD(&NativeMapView::getTileLodScale, "nativeGetTileLodScale"),
|
||||
METHOD(&NativeMapView::setTileLodPitchThreshold, "nativeSetTileLodPitchThreshold"),
|
||||
METHOD(&NativeMapView::getTileLodPitchThreshold, "nativeGetTileLodPitchThreshold"),
|
||||
METHOD(&NativeMapView::setTileLodZoomShift, "nativeSetTileLodZoomShift"),
|
||||
METHOD(&NativeMapView::getTileLodZoomShift, "nativeGetTileLodZoomShift"),
|
||||
METHOD(&NativeMapView::triggerRepaint, "nativeTriggerRepaint"),
|
||||
METHOD(&NativeMapView::isRenderingStatsViewEnabled, "nativeIsRenderingStatsViewEnabled"),
|
||||
METHOD(&NativeMapView::enableRenderingStatsView, "nativeEnableRenderingStatsView"));
|
||||
|
||||
@@ -302,6 +302,22 @@ public:
|
||||
|
||||
jni::jboolean getTileCacheEnabled(JNIEnv&);
|
||||
|
||||
void setTileLodMinRadius(JNIEnv&, jni::jdouble);
|
||||
|
||||
jni::jdouble getTileLodMinRadius(JNIEnv&);
|
||||
|
||||
void setTileLodScale(JNIEnv&, jni::jdouble);
|
||||
|
||||
jni::jdouble getTileLodScale(JNIEnv&);
|
||||
|
||||
void setTileLodPitchThreshold(JNIEnv&, jni::jdouble);
|
||||
|
||||
jni::jdouble getTileLodPitchThreshold(JNIEnv&);
|
||||
|
||||
void setTileLodZoomShift(JNIEnv&, jni::jdouble);
|
||||
|
||||
jni::jdouble getTileLodZoomShift(JNIEnv&);
|
||||
|
||||
mbgl::Map& getMap();
|
||||
|
||||
void triggerRepaint(JNIEnv&);
|
||||
|
||||
@@ -358,6 +358,101 @@ public final class MapLibreMap {
|
||||
return nativeMapView.getTileCacheEnabled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Camera based tile level of detail controls
|
||||
*
|
||||
* @param radius minimum radius around the view point in unit of tiles in which the fine
|
||||
* grained zoom level tiles are always used when performing LOD
|
||||
* radius must be greater than 1 (At least 1 fine detailed tile is present)
|
||||
* A smaller radius value may improve performance at the cost of quality (tiles away from
|
||||
* camera use lower Zoom levels)
|
||||
*/
|
||||
public void setTileLodMinRadius(@FloatRange(from = 1, fromInclusive = true) double radius) {
|
||||
nativeMapView.setTileLodMinRadius(radius);
|
||||
}
|
||||
|
||||
/**
|
||||
* Camera based tile level of detail controls
|
||||
*
|
||||
* @return minimum radius around the view point in unit of tiles in which the fine grained
|
||||
* zoom level tiles are always used when performing LOD
|
||||
* @see MapLibreMap#setTileLodMinRadius(double)
|
||||
*/
|
||||
public double getTileLodMinRadius() {
|
||||
return nativeMapView.getTileLodMinRadius();
|
||||
}
|
||||
|
||||
/**
|
||||
* Camera based tile level of detail controls
|
||||
*
|
||||
* @param scale factor for the distance to the camera view point
|
||||
* A value larger than 1 increases the distance to the camera view point reducing LOD
|
||||
* Larger values may improve performance at the cost of quality (tiles away from camera
|
||||
* use lower Zoom levels)
|
||||
*/
|
||||
public void setTileLodScale(@FloatRange(from = 0, fromInclusive = false) double scale) {
|
||||
nativeMapView.setTileLodScale(scale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Camera based tile level of detail controls
|
||||
*
|
||||
* @return scale factor for the distance to the camera view point
|
||||
* @see MapLibreMap#setTileLodScale(double)
|
||||
*/
|
||||
public double getTileLodScale() {
|
||||
return nativeMapView.getTileLodScale();
|
||||
}
|
||||
|
||||
/**
|
||||
* Camera based tile level of detail controls
|
||||
*
|
||||
* @param threshold pitch angle in radians above which LOD calculation is performed
|
||||
* A smaller radius value may improve performance at the cost of quality
|
||||
*/
|
||||
public void setTileLodPitchThreshold(@FloatRange(from = 0, to = Math.PI) double threshold) {
|
||||
nativeMapView.setTileLodPitchThreshold(threshold);
|
||||
}
|
||||
|
||||
/**
|
||||
* Camera based tile level of detail controls
|
||||
*
|
||||
* @return pitch angle threshold in radians above which LOD calculation is performed
|
||||
* @see MapLibreMap#setTileLodPitchThreshold(double)
|
||||
*/
|
||||
public double getTileLodPitchThreshold() {
|
||||
return nativeMapView.getTileLodPitchThreshold();
|
||||
}
|
||||
|
||||
/**
|
||||
* Camera based tile level of detail controls
|
||||
*
|
||||
* @param shift shift applied to the Zoom level during LOD calculation
|
||||
* A negative value shifts the Zoom level to a coarser level reducing quality but
|
||||
* improving performance
|
||||
* A positive value shifts the Zoom level to a finer level increasing details but
|
||||
* negatively affecting performance
|
||||
* A value of zero (default) does not apply any shift to the Zoom level
|
||||
* It is not recommended to change the default value unless performance is critical
|
||||
* and the loss of quality is acceptable. A value of -1 reduces the number of
|
||||
* displayed tiles by a factor of 4 on average
|
||||
* It is recommended to first configure the pixelRatio before adjusting
|
||||
* TileLodZoomShift. {@link MapLibreMapOptions#pixelRatio(float)}
|
||||
*/
|
||||
public void setTileLodZoomShift(double shift) {
|
||||
nativeMapView.setTileLodZoomShift(shift);
|
||||
}
|
||||
|
||||
/**
|
||||
* Camera based tile level of detail controls
|
||||
*
|
||||
* @return shift applied to the Zoom level during LOD calculation
|
||||
* @see MapLibreMap#setTileLodZoomShift(double)
|
||||
*/
|
||||
public double getTileLodZoomShift() {
|
||||
return nativeMapView.getTileLodZoomShift();
|
||||
}
|
||||
|
||||
//
|
||||
// MinZoom
|
||||
//
|
||||
|
||||
@@ -244,6 +244,22 @@ interface NativeMap {
|
||||
|
||||
boolean getTileCacheEnabled();
|
||||
|
||||
void setTileLodMinRadius(double radius);
|
||||
|
||||
double getTileLodMinRadius();
|
||||
|
||||
void setTileLodScale(double scale);
|
||||
|
||||
double getTileLodScale();
|
||||
|
||||
void setTileLodPitchThreshold(double threshold);
|
||||
|
||||
double getTileLodPitchThreshold();
|
||||
|
||||
void setTileLodZoomShift(double shift);
|
||||
|
||||
double getTileLodZoomShift();
|
||||
|
||||
void setGestureInProgress(boolean inProgress);
|
||||
|
||||
float getPixelRatio();
|
||||
|
||||
@@ -855,6 +855,70 @@ final class NativeMapView implements NativeMap {
|
||||
}
|
||||
return nativeGetTileCacheEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTileLodMinRadius(double radius) {
|
||||
if (checkState("setTileLodMinRadius")) {
|
||||
return;
|
||||
}
|
||||
nativeSetTileLodMinRadius(radius);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getTileLodMinRadius() {
|
||||
if (checkState("getTileLodMinRadius")) {
|
||||
return 0;
|
||||
}
|
||||
return nativeGetTileLodMinRadius();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTileLodScale(double scale) {
|
||||
if (checkState("setTileLodScale")) {
|
||||
return;
|
||||
}
|
||||
nativeSetTileLodScale(scale);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getTileLodScale() {
|
||||
if (checkState("getTileLodScale")) {
|
||||
return 0;
|
||||
}
|
||||
return nativeGetTileLodScale();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTileLodPitchThreshold(double threshold) {
|
||||
if (checkState("setTileLodPitchThreshold")) {
|
||||
return;
|
||||
}
|
||||
nativeSetTileLodPitchThreshold(threshold);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getTileLodPitchThreshold() {
|
||||
if (checkState("getTileLodPitchThreshold")) {
|
||||
return 0;
|
||||
}
|
||||
return nativeGetTileLodPitchThreshold();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTileLodZoomShift(double shift) {
|
||||
if (checkState("setTileLodZoomShift")) {
|
||||
return;
|
||||
}
|
||||
nativeSetTileLodZoomShift(shift);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getTileLodZoomShift() {
|
||||
if (checkState("getTileLodZoomShift")) {
|
||||
return 0;
|
||||
}
|
||||
return nativeGetTileLodZoomShift();
|
||||
}
|
||||
// Runtime style Api
|
||||
|
||||
@Override
|
||||
@@ -1644,6 +1708,30 @@ final class NativeMapView implements NativeMap {
|
||||
@Keep
|
||||
private native int nativeGetPrefetchZoomDelta();
|
||||
|
||||
@Keep
|
||||
private native void nativeSetTileLodMinRadius(double radius);
|
||||
|
||||
@Keep
|
||||
private native double nativeGetTileLodMinRadius();
|
||||
|
||||
@Keep
|
||||
private native void nativeSetTileLodScale(double scale);
|
||||
|
||||
@Keep
|
||||
private native double nativeGetTileLodScale();
|
||||
|
||||
@Keep
|
||||
private native void nativeSetTileLodPitchThreshold(double threshold);
|
||||
|
||||
@Keep
|
||||
private native double nativeGetTileLodPitchThreshold();
|
||||
|
||||
@Keep
|
||||
private native void nativeSetTileLodZoomShift(double shift);
|
||||
|
||||
@Keep
|
||||
private native double nativeGetTileLodZoomShift();
|
||||
|
||||
@Override
|
||||
public long getNativePtr() {
|
||||
return nativePtr;
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <mbgl/gfx/backend_scope.hpp>
|
||||
#include <mbgl/map/camera.hpp>
|
||||
#include <mbgl/math/angles.hpp>
|
||||
#include <mbgl/math/clamp.hpp>
|
||||
#include <mbgl/renderer/renderer.hpp>
|
||||
#include <mbgl/style/expression/dsl.hpp>
|
||||
#include <mbgl/style/image.hpp>
|
||||
@@ -68,7 +69,6 @@
|
||||
using namespace std::numbers;
|
||||
|
||||
#ifdef ENABLE_LOCATION_INDICATOR
|
||||
|
||||
namespace {
|
||||
const std::string mbglPuckAssetsPath{MLN_ASSETS_PATH};
|
||||
|
||||
@@ -83,7 +83,7 @@ std::array<double, 3> toArray(const mbgl::LatLng &crd) {
|
||||
return {crd.latitude(), crd.longitude(), 0};
|
||||
}
|
||||
} // namespace
|
||||
#endif
|
||||
#endif // ENABLE_LOCATION_INDICATOR
|
||||
|
||||
class SnapshotObserver final : public mbgl::MapSnapshotterObserver {
|
||||
public:
|
||||
@@ -99,6 +99,76 @@ public:
|
||||
};
|
||||
|
||||
namespace {
|
||||
|
||||
enum class TileLodMode {
|
||||
Default, // Default Tile LOD parameters
|
||||
NoLod, // Disable LOD
|
||||
Reduced, // Reduce LOD away from camera
|
||||
Aggressive, // Aggressively reduce LOD away from camera at the detriment of quality
|
||||
};
|
||||
|
||||
constexpr TileLodMode nextTileLodMode(TileLodMode current) {
|
||||
switch (current) {
|
||||
case TileLodMode::Default:
|
||||
return TileLodMode::NoLod;
|
||||
case TileLodMode::NoLod:
|
||||
return TileLodMode::Reduced;
|
||||
case TileLodMode::Reduced:
|
||||
return TileLodMode::Aggressive;
|
||||
case TileLodMode::Aggressive:
|
||||
return TileLodMode::Default;
|
||||
default:
|
||||
return TileLodMode::Default;
|
||||
}
|
||||
}
|
||||
|
||||
void cycleTileLodMode(mbgl::Map &map) {
|
||||
// TileLodMode::Default parameters
|
||||
static const auto defaultRadius = map.getTileLodMinRadius();
|
||||
static const auto defaultScale = map.getTileLodScale();
|
||||
static const auto defaultTilePitchThreshold = map.getTileLodPitchThreshold();
|
||||
|
||||
static TileLodMode mode = TileLodMode::Default;
|
||||
mode = nextTileLodMode(mode);
|
||||
|
||||
switch (mode) {
|
||||
case TileLodMode::Default:
|
||||
map.setTileLodMinRadius(defaultRadius);
|
||||
map.setTileLodScale(defaultScale);
|
||||
map.setTileLodPitchThreshold(defaultTilePitchThreshold);
|
||||
mbgl::Log::Info(mbgl::Event::General, "Tile LOD mode: default");
|
||||
break;
|
||||
case TileLodMode::NoLod:
|
||||
// When LOD is off we set a maximum PitchThreshold
|
||||
map.setTileLodPitchThreshold(std::numbers::pi);
|
||||
mbgl::Log::Info(mbgl::Event::General, "Tile LOD mode: disabled");
|
||||
break;
|
||||
case TileLodMode::Reduced:
|
||||
map.setTileLodMinRadius(2);
|
||||
map.setTileLodScale(1.5);
|
||||
map.setTileLodPitchThreshold(std::numbers::pi / 4);
|
||||
mbgl::Log::Info(mbgl::Event::General, "Tile LOD mode: reduced");
|
||||
break;
|
||||
case TileLodMode::Aggressive:
|
||||
map.setTileLodMinRadius(1);
|
||||
map.setTileLodScale(2);
|
||||
map.setTileLodPitchThreshold(0);
|
||||
mbgl::Log::Info(mbgl::Event::General, "Tile LOD mode: aggressive");
|
||||
break;
|
||||
}
|
||||
map.triggerRepaint();
|
||||
}
|
||||
|
||||
void tileLodZoomShift(mbgl::Map &map, bool positive) {
|
||||
constexpr auto tileLodZoomShiftStep = 0.25;
|
||||
auto shift = positive ? tileLodZoomShiftStep : -tileLodZoomShiftStep;
|
||||
shift = map.getTileLodZoomShift() + shift;
|
||||
shift = mbgl::util::clamp(shift, -2.5, 2.5);
|
||||
mbgl::Log::Info(mbgl::Event::OpenGL, "Zoom shift: " + std::to_string(shift));
|
||||
map.setTileLodZoomShift(shift);
|
||||
map.triggerRepaint();
|
||||
}
|
||||
|
||||
void addFillExtrusionLayer(mbgl::style::Style &style, bool visible) {
|
||||
MLN_TRACE_FUNC();
|
||||
|
||||
@@ -288,6 +358,9 @@ GLFWView::GLFWView(bool fullscreen_,
|
||||
printf("- Press `F1` to generate a render test for the current view\n");
|
||||
printf("\n");
|
||||
printf("- Press `Tab` to cycle through the map debug options\n");
|
||||
printf("- Press `F6` to cycle through Tile LOD modes\n");
|
||||
printf("- Press `F7` to lower the zoom level without changing the camera\n");
|
||||
printf("- Press `F8` to higher the zoom level without changing the camera\n");
|
||||
printf("- Press `Esc` to quit\n");
|
||||
printf("\n");
|
||||
printf(
|
||||
@@ -567,6 +640,15 @@ void GLFWView::onKey(GLFWwindow *window, int key, int /*scancode*/, int action,
|
||||
view->freeCameraDemoStartTime = mbgl::Clock::now();
|
||||
view->invalidate();
|
||||
} break;
|
||||
case GLFW_KEY_F6: {
|
||||
cycleTileLodMode(*view->map);
|
||||
} break;
|
||||
case GLFW_KEY_F7: {
|
||||
tileLodZoomShift(*view->map, false);
|
||||
} break;
|
||||
case GLFW_KEY_F8: {
|
||||
tileLodZoomShift(*view->map, true);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -121,10 +121,19 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
|
||||
MBXSettingsMiscellaneousShowCustomLocationManager,
|
||||
MBXSettingsMiscellaneousOrnamentsPlacement,
|
||||
MBXSettingsMiscellaneousLatLngBoundsWithPadding,
|
||||
MBXSettingsMiscellaneousCycleTileLOD,
|
||||
MBXSettingsMiscellaneousPrintLogFile,
|
||||
MBXSettingsMiscellaneousDeleteLogFile
|
||||
};
|
||||
|
||||
typedef NS_ENUM(NSInteger, MBXTileLodMode) {
|
||||
MBXTileLodModeNoLod = 0,
|
||||
MBXTileLodModeDefault,
|
||||
MBXTileLodModeReduced,
|
||||
MBXTileLodModeAggressive,
|
||||
MBXTileLodModeCount
|
||||
};
|
||||
|
||||
// Utility methods
|
||||
CLLocationCoordinate2D coordinateCentered(CLLocationCoordinate2D origin, CLLocationDegrees bearing, CLLocationDistance distance) {
|
||||
|
||||
@@ -245,6 +254,8 @@ CLLocationCoordinate2D randomWorldCoordinate(void) {
|
||||
|
||||
NSDictionary* _pointFeatures;
|
||||
NSLock* _loadLock;
|
||||
|
||||
MBXTileLodMode _tileLodMode;
|
||||
}
|
||||
|
||||
// MARK: - Setup & Teardown
|
||||
@@ -474,7 +485,8 @@ CLLocationCoordinate2D randomWorldCoordinate(void) {
|
||||
[NSString stringWithFormat:@"Turn %@ Content Insets", (_contentInsetsEnabled ? @"Off" : @"On")],
|
||||
@"View Route Simulation",
|
||||
@"Ornaments Placement",
|
||||
@"Lat Long bounds with padding"
|
||||
@"Lat Long bounds with padding",
|
||||
[NSString stringWithFormat:@"Cycle tile LOD mode: %@", [self getTileLodModeName]]
|
||||
]];
|
||||
|
||||
break;
|
||||
@@ -807,6 +819,9 @@ CLLocationCoordinate2D randomWorldCoordinate(void) {
|
||||
case MBXSettingsMiscellaneousLatLngBoundsWithPadding:
|
||||
[self flyToWithLatLngBoundsAndPadding];
|
||||
break;
|
||||
case MBXSettingsMiscellaneousCycleTileLOD:
|
||||
[self cycleTileLodMode];
|
||||
break;
|
||||
default:
|
||||
NSAssert(NO, @"All miscellaneous setting rows should be implemented");
|
||||
break;
|
||||
@@ -1992,6 +2007,114 @@ CLLocationCoordinate2D randomWorldCoordinate(void) {
|
||||
}];
|
||||
}
|
||||
|
||||
-(void)cycleTileLodMode
|
||||
{
|
||||
// TileLodMode::Default parameters
|
||||
static const double defaultRadius = self.mapView.tileLodMinRadius;
|
||||
static const double defaultScale = self.mapView.tileLodScale;
|
||||
static const double defaultPitchThreshold = self.mapView.tileLodPitchThreshold;
|
||||
|
||||
_tileLodMode = static_cast<MBXTileLodMode>((static_cast<int>(_tileLodMode) + 1) % static_cast<int>(MBXTileLodModeCount));
|
||||
|
||||
switch (_tileLodMode) {
|
||||
case MBXTileLodModeDefault:
|
||||
self.mapView.tileLodMinRadius = defaultRadius;
|
||||
self.mapView.tileLodScale = defaultScale;
|
||||
self.mapView.tileLodPitchThreshold = defaultPitchThreshold;
|
||||
break;
|
||||
case MBXTileLodModeNoLod:
|
||||
// When LOD is off we set a maximum PitchThreshold
|
||||
self.mapView.tileLodPitchThreshold = M_PI;
|
||||
break;
|
||||
case MBXTileLodModeReduced:
|
||||
self.mapView.tileLodMinRadius = 2;
|
||||
self.mapView.tileLodScale = 1.5;
|
||||
self.mapView.tileLodPitchThreshold = M_PI / 4;
|
||||
break;
|
||||
case MBXTileLodModeAggressive:
|
||||
self.mapView.tileLodMinRadius = 1;
|
||||
self.mapView.tileLodScale = 2;
|
||||
self.mapView.tileLodPitchThreshold = 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// update UI
|
||||
static UISlider* zoomSlider = nil;
|
||||
if (zoomSlider && _tileLodMode == MBXTileLodModeNoLod) {
|
||||
[zoomSlider removeFromSuperview];
|
||||
zoomSlider = nil;
|
||||
}
|
||||
|
||||
if (!zoomSlider && _tileLodMode != MBXTileLodModeNoLod) {
|
||||
const float zoomShiftRange = 5.0f;
|
||||
zoomSlider = [[UISlider alloc] init];
|
||||
zoomSlider.minimumValue = -zoomShiftRange / 2.0f;
|
||||
zoomSlider.maximumValue = zoomShiftRange / 2.0f;
|
||||
zoomSlider.value = self.mapView.tileLodZoomShift;
|
||||
zoomSlider.continuous = NO;
|
||||
|
||||
[zoomSlider addTarget:self action:@selector(updateTileLodZoom:) forControlEvents:UIControlEventValueChanged];
|
||||
[self.view addSubview:zoomSlider];
|
||||
|
||||
zoomSlider.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
zoomSlider.frame = CGRectMake(0, 0, 100, 100);
|
||||
|
||||
NSArray<NSLayoutConstraint*>* layout = @[
|
||||
[NSLayoutConstraint constraintWithItem:zoomSlider
|
||||
attribute:NSLayoutAttributeCenterX
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:self.view
|
||||
attribute:NSLayoutAttributeCenterX
|
||||
multiplier:1
|
||||
constant:0],
|
||||
[NSLayoutConstraint constraintWithItem:zoomSlider
|
||||
attribute:NSLayoutAttributeWidth
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:self.view
|
||||
attribute:NSLayoutAttributeWidth
|
||||
multiplier:0.5
|
||||
constant:0],
|
||||
[NSLayoutConstraint constraintWithItem:zoomSlider
|
||||
attribute:NSLayoutAttributeTop
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:self.view
|
||||
attribute:NSLayoutAttributeCenterY
|
||||
multiplier:1
|
||||
constant:0],
|
||||
[zoomSlider.bottomAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.bottomAnchor]];
|
||||
|
||||
[self.view addConstraints:layout];
|
||||
}
|
||||
|
||||
[self.mapView triggerRepaint];
|
||||
}
|
||||
|
||||
-(void)updateTileLodZoom:(id)sender {
|
||||
UISlider* slider = (UISlider*)sender;
|
||||
|
||||
self.mapView.tileLodZoomShift = slider.value;
|
||||
[self.mapView triggerRepaint];
|
||||
|
||||
NSLog(@"Tile LOD zoom shift: %f", self.mapView.tileLodZoomShift);
|
||||
}
|
||||
|
||||
-(NSString*)getTileLodModeName {
|
||||
switch (_tileLodMode) {
|
||||
case MBXTileLodModeDefault:
|
||||
return @"Default";
|
||||
case MBXTileLodModeNoLod:
|
||||
return @"Disabled";
|
||||
case MBXTileLodModeReduced:
|
||||
return @"Reduced";
|
||||
case MBXTileLodModeAggressive:
|
||||
return @"Aggressive";
|
||||
default:
|
||||
return @"";
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Random World Tour
|
||||
|
||||
- (void)addAnnotations:(NSInteger)numAnnotations aroundCoordinate:(CLLocationCoordinate2D)coordinate radius:(CLLocationDistance)radius {
|
||||
|
||||
@@ -463,6 +463,50 @@ MLN_EXPORT
|
||||
|
||||
@property (nonatomic, assign) BOOL tileCacheEnabled;
|
||||
|
||||
// MARK: Tile LOD controls
|
||||
|
||||
/**
|
||||
Camera based tile level of detail controls
|
||||
|
||||
Minimum radius around the view point in unit of tiles in which the fine
|
||||
grained zoom level tiles are always used when performing LOD
|
||||
radius must be greater than 1 (At least 1 fine detailed tile is present)
|
||||
A smaller radius value may improve performance at the cost of quality (tiles away from
|
||||
camera use lower Zoom levels)
|
||||
*/
|
||||
@property (nonatomic, assign) double tileLodMinRadius;
|
||||
|
||||
/**
|
||||
Camera based tile level of detail controls
|
||||
|
||||
Factor for the distance to the camera view point
|
||||
A value larger than 1 increases the distance to the camera view point reducing LOD
|
||||
Larger values may improve performance at the cost of quality (tiles away from camera
|
||||
use lower Zoom levels)
|
||||
*/
|
||||
@property (nonatomic, assign) double tileLodScale;
|
||||
|
||||
/**
|
||||
Camera based tile level of detail controls
|
||||
|
||||
Pitch angle in radians above which LOD calculation is performed
|
||||
A smaller radius value may improve performance at the cost of quality
|
||||
*/
|
||||
@property (nonatomic, assign) double tileLodPitchThreshold;
|
||||
|
||||
/**
|
||||
Camera based tile level of detail controls
|
||||
|
||||
Shift applied to the Zoom level during LOD calculation
|
||||
A negative value shifts the Zoom level to a coarser level reducing quality but improving
|
||||
performance A positive value shifts the Zoom level to a finer level increasing details but
|
||||
negatively affecting performance A value of zero (default) does not apply any shift to the Zoom
|
||||
level It is not recommended to change the default value unless performance is critical and the loss
|
||||
of quality is acceptable. A value of -1 reduces the number of displayed tiles by a factor of 4 on
|
||||
average It is recommended to first configure the pixelRatio before adjusting TileLodZoomShift.
|
||||
*/
|
||||
@property (nonatomic, assign) double tileLodZoomShift;
|
||||
|
||||
// MARK: Displaying the User’s Location
|
||||
|
||||
/**
|
||||
|
||||
@@ -3231,6 +3231,46 @@ static void *windowScreenContext = &windowScreenContext;
|
||||
return _rendererFrontend->getTileCacheEnabled();
|
||||
}
|
||||
|
||||
- (void)setTileLodMinRadius:(double)tileLodMinRadius
|
||||
{
|
||||
_mbglMap->setTileLodMinRadius(tileLodMinRadius);
|
||||
}
|
||||
|
||||
- (double)tileLodMinRadius
|
||||
{
|
||||
return _mbglMap->getTileLodMinRadius();
|
||||
}
|
||||
|
||||
- (void)setTileLodScale:(double)tileLodScale
|
||||
{
|
||||
_mbglMap->setTileLodScale(tileLodScale);
|
||||
}
|
||||
|
||||
- (double)tileLodScale
|
||||
{
|
||||
return _mbglMap->getTileLodScale();
|
||||
}
|
||||
|
||||
-(void)setTileLodPitchThreshold:(double)tileLodPitchThreshold
|
||||
{
|
||||
_mbglMap->setTileLodPitchThreshold(tileLodPitchThreshold);
|
||||
}
|
||||
|
||||
-(double)tileLodPitchThreshold
|
||||
{
|
||||
return _mbglMap->getTileLodPitchThreshold();
|
||||
}
|
||||
|
||||
-(void)setTileLodZoomShift:(double)tileLodZoomShift
|
||||
{
|
||||
_mbglMap->setTileLodZoomShift(tileLodZoomShift);
|
||||
}
|
||||
|
||||
-(double)tileLodZoomShift
|
||||
{
|
||||
return _mbglMap->getTileLodZoomShift();
|
||||
}
|
||||
|
||||
// MARK: - Accessibility -
|
||||
|
||||
- (NSString *)accessibilityValue
|
||||
|
||||
@@ -202,6 +202,50 @@ MLN_EXPORT IB_DESIGNABLE @interface MLNMapView : NSView<MLNStylable>
|
||||
*/
|
||||
@property (nonatomic, assign) BOOL prefetchesTiles;
|
||||
|
||||
// MARK: Tile LOD controls
|
||||
|
||||
/**
|
||||
Camera based tile level of detail controls
|
||||
|
||||
Minimum radius around the view point in unit of tiles in which the fine
|
||||
grained zoom level tiles are always used when performing LOD
|
||||
radius must be greater than 1 (At least 1 fine detailed tile is present)
|
||||
A smaller radius value may improve performance at the cost of quality (tiles away from
|
||||
camera use lower Zoom levels)
|
||||
*/
|
||||
@property (nonatomic, assign) double tileLodMinRadius;
|
||||
|
||||
/**
|
||||
Camera based tile level of detail controls
|
||||
|
||||
Factor for the distance to the camera view point
|
||||
A value larger than 1 increases the distance to the camera view point reducing LOD
|
||||
Larger values may improve performance at the cost of quality (tiles away from camera
|
||||
use lower Zoom levels)
|
||||
*/
|
||||
@property (nonatomic, assign) double tileLodScale;
|
||||
|
||||
/**
|
||||
Camera based tile level of detail controls
|
||||
|
||||
Pitch angle in radians above which LOD calculation is performed
|
||||
A smaller radius value may improve performance at the cost of quality
|
||||
*/
|
||||
@property (nonatomic, assign) double tileLodPitchThreshold;
|
||||
|
||||
/**
|
||||
Camera based tile level of detail controls
|
||||
|
||||
Shift applied to the Zoom level during LOD calculation
|
||||
A negative value shifts the Zoom level to a coarser level reducing quality but improving
|
||||
performance A positive value shifts the Zoom level to a finer level increasing details but
|
||||
negatively affecting performance A value of zero (default) does not apply any shift to the Zoom
|
||||
level It is not recommended to change the default value unless performance is critical and the loss
|
||||
of quality is acceptable. A value of -1 reduces the number of displayed tiles by a factor of 4 on
|
||||
average It is recommended to first configure the pixelRatio before adjusting TileLodZoomShift.
|
||||
*/
|
||||
@property (nonatomic, assign) double tileLodZoomShift;
|
||||
|
||||
// MARK: Manipulating the Viewpoint
|
||||
|
||||
/**
|
||||
|
||||
@@ -710,6 +710,46 @@ public:
|
||||
return _mbglMap->getPrefetchZoomDelta() > 0 ? YES : NO;
|
||||
}
|
||||
|
||||
- (void)setTileLodMinRadius:(double)tileLodMinRadius
|
||||
{
|
||||
_mbglMap->setTileLodMinRadius(tileLodMinRadius);
|
||||
}
|
||||
|
||||
- (double)tileLodMinRadius
|
||||
{
|
||||
return _mbglMap->getTileLodMinRadius();
|
||||
}
|
||||
|
||||
- (void)setTileLodScale:(double)tileLodScale
|
||||
{
|
||||
_mbglMap->setTileLodScale(tileLodScale);
|
||||
}
|
||||
|
||||
- (double)tileLodScale
|
||||
{
|
||||
return _mbglMap->getTileLodScale();
|
||||
}
|
||||
|
||||
-(void)setTileLodPitchThreshold:(double)tileLodPitchThreshold
|
||||
{
|
||||
_mbglMap->setTileLodPitchThreshold(tileLodPitchThreshold);
|
||||
}
|
||||
|
||||
-(double)tileLodPitchThreshold
|
||||
{
|
||||
return _mbglMap->getTileLodPitchThreshold();
|
||||
}
|
||||
|
||||
-(void)setTileLodZoomShift:(double)tileLodZoomShift
|
||||
{
|
||||
_mbglMap->setTileLodZoomShift(tileLodZoomShift);
|
||||
}
|
||||
|
||||
-(double)tileLodZoomShift
|
||||
{
|
||||
return _mbglMap->getTileLodZoomShift();
|
||||
}
|
||||
|
||||
- (mbgl::Renderer *)renderer {
|
||||
return _rendererFrontend->getRenderer();
|
||||
}
|
||||
|
||||
@@ -547,6 +547,38 @@ FreeCameraOptions Map::getFreeCameraOptions() const {
|
||||
return impl->transform.getFreeCameraOptions();
|
||||
}
|
||||
|
||||
void Map::setTileLodMinRadius(double radius) {
|
||||
impl->tileLodMinRadius = radius;
|
||||
}
|
||||
|
||||
double Map::getTileLodMinRadius() const {
|
||||
return impl->tileLodMinRadius;
|
||||
}
|
||||
|
||||
void Map::setTileLodScale(double scale) {
|
||||
impl->tileLodScale = scale;
|
||||
}
|
||||
|
||||
double Map::getTileLodScale() const {
|
||||
return impl->tileLodScale;
|
||||
}
|
||||
|
||||
void Map::setTileLodPitchThreshold(double threshold) {
|
||||
impl->tileLodPitchThreshold = threshold;
|
||||
}
|
||||
|
||||
double Map::getTileLodPitchThreshold() const {
|
||||
return impl->tileLodPitchThreshold;
|
||||
}
|
||||
|
||||
void Map::setTileLodZoomShift(double shift) {
|
||||
impl->tileLodZoomShift = shift;
|
||||
}
|
||||
|
||||
double Map::getTileLodZoomShift() const {
|
||||
return impl->tileLodZoomShift;
|
||||
}
|
||||
|
||||
ClientOptions Map::getClientOptions() const {
|
||||
return impl->fileSource ? impl->fileSource->getClientOptions() : ClientOptions();
|
||||
}
|
||||
|
||||
@@ -130,7 +130,11 @@ void Map::Impl::onUpdate() {
|
||||
fileSource,
|
||||
prefetchZoomDelta,
|
||||
bool(stillImageRequest),
|
||||
crossSourceCollisions};
|
||||
crossSourceCollisions,
|
||||
tileLodMinRadius,
|
||||
tileLodScale,
|
||||
tileLodPitchThreshold,
|
||||
tileLodZoomShift};
|
||||
|
||||
rendererFrontend.update(std::make_shared<UpdateParameters>(std::move(params)));
|
||||
}
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
#include <mbgl/util/size.hpp>
|
||||
#include <mbgl/tile/tile_operation.hpp>
|
||||
|
||||
#include <numbers>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
class FileSource;
|
||||
@@ -104,6 +106,11 @@ public:
|
||||
bool loading = false;
|
||||
bool rendererFullyLoaded;
|
||||
std::unique_ptr<StillImageRequest> stillImageRequest;
|
||||
|
||||
double tileLodMinRadius = 3;
|
||||
double tileLodScale = 1;
|
||||
double tileLodPitchThreshold = (60.0 / 180.0) * std::numbers::pi;
|
||||
double tileLodZoomShift = 0;
|
||||
};
|
||||
|
||||
// Forward declaration of this method is required for the MapProjection class
|
||||
|
||||
@@ -113,11 +113,16 @@ static constexpr std::string_view BackgroundPatternShaderName = "BackgroundPatte
|
||||
void RenderBackgroundLayer::update(gfx::ShaderRegistry& shaders,
|
||||
gfx::Context& context,
|
||||
const TransformState& state,
|
||||
const std::shared_ptr<UpdateParameters>&,
|
||||
const std::shared_ptr<UpdateParameters>& updateParameters,
|
||||
[[maybe_unused]] const RenderTree& renderTree,
|
||||
[[maybe_unused]] UniqueChangeRequestVec& changes) {
|
||||
assert(updateParameters);
|
||||
const auto zoom = state.getIntegerZoom();
|
||||
const auto tileCover = util::tileCover(state, zoom);
|
||||
const auto tileCover = util::tileCover({state,
|
||||
updateParameters->tileLodMinRadius,
|
||||
updateParameters->tileLodScale,
|
||||
updateParameters->tileLodPitchThreshold},
|
||||
zoom);
|
||||
|
||||
// renderTiles is always empty, we use tileCover instead
|
||||
if (tileCover.empty()) {
|
||||
|
||||
@@ -55,7 +55,10 @@ PaintParameters::PaintParameters(gfx::Context& context_,
|
||||
RenderStaticData& staticData_,
|
||||
LineAtlas& lineAtlas_,
|
||||
PatternAtlas& patternAtlas_,
|
||||
uint64_t frameCount_)
|
||||
uint64_t frameCount_,
|
||||
double tileLodMinRadius_,
|
||||
double tileLodScale_,
|
||||
double tileLodPitchThreshold_)
|
||||
: context(context_),
|
||||
backend(backend_),
|
||||
encoder(context.createCommandEncoder()),
|
||||
@@ -70,7 +73,10 @@ PaintParameters::PaintParameters(gfx::Context& context_,
|
||||
timePoint(timePoint_),
|
||||
pixelRatio(pixelRatio_),
|
||||
shaders(*staticData_.shaders),
|
||||
frameCount(frameCount_) {
|
||||
frameCount(frameCount_),
|
||||
tileLodMinRadius(tileLodMinRadius_),
|
||||
tileLodScale(tileLodScale_),
|
||||
tileLodPitchThreshold(tileLodPitchThreshold_) {
|
||||
pixelsToGLUnits = {{2.0f / state.getSize().width, -2.0f / state.getSize().height}};
|
||||
|
||||
if (state.getViewportMode() == ViewportMode::FlippedY) {
|
||||
|
||||
@@ -57,7 +57,10 @@ public:
|
||||
RenderStaticData&,
|
||||
LineAtlas&,
|
||||
PatternAtlas&,
|
||||
uint64_t frameCount);
|
||||
uint64_t frameCount,
|
||||
double tileLodMinRadius,
|
||||
double tileLodScale,
|
||||
double tileLodPitchThreshold);
|
||||
~PaintParameters();
|
||||
|
||||
gfx::Context& context;
|
||||
@@ -130,6 +133,10 @@ public:
|
||||
static constexpr float depthEpsilon = 1.0f / (1 << 12);
|
||||
#endif
|
||||
static constexpr int maxStencilValue = 255;
|
||||
|
||||
double tileLodMinRadius;
|
||||
double tileLodScale;
|
||||
double tileLodPitchThreshold;
|
||||
};
|
||||
|
||||
} // namespace mbgl
|
||||
|
||||
@@ -194,6 +194,10 @@ std::unique_ptr<RenderTree> RenderOrchestrator::createRenderTree(
|
||||
.glyphManager = glyphManager,
|
||||
.prefetchZoomDelta = updateParameters->prefetchZoomDelta,
|
||||
.threadPool = threadPool,
|
||||
.tileLodMinRadius = updateParameters->tileLodMinRadius,
|
||||
.tileLodScale = updateParameters->tileLodScale,
|
||||
.tileLodPitchThreshold = updateParameters->tileLodPitchThreshold,
|
||||
.tileLodZoomShift = updateParameters->tileLodZoomShift,
|
||||
.dynamicTextureAtlas = dynamicTextureAtlas};
|
||||
|
||||
glyphManager->setURL(updateParameters->glyphURL);
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <mbgl/renderer/renderer_observer.hpp>
|
||||
#include <mbgl/renderer/render_static_data.hpp>
|
||||
#include <mbgl/renderer/render_tree.hpp>
|
||||
#include <mbgl/renderer/update_parameters.hpp>
|
||||
#include <mbgl/shaders/program_parameters.hpp>
|
||||
#include <mbgl/util/convert.hpp>
|
||||
#include <mbgl/util/string.hpp>
|
||||
@@ -84,12 +85,13 @@ void Renderer::Impl::setObserver(RendererObserver* observer_) {
|
||||
observer = observer_ ? observer_ : &nullObserver();
|
||||
}
|
||||
|
||||
void Renderer::Impl::render(const RenderTree& renderTree,
|
||||
[[maybe_unused]] const std::shared_ptr<UpdateParameters>& updateParameters) {
|
||||
void Renderer::Impl::render(const RenderTree& renderTree, const std::shared_ptr<UpdateParameters>& updateParameters) {
|
||||
MLN_TRACE_FUNC();
|
||||
auto& context = backend.getContext();
|
||||
context.setObserver(this);
|
||||
|
||||
assert(updateParameters);
|
||||
|
||||
#if MLN_RENDER_BACKEND_METAL
|
||||
if constexpr (EnableMetalCapture) {
|
||||
const auto& mtlBackend = static_cast<mtl::RendererBackend&>(backend);
|
||||
@@ -189,7 +191,10 @@ void Renderer::Impl::render(const RenderTree& renderTree,
|
||||
*staticData,
|
||||
renderTree.getLineAtlas(),
|
||||
renderTree.getPatternAtlas(),
|
||||
frameCount};
|
||||
frameCount,
|
||||
updateParameters->tileLodMinRadius,
|
||||
updateParameters->tileLodScale,
|
||||
updateParameters->tileLodPitchThreshold};
|
||||
|
||||
parameters.symbolFadeChange = renderTreeParameters.symbolFadeChange;
|
||||
parameters.opaquePassCutoff = renderTreeParameters.opaquePassCutOff;
|
||||
|
||||
@@ -133,7 +133,9 @@ void RenderImageSource::update(Immutable<style::Source::Impl> baseImpl_,
|
||||
|
||||
bool hasVisibleTile = false;
|
||||
// Add additional wrapped tile ids if neccessary
|
||||
auto idealTiles = util::tileCover(transformState, static_cast<uint8_t>(transformState.getZoom()));
|
||||
auto idealTiles = util::tileCover(
|
||||
{transformState, parameters.tileLodMinRadius, parameters.tileLodScale, parameters.tileLodPitchThreshold},
|
||||
static_cast<uint8_t>(transformState.getZoom()));
|
||||
for (auto tile : idealTiles) {
|
||||
if (tile.wrap != 0 && tileCover[0].canonical.isChildOf(tile.canonical)) {
|
||||
tileIds.emplace_back(tile.wrap, tileCover[0].canonical);
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <mbgl/actor/scheduler.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <numbers>
|
||||
|
||||
#include <mapbox/std/weak.hpp>
|
||||
|
||||
@@ -32,6 +33,10 @@ public:
|
||||
std::shared_ptr<GlyphManager> glyphManager;
|
||||
const uint8_t prefetchZoomDelta;
|
||||
TaggedScheduler threadPool;
|
||||
double tileLodMinRadius = 3;
|
||||
double tileLodScale = 1;
|
||||
double tileLodPitchThreshold = (60.0 / 180.0) * std::numbers::pi;
|
||||
double tileLodZoomShift = 0;
|
||||
gfx::DynamicTextureAtlasPtr dynamicTextureAtlas;
|
||||
};
|
||||
|
||||
|
||||
@@ -89,9 +89,14 @@ void TilePyramid::update(const std::vector<Immutable<style::LayerProperties>>& l
|
||||
|
||||
handleWrapJump(static_cast<float>(parameters.transformState.getLatLng().longitude()));
|
||||
|
||||
// Optionally shift the zoom level
|
||||
double zoom = util::clamp<double>(parameters.transformState.getZoom() + parameters.tileLodZoomShift,
|
||||
parameters.transformState.getMinZoom(),
|
||||
parameters.transformState.getMaxZoom());
|
||||
|
||||
const auto type = sourceImpl.type;
|
||||
// Determine the overzooming/underzooming amounts and required tiles.
|
||||
int32_t overscaledZoom = util::coveringZoomLevel(parameters.transformState.getZoom(), type, tileSize);
|
||||
int32_t overscaledZoom = util::coveringZoomLevel(zoom, type, tileSize);
|
||||
int32_t tileZoom = overscaledZoom;
|
||||
int32_t panZoom = zoomRange.max;
|
||||
|
||||
@@ -103,6 +108,11 @@ void TilePyramid::update(const std::vector<Immutable<style::LayerProperties>>& l
|
||||
std::vector<OverscaledTileID> idealTiles;
|
||||
std::vector<OverscaledTileID> panTiles;
|
||||
|
||||
util::TileCoverParameters tileCoverParameters = {parameters.transformState,
|
||||
parameters.tileLodMinRadius,
|
||||
parameters.tileLodScale,
|
||||
parameters.tileLodPitchThreshold};
|
||||
|
||||
if (overscaledZoom >= zoomRange.min) {
|
||||
int32_t idealZoom = std::min<int32_t>(zoomRange.max, overscaledZoom);
|
||||
|
||||
@@ -123,11 +133,11 @@ void TilePyramid::update(const std::vector<Immutable<style::LayerProperties>>& l
|
||||
}
|
||||
|
||||
if (panZoom < idealZoom) {
|
||||
panTiles = util::tileCover(parameters.transformState, panZoom);
|
||||
panTiles = util::tileCover(tileCoverParameters, panZoom);
|
||||
}
|
||||
}
|
||||
|
||||
idealTiles = util::tileCover(parameters.transformState, idealZoom, tileZoom);
|
||||
idealTiles = util::tileCover(tileCoverParameters, idealZoom, tileZoom);
|
||||
if (parameters.mode == MapMode::Tile && type != SourceType::Raster && type != SourceType::RasterDEM &&
|
||||
idealTiles.size() > 1) {
|
||||
mbgl::Log::Warning(mbgl::Event::General,
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <mbgl/util/chrono.hpp>
|
||||
#include <mbgl/util/immutable.hpp>
|
||||
|
||||
#include <numbers>
|
||||
#include <vector>
|
||||
|
||||
#include <mapbox/std/weak.hpp>
|
||||
@@ -44,6 +45,11 @@ public:
|
||||
const bool stillImageRequest;
|
||||
|
||||
const bool crossSourceCollisions;
|
||||
|
||||
double tileLodMinRadius = 3;
|
||||
double tileLodScale = 1;
|
||||
double tileLodPitchThreshold = (60.0 / 180.0) * std::numbers::pi;
|
||||
double tileLodZoomShift = 0;
|
||||
};
|
||||
|
||||
} // namespace mbgl
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#include <mbgl/map/transform_state.hpp>
|
||||
#include <mbgl/math/log2.hpp>
|
||||
#include <mbgl/util/bounding_volumes.hpp>
|
||||
#include <mbgl/util/constants.hpp>
|
||||
@@ -9,7 +8,6 @@
|
||||
|
||||
#include <functional>
|
||||
#include <list>
|
||||
#include <numbers>
|
||||
|
||||
using namespace std::numbers;
|
||||
|
||||
@@ -157,7 +155,7 @@ int32_t coveringZoomLevel(double zoom, style::SourceType type, uint16_t size) no
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<OverscaledTileID> tileCover(const TransformState& state,
|
||||
std::vector<OverscaledTileID> tileCover(const TileCoverParameters& state,
|
||||
uint8_t z,
|
||||
const std::optional<uint8_t>& overscaledZ) {
|
||||
struct Node {
|
||||
@@ -173,23 +171,26 @@ std::vector<OverscaledTileID> tileCover(const TransformState& state,
|
||||
double sqrDist;
|
||||
};
|
||||
|
||||
const auto& transform = state.transformState;
|
||||
const double numTiles = std::pow(2.0, z);
|
||||
const double worldSize = Projection::worldSize(state.getScale());
|
||||
const uint8_t minZoom = state.getPitch() <= (60.0 / 180.0) * pi ? z : 0;
|
||||
const double worldSize = Projection::worldSize(transform.getScale());
|
||||
const uint8_t minZoom = transform.getPitch() <= state.tileLodPitchThreshold ? z : 0;
|
||||
const uint8_t maxZoom = z;
|
||||
const uint8_t overscaledZoom = overscaledZ.value_or(z);
|
||||
const bool flippedY = state.getViewportMode() == ViewportMode::FlippedY;
|
||||
const uint8_t overscaledZoom = std::max(overscaledZ.value_or(z), z);
|
||||
const bool flippedY = transform.getViewportMode() == ViewportMode::FlippedY;
|
||||
|
||||
const auto centerPoint =
|
||||
TileCoordinate::fromScreenCoordinate(state, z, {state.getSize().width / 2.0, state.getSize().height / 2.0}).p;
|
||||
const auto centerPoint = TileCoordinate::fromScreenCoordinate(
|
||||
transform, z, {transform.getSize().width / 2.0, transform.getSize().height / 2.0})
|
||||
.p;
|
||||
|
||||
const vec3 centerCoord = {{centerPoint.x, centerPoint.y, 0.0}};
|
||||
|
||||
const Frustum frustum = Frustum::fromInvProjMatrix(state.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
|
||||
// surrounding the center location
|
||||
const double radiusOfMaxLvlLodInTiles = 3;
|
||||
assert(state.tileLodMinRadius >= 1);
|
||||
const double radiusOfMaxLvlLodInTiles = std::max(1.0, state.tileLodMinRadius);
|
||||
|
||||
const auto newRootTile = [&](int16_t wrap) -> Node {
|
||||
return {AABB({{wrap * numTiles, 0.0, 0.0}}, {{(wrap + 1) * numTiles, numTiles, 0.0}}),
|
||||
@@ -235,12 +236,14 @@ std::vector<OverscaledTileID> tileCover(const TransformState& state,
|
||||
// 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
|
||||
// size of its children (per dimension) we can define distance
|
||||
// thresholds for each relative level: f(k) = offset + 2 + 4 + 8 + 16 +
|
||||
// ... + 2^k. This is the same as "offset+2^(k+1)-2"
|
||||
// thresholds for each relative level:
|
||||
// f(k) = offset + 2 + 4 + 8 + 16 + ... + 2^k
|
||||
// This is the same as:
|
||||
// f(k) = offset + 2^(k+1)-2
|
||||
const double distToSplit = radiusOfMaxLvlLodInTiles + (1 << (maxZoom - node.zoom)) - 2;
|
||||
|
||||
// Have we reached the target depth or is the tile too far away to be any split further?
|
||||
if (node.zoom == maxZoom || (*longestDim > distToSplit && node.zoom >= minZoom)) {
|
||||
if (node.zoom == maxZoom || (*longestDim * state.tileLodScale > distToSplit && node.zoom >= minZoom)) {
|
||||
// Perform precise intersection test between the frustum and aabb.
|
||||
// This will cull < 1% false positives missed by the original test
|
||||
if (node.fullyVisible || frustum.intersectsPrecise(node.aabb, true) != IntersectionResult::Separate) {
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include <mbgl/map/transform_state.hpp>
|
||||
#include <mbgl/tile/tile_id.hpp>
|
||||
#include <mbgl/style/types.hpp>
|
||||
#include <mbgl/util/geometry.hpp>
|
||||
#include <mbgl/util/range.hpp>
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <numbers>
|
||||
#include <optional>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
class TransformState;
|
||||
class LatLngBounds;
|
||||
|
||||
namespace util {
|
||||
@@ -31,9 +33,16 @@ private:
|
||||
std::unique_ptr<Impl> impl;
|
||||
};
|
||||
|
||||
struct TileCoverParameters {
|
||||
TransformState transformState;
|
||||
double tileLodMinRadius = 3;
|
||||
double tileLodScale = 1;
|
||||
double tileLodPitchThreshold = (60.0 / 180.0) * std::numbers::pi;
|
||||
};
|
||||
|
||||
int32_t coveringZoomLevel(double z, style::SourceType type, uint16_t tileSize) noexcept;
|
||||
|
||||
std::vector<OverscaledTileID> tileCover(const TransformState&,
|
||||
std::vector<OverscaledTileID> tileCover(const TileCoverParameters& state,
|
||||
uint8_t z,
|
||||
const std::optional<uint8_t>& overscaledZ = std::nullopt);
|
||||
std::vector<UnwrappedTileID> tileCover(const LatLngBounds&, uint8_t z);
|
||||
|
||||
@@ -84,6 +84,7 @@ add_library(
|
||||
${PROJECT_SOURCE_DIR}/test/tile/tile_cache.test.cpp
|
||||
${PROJECT_SOURCE_DIR}/test/tile/tile_coordinate.test.cpp
|
||||
${PROJECT_SOURCE_DIR}/test/tile/tile_id.test.cpp
|
||||
${PROJECT_SOURCE_DIR}/test/tile/tile_lod.test.cpp
|
||||
${PROJECT_SOURCE_DIR}/test/tile/vector_tile.test.cpp
|
||||
${PROJECT_SOURCE_DIR}/test/util/action_journal.test.cpp
|
||||
${PROJECT_SOURCE_DIR}/test/util/async_task.test.cpp
|
||||
|
||||
BIN
test/fixtures/tile_lod/disabled/expected.png
vendored
Normal file
BIN
test/fixtures/tile_lod/disabled/expected.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 872 B |
BIN
test/fixtures/tile_lod/pitchThreshold-line/expected.png
vendored
Normal file
BIN
test/fixtures/tile_lod/pitchThreshold-line/expected.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.1 KiB |
BIN
test/fixtures/tile_lod/pitchThreshold-polyline/expected.png
vendored
Normal file
BIN
test/fixtures/tile_lod/pitchThreshold-polyline/expected.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
BIN
test/fixtures/tile_lod/radius/expected.png
vendored
Normal file
BIN
test/fixtures/tile_lod/radius/expected.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.7 KiB |
BIN
test/fixtures/tile_lod/scale/expected.png
vendored
Normal file
BIN
test/fixtures/tile_lod/scale/expected.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.3 KiB |
@@ -74,5 +74,12 @@ void checkImage(const std::string& base,
|
||||
double imageThreshold = 0,
|
||||
double pixelThreshold = 0);
|
||||
|
||||
void checkImages(const std::vector<std::string>& possibleExpected,
|
||||
const PremultipliedImage& actual,
|
||||
double imageThreshold = 0,
|
||||
double pixelThreshold = 0);
|
||||
|
||||
double getImageDiff(const std::string& base, const PremultipliedImage& actual, double pixelThreshold = 0);
|
||||
|
||||
} // namespace test
|
||||
} // namespace mbgl
|
||||
|
||||
@@ -34,17 +34,17 @@ public:
|
||||
|
||||
VectorTileTest()
|
||||
: threadPool(Scheduler::GetBackground(), uniqueID),
|
||||
tileParameters{1.0,
|
||||
MapDebugOptions(),
|
||||
transformState,
|
||||
fileSource,
|
||||
MapMode::Continuous,
|
||||
annotationManager.makeWeakPtr(),
|
||||
imageManager,
|
||||
glyphManager,
|
||||
0,
|
||||
threadPool,
|
||||
dynamicTextureAtlas},
|
||||
tileParameters{.pixelRatio = 1.0,
|
||||
.debugOptions = MapDebugOptions(),
|
||||
.transformState = transformState,
|
||||
.fileSource = fileSource,
|
||||
.mode = MapMode::Continuous,
|
||||
.annotationManager = annotationManager.makeWeakPtr(),
|
||||
.imageManager = imageManager,
|
||||
.glyphManager = glyphManager,
|
||||
.prefetchZoomDelta = 0,
|
||||
.threadPool = threadPool,
|
||||
.dynamicTextureAtlas = dynamicTextureAtlas},
|
||||
style{fileSource, 1, threadPool} {}
|
||||
|
||||
~VectorTileTest() {
|
||||
|
||||
@@ -9,12 +9,16 @@
|
||||
#pragma warning(disable : 4244)
|
||||
#endif
|
||||
|
||||
#include <gmock/gmock-matchers.h>
|
||||
#include <gmock/gmock-more-matchers.h>
|
||||
#include <mapbox/pixelmatch.hpp>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
using namespace ::testing;
|
||||
|
||||
namespace mbgl {
|
||||
namespace test {
|
||||
|
||||
@@ -22,10 +26,27 @@ void checkImage(const std::string& base,
|
||||
const PremultipliedImage& actual,
|
||||
double imageThreshold,
|
||||
double pixelThreshold) {
|
||||
EXPECT_LE(getImageDiff(base, actual, pixelThreshold), imageThreshold);
|
||||
}
|
||||
|
||||
void checkImages(const std::vector<std::string>& possibleExpected,
|
||||
const PremultipliedImage& actual,
|
||||
double imageThreshold,
|
||||
double pixelThreshold) {
|
||||
std::vector<double> diffs(0.0, possibleExpected.size());
|
||||
|
||||
for (const auto& expected : possibleExpected) {
|
||||
diffs.push_back(getImageDiff(expected, actual, pixelThreshold));
|
||||
}
|
||||
|
||||
EXPECT_THAT(diffs, Contains(Le(imageThreshold)));
|
||||
}
|
||||
|
||||
double getImageDiff(const std::string& base, const PremultipliedImage& actual, double pixelThreshold) {
|
||||
#if !TEST_READ_ONLY
|
||||
if (getenv("UPDATE")) {
|
||||
util::write_file(base + "/expected.png", encodePNG(actual));
|
||||
return;
|
||||
return 0.0;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -44,7 +65,9 @@ void checkImage(const std::string& base,
|
||||
util::write_file(base + "/actual.png", encodePNG(actual));
|
||||
#endif
|
||||
|
||||
ASSERT_EQ(expected.size, actual.size);
|
||||
if (expected.size != actual.size) {
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
uint64_t pixels = mapbox::pixelmatch(actual.data.get(),
|
||||
expected.data.get(),
|
||||
@@ -53,11 +76,11 @@ void checkImage(const std::string& base,
|
||||
diff.data.get(),
|
||||
pixelThreshold);
|
||||
|
||||
EXPECT_LE(static_cast<double>(pixels) / (expected.size.width * expected.size.height), imageThreshold);
|
||||
|
||||
#if !TEST_READ_ONLY
|
||||
util::write_file(base + "/diff.png", encodePNG(diff));
|
||||
#endif
|
||||
|
||||
return static_cast<double>(pixels) / (expected.size.width * expected.size.height);
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
|
||||
@@ -70,17 +70,17 @@ public:
|
||||
Style style;
|
||||
|
||||
TileParameters tileParameters(MapMode mapMode = MapMode::Continuous) {
|
||||
return {1.0,
|
||||
MapDebugOptions(),
|
||||
transformState,
|
||||
fileSource,
|
||||
mapMode,
|
||||
annotationManager.makeWeakPtr(),
|
||||
imageManager,
|
||||
glyphManager,
|
||||
0,
|
||||
threadPool,
|
||||
dynamicTextureAtlas};
|
||||
return {.pixelRatio = 1.0,
|
||||
.debugOptions = MapDebugOptions(),
|
||||
.transformState = transformState,
|
||||
.fileSource = fileSource,
|
||||
.mode = mapMode,
|
||||
.annotationManager = annotationManager.makeWeakPtr(),
|
||||
.imageManager = imageManager,
|
||||
.glyphManager = glyphManager,
|
||||
.prefetchZoomDelta = 0,
|
||||
.threadPool = threadPool,
|
||||
.dynamicTextureAtlas = dynamicTextureAtlas};
|
||||
};
|
||||
|
||||
SourceTest()
|
||||
|
||||
@@ -36,17 +36,17 @@ public:
|
||||
style::Style style;
|
||||
|
||||
CustomTileTest()
|
||||
: tileParameters{1.0,
|
||||
MapDebugOptions(),
|
||||
transformState,
|
||||
fileSource,
|
||||
MapMode::Continuous,
|
||||
annotationManager.makeWeakPtr(),
|
||||
imageManager,
|
||||
glyphManager,
|
||||
0,
|
||||
{Scheduler::GetBackground(), uniqueID},
|
||||
dynamicTextureAtlas},
|
||||
: tileParameters{.pixelRatio = 1.0,
|
||||
.debugOptions = MapDebugOptions(),
|
||||
.transformState = transformState,
|
||||
.fileSource = fileSource,
|
||||
.mode = MapMode::Continuous,
|
||||
.annotationManager = annotationManager.makeWeakPtr(),
|
||||
.imageManager = imageManager,
|
||||
.glyphManager = glyphManager,
|
||||
.prefetchZoomDelta = 0,
|
||||
.threadPool = {Scheduler::GetBackground(), uniqueID},
|
||||
.dynamicTextureAtlas = dynamicTextureAtlas},
|
||||
style{fileSource, 1, tileParameters.threadPool} {}
|
||||
};
|
||||
|
||||
|
||||
@@ -37,17 +37,17 @@ public:
|
||||
style::Style style;
|
||||
|
||||
GeoJSONTileTest()
|
||||
: tileParameters{1.0,
|
||||
MapDebugOptions(),
|
||||
transformState,
|
||||
fileSource,
|
||||
MapMode::Continuous,
|
||||
annotationManager.makeWeakPtr(),
|
||||
imageManager,
|
||||
glyphManager,
|
||||
0,
|
||||
{Scheduler::GetBackground(), uniqueID},
|
||||
dynamicTextureAtlas},
|
||||
: tileParameters{.pixelRatio = 1.0,
|
||||
.debugOptions = MapDebugOptions(),
|
||||
.transformState = transformState,
|
||||
.fileSource = fileSource,
|
||||
.mode = MapMode::Continuous,
|
||||
.annotationManager = annotationManager.makeWeakPtr(),
|
||||
.imageManager = imageManager,
|
||||
.glyphManager = glyphManager,
|
||||
.prefetchZoomDelta = 0,
|
||||
.threadPool = {Scheduler::GetBackground(), uniqueID},
|
||||
.dynamicTextureAtlas = dynamicTextureAtlas},
|
||||
style{fileSource, 1, tileParameters.threadPool} {}
|
||||
};
|
||||
|
||||
|
||||
@@ -31,17 +31,17 @@ public:
|
||||
style::Style style;
|
||||
|
||||
RasterDEMTileTest()
|
||||
: tileParameters{1.0,
|
||||
MapDebugOptions(),
|
||||
transformState,
|
||||
fileSource,
|
||||
MapMode::Continuous,
|
||||
annotationManager.makeWeakPtr(),
|
||||
imageManager,
|
||||
glyphManager,
|
||||
0,
|
||||
{Scheduler::GetBackground(), uniqueID},
|
||||
dynamicTextureAtlas},
|
||||
: tileParameters{.pixelRatio = 1.0,
|
||||
.debugOptions = MapDebugOptions(),
|
||||
.transformState = transformState,
|
||||
.fileSource = fileSource,
|
||||
.mode = MapMode::Continuous,
|
||||
.annotationManager = annotationManager.makeWeakPtr(),
|
||||
.imageManager = imageManager,
|
||||
.glyphManager = glyphManager,
|
||||
.prefetchZoomDelta = 0,
|
||||
.threadPool = {Scheduler::GetBackground(), uniqueID},
|
||||
.dynamicTextureAtlas = dynamicTextureAtlas},
|
||||
style{fileSource, 1, tileParameters.threadPool} {}
|
||||
};
|
||||
|
||||
|
||||
@@ -31,17 +31,17 @@ public:
|
||||
style::Style style;
|
||||
|
||||
RasterTileTest()
|
||||
: tileParameters{1.0,
|
||||
MapDebugOptions(),
|
||||
transformState,
|
||||
fileSource,
|
||||
MapMode::Continuous,
|
||||
annotationManager.makeWeakPtr(),
|
||||
imageManager,
|
||||
glyphManager,
|
||||
0,
|
||||
{Scheduler::GetBackground(), uniqueID},
|
||||
dynamicTextureAtlas},
|
||||
: tileParameters{.pixelRatio = 1.0,
|
||||
.debugOptions = MapDebugOptions(),
|
||||
.transformState = transformState,
|
||||
.fileSource = fileSource,
|
||||
.mode = MapMode::Continuous,
|
||||
.annotationManager = annotationManager.makeWeakPtr(),
|
||||
.imageManager = imageManager,
|
||||
.glyphManager = glyphManager,
|
||||
.prefetchZoomDelta = 0,
|
||||
.threadPool = {Scheduler::GetBackground(), uniqueID},
|
||||
.dynamicTextureAtlas = dynamicTextureAtlas},
|
||||
style{fileSource, 1, tileParameters.threadPool} {}
|
||||
};
|
||||
|
||||
|
||||
156
test/tile/tile_lod.test.cpp
Normal file
156
test/tile/tile_lod.test.cpp
Normal file
@@ -0,0 +1,156 @@
|
||||
#include <gmock/gmock.h>
|
||||
|
||||
#include <mbgl/map/map.hpp>
|
||||
#include <mbgl/map/map_observer.hpp>
|
||||
#include <mbgl/gfx/headless_frontend.hpp>
|
||||
#include <mbgl/util/run_loop.hpp>
|
||||
#include <mbgl/util/io.hpp>
|
||||
#include <mbgl/util/logging.hpp>
|
||||
#include <mbgl/style/style.hpp>
|
||||
#include <mbgl/style/layers/fill_layer.hpp>
|
||||
#include <mbgl/style/layers/line_layer.hpp>
|
||||
#include <mbgl/style/layers/circle_layer.hpp>
|
||||
#include <mbgl/style/sources/custom_geometry_source.hpp>
|
||||
#include <mbgl/test/util.hpp>
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
using namespace mbgl;
|
||||
using namespace mbgl::util;
|
||||
using namespace ::testing;
|
||||
|
||||
class TileLODTest {
|
||||
public:
|
||||
util::RunLoop loop;
|
||||
HeadlessFrontend frontend;
|
||||
Map map;
|
||||
|
||||
uint32_t tileCount = 0;
|
||||
|
||||
public:
|
||||
TileLODTest(const CameraOptions& cameraOptions = CameraOptions())
|
||||
: frontend(1.0f),
|
||||
map(frontend,
|
||||
MapObserver::nullObserver(),
|
||||
MapOptions().withMapMode(MapMode::Static).withSize(frontend.getSize()),
|
||||
ResourceOptions().withCachePath(":memory:").withAssetPath("test/fixtures/api/assets")) {
|
||||
map.jumpTo(cameraOptions);
|
||||
|
||||
map.setDebug(MapDebugOptions::TileBorders);
|
||||
map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json"));
|
||||
|
||||
style::CustomGeometrySource::Options options;
|
||||
options.fetchTileFunction = [&](const mbgl::CanonicalTileID& tileID) {
|
||||
auto source = static_cast<style::CustomGeometrySource*>(map.getStyle().getSource("custom"));
|
||||
if (!source) {
|
||||
return;
|
||||
}
|
||||
|
||||
const LatLngBounds bounds(tileID);
|
||||
const LatLng& center = bounds.center();
|
||||
|
||||
mapbox::geojson::feature feature;
|
||||
feature.geometry = mapbox::geometry::geometry<double>(Point<double>{center.longitude(), center.latitude()});
|
||||
|
||||
FeatureCollection features;
|
||||
features.emplace_back(feature);
|
||||
|
||||
source->setTileData(tileID, features);
|
||||
|
||||
++tileCount;
|
||||
};
|
||||
|
||||
map.getStyle().addSource(std::make_unique<style::CustomGeometrySource>("custom", options));
|
||||
|
||||
auto layer = std::make_unique<style::CircleLayer>("circles", "custom");
|
||||
layer->setCircleColor(Color{0.0, 0.0, 1.0, 0.0});
|
||||
map.getStyle().addLayer(std::move(layer));
|
||||
}
|
||||
|
||||
void checkImage(std::string image, uint32_t expectedTileCount, double imageThreshold = 0.0002) {
|
||||
checkImages(std::vector<std::string>{image}, expectedTileCount, imageThreshold);
|
||||
}
|
||||
|
||||
void checkImages(std::vector<std::string> images, uint32_t expectedTileCount, double imageThreshold) {
|
||||
const auto result = frontend.render(map);
|
||||
|
||||
// check this after frontend.render()
|
||||
EXPECT_EQ(tileCount, expectedTileCount);
|
||||
|
||||
std::transform(images.begin(), images.end(), images.begin(), [](const auto& img) {
|
||||
return "test/fixtures/tile_lod/" + img;
|
||||
});
|
||||
|
||||
test::checkImages(images, result.image, imageThreshold, 0.01);
|
||||
}
|
||||
};
|
||||
|
||||
TEST(TileLOD, disabled) {
|
||||
TileLODTest test(CameraOptions().withZoom(10.0));
|
||||
|
||||
test.map.setTileLodPitchThreshold(std::numbers::pi);
|
||||
|
||||
test.checkImage("disabled", 4);
|
||||
}
|
||||
|
||||
TEST(TileLOD, zoomShift) {
|
||||
TileLODTest test(CameraOptions().withZoom(12.0));
|
||||
|
||||
test.map.setTileLodZoomShift(4.0);
|
||||
test.frontend.render(test.map);
|
||||
EXPECT_EQ(test.tileCount, 64);
|
||||
|
||||
test.tileCount = 0;
|
||||
|
||||
test.map.setTileLodZoomShift(-4.0);
|
||||
test.frontend.render(test.map);
|
||||
EXPECT_EQ(test.tileCount, 4);
|
||||
}
|
||||
|
||||
TEST(TileLOD, pitchThreshold) {
|
||||
TileLODTest test(CameraOptions().withZoom(10.0));
|
||||
|
||||
constexpr double pitch = 45.0;
|
||||
test.map.setTileLodZoomShift(4.0);
|
||||
test.map.setTileLodMinRadius(1.0);
|
||||
test.map.setTileLodScale(1.0);
|
||||
test.map.setTileLodPitchThreshold(pitch / 180.0 * std::numbers::pi);
|
||||
|
||||
test.map.jumpTo(CameraOptions().withPitch(pitch - 1.0));
|
||||
test.frontend.render(test.map);
|
||||
EXPECT_EQ(test.tileCount, 140);
|
||||
test.tileCount = 0;
|
||||
|
||||
test.map.jumpTo(CameraOptions().withPitch(pitch + 1.0));
|
||||
test.checkImages(
|
||||
{
|
||||
"pitchThreshold-line",
|
||||
"pitchThreshold-polyline",
|
||||
},
|
||||
22,
|
||||
0.0015);
|
||||
}
|
||||
|
||||
TEST(TileLOD, scale) {
|
||||
TileLODTest test(CameraOptions().withZoom(0.0));
|
||||
|
||||
test.map.setTileLodMinRadius(1.0);
|
||||
test.map.setTileLodScale(0.4);
|
||||
test.map.setTileLodPitchThreshold(-1.0);
|
||||
|
||||
test.map.setTileLodZoomShift(5.0);
|
||||
|
||||
test.checkImage("scale", 112);
|
||||
}
|
||||
|
||||
TEST(TileLOD, radius) {
|
||||
TileLODTest test(CameraOptions().withZoom(0.0));
|
||||
|
||||
test.map.setTileLodMinRadius(5.0);
|
||||
test.map.setTileLodScale(1.0);
|
||||
test.map.setTileLodPitchThreshold(-1.0);
|
||||
|
||||
test.map.setTileLodZoomShift(5.0);
|
||||
|
||||
test.checkImage("radius", 172);
|
||||
}
|
||||
@@ -44,7 +44,7 @@ TEST(TileCover, Pitch) {
|
||||
.withPitch(40.0));
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
TEST(TileCover, PitchIssue15442) {
|
||||
@@ -62,7 +62,7 @@ TEST(TileCover, PitchIssue15442) {
|
||||
|
||||
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}}}),
|
||||
util::tileCover(transform.getState(), 2));
|
||||
util::tileCover({transform.getState()}, 2));
|
||||
}
|
||||
|
||||
TEST(TileCover, PitchOverAllowedByContentInsets) {
|
||||
@@ -80,7 +80,7 @@ TEST(TileCover, PitchOverAllowedByContentInsets) {
|
||||
|
||||
EXPECT_EQ(
|
||||
(std::vector<OverscaledTileID>{{3, 4, 3}, {3, 3, 3}, {3, 4, 4}, {3, 3, 4}, {3, 4, 2}, {3, 5, 3}, {3, 5, 2}}),
|
||||
util::tileCover(transform.getState(), 3));
|
||||
util::tileCover({transform.getState()}, 3));
|
||||
}
|
||||
|
||||
TEST(TileCover, PitchWithLargerResultSet) {
|
||||
@@ -96,7 +96,7 @@ TEST(TileCover, PitchWithLargerResultSet) {
|
||||
.withBearing(-142.2630000003529176)
|
||||
.withPitch(60.0));
|
||||
|
||||
auto cover = util::tileCover(transform.getState(), 5);
|
||||
auto cover = util::tileCover({transform.getState()}, 5);
|
||||
// Returned vector has above 100 elements, we check first 16 as there is a
|
||||
// plan to return lower LOD for distant tiles.
|
||||
EXPECT_EQ((std::vector<OverscaledTileID>{{5, 15, 16},
|
||||
@@ -148,7 +148,7 @@ TEST(TileCover, CoordinatesAreUnwrapped) {
|
||||
|
||||
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(transform.getState(), 1));
|
||||
util::tileCover({transform.getState()}, 1));
|
||||
}
|
||||
|
||||
TEST(TileCover, DifferentOverscaledZ) {
|
||||
@@ -165,7 +165,7 @@ TEST(TileCover, DifferentOverscaledZ) {
|
||||
|
||||
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(transform.getState(), 2, 3));
|
||||
util::tileCover({transform.getState()}, 2, 3));
|
||||
}
|
||||
|
||||
TEST(TileCover, DifferentOverscaledZWithPitch) {
|
||||
@@ -186,7 +186,7 @@ TEST(TileCover, DifferentOverscaledZWithPitch) {
|
||||
{5, 0, {3, 3, 1}},
|
||||
{5, 0, {3, 4, 1}},
|
||||
{5, 0, {3, 2, 1}}}),
|
||||
util::tileCover(transform.getState(), 3, 5));
|
||||
util::tileCover({transform.getState()}, 3, 5));
|
||||
}
|
||||
|
||||
TEST(TileCover, DifferentOverscaledZWrapped) {
|
||||
@@ -201,7 +201,7 @@ TEST(TileCover, DifferentOverscaledZWrapped) {
|
||||
|
||||
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(transform.getState(), 1, 2));
|
||||
util::tileCover({transform.getState()}, 1, 2));
|
||||
}
|
||||
|
||||
TEST(TileCover, FlippedY) {
|
||||
@@ -216,7 +216,7 @@ TEST(TileCover, FlippedY) {
|
||||
.withZoom(1.0));
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
TEST(TileCover, FlippedYPitch) {
|
||||
@@ -233,7 +233,7 @@ TEST(TileCover, FlippedYPitch) {
|
||||
.withPitch(40.0));
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
TEST(TileCover, FlippedYHelsinki) {
|
||||
@@ -248,7 +248,7 @@ TEST(TileCover, FlippedYHelsinki) {
|
||||
.withZoom(11.447425));
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
TEST(TileCoverStream, Arctic) {
|
||||
|
||||
Reference in New Issue
Block a user