Dev
Tech Stack
The libraries and services this app is built on
- MapLibre GL v5 via React Map GL — the map renderer everything else layers on top of
- nuqs — shareable URL query-string state (viewport, viz toggles, sources)
- jotai — local persistence (
atomWithStorage) for API keys and UI collapse state - shadcn/ui on Base UI — component primitives (dialogs, dropdowns, tooltips, tabs) and styling, plus Tailwind CSS v4
- onthegomap/maplibre-contour — contour lines
- geomatico/maplibre-cog-protocol — direct in-browser Cloud-Optimized GeoTIFF streaming
- geotiff.js — parses GeoTIFF responses client-side (WMS float32 DEM bridge, COG metadata)
- Turf.js — geospatial calculations (bounds, truncation, geometry helpers)
- chroma.js — color-ramp interpolation behind every hypsometric/derivative-mode color ramp
- Terra Draw — the Drawing tool's sketch/measure primitives, via its own MapLibre GL adapter
- @esri/wayback-core — the SDK behind ESRI Wayback's release catalog and per-tile capture dates
- Coachmark — the guided product tour (spotlight, step popovers, branching paths)
- Sugar High — lightweight syntax highlighting (Settings dialog code snippets, GDAL export commands)
See Setup for how these fit together (state architecture, custom MapLibre protocols, no-backend design).
MapLibre 6
The app runs MapLibre GL JS 6 (branch maplibre-v6, ported 2026-09-24). What changed for this codebase, and what did not:
- Worker. Six is ESM-only and no longer finds its own worker under a bundler, so
main.tsxpoints it at Vite's?worker&urlchunk once at startup. It is the same worker as before; only the setup changed. - Render-to-texture. Draped layers, which is every custom raster protocol in this app, go through terrain's render-to-texture path. Six rebuilt that cache for a higher hit rate, reuses the DEM texture, skips sources that never drape, and samples the result through mipmaps at high pitch. None of it needed code changes here; it is the reason most modes feel faster.
map.transformis gone.Mapcomposes aCameranow, and the live transform ismap._camera.transform.lib/maplibre-internals.tswraps that for the camera sync, the live GL layers and the bounds fence, and puts a read-onlymap.transformback on each map for plugins still written against 5.- Elevation. Six eases the camera target's elevation itself and re-solves zoom and centre at the end of a gesture. The app's own settle now defers to that; see camera sync.