Skip to content

Commit ea73618

Browse files
committed
improve lod tile loading for projections (#11225)
The previous approach was mistakenly dependent on the center of the viewport. The intent was to use this as a reference size for what a tile should be. But at lower zoom levels, if you pan away from the center of the projection the tiles at the center of the screen may not be the the ideal size.
1 parent dbde39a commit ea73618

File tree

2 files changed

+19
-17
lines changed

2 files changed

+19
-17
lines changed

src/geo/projection/adjustments.js

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,24 @@ import type {Projection} from './index.js';
88
import type Transform from '../transform.js';
99

1010
export default function getProjectionAdjustments(transform: Transform, withoutRotation?: boolean) {
11-
const projection = transform.projection;
12-
1311
const interpT = getInterpolationT(transform);
14-
15-
const zoomAdjustment = getZoomAdjustment(projection, transform.center);
16-
const zoomAdjustmentOrigin = getZoomAdjustment(projection, LngLat.convert(projection.center));
17-
const scaleAdjustment = Math.pow(2, zoomAdjustment * interpT + (1 - interpT) * zoomAdjustmentOrigin);
18-
1912
const matrix = getShearAdjustment(transform.projection, transform.zoom, transform.center, interpT, withoutRotation);
2013

14+
const scaleAdjustment = getScaleAdjustment(transform);
2115
mat4.scale(matrix, matrix, [scaleAdjustment, scaleAdjustment, 1]);
2216

2317
return matrix;
2418
}
2519

20+
export function getScaleAdjustment(transform: Transform) {
21+
const projection = transform.projection;
22+
const interpT = getInterpolationT(transform);
23+
const zoomAdjustment = getZoomAdjustment(projection, transform.center);
24+
const zoomAdjustmentOrigin = getZoomAdjustment(projection, LngLat.convert(projection.center));
25+
const scaleAdjustment = Math.pow(2, zoomAdjustment * interpT + (1 - interpT) * zoomAdjustmentOrigin);
26+
return scaleAdjustment;
27+
}
28+
2629
export function getProjectionAdjustmentInverted(transform: Transform) {
2730
const m = getProjectionAdjustments(transform, true);
2831
return mat2.invert([], [

src/geo/transform.js

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {Aabb, Frustum, Ray} from '../util/primitives.js';
1414
import EdgeInsets from './edge_insets.js';
1515
import {FreeCamera, FreeCameraOptions, orientationFromFrame} from '../ui/free_camera.js';
1616
import assert from 'assert';
17-
import getProjectionAdjustments, {getProjectionAdjustmentInverted} from './projection/adjustments.js';
17+
import getProjectionAdjustments, {getProjectionAdjustmentInverted, getScaleAdjustment} from './projection/adjustments.js';
1818
import {getPixelsToTileUnitsMatrix} from '../source/pixels_to_tile_units.js';
1919

2020
import {UnwrappedTileID, OverscaledTileID, CanonicalTileID} from '../source/tile_id.js';
@@ -724,7 +724,9 @@ class Transform {
724724
const maxRange = options.isTerrainDEM && this._elevation ? this._elevation.exaggeration() * 10000 : this._centerAltitude;
725725
const minRange = options.isTerrainDEM ? -maxRange : this._elevation ? this._elevation.getMinElevationBelowMSL() : 0;
726726

727-
const sizeAtMercatorCoord = mc => {
727+
const scaleAdjustment = getScaleAdjustment(this);
728+
729+
const relativeScaleAtMercatorCoord = mc => {
728730
// Calculate how scale compares between projected coordinates and mercator coordinates.
729731
// Returns a length. The units don't matter since the result is only
730732
// used in a ratio with other values returned by this function.
@@ -748,11 +750,9 @@ class Transform {
748750

749751
// Calculate the size of a projected square that would have the
750752
// same area as the reprojected square.
751-
return Math.sqrt(dx * dy) / offset;
753+
return Math.sqrt(dx * dy) * scaleAdjustment / offset;
752754
};
753755

754-
const centerSize = sizeAtMercatorCoord(MercatorCoordinate.fromLngLat(this.center));
755-
756756
const aabbForTile = (z, x, y, wrap, min, max) => {
757757
const tt = tileTransform({z, x, y}, this.projection);
758758
const tx = tt.x / tt.scale;
@@ -854,21 +854,20 @@ class Transform {
854854
dzSqr = square(it.aabb.distanceZ(cameraPoint) * meterToTile);
855855
}
856856

857-
let scaleAdjustment = 1;
857+
let tileScaleAdjustment = 1;
858858
if (!isMercator && actualZ <= 5) {
859859
// In other projections, not all tiles are the same size.
860860
// Account for the tile size difference by adjusting the distToSplit.
861861
// Adjust by the ratio of the area at the tile center to the area at the map center.
862862
// Adjustments are only needed at lower zooms where tiles are not similarly sized.
863863
const numTiles = Math.pow(2, it.zoom);
864-
const tileCenterSize = sizeAtMercatorCoord(new MercatorCoordinate((it.x + 0.5) / numTiles, (it.y + 0.5) / numTiles));
865-
const areaRatio = tileCenterSize / centerSize;
864+
const relativeScale = relativeScaleAtMercatorCoord(new MercatorCoordinate((it.x + 0.5) / numTiles, (it.y + 0.5) / numTiles));
866865
// Fudge the ratio slightly so that all tiles near the center have the same zoom level.
867-
scaleAdjustment = areaRatio > 0.85 ? 1 : areaRatio;
866+
tileScaleAdjustment = relativeScale > 0.85 ? 1 : relativeScale;
868867
}
869868

870869
const distanceSqr = dx * dx + dy * dy + dzSqr;
871-
const distToSplit = (1 << maxZoom - it.zoom) * zoomSplitDistance * scaleAdjustment;
870+
const distToSplit = (1 << maxZoom - it.zoom) * zoomSplitDistance * tileScaleAdjustment;
872871
const distToSplitSqr = square(distToSplit * distToSplitScale(Math.max(dzSqr, cameraHeightSqr), distanceSqr));
873872

874873
return distanceSqr < distToSplitSqr;

0 commit comments

Comments
 (0)