/** * Bulk terrain heightfield (spec item C2 / "Elevation profile" — the shared * infrastructure both reference, "sampling shares C2's bulk heightfield, not * per-point elevationAt()"). `terrain-sample.ts`'s `sampleTerrainElevation` * is one fetch per point — fine for volume mode's handful of footprint * vertices, but the wrong shape for a profile's tens-to-hundreds of samples * along a line or the volumetrics grid's 10⁵–10⁶ cells: nearby points share * DEM tiles, so fetching (and decoding) each tile once and reading every * point out of it in memory is both fewer round-trips and what the per-cell * volume integrator (volumetrics.ts) builds on. * * This module is the DOM-TOUCHING half (fetch + canvas decode); the pixel * math — bilinear sampling with tile-seam handling — lives in * volumetrics.ts's `sampleBilinear`, which is pure and shared with the * volumetrics worker (decoded tiles postMessage across; canvases don't). */ import type { LngLat } from "./geodesy"; import type { TerrainIR } from "./terrain"; import { type HeightfieldTiles } from "./volumetrics"; /** A resolved, in-memory set of DEM tiles — `elevationAt` never touches the network. */ export interface TerrainHeightfield { /** Ground elevation (meters) at `lngLat` — bilinear-interpolated, seam-correct — or `null` if no covering tile loaded/decoded. Ignores `terrain.exaggeration`, same as `sampleTerrainElevation`. */ elevationAt(lngLat: LngLat): number | null; /** The raw decoded tile set — the transferable form volumetrics.ts (and its worker) consume directly. */ tiles: HeightfieldTiles; } /** * Resolves every DEM tile `points` touches — INCLUDING the neighbor tiles a * bilinear read at a tile-edge point spills into (the seam case: a point * within half a pixel of a tile border reads up to 3 pixels from adjacent * tiles; without pre-loading those, edge points would silently degrade to * fewer-corner interpolation) — fetches + decodes each ONE TIME * (deduplicated, in parallel), and returns a synchronous bilinear lookup. A * tile that fails to load makes points inside it resolve to `null` (same * "treat as no terrain" contract as `sampleTerrainElevation`) without * affecting other tiles. */ export declare function loadHeightfield(points: LngLat[], terrain: TerrainIR): Promise; /** * Resolves the DEM tile set COVERING a lng/lat bounding box — the * volumetrics grid's shape of demand ("every cell inside the polygon's * bbox"), which a point list can't express without enumerating 10⁶ cells. * Starts at the provider's `maxZoom` and steps down until the cover fits * `MAX_BOUNDS_TILES` — the returned `tiles.zoom` records what was actually * used, and the integrator derives its GSD (and error model) from that, so * a coarsened cover is REPORTED coarser, never silently pretended finer. */ export declare function loadHeightfieldForBounds(bounds: { west: number; south: number; east: number; north: number; }, terrain: TerrainIR): Promise;