From d8046040915e84486dd3a1b9b13d9936dab4790f Mon Sep 17 00:00:00 2001 From: Ansis Brammanis Date: Thu, 4 Nov 2021 16:18:58 -0400 Subject: [PATCH 1/2] improve lod tile loading for projections 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. --- src/geo/projection/adjustments.js | 17 ++++++++++------- src/geo/transform.js | 21 ++++++++++----------- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/src/geo/projection/adjustments.js b/src/geo/projection/adjustments.js index f652d3e0662..e16241eca44 100644 --- a/src/geo/projection/adjustments.js +++ b/src/geo/projection/adjustments.js @@ -8,21 +8,24 @@ import type {Projection} from './index.js'; import type Transform from '../transform.js'; export default function getProjectionAdjustments(transform: Transform, withoutRotation?: boolean) { - const projection = transform.projection; - const interpT = getInterpolationT(transform); - - const zoomAdjustment = getZoomAdjustment(projection, transform.center); - const zoomAdjustmentOrigin = getZoomAdjustment(projection, LngLat.convert(projection.center)); - const scaleAdjustment = Math.pow(2, zoomAdjustment * interpT + (1 - interpT) * zoomAdjustmentOrigin); - const matrix = getShearAdjustment(transform.projection, transform.zoom, transform.center, interpT, withoutRotation); + const scaleAdjustment = getScaleAdjustment(transform); mat4.scale(matrix, matrix, [scaleAdjustment, scaleAdjustment, 1]); return matrix; } +export function getScaleAdjustment(transform: Transform) { + const projection = transform.projection; + const interpT = getInterpolationT(transform); + const zoomAdjustment = getZoomAdjustment(projection, transform.center); + const zoomAdjustmentOrigin = getZoomAdjustment(projection, LngLat.convert(projection.center)); + const scaleAdjustment = Math.pow(2, zoomAdjustment * interpT + (1 - interpT) * zoomAdjustmentOrigin); + return scaleAdjustment; +} + export function getProjectionAdjustmentInverted(transform: Transform) { const m = getProjectionAdjustments(transform, true); return mat2.invert([], [ diff --git a/src/geo/transform.js b/src/geo/transform.js index d9c0721ff83..f2eb0f2a55f 100644 --- a/src/geo/transform.js +++ b/src/geo/transform.js @@ -14,7 +14,7 @@ import {Aabb, Frustum, Ray} from '../util/primitives.js'; import EdgeInsets from './edge_insets.js'; import {FreeCamera, FreeCameraOptions, orientationFromFrame} from '../ui/free_camera.js'; import assert from 'assert'; -import getProjectionAdjustments, {getProjectionAdjustmentInverted} from './projection/adjustments.js'; +import getProjectionAdjustments, {getProjectionAdjustmentInverted, getScaleAdjustment} from './projection/adjustments.js'; import {getPixelsToTileUnitsMatrix} from '../source/pixels_to_tile_units.js'; import {UnwrappedTileID, OverscaledTileID, CanonicalTileID} from '../source/tile_id.js'; @@ -720,7 +720,9 @@ class Transform { const maxRange = options.isTerrainDEM && this._elevation ? this._elevation.exaggeration() * 10000 : this._centerAltitude; const minRange = options.isTerrainDEM ? -maxRange : this._elevation ? this._elevation.getMinElevationBelowMSL() : 0; - const sizeAtMercatorCoord = mc => { + const scaleAdjustment = getScaleAdjustment(this); + + const relativeScaleAtMercatorCoord = mc => { // Calculate how scale compares between projected coordinates and mercator coordinates. // Returns a length. The units don't matter since the result is only // used in a ratio with other values returned by this function. @@ -744,11 +746,9 @@ class Transform { // Calculate the size of a projected square that would have the // same area as the reprojected square. - return Math.sqrt(dx * dy) / offset; + return Math.sqrt(dx * dy) * scaleAdjustment / offset; }; - const centerSize = sizeAtMercatorCoord(MercatorCoordinate.fromLngLat(this.center)); - const aabbForTile = (z, x, y, wrap, min, max) => { const tt = tileTransform({z, x, y}, this.projection); const tx = tt.x / tt.scale; @@ -850,21 +850,20 @@ class Transform { dzSqr = square(it.aabb.distanceZ(cameraPoint) * meterToTile); } - let scaleAdjustment = 1; - if (!isMercator && actualZ <= 5) { + let tileScaleAdjustment = 1; + if (actualZ <= 5) { // In other projections, not all tiles are the same size. // Account for the tile size difference by adjusting the distToSplit. // Adjust by the ratio of the area at the tile center to the area at the map center. // Adjustments are only needed at lower zooms where tiles are not similarly sized. const numTiles = Math.pow(2, it.zoom); - const tileCenterSize = sizeAtMercatorCoord(new MercatorCoordinate((it.x + 0.5) / numTiles, (it.y + 0.5) / numTiles)); - const areaRatio = tileCenterSize / centerSize; + const relativeScale = relativeScaleAtMercatorCoord(new MercatorCoordinate((it.x + 0.5) / numTiles, (it.y + 0.5) / numTiles)); // Fudge the ratio slightly so that all tiles near the center have the same zoom level. - scaleAdjustment = areaRatio > 0.85 ? 1 : areaRatio; + tileScaleAdjustment = relativeScale > 0.85 ? 1 : relativeScale; } const distanceSqr = dx * dx + dy * dy + dzSqr; - const distToSplit = (1 << maxZoom - it.zoom) * zoomSplitDistance * scaleAdjustment; + const distToSplit = (1 << maxZoom - it.zoom) * zoomSplitDistance * tileScaleAdjustment; const distToSplitSqr = square(distToSplit * distToSplitScale(Math.max(dzSqr, cameraHeightSqr), distanceSqr)); return distanceSqr < distToSplitSqr; From 826d3c1c240cd2b4355d9660670b815997c645f1 Mon Sep 17 00:00:00 2001 From: Ansis Brammanis Date: Thu, 4 Nov 2021 16:21:24 -0400 Subject: [PATCH 2/2] fixup --- src/geo/transform.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/geo/transform.js b/src/geo/transform.js index f2eb0f2a55f..fda0b36b58b 100644 --- a/src/geo/transform.js +++ b/src/geo/transform.js @@ -851,7 +851,7 @@ class Transform { } let tileScaleAdjustment = 1; - if (actualZ <= 5) { + if (!isMercator && actualZ <= 5) { // In other projections, not all tiles are the same size. // Account for the tile size difference by adjusting the distToSplit. // Adjust by the ratio of the area at the tile center to the area at the map center.