UNPKG

maplibre-gl

Version:

BSD licensed community fork of mapbox-gl, a WebGL interactive maps library

93 lines (86 loc) • 4.97 kB
import type {Terrain} from '../render/terrain.ts'; import type {ITransform} from '../geo/transform_interface.ts'; /** Who holds the center elevation: a gesture, or an animation with `freezeElevation`. */ export type ElevationHolder = 'gesture' | 'animation'; /** * A hold on the center elevation, see {@link Camera.holdElevation}: one that started where no DEM data under a center * clamped to the ground had loaded, or whose terrain changed to one without DEM data there, waits for it and follows * the terrain drawn under the center until the tile there has its own. It holds no transform: a gesture's takes on the * terrain change, on the requested camera state its frames read; an animation's on its next frame, on the transform it * edits, which a projection change does not replace, or at its end when nothing ran in between. */ export class ElevationHold { /** * Where the hold stands with the DEM data under the center: * - `loaded`: the tile under the center had its DEM data when the hold started, so the hold keeps its elevation. * - `awaiting`: no DEM data under the center yet, from the start or since a terrain change left none there; the * hold follows the terrain drawn there, see {@link take}. * - `taken`: the hold waited and then took the elevation of the tile's own DEM data. Unlike `loaded`, the elevation * came during the hold, so the end keeps the zoom where that data is missing again, see * {@link Camera.putCenterBackOnTerrain}. */ private _dem: 'loaded' | 'awaiting' | 'taken'; /** * While {@link Camera._keepCameraAboveTerrain} has lifted the held elevation: the elevation held and how far above * it the camera was lifted, so later frames can lower it again as the terrain allows and tell the lifted elevation * from one a take set anew, which carries no lift; null while nothing is lifted. */ lift: {heldElevation: number; height: number} | null = null; /** * Whether {@link Camera._keepCameraAboveTerrain} keeps the drawn terrain below where the center would sit at maxZoom, * see {@link Camera._terrainHeightAboveMaxZoomForCenter}: from the start of a hold that starts with the terrain below * that point, as on the ground at rest, or from the first frame the terrain is below it. A hold that starts with the * terrain above it, as behind a crest, leaves the terrain there. */ keepsTerrainBelowMaxZoomForCenter: boolean; private _terrainChanged = false; /** * @param holder - who holds the elevation * @param startedWithoutDem - whether the hold started without DEM data under the center, and so waits for it * @param terrainBelowMaxZoomForCenter - whether the drawn terrain is below where the center would sit at maxZoom when * the hold starts, see {@link Camera._terrainHeightAboveMaxZoomForCenter} */ constructor(readonly holder: ElevationHolder, startedWithoutDem: boolean, terrainBelowMaxZoomForCenter: boolean) { this._dem = startedWithoutDem ? 'awaiting' : 'loaded'; this.keepsTerrainBelowMaxZoomForCenter = terrainBelowMaxZoomForCenter; } /** Whether the hold waited for DEM data under the center and took the data of the tile there. */ get tookDem(): boolean { return this._dem === 'taken'; } /** Asks the next {@link take} to check for DEM data under the center, after the terrain changed. */ noteTerrainChange(): void { this._terrainChanged = true; } /** * While the hold waits, takes the elevation the terrain draws under the center: a loaded parent tile's DEM data, * drawn in place of the tile's own until that lands, and then the tile's own, which ends the wait. After a terrain * change that leaves no DEM data under the center, puts the center back at the 0 the terrain gives there and * waits again. * @param tr - the transform the gesture or animation edits * @param terrain - the terrain under it * @returns whether it changed the elevation */ take(tr: ITransform, terrain: Terrain): boolean { let changed = false; if (this._terrainChanged) { this._terrainChanged = false; if (terrain.getElevationAndDEMStateForLngLat(tr.center, tr).demState !== 'loaded') { tr.setElevation(0); this._dem = 'awaiting'; changed = true; } } if (this._dem !== 'awaiting') return changed; const elevation = terrain.getDrawnElevationForLngLat(tr.center, true); if (elevation !== undefined) { tr.setElevation(elevation); this._dem = 'taken'; return true; } const drawnElevation = terrain.getDrawnElevationForLngLat(tr.center); if (drawnElevation === undefined || drawnElevation === tr.elevation) return changed; tr.setElevation(drawnElevation); return true; } }