# Terrain explorer implementation list

Started 2026-10-01. This is the tracked scope for extending the whole-map demo.
Statuses describe runnable demo support, not merely existence of an SDK function.
Current architecture and remaining delivery stages: [TERRAIN-ARCHITECTURE.md](TERRAIN-ARCHITECTURE.md).
Historical sections below record earlier limitations; the method table and final dated status take precedence.

## Delivery and verification contract

First increment: selectable local measures, physical units and fixed legends,
point inspection, actual elevation (1×) independent of mesh exaggeration,
neighbor halos, and explicit resolution/radius limits. Preserve current relief
methods and links. Verify plane slope/aspect in all cardinal directions, flat
sentinels, quadratic curvature, neighborhood statistics, live rendering and
mobile/3D behavior. Compare CPU/GPU definitions before claiming engine parity.
Hydrology must not masquerade as independently computed per-tile accumulation.

## Method list

| Family | Measures | Demo status | Execution / next step |
|---|---|---|---|
| Height | Elevation, contours, elevation bands | Implemented · WebGL2 / WebGPU | Local GPU |
| Orientation | Slope, aspect, normals, northness, eastness | Implemented · WebGL2 / WebGPU | Horn gradient, down-slope compass aspect; Folia WASM slope/aspect parity verified |
| Curvature | Laplacian, profile, plan, mean, Gaussian | Implemented · WebGL2 / WebGPU | Central Hessian; Folia WASM Laplacian/profile/plan parity verified |
| Position | TPI, standardized TPI, local relief | Implemented · WebGL2 / WebGPU | Square neighborhoods with physical radius |
| Roughness | Elevation range, standard deviation, TRI Wilson/Riley, VRM, area ratio | Implemented · WebGL2 / WebGPU | Local GPU |
| Existing lighting | Sculpted, hillshade, multidirectional, VAT, diffuse | Implemented | Existing WebGL2 shaders |
| Existing enclosure | SVF, positive openness | Implemented | Direct GPU / cached GPU / Folia horizon bins; signed GPU openness |
| Enclosure extension | Negative openness, directional SVF, directional horizons | Implemented | Separate inverted-elevation horizon cache; azimuth-weighted SVF is an adaptation |
| Landforms | TPI slope-position, multiscale TPI, geomorphons | Implemented · WebGL2 | Six position classes, nine multiscale classes, ten bounded LOS geomorphon forms; GRASS comparison breadth pending |
| Water conditioning | Depression fill/breach, depression depth | Regional fill and native least-cost breach implemented | Browser WASM fill or existing pinned Folia engine breach + explicit residual fill; cut/raise metadata |
| Drainage | Flow direction, accumulation, streams, catchments | Connected regional D8 implemented | Deterministic plateau routing and basin edge-contact provenance; catchments remain finite-domain |
| Hydrological indices | TWI, stream power, HAND | Implemented · regional WASM | Canonical Rust indices on original DEM and supplied connected D8 graph; explicit slope floor and stream threshold |
| Visibility | Viewshed, local dominance | Regional viewshed and dominance point probe implemented | Existing engine LOS kernel shared into compute/WASM; map-wide dominance remains pending |
| Exposure | Directional shelter, wind exposure | Point probe implemented | Canonical horizon-angle shelter and geometric openness proxy; no wind-speed simulation |
| Solar | Cast sun shadows, sunshine duration, direct/diffuse irradiation | Point scenario implemented | Canonical ephemeris + local horizons + explicit fluxes; map-wide layers and distant terrain pending |
| Further geometry | Principal curvatures, tangential curvature, contour vectors | Implemented · CPU/WebGL/WebGPU geometry; regional vectors | Upward graph curvature and marching-squares segments; stitching/generalization pending |

## Backend architecture

Use Folia compute kernels as reusable CPU implementations and engine recipes for
regional analysis, metadata, validation and export. The full Folia engine is not
required in every browser shading update. Existing horizon WASM/native workers
already call canonical Folia kernels.

Canonical CPU aspect, hillshade and mixed-Hessian orientation are corrected,
including the engine adapter. The native rendered terrain shader agrees on the
mixed-Hessian convention. Down-slope compass aspect is clockwise from north.
Canonical total curvature is negative Laplacian; the demo's positive Laplacian
negates it explicitly.

`terrain-analysis.wgsl` now runs styles9–30 in browser WebGPU, with explicit
WebGL2 fallback when unavailable. The plain WGSL source can be reused by native
wgpu, but this new compute shader has not been wired into or executed by a native
runtime. The existing native rendered shader passed compilation/translation and
focused curvature parity checks. Folia WASM supplies eight numerical layers:
slope, aspect, Laplacian, profile/plan curvature, TPI and Wilson/Riley TRI.
Other requested CPU/WebGPU layers explicitly fall back to WebGL2.

WGSL does not guarantee every device supports a workload; WebGL2 cannot execute
WebGPU compute kernels. Automated browser checks use software Chromium, not
phone hardware. The numerical readback benchmark measures actual supported backends on the same
tile, with synchronized output, upload, worker roundtrip and shared output encoding
reported separately. It is a readback test, not production map FPS; device metadata
distinguishes hardware and software. Physical phone timing remains unmeasured.

Analysis uses native downloaded DEM resolution; overzoom adds display pixels,
not elevation detail. Local neighborhood radii are bounded and reported with
effective ground scale. Larger regions need a separate execution path. All
first-increment analysis measures use physical elevations without the artistic
shading exaggeration. No new map-wide derivative data is precached.

Primary references: [GRASS terrain derivatives](https://grass.osgeo.org/grass-stable/manuals/r.slope.aspect.html),
[RVT visualizations](https://rvt-py.readthedocs.io/en/latest/listofvis_main.html),
[GRASS geomorphons](https://grass.osgeo.org/grass-stable/manuals/r.geomorphon.html),
[GRASS watersheds](https://grass.osgeo.org/grass-stable/manuals/r.watershed.html),
[wgpu backends](https://docs.rs/wgpu/latest/wgpu/struct.Backends.html).

## First increment (historical; superseded by stages below)

22 new layers are available in the Visualization selector's Terrain measures group.
Use `gpu-terrain.html?site=wasatch&measure=slope` (names slugged to lowercase)
or `measure=10` (catalog ID). Legends use fixed user-editable ranges; aspect is
cyclic and normals encode east/north/up RGB. Click sampling returns a raw value
from the shader at the stated source zoom. Slope and normals use Horn gradients.
Curvature uses central Hessian differences: profile and plan use negative sign;
mean and Laplacian are positive for bowls. Gaussian curvature has units 1/m².
Aspect is undefined on exactly flat terrain; northness/eastness emit neutral 0.
Neighborhood statistics use every cell in a square with ceil(radius/spacing),
capped at 16 cells in each direction, not sparse samples. TPI and standard
deviation exclude the center; local relief removes the mean including center.
VRM averages 3×3 normals with 5×5 elevation support. Area ratio is the local
planar approximation sqrt(1 + gradient²), not an integrated triangulated area.
Contours are rendered isolines, not vector features. All analysis ignores
artistic zfactor; optional hillshade darkening and 3D mesh exaggeration affect
display only. No new native/WASM derivative endpoint or WebGPU backend is claimed.

Independent acceptance checks in `check-terrain-derivatives.cjs` cover flat and
plane cases for all 22 layers, compass cardinal directions, mixed quadratic
curvature, exact neighborhood moments, and backend bypass. Live checks cover
representative layers, raw point inspection, 3D draping, mobile layout and
returning to the original relief preset. Full Folia numeric parity and terrain
classification/regional products remain pending.

Display gate strengthened after a stale-layer screenshot report: final aspect
RGB is checked against an independent hue conversion, and the actual map canvas
must change from low-chroma slope colors to high-chroma aspect colors. Pending
legends identify old tiles until the current revision has painted. Versioned
worker imports and a version handshake prevent mixing old and new code after
refresh. The original user's persistent freeze remains unconfirmed on their GPU.

## Originally approved stages (2026-10-01; stage3 superseded below)

1. Correct canonical CPU aspect/hillshade and mixed-Hessian orientation. Expose
   eight canonical numeric layers in WASM and compare their actual raw output
   with browser GPU fixtures (absolute error <=1e-4 on analytic fixtures, <=.005
   on high-elevation real f32 DEMs; no exact transcendental bit-parity claim).
2. Browser WebGPU backend using portable WGSL for styles9–30. Match raw analytic
   fixtures within .001 and final synthetic colors within 2 RGB units. Feature
   detection must retain WebGL2 when WebGPU is unavailable; actual execution
   required before marking implemented. Native wgpu reuse of the source is
   possible, but no native runtime verification is implied by browser execution.
3. Lock analysis source zoom independently of display zoom. Render derivatives
   at the chosen source zoom, then compose/crop output for display; never
   recompute gradients from resampled heights. Bound child coverage to 16 tiles
   (display minimum zoom = lock minus2). Raw sample at fixed coordinates remains
   equal when display zoom changes; source-cell scale and effective radius shown.
4. Negative openness uses signed horizons of inverted elevations; cached modes
   need a separate horizon cube. Directional sky visibility weights SVF by
   azimuth (an adaptation, not exact RVT ASVF); directional horizon display
   interpolates existing bin slopes. Flat/inverted-cone and direct/cache gates.
5. TPI slope-position classes and a separate connected2×2-tile regional
   hydrology page. Canonical Folia filling + D8 + accumulation; finite-domain
   outlets/labels. Explicit unresolved-flat counts and boundary limits. This
   prototype does not claim exact GRASS geomorphons or complete catchments.

No geographic split is tuned to test outcomes: analytic fixtures plus the
existing Huger/Wooster/Wasatch examples remain the verification domains.

## Approved stages implemented and verified

All five stages are now implemented. Stage3's first implementation composed
fixed-source results into display PNGs; the current revision instead draws each
native512px analysis tile directly. Follow display zoom selects native map tiles;
Fixed source zoom0–16 keeps analysis scale while viewing at other zooms. Snap
analysis to display zoom sets a new fixed zoom from the rounded view, capped16.
MapLibre's visual tile footprint selects the fixed level; source min/max enforce
it even with3D. PNG dimensions and elevations are unchanged. Cached native
results are reused across view changes; no PNG mosaics/crops or hard zoom-out
floor. Fine fixed zoom over a wider area can require many source tiles.
Query analysisZoom and settings export preserve the lock. Raw sampling follows
the active analysis zoom; the upstream viewer baseline has separate DEM behavior.


Negative openness traces inverted elevations separately. GPU cached mode keeps
an additional signed horizon cube; selecting native/WASM for this layer uses
explicit GPU fallback. Directional sky visibility weights existing visibility
by azimuth and strength (strength0 equals isotropic SVF); it is not exact RVT
ASVF. Directional horizon interpolates neighboring positive horizon-bin slopes.
These latter two layers can reuse GPU, native Rust or WASM caches.

TPI slope-position classes use standardized TPI thresholds ±0.5 and ±1 with a
5° flat-slope threshold. The separate `terrain-region.html` page downloads a
connected 2×2 source-tile domain, computes Folia filling/D8/accumulation once,
and recolors cached arrays. The boundary, terminal cells and unresolved paths
are explicit. This remains a finite-region prototype: no full watershed,
breaching, robust flat routing, HAND/TWI, or exact geomorphons claim.

Saved browser results: `assets/terrain-stages/results.json`. Gates:

- `check-terrain-stages.cjs`: actual Folia WASM/WebGL/WebGPU fixtures, all22 WGSL
  layers, eight CPU layers, Huger/Wooster/Wasatch samples, four-backend directional
  horizons, negative openness, landforms, native tile zoom, live/mobile/3D and connected
  drainage passed. Synthetic raw maximum errors: CPU4.85e-8, WebGPU3.82e-6;
  synthetic colors identical. Real CPU maximum error1.53e-5. The previous fixed-source version preserved its sampled slope across zoom;
  the current gate verifies native source tile zoom and512px size instead.
- `check-terrain-region.cjs`: chains, joins, cycles, invalid/outside paths and
  distinct terminal labels passed. Real region: 1,048,576 cells, 0.57% no-lower-D8
  neighbors, zero unresolved paths; this does not prove complete flat routing.
- Existing derivative and four-backend research-relief browser suites passed.
- `cargo test -p folia-compute --features terrain --lib terrain_ops::tests`:25passed.
- Engine terrain unit tests:15passed; GDAL terrain parity:10passed; focal GDAL:9passed.
- `cargo test -p folia-render --test terrain_shader_golden`:3passed, including WGSL
  and GLSL ES translation; focused native focal CPU/GPU parity:1passed.
- Engine build/check, bridge wasm32 check/release build, scoped Rust formatting,
  JavaScript syntax and diff checks passed.

The broad `cargo test -p folia-engine --tests` gate is **not green**: 1,233 library
checks passed, four failed and four were ignored. Remaining failures are unrelated
catalog JSON ordering, two table-transform error-message assertions and a
partition lock missing scene-query admission evidence. Because the library stage
failed, the broad integration stage did not execute; only the focused integration
filters above are verified. Hardware/phone performance, native execution of the
new compute WGSL, projection/nodata breadth and remaining method-list entries
still need separate work.

The saved `assets/terrain-stages/results.json` includes the updated native-tile
zoom gate. Earlier fixed-source samples above describe the previous implementation.
Native requests at display13/14/15 return source13/14/15 respectively; display17
uses source16. No additional mosaic/downsample encoding is performed.

Current native-tile browser gate passed, including fractional zoom14.7 → source15,
all existing numerical/horizon fixtures, mobile/3D and connected drainage.

Current fixed-native gate also passed source13 at display13/15/12/11.7 with
identical sampled values and spacing, fixed3D, snapping14.7 to15 and follow-mode
restoration. Earlier removed-lock notes above are historical. Direct native
rendering relies on MapLibre's documented visual tileSize rather than resampling
or changing the downloaded512px grids.

## Further stages (2026-10-02)

Scope/gates were committed in `terrain-next-protocol.json` before implementation.

1. **Native work scheduling:** two tile jobs and four shared DEM download jobs at
   once. Pending work is ranked by current map-center distance. A consumer abort
   does not interrupt a download needed elsewhere; the last consumer aborts it.
   Settings changes cancel old queued/preparation work. Already-dispatched GPU
   calls are not preempted; canceled outputs cannot paint. CPU-to-GPU follow-up
   is checked for cancellation. Native encoded cache is bounded 32 MiB/32 entries.
   The visible tile/halo estimate is conservative for pitched views. Fixed
   views above the editable budget (desktop 128 / mobile 64; choices64–512) pause
   fine analysis and show an explicit MapLibre overview, retaining the lock.
   Zooming back in or snapping resumes; no fake coarser analysis is substituted.
2. **On-device numerical benchmark:** actual WebGL2/WebGPU/Folia WASM paths;
   unsupported combinations are skipped, never mislabeled fallback. One cold
   and three warm calls use the same tile/parameters. Upload, synchronized
   calculation+readback, worker roundtrip, shared CPU colors/PNG and payload
   sizes are separate. Readback/roundtrip overlap; do not sum columns. Shared
   colors/PNG are benchmark-only overhead, not the production GPU palette pass.
   Cached or explicit fresh DEM preparation, network/decode sums, request/byte counts
   and device/renderer are recorded. Request sums overlap; preparation wall time
   is reported separately.
   JSON can be downloaded on each device. Phone WebGPU needs a trusted secure
   origin; otherwise it is explicitly unavailable. Physical phone unmeasured.
3. **Resolution controls:** source options display approximate meters/cell at
   map center. Target meters chooses the nearest supported native source zoom,
   not an interpolated grid. View at analysis zoom moves the view to the selected
   analysis; Snap analysis to display zoom does the inverse. Latitudinal scale
   changes remain explicit, with actual source-cell spacing in tile/sample info.
4. **Conditioned regional routing:** canonical Folia priority fill retained.
   A separate JS regional pass routes each exact-height plateau toward lower
   terrain, otherwise region-edge exits, otherwise a single deterministic closed
   terminal. Breadth-first parents make flat paths acyclic; topological D8
   accumulation and terminal labels are rebuilt. Terminal type and upstream
   region-edge contact are separate: an interior terminal can still have a
   truncated upstream basin. Absence of contact is not proof of external
   completeness. TWI/HAND/full external catchments remain pending.
5. **Landforms:** style 35 combines center-excluded standardized TPI in near/broad
   squares (caps 8/16 cells, broad target 3×radius), fixed thresholds and 5° slope;
   it is a documented nine-class adaptation. Style 36 uses eight LOS directions,
   inclusive circular physical radius 2–16 cells, fixed 1° threshold, ANGLEV1
   comparisons and GRASS's common-form count table. ANGLEV1 ties become flat,
   including exact planar LOS profiles. Adaptive search, skip radius, distance
   relaxation, border nulling and all 498 pattern outputs are not implemented.
   Primary references: [GRASS form lookup](https://github.com/OSGeo/grass/blob/main/raster/r.geomorphon/geom.c),
   [LOS comparisons](https://github.com/OSGeo/grass/blob/main/raster/r.geomorphon/pattern.c),
   [manual](https://grass.osgeo.org/grass-stable/manuals/r.geomorphon.html).

Saved checks and observed measurements are in `assets/terrain-next/`. Automated
software correctness and physical desktop timing are kept separate; results
state renderer metadata. Focused verification covers queue concurrency/priority,
refcount cancellation, numeric benchmark skips/finite output, plateau spills,
closed flats, boundary inflow, mass conservation, rotational/reflection patterns,
GPU class/color output, resolution controls, overview budget, mobile/3D and a
real 1048576-cell region. Exact phone performance and further method-list entries
remain future work. The earlier broad engine test failures remain unrelated;
this stage changes browser JavaScript and reused the rebuilt local horizon helper.

Physical desktop benchmark verified Apple M1 Max ANGLE Metal and Apple metal-3
WebGPU; three warm samples for one Wasatch slope tile ranged 3.2–4.6/1.0–3.3/
8.2–9.9 ms for WebGL2/WebGPU/WASM synchronized calculation+readback. These are
recorded observations, not general backend or map-FPS guarantees. Fresh network
preparation measured 9 DEM requests/1.45 MB and 362.7 mswall time. Default headless
Chromium initially selected SwiftShader; that attempt is stored separately and
is not counted as hardware evidence.


## Expansion increment (2026-10-02)

The acceptance contract was committed in `terrain-expansion-protocol.json` before
work. New reusable compute modules cover geometry/vector contours, regional
indices, visibility, solar position and exposure/local dominance. Engine viewshed
and solar adapters call/reexport their existing kernels from the shared compute
crate, preserving algorithm behavior and public engine API paths. New family
recipe registration is not implied by adding a kernel.

The whole-map catalog now has 40 layers. New IDs 37/38/39 are minimum principal,
maximum principal and tangential normal curvature, respectively, in 1/m. Bowls
are positive under the upward surface normal. Tangential curvature is undefined
on a flat surface and uses an explicit zero display sentinel. All three run in
WebGL2, WebGPU and canonical Folia WASM.

The regional page adds TWI/SPI/HAND, original-DEM observer viewsheds, a local
solar/shelter/dominance point probe and true vector segment export. TWI uses
specific contributing area = accumulation count × cell width and an explicit
positive slope floor. SPI uses actual slope and is a geometry proxy, not watts.
HAND uses original elevation differences following the conditioned graph; no
reachable stream, cycles and negative relative heights produce missing values.
Boundary provenance still applies to every regional hydrological result.

Point solar output is a user-flux scenario, not observed irradiation. Map-wide
solar/exposure/dominance, full engine registration, geomorphon breadth, regional
routing migration, distant horizons and physical-phone evidence remain tracked
in the architecture document. No claims of complete watersheds or newly measured
phone performance are made.


Native portability and conditioning follow-through: the exact browser WGSL now
executes on native wgpu/Metal. Manufactured slope and geometry raw/RGB fixtures
passed on Apple M1 Max. This proves execution of the shared source for those
fixtures, not universal GPU support or phone performance. A native engine example
adapts the existing pinned least-cost breach operation for the local demo server;
its synthetic fixture requires an actual barrier cut. The regional browser test
runs native conditioning, reports lowered/raised cells, and preserves original
height data for other products. Browser-only hosting explicitly leaves native
breaching unavailable.

Verification: compute/engine checks and WASM release build passed; 54 focused
compute tests, 25 existing engine viewshed tests and 15 solar tests passed. A stale
handle-API aspect assertion was corrected from mathematical west=180° to the
established compass west=270°; the implementation was unchanged. New geometry
shader probes, 40-layer catalog/mobile checks, regional expansion/browser export,
native breaching, and connected-routing checks passed. Broad unrelated engine
checks and physical-phone measurements were not rerun for this increment.
The pre-existing handle_ops.rs formatting differences outside the changed aspect
assertion were preserved; all other owned Rust files pass the scoped format check.

## Unified explorer increment · 2026-10-03

Regional products are now discoverable in the main layer browser. The catalog
contains 40 existing local layers and 11 regional layers/tools. Regional analysis
follows map navigation automatically and can be locked; display navigation stays
independent of fixed source resolution. The regional worker and Rust kernels
are unchanged. Viewshed and point exposure respond to observer placement/drag.
Native breaching is a hydrology conditioning option. The finite domain outline
and outside-terrain limitations remain explicit; automatic navigation does not
make local catchments complete. See `TERRAIN-UX-PROTOCOL.md`.

Verification: 51-entry catalog/preview, keyboard and mobile checks passed. Live
regional checks passed automatic HAND, cached layer switching, follow/lock, fixed
source detail, native four-tile pixels and actual displayed color, click/drag
viewsheds (including automatic coverage relocation), repeated 3D transitions,
cached point exposure, GeoJSON contour export, native breaching and retained
results after download failure. An injected local protocol error verifies safe
source recreation. These are headless Chromium/SwiftShader checks; no physical
phone or hardware-performance result is inferred. Saved evidence:
`assets/terrain-expansion/unified-ux-results.json`.
