/** * Terrain (spec: "Terrain") — 3D elevation surface + per-layer draping, * declared as `terrain*` attributes on (the basemap=/lighting= * scene-attribute precedent: free undo/redo, live MutationObserver * switching). * * * * * The seventh plugin registry (`OmMap.registerTerrain`) resolves preset * names; a `{z}/{x}/{y}` URL is bring-your-own DEM (requires * terrain-decoder). Geographic layers DRAPE onto the surface by default; * a per-layer `terrain="drape|offset|off"` attribute overrides (3D-model * layers default to `offset` — models sit ON terrain, not painted onto * it). * * Terrain REPLACES an active basemap: a flat MapLibre canvas sits at sea * level and visibly desyncs from a raised surface (true coexistence is * the interleaved-compositing TODO). RuntimeCore suppresses the basemap * while terrain is active and restores it when terrain turns off. * * `applyTerrain` is PURE (unit-testable without a GPU — headless never * reaches the deck handoff): it computes each layer's terrain patches and * the terrain-surface layer props; RuntimeCore feeds them to buildLayers. */ import type { Layer } from "@deck.gl/core"; import type { LayerIR } from "./ir"; /** RGB DEM decoder: height = offset + R·rScaler + G·gScaler + B·bScaler. */ export interface ElevationDecoder { rScaler: number; gScaler: number; bScaler: number; offset: number; } export declare const DECODERS: Readonly>; export interface TerrainPreset { /** `{z}/{x}/{y}` DEM tile URL template (may carry a `{key}` placeholder for keyed providers). */ elevationData: string; /** Decoder name ("terrarium" / "mapbox-rgb") or explicit scalers. */ decoder: keyof typeof DECODERS | ElevationDecoder; /** * The provider's REAL tileset cap — deck over-samples past it for closer * cameras; a too-high cap requests tiles that 404 and blanks the terrain. */ maxZoom: number; /** Optional draped imagery `{z}/{x}/{y}` URL template. */ texture?: string; /** Display name (switcher UIs). */ label?: string; /** Which key `{key}` substitutes — the basemap-registry convention. */ requiresKey?: "maptiler"; } export declare function registerTerrain(name: string, preset: TerrainPreset): void; export declare function getTerrain(name: string): TerrainPreset | undefined; export declare function getAllTerrains(): ReadonlyMap; export interface TerrainIR { elevationData: string; elevationDecoder: ElevationDecoder; maxZoom: number; /** Multiplies decoded DEM heights (1 = true relief). */ exaggeration: number; texture?: string; } export interface ResolvedTerrain { ir: TerrainIR | null; /** Resolution problem (unknown preset / missing key / missing decoder) — the caller decides how loudly to fail; `ir` is null when unresolvable. */ error?: string; } /** Is this `terrain` value a bring-your-own DEM URL rather than a preset name? (The basemap isStyleUrl convention.) */ export declare function isDemUrl(raw: string): boolean; /** * `terrain*` attributes → TerrainIR. Returns `{ir: null}` when `terrain` * is absent/"off"; `{ir: null, error}` when set but unresolvable (unknown * preset, keyless keyed preset, raw URL without a decoder) — terrain * renders NOTHING in that case rather than a wrong surface, and validation * carries the loud version of the same message. */ export declare function parseTerrainAttrs(getAttr: (name: string) => string | null, maptilerKey?: string): ResolvedTerrain; export declare const TERRAIN_LAYER_ID = "__onlymapjs-terrain"; export type LayerTerrainMode = "drape" | "offset" | "off"; export declare const LAYER_TERRAIN_MODES: readonly ["drape", "offset", "off"]; export declare function defaultTerrainMode(type: string): LayerTerrainMode; export interface TerrainLayerPatch { /** * The deck-layer id to construct with. Suffixed `#t` while * terrain is active: adding/removing an extension on a LIVE layer * recompiles shaders and corrupts attribute state (the DataFilterExtension * birth-mount rule), and a stale TerrainExtension vertical offset survives * a terrain toggle on a stable id — a fresh id forces clean teardown + * rebuild. RuntimeCore maps these generated ids back to authored ids for * picks without guessing from the string shape. */ deckId: string; /** Extra constructor props: extensions (APPENDED to existing — DataFilterExtension stays mounted) + terrainDrawMode. Empty for mode "off". */ extraProps: Record; } /** * Per-layer terrain patches, PURE. `terrainActive` false → identity patches * (stable ids, no props). Runtime-internal layers (trace temps, draw * preview, tooltip plumbing) default OFF — they're screen-space/transient — * but an internal layer that explicitly sets `terrain` (spec: "Cut/fill * volume measurement": the shared draw-preview and measure's committed- * geometry/label layers opt into "drape" so their 2D lng/lat vertices sit ON * the terrain surface instead of at literal sea level, invisible below/ * behind a raised mesh) wins over that default, same as an authored layer. */ export declare function applyTerrain(irs: readonly LayerIR[], terrainActive: boolean, generation: number): Map; /** * The terrain-surface layer itself. `operation: "terrain+draw"` makes it * double as the elevation surface extension-carrying layers drape onto; * `pickable: "3d"` runs the depth pick pass so click/hover coordinates * unproject at the SURFACE's depth instead of z=0 sea level (without it, * tilted-view picks/drawn vertices land behind the cursor). Exempt from * license gates by the `__onlymapjs-` prefix. */ export declare function buildTerrainLayer(ir: TerrainIR): Layer;