Files
gods-eye-view/src/maps/terrain.js
T
Aditya d6c1ea0f4b fix(maps): retry the real imagery and terrain provider after a fallback
In the keyless setup a single transient failure while loading the default
map layers locked in the degraded fallback until reload.

_getImageryProvider cached the construction-fallback resolution (OSM) under
the Esri stack id, so every later "Esri Satellite" selection showed OSM.
createKeylessTerrain turns any Re:Earth failure into a flat
EllipsoidTerrainProvider; the controller cached it and set _terrainMode to
'keyless', so a boot-time network blip left the globe flat.

Fallbacks are still cached and shown at once, and no switch waits on a
retry. One controller timer now retries the real provider behind whichever
fallback is on screen (Esri's OSM stand-in, or flat terrain on a shown
globe), starting at 30 seconds and doubling to a 10-minute cap while it
keeps failing. A failed retry leaves the cached fallback and the installed
terrain untouched. A success replaces the cache entry and applies it live:
terrain is installed in place with a render request (or, behind a 3D
tileset, picked up by the next globe activation), and Esri is reselected
automatically, after any in-flight switch settles. A switch that read the
flat fallback re-reads the terrain cache before installing, and destroy()
cancels the timer and releases a provider built after it.
2026-10-16 16:52:01 +05:30

65 lines
2.2 KiB
JavaScript

import * as Cesium from 'cesium';
import { createTerrainRetryPolicy } from './terrainRetry.js';
/** Re:Earth / Mapterhorn ellipsoidal quantized mesh, CC BY 4.0. */
export const KEYLESS_TERRAIN_URL =
'https://terrain.reearth.land/cesium-mesh/ellipsoid';
/** These factories are lazy: a hidden globe must not trigger terrain loading. */
export async function createWorldTerrain(accessToken, { signal } = {}) {
accessToken = String(accessToken || '').trim();
if (!accessToken)
throw new Error('World terrain requires an explicit ion token');
signal?.throwIfAborted();
const resource = await Cesium.IonResource.fromAssetId(1, { accessToken });
signal?.throwIfAborted();
return {
provider: await Cesium.CesiumTerrainProvider.fromUrl(resource, {
requestVertexNormals: true,
requestWaterMask: false,
ellipsoid: Cesium.Ellipsoid.WGS84,
}),
};
}
function logThrottle({ statusCode, delayMs, inWindow }) {
// One line per cooldown window, not per tile: a burst throttles dozens.
if (inWindow !== 1) return;
console.warn(
`[MapStack] Re:Earth terrain answered ${statusCode}; retrying throttled tiles after ${Math.round(delayMs)} ms`,
);
}
/**
* The Re:Earth resource with the tile retry policy attached. Cesium copies
* `retryCallback`/`retryAttempts` onto every derived resource, so layer.json
* and each `{z}/{x}/{y}.terrain` fetch inherit it (see `terrainRetry.js`).
* @param {ReturnType<typeof createTerrainRetryPolicy>} [policy]
* @returns {Cesium.Resource}
*/
export function createKeylessTerrainResource(
policy = createTerrainRetryPolicy({ onThrottle: logThrottle }),
) {
return new Cesium.Resource({
url: KEYLESS_TERRAIN_URL,
retryCallback: policy.retryCallback,
retryAttempts: policy.retryAttempts,
});
}
export async function createKeylessTerrain() {
try {
return {
provider: await Cesium.CesiumTerrainProvider.fromUrl(
createKeylessTerrainResource(),
),
};
} catch (error) {
console.warn(
'[MapStack] Re:Earth terrain unavailable, falling back to flat ellipsoid terrain:',
error,
);
return { provider: new Cesium.EllipsoidTerrainProvider(), fallback: true };
}
}