fix(transit): start terrain heights afresh when the provider or globe changes

A height read after the camera and tiles settled was kept until either
changed, but swapping the terrain provider or the globe with a still camera
changed neither, so the previous terrain's elevation could stand
indefinitely. The cache is now cleared when either identity changes, checked
before the flat-globe answer so terrain -> flat -> terrain is covered too.

Found in review by kvnloo.

Co-Authored-By: Claude Fable 5.2 <[email protected]>
This commit is contained in:
Mazeyar Moeini FeizabadiandClaude Fable 5.2 committed 2026-11-13 03:22:10 +01:00
1 parent d5f7926c7e
commit 87674240bd
3 files changed
+49 -6

No files matched your search

@@ -2,5 +2,6 @@
globe, most visibly while zooming over a large fleet. Every reread asked the
terrain for the height of every trail fix of every vehicle in view; heights
are now kept per point, read again only while the camera moves or tiles
load, and read within a time budget that puts vehicle positions first
(mazy1998, #1688).
load, and read within a time budget that puts vehicle positions first.
Switching the terrain provider or globe starts the kept heights afresh
(mazy1998, review by kvnloo, #1688).
+12 -4
View File
@@ -78,6 +78,9 @@ export function createHeight({ state, services, parts }) {
cameraLat = NaN,
cameraHeight = NaN;
const terrainStats = { reads: 0, reused: 0, deferred: 0, borrowed: 0 };
/** The globe and terrain provider the kept heights were read from. */
let terrainGlobeRead = null,
terrainProviderRead = null;
/** A number unique to a point on a `scale`-per-degree grid. */
function gridKey(lat, lon, scale) {
@@ -219,11 +222,16 @@ export function createHeight({ state, services, parts }) {
// On a terrain globe, use the loaded surface under the marker. This reads
// tiles already loaded and does not launch a terrain request.
const globe = state._viewer?.scene?.globe;
const provider = state._viewer?.terrainProvider;
// A height belongs to the terrain it was read from. Checked before the
// flat-globe answer, so terrain → flat → terrain starts afresh too.
if (globe !== terrainGlobeRead || provider !== terrainProviderRead) {
clearTerrainHeights();
terrainGlobeRead = globe;
terrainProviderRead = provider;
}
if (globe?.show === true) {
if (
state._viewer?.terrainProvider instanceof
Cesium.EllipsoidTerrainProvider
)
if (provider instanceof Cesium.EllipsoidTerrainProvider)
return { cell, height: GROUND_FLOOR_LIFT_M, prior: null };
const rendered = globe.getHeight
? terrainHeight(globe, lat, lon, need)
+34
View File
@@ -262,6 +262,40 @@ test('once the camera and the tiles have settled a height is kept until either c
assert.equal(reads.length, 5);
});
test('another terrain provider or globe is read afresh, however settled the old heights were', (t) => {
const { height, state, globe, world, clock, reads } = terrain(t);
const viewer = state._viewer;
const at = () => height.requestHeight(42.36, -71.06).height;
// Read once the camera and tiles have long settled: kept indefinitely.
height.requestHeight(10, 10);
clock.now += TERRAIN_SETTLE_MS * 2;
reads.length = 0;
assert.equal(at(), 100 + LIFT);
clock.now += TERRAIN_SETTLE_MS * 10;
world.surface = () => 250;
assert.equal(at(), 100 + LIFT, 'same terrain: the kept height stands');
assert.equal(reads.length, 1);
viewer.terrainProvider = {};
assert.equal(at(), 250 + LIFT, 'a new provider is a new surface');
assert.equal(reads.length, 2);
world.surface = () => 400;
viewer.scene.globe = { show: true, getHeight: globe.getHeight };
assert.equal(at(), 400 + LIFT, 'and so is a new globe');
assert.equal(reads.length, 3);
// Terrain → flat → the same terrain provider: nothing asks the terrain
// while the globe is flat, yet the old heights must not come back.
const real = viewer.terrainProvider;
viewer.terrainProvider = new Cesium.EllipsoidTerrainProvider();
assert.equal(at(), LIFT);
world.surface = () => 30;
viewer.terrainProvider = real;
assert.equal(at(), 30 + LIFT);
assert.equal(reads.length, 4);
});
test('a flat globe answers without reading terrain', (t) => {
const { height, reads } = terrain(t, {
provider: new Cesium.EllipsoidTerrainProvider(),