State reference (URL & storage)
Every URL parameter the app reads, generated from the source at build time — state parameters mirrored by nuqs, instruction parameters read once on load, and the settings kept in localStorage
The tables on this page are generated from the app's source by docs/scripts/build-url-params.mjs, run before every docs build, so the deployed pages cannot drift (the tracked copies in git are refreshed by hand, pnpm run url-params, and may lag between releases). For how to compose a link or an iframe, see Embedding & URL parameters; for an interactive form over the same parameters, see URL as an API.
State parameters
Read and written by nuqs from QUERY_STATE_PARSERS in components/TerrainViewer.tsx. Booleans are true/false, lists are comma-separated, colours are hex, and a parameter at its default is omitted from the address bar. In the URL the terrain source keys are terrainSourceA…terrainSourceH; the same fields are sourceA…sourceH inside the code and inside a project preset's initialState (see lib/url-keys.ts), and the old URL spelling is still accepted.
| Parameter | Type | Default | Note |
|---|---|---|---|
| project | string | "" | Embed/project convenience params: `project` looks up a named preset in lib/projects.json (see lib/project-config.ts); terrainUrl/basemapUrl let an embedder point straight at a raw tile/COG URL withou… |
| terrainUrl | string | "" | |
| basemapUrl | string | "" | |
| terrainType | string | "" | Explicit source type for terrainUrl/basemapUrl when it's a raw URL (not an existing source id) — type can't always be inferred from the URL shape alone (e.g. |
| basemapType | string | "" | |
| viewMode | one of 2d | globe | 3d | "3d" | |
| appMode | one of terrain | historical | "terrain" | Which sidebar layout the ModePicker (TerrainControlPanel.tsx) shows — "historical" strips it down to just basemap browsing. |
| splitStyle | one of off | overlay | side-by-side | "off" | Master split control — replaces the old plain splitScreen boolean. |
| gridLayout | one of 2x1 | 3x1 | 4x1 | 1x2 | 2x2 | 3x2 | 4x2 | "2x1" | Only meaningful once splitStyle !== "off"; "overlay" always behaves as "2x1" regardless of this value (see the splitContainer render below). |
| splitBlendMode | string | "difference" | "overlay"-only: pane B's CSS mix-blend-mode + opacity against pane A. |
| splitBlendModeEnabled | boolean | true | Lets a picked blend mode be toggled off (falls back to plain "normal") without losing/resetting it — a quick way to flip back and forth between a blended and a plain overlay comparison for the same p… |
| overlayOpacity | number | 1.0 | |
| splitRatio | number | 0.5 | Split gutter position (overlay clip boundary / side-by-side divider) as a 0-1 ratio of the available width. |
| matchColorsToA | boolean | false | "overlay"-only: "Match Colors (View A as reference)" — live-samples both panes' CURRENTLY RENDERED canvases (whatever tiles are actually loaded in the viewport, re-sampled at most once/second) and re… |
| matchColorsColorSpace | string | "rgb" | Which color space the matching above is computed/applied in — "rgb" (default) is a live CSS filter; the others are a real per-pixel JS conversion, capped to a low working resolution (see HistogramMat… |
| showCaptureDatePill | one of auto | off | date | source-date" as const | "auto" | Compare and Blend's "Show Capture Date" toggle — a small per-view pill for whichever views are actually on a dated historical source. |
| terrainSourceC | string | "aws" | C-H only ever matter for gridLayout "2x2"/"3x1"/"3x2"/"4x1"/"4x2" — same shape as sourceA/B, just extra slots so bigger grids need no schema change. |
| terrainSourceD | string | "mapterhorn" | |
| terrainSourceE | string | "maptiler" | |
| terrainSourceF | string | "aws" | |
| terrainSourceG | string | "mapterhorn" | |
| terrainSourceH | string | "maptiler" | |
| basemapPerView | boolean | true | |
| basemapSourceA | string | "esri" | |
| basemapSourceB | string | "google" | |
| basemapSourceC | string | "esri" | |
| basemapSourceD | string | "google" | |
| basemapSourceE | string | "esri" | |
| basemapSourceF | string | "google" | |
| basemapSourceG | string | "esri" | |
| basemapSourceH | string | "google" | |
| overlayBasemapIds | list of string (comma-separated) | [] | 'overlay'-role custom basemap sources currently stacked on top of the active basemap (see basemap-byod-section.tsx's checkbox list) — shared across A/B, only meaningful in split-or-radio basemap mode… |
| date | integer | 0 | The ONE scrubbed date (epoch ms) for whichever concrete historical source is active on this side (historicalActiveSource(A/B) below) — Wayback/HLS/GE-Historical/Planet/EOX-S2/Bing all share this sing… |
| dateA | integer | 0 | |
| dateB | integer | 0 | |
| dateC | integer | 0 | |
| dateD | integer | 0 | |
| dateE | integer | 0 | |
| dateF | integer | 0 | |
| dateG | integer | 0 | |
| dateH | integer | 0 | |
| historicalActiveSource | one of wayback | hls | ge-historical | planet | eox-s2" as const | "wayback" | Which concrete underlying source (wayback/hls/ge-historical/planet/ eox-s2) actually renders when basemapSource(A/B) === "historical" — the sidebar only ever exposes one combined "Historical Imagery"… |
| historicalActiveSourceA | one of wayback | hls | ge-historical | planet | eox-s2" as const | "wayback" | |
| historicalActiveSourceB | one of wayback | hls | ge-historical | planet | eox-s2" as const | "wayback" | |
| historicalActiveSourceC | one of wayback | hls | ge-historical | planet | eox-s2" as const | "wayback" | |
| historicalActiveSourceD | one of wayback | hls | ge-historical | planet | eox-s2" as const | "wayback" | |
| historicalActiveSourceE | one of wayback | hls | ge-historical | planet | eox-s2" as const | "wayback" | |
| historicalActiveSourceF | one of wayback | hls | ge-historical | planet | eox-s2" as const | "wayback" | |
| historicalActiveSourceG | one of wayback | hls | ge-historical | planet | eox-s2" as const | "wayback" | |
| historicalActiveSourceH | one of wayback | hls | ge-historical | planet | eox-s2" as const | "wayback" | |
| timelineSources | list of string (comma-separated) | ["wayback", "ge-historical", "bing"] | Which historical sources' ticks are aggregated onto the shared timeline (pill toggles in historical-timeline-panel.tsx) — independent of which single source is actually "active"/rendered on the map. |
| timelineSourcesA | list of string (comma-separated) | ["wayback", "ge-historical", "bing"] | Per-side variants of timelineSources above — only meaningful when both basemapPerView AND splitStyle!=="off" are on (dualMode) AND the timeline's sync toggle is off, letting each active view aggregat… |
| timelineSourcesB | list of string (comma-separated) | ["wayback", "ge-historical", "bing"] | |
| timelineSourcesC | list of string (comma-separated) | ["wayback", "ge-historical", "bing"] | |
| timelineSourcesD | list of string (comma-separated) | ["wayback", "ge-historical", "bing"] | |
| timelineSourcesE | list of string (comma-separated) | ["wayback", "ge-historical", "bing"] | |
| timelineSourcesF | list of string (comma-separated) | ["wayback", "ge-historical", "bing"] | |
| timelineSourcesG | list of string (comma-separated) | ["wayback", "ge-historical", "bing"] | |
| timelineSourcesH | list of string (comma-separated) | ["wayback", "ge-historical", "bing"] | |
| historicalTimelineCollapsed | boolean | false | Whether the historical timeline's full bar (header + track) is collapsed down to just the small floating clock-icon toggle button. |
| sidebarCollapsed | boolean | false | The side panel, folded. |
| coverageOverlays | list of string (comma-separated) | Coverage footprints drawn on the map (Source Info -> Coverage overlays). | |
| drawingUrl | list of string (comma-separated) | [] | Remote vector data in the Drawing tool, one layer per URL (comma- separated; nuqs encodes commas inside an item). |
| historicalControlsExpanded | boolean | true | Whether the timeline's title/source-pills/resolution-chips header row is shown (true) or the panel is in its minimal, track-only mode with just a small floating cog+collapse chip (false). |
| resolutionClasses | list of string (comma-separated) | ["vhr", "medium"] | Resolution-class filter alongside the source pills above — "vhr" (Wayback/GE Historical/Bing, sub-meter-ish) vs "medium" (HLS's 10-30m Landsat/Sentinel-2, Planet's ~4.7m monthly mosaic) — see SOURCE_… |
| colorRamp | string | "mby" | colorRamp: parseAsString.withDefault("mby"), |
| customStops | ramp-stops | DEFAULT_SLOPE_CUSTOM_STOPS | |
| customStopsDiscrete | boolean | false | |
| showHillshade | boolean | true | Native MapLibre Hillshade — its own independent viz mode, entirely separate from "Lighting Effects" (Matcap/Phong) below. |
| hillshadeOpacity | number | 1.0 | |
| showLightingEffects | boolean | false | "Lighting Effects" viz mode — master toggle/opacity, housing two sub-modes: Matcap first, Phong second (see lighting-effects-options-section.tsx). |
| lightingEffectsOpacity | number | 1.0 | |
| showMatcap | boolean | false | "Matcap" sub-mode (lib/matcap-protocol.ts) — a plain raster overlay (draped over 3D terrain the same automatic way the raster basemap is) that looks up color from a material-capture image using the s… |
| matcapOpacity | number | 1.0 | |
| matcapTextureId | string | DEFAULT_MATCAP_ID | |
| matcapRotationDeg | number | 0 | "Sphere Rotation" — spins the matcap lookup independently of the map's own bearing (a raster tile is baked once per z/x/y, so it can't track live bearing the way a real-time GPU shader could). |
| matcapRenderer | one of raster | live" as const | "live" | "raster" (default): lib/matcap-protocol.ts's plain raster-tile pipeline — drapes correctly over 3D terrain exaggeration AND globe, but every rotation/exaggeration change costs a real tile refetch. |
| matcapLightRelativeToCamera | boolean | true | "Light Anchor", ported from Phong's phongLightRelativeToCamera — only meaningful in "live" (2D Fast): off (Absolute) samples the material by the tile-space normal, pinned to compass directions and id… |
| showPhong | boolean | true | "Phong" sub-mode (lib/phong-protocol.ts) — a plain raster overlay doing real ambient+diffuse+specular shading from a compass-fixed light (state.illuminationDir/illuminationAlt below — the same fields… |
| phongOpacity | number | 1.0 | |
| phongDiffuseStrength | number | 0.8 | |
| phongSpecularStrength | number | 0.2 | |
| phongLightRelativeToCamera | boolean | true | Off (default — briefly flipped to Camera on 2026-08-20, reverted the same day on the user's follow-up): illuminationDir is a compass azimuth, fixed to the world, matching maplibre's own hillshade ill… |
| phongRenderer | one of raster | live" as const | "live" | "raster" (default): lib/phong-protocol.ts's plain raster-tile pipeline — drapes correctly over 3D terrain exaggeration AND globe, but every light/strength/exaggeration change costs a real tile refetc… |
| showShadows | boolean | false | "Shadows" sub-mode (lib/shadow-protocol.ts) — a plain binary raster mask: for each pixel, marches toward the sun's actual azimuth (state.illuminationDir/illuminationAlt below — same shared light as H… |
| shadowOpacity | number | 0.6 | |
| shadowRadiusPx | number | 32 | "Search Radius" — same convention as SVF/Openness: how many same-zoom pixels the single ray marches outward looking for an obstruction. |
| lightUseDatetime | boolean | false | "Datetime-based" light: when on, illuminationDir/illuminationAlt are driven from a physically-plausible sun position (see lib/solar-position.ts) computed from the viewport-center lat/lng + these day-… |
| lightTimeMode | one of local | utc" as const | "local" | Which clock convention lightTimeOfDay is expressed in — "local": the real civil wall-clock time at the viewport lat/lng, including that location's actual DST rules (see lib/timezone.ts); "utc": UTC d… |
| showColorRelief | boolean | false | |
| colorReliefOpacity | number | 0.35 | |
| showTerrainAnalysis | boolean | false | Master toggles for what used to be one merged "Slope and More" viz mode, now split into Terrain Analysis (surface derivatives + neighborhood statistics: Slope/Aspect/Curvature/Det Hessian/Blobness/TP… |
| terrainAnalysisOpacity | number | 1.0 | Master opacity for Terrain Analysis — composites (multiplies) with each sub-mode's own opacity below, rather than replacing it. |
| showReliefVisualization | boolean | false | |
| reliefVisualizationOpacity | number | 1.0 | |
| showSlope | boolean | true | |
| slopeOpacity | number | 1.0 | |
| slopeColorRamp | string | "slope-plantopo" | |
| slopeSourceMode | one of plantopo | client | "client" | |
| slopeMinDegrees | number | 0 | |
| slopeMaxDegrees | number | 55 | |
| slopeInvertColorRamp | boolean | false | |
| slopeCustomStops | ramp-stops | DEFAULT_SLOPE_CUSTOM_STOPS | Only read when slopeColorRamp === "custom" — see computeColorReliefPaint's dedicated branch for that ramp id in MapLayers.tsx. |
| slopeCustomStopsDiscrete | boolean | false | When the custom ramp is selected, render its stops as hard discrete bands (each color holds until the next stop) instead of a continuous gradient. |
| showAspect | boolean | false | |
| aspectOpacity | number | 0.5 | |
| aspectColorRamp | string | "aspect-compass" | |
| aspectMinDegrees | number | 0 | |
| aspectMaxDegrees | number | 360 | |
| aspectShiftDegrees | number | 0 | |
| aspectInvertColorRamp | boolean | false | |
| aspectCustomStops | ramp-stops | DEFAULT_SLOPE_CUSTOM_STOPS | |
| aspectCustomStopsDiscrete | boolean | false | |
| showTri | boolean | false | |
| triOpacity | number | 1.0 | |
| triColorRamp | string | "tri-default" | |
| triMin | number | 0 | |
| triMax | number | 50 | |
| triInvertColorRamp | boolean | false | |
| triCustomStops | ramp-stops | DEFAULT_SLOPE_CUSTOM_STOPS | |
| triCustomStopsDiscrete | boolean | false | |
| showCurvature | boolean | false | |
| curvatureOpacity | number | 1.0 | |
| curvatureMode | one of combined | profile | plan | det-hessian | casorati | shape-index | "combined" | |
| curvatureColorRamp | string | "curvature-diverging" | |
| curvatureMin | number | -20 | |
| curvatureMax | number | 20 | |
| curvatureInvertColorRamp | boolean | false | |
| curvatureSymmetric | boolean | true | |
| curvatureCustomStops | ramp-stops | DEFAULT_SLOPE_CUSTOM_STOPS | |
| curvatureCustomStopsDiscrete | boolean | false | |
| showTpi | boolean | false | |
| tpiOpacity | number | 1.0 | |
| tpiColorRamp | string | "tpi-diverging" | |
| tpiMin | number | -20 | |
| tpiMax | number | 20 | |
| tpiInvertColorRamp | boolean | false | |
| tpiSymmetric | boolean | true | |
| tpiCustomStops | ramp-stops | DEFAULT_SLOPE_CUSTOM_STOPS | |
| tpiCustomStopsDiscrete | boolean | false | |
| showLrm | boolean | true | |
| lrmOpacity | number | 1.0 | |
| lrmColorRamp | string | "lrm-diverging" | |
| lrmMin | number | -20 | |
| lrmMax | number | 20 | |
| lrmInvertColorRamp | boolean | false | |
| lrmSymmetric | boolean | true | |
| lrmRadius | number | 16 | |
| lrmCustomStops | ramp-stops | DEFAULT_SLOPE_CUSTOM_STOPS | |
| lrmCustomStopsDiscrete | boolean | false | |
| showRoughness | boolean | false | |
| roughnessOpacity | number | 1.0 | |
| roughnessColorRamp | string | "roughness-default" | |
| roughnessMin | number | 0 | |
| roughnessMax | number | 50 | |
| roughnessInvertColorRamp | boolean | false | |
| roughnessCustomStops | ramp-stops | DEFAULT_SLOPE_CUSTOM_STOPS | |
| roughnessCustomStopsDiscrete | boolean | false | |
| showShapeIndex | boolean | false | |
| shapeIndexOpacity | number | 1.0 | |
| shapeIndexColorRamp | string | "custom" | |
| shapeIndexMin | number | -1 | |
| shapeIndexMax | number | 1 | |
| shapeIndexInvertColorRamp | boolean | false | |
| shapeIndexSymmetric | boolean | true | |
| shapeIndexCustomStops | ramp-stops | DEFAULT_SHAPE_INDEX_CUSTOM_STOPS | |
| shapeIndexCustomStopsDiscrete | boolean | true | |
| showBlobness | boolean | false | |
| blobnessOpacity | number | 1.0 | |
| blobnessColorRamp | string | "blobness-default" | |
| blobnessMin | number | 0 | |
| blobnessMax | number | 50 | |
| blobnessInvertColorRamp | boolean | false | |
| blobnessCustomStops | ramp-stops | DEFAULT_SLOPE_CUSTOM_STOPS | |
| blobnessCustomStopsDiscrete | boolean | false | |
| showEigenRatio | boolean | false | |
| eigenRatioOpacity | number | 1.0 | |
| eigenRatioColorRamp | string | "eigen-ratio-default" | |
| eigenRatioMin | number | 0 | |
| eigenRatioMax | number | 100 | |
| eigenRatioInvertColorRamp | boolean | false | |
| eigenRatioCustomStops | ramp-stops | DEFAULT_SLOPE_CUSTOM_STOPS | |
| eigenRatioCustomStopsDiscrete | boolean | false | |
| showOrientation | boolean | false | |
| orientationOpacity | number | 1.0 | |
| orientationColorRamp | string | "orientation-default" | |
| orientationMin | number | 0 | |
| orientationMax | number | 180 | |
| orientationInvertColorRamp | boolean | false | |
| orientationCustomStops | ramp-stops | DEFAULT_SLOPE_CUSTOM_STOPS | |
| orientationCustomStopsDiscrete | boolean | false | |
| showSvf | boolean | false | |
| svfOpacity | number | 1.0 | |
| svfColorRamp | string | "svf-default" | |
| svfMin | number | 0 | |
| svfMax | number | 100 | |
| svfInvertColorRamp | boolean | false | |
| svfRadius | number | 8 | |
| svfPrecision | one of precise | fast | "fast" | |
| svfCustomStops | ramp-stops | DEFAULT_SLOPE_CUSTOM_STOPS | |
| svfCustomStopsDiscrete | boolean | false | |
| showOpenness | boolean | false | |
| opennessOpacity | number | 1.0 | |
| opennessColorRamp | string | "openness-default" | |
| opennessMin | number | -15 | |
| opennessMax | number | 15 | |
| opennessInvertColorRamp | boolean | false | |
| opennessSymmetric | boolean | true | |
| opennessRadius | number | 8 | |
| opennessMode | one of positive | negative | "positive" | |
| opennessPrecision | one of precise | fast | "fast" | |
| opennessCustomStops | ramp-stops | DEFAULT_SLOPE_CUSTOM_STOPS | |
| opennessCustomStopsDiscrete | boolean | false | |
| showLocalDominance | boolean | false | Local Dominance (Hesse 2016) — Relief Visualization mode, see lib/local-dominance-protocol.ts. |
| localDominanceOpacity | number | 1.0 | |
| localDominanceColorRamp | string | "local-dominance-default" | |
| localDominanceMin | number | -5 | |
| localDominanceMax | number | 15 | |
| localDominanceInvertColorRamp | boolean | false | |
| localDominanceSymmetric | boolean | false | |
| localDominanceMinRadius | number | 8 | |
| localDominanceMaxRadius | number | 32 | |
| localDominanceCustomStops | ramp-stops | DEFAULT_SLOPE_CUSTOM_STOPS | |
| localDominanceCustomStopsDiscrete | boolean | false | |
| showPlaneSlicer | boolean | false | Plane Slicer — Tools: Elevation Picker sub-section. |
| planeSlicerReferenceMode | one of absolute | lrm | "absolute" | |
| planeSlicerValue | number | 0 | Absolute and LRM keep independent threshold values — the two reference frames have wildly different natural ranges (metres of real elevation vs. |
| planeSlicerValueLrm | number | 0 | |
| planeSlicerSide | one of above | below | "below" | |
| planeSlicerColor | string | "#3388ff" | |
| planeSlicerOpacity | number | 0.6 | |
| elevationPickerReferenceMode | one of absolute | lrm | "absolute" | Same Absolute/LRM reference as Plane Slicer, for the Elevation Picker's point/profile sampling itself (lib/elevation-query.ts) — "LRM" here has no 3D-mesh equivalent (map.queryTerrainElevation() only… |
| tellsBeta | boolean | false | Experimental — opt-in via Settings (or ?tellsBeta=true directly) so it doesn't clutter Visualization Modes for everyone by default. |
| sunShadowBeta | boolean | true | Same opt-in-beta gate as tellsBeta above, for Tools: Sun Shadow Calculator. |
| historicalBeta | boolean | true | Same opt-in-beta gate as tellsBeta above, for the historical-imagery basemaps (Wayback/HLS/GE Historical/Planet) + bottom timeline panel. |
| showTellsDetector | boolean | false | Master on/off (Visualization Modes' "Tells (Mound Detector)" checkbox) — gates the sidebar's Mound Candidates section as well as the map layer. |
| tellsMarkersVisible | boolean | true | Mound Candidates section's own "Show mound candidates" checkbox — a pure paint-visibility toggle for markers already being computed, independent of showTellsDetector so it can't also collapse the sec… |
| tellsStyle | one of outline | byBlobness | byPlan | byDetHessian | byLrm | "outline" | |
| tellsOutlineColor | color | "#ef4444" | |
| tellsScaleMarkers | boolean | true | Only meaningful with tellMeasureScale on: draw each marker at tellsScaleMultiplier x its measured diameter (real-world meters, zoom-scaled) instead of fixed px. |
| tellsScaleMultiplier | number | TELLS_MEASURED_SCALE_MULTIPLIER_DEFAULT | |
| tellSize | number | 100 | |
| tellRadius | number | 4 | |
| tellMinRelief | number | 1.5 | |
| tellBlobnessMin | number | 0 | |
| tellPlanMin | number | 0 | |
| tellDetHessianMin | number | 0 | |
| tellMeasureScale | boolean | true | |
| tellVetoResolution | one of fine | coarse | "coarse" | |
| tellsPolarity | one of mounds | pits | both | "mounds" | Which extrema the detector reports: mounds (local maxima of the relief, the archaeological tell), pits (minima: quarries, sinkholes, craters, cisterns, robbed-out tombs) or both. |
| tellsPitColor | color | "#3b82f6" | |
| tellsFrozen | boolean | false | "Frozen" pins the currently-computed candidates in place (snapshotted into a plain geojson source, see the tellsFrozenSnapshot state and TellsSource's frozen/frozenSnapshot props below) so panning/zo… |
| showContoursAndGraticules | boolean | false | |
| showContours | boolean | true | |
| showContourLabels | boolean | true | |
| showGraticules | boolean | false | |
| showRasterBasemap | boolean | false | |
| showBackground | boolean | false | |
| matchThemeColors | boolean | true | Sky/horizon/fog colors for the Background + Fog/Sky mode (background- options-section.tsx) — URL-shareable state like every other viz-mode field, not a localStorage preference (was previously a plain… |
| skyColor | color | "#80ccff" | |
| skyHorizonBlend | number | 0.5 | |
| horizonColor | color | "#ccddff" | |
| horizonFogBlend | number | 0.5 | |
| fogColor | color | "#fcf0dd" | |
| fogGroundBlend | number | 0.2 | |
| backgroundLayerActive | boolean | true | |
| rasterBasemapOpacity | number | 1.0 | Viz-mode master opacity (the "Raster Basemap" checkbox's own slider) — composites (multiplies) with basemapSourceOpacity below for the single/split basemap layer, same pattern as Slope-and-More's mas… |
| basemapSourceOpacity | number | 1.0 | Basemap-solo opacity (the "Basemap Opacity" slider inside the Basemap Source section) — only affects the single/split basemap layer, not overlays. |
| exaggeration | number | 1 | |
| lat | number | 21.4208 | Whole-earth start, the same view handleGoHome resets to and historical mode already used. |
| lng | number | 0 | |
| zoom | number | 1.52 | |
| pitch | number | 0 | -- try getting out of pitch 0 loop in 3d pitch: parseAsFloat.withDefault(60.001), |
| bearing | number | 0 | |
| hillshadeMethod | one of standard | combined | igor | basic | aspect-multidir | multidir-colors | "combined" | -- |
| illuminationDir | number | 315 | |
| illuminationAlt | number | 45 | |
| shadowColor | color | "#000000" | shadowColor: parseAsString.withDefault("#000000"), highlightColor: parseAsString.withDefault("#FFFFFF"), accentColor: parseAsString.withDefault("#808080"), |
| highlightColor | color | "#FFFFFF" | |
| accentColor | color | "#808080" | |
| hillshadeExag | number | 1.0 | graticuleColor: parseAsString.withDefault("#000"), graticuleColor: parseAsString, // don't use default to sync with theme |
| contourReferenceMode | one of absolute | lrm | "absolute" | Absolute ("real altitude") vs LRM ("height above/below the local neighborhood mean") — same reference concept Plane Slicer introduced (PLANE_SLICER_REFERENCE_MODES above), applied to what the contour… |
| contourMinor | number | 50 | |
| contourMajor | number | 200 | |
| contourMinorLrm | number | 5 | Absolute and LRM keep independent interval values, same reasoning as planeSlicerValue/planeSlicerValueLrm — LRM's natural range (roughly ±100m of local relief) is much narrower than real elevation's,… |
| contourMajorLrm | number | 20 | |
| contourWeight | number | 1 | Multiplies both major and minor contour line-width — default (1) keeps today's major-vs-minor ratio, 2/4 make both proportionally bolder. |
| contourColor | string | "" | Contour line / graticule colors — empty string means "auto" (theme-adaptive: the contour lines fall back to translucent black/white by theme, the grid to themeAntiColor). |
| customHypsoMinMax | boolean | false | |
| hypsoSymmetric | boolean | false | Min/Max mirrored around 0 (one magnitude), like the curvature ramp: for height-above-ground or elevation-change grids (DSM − DTM, dh). |
| minElevation | number | 0 | |
| maxElevation | number | 8100 | |
| hypsoSliderMinBound | number | -8000 | |
| hypsoSliderMaxBound | number | 5000 | |
| graticuleWidth | number | 1.0 | |
| graticuleColor | string | "" | |
| showGraticuleLabels | boolean | false | |
| graticuleDensity | number | 0 | |
| minimapMinimized | boolean | true | |
| invertColorRamp | boolean | false | Keyframe/360 animation state (animDuration, animLoopMode, animSmoothCamera, animPlaying, animPlaying360, animPose1, animPose2Delta) lives in its own nuqs hook inside CameraUtilities.tsx, not in this … |
| maxBoundsMode | one of none | terrain | raster | union | custom | "terrain" | Max map bounds (Settings > Map Bounds) — constrains pan/zoom rather than a one-shot camera fly like the smart-zoom/fit-to-bounds features above. |
| maxBoundsBuffer | number | 0 | |
| maxBoundsWest | number | -180 | |
| maxBoundsSouth | number | -85 | |
| maxBoundsEast | number | 180 | |
| maxBoundsNorth | number | 85 |
306 parameters read from the app source at build time; 5 multi-line declarations not parsed.
Instruction parameters
Read once on load, never written by the app, left in place in the URL. They either load something or apply a one-time setting.
| Parameter | Type | Effect |
|---|---|---|
| project | string | A named preset from lib/projects.json: hidden panels, initial state, bundled sources. |
| terrainUrl | string | View A's terrain as a URL, registered under a fixed id (older form of terrainSourceA=https://…). |
| basemapUrl | string | View A's basemap as a URL (older form of basemapSource=https://…). |
| terrainType | string | Type of URL terrain sources (cog, terrarium, terrainrgb, wms-raw, tilejson …) instead of the {z} guess. |
| basemapType | string | Type of URL basemap sources (cog, tms, wms, wmts, tilejson) instead of the {z} guess. |
| viaTitiler | boolean | Route URL COGs through titiler (files not in Web Mercator). |
| addSources | list of string (comma-separated) | Library entries (by id) made available in the BYOD lists without being selected. |
| addTerrainUrl | list of string (repeat the parameter) | URL terrain sources made available without being selected (repeat the parameter). |
| addBasemapUrl | list of string (repeat the parameter) | URL basemaps made available without being selected (repeat the parameter). |
| addOverlayUrl | list of string (repeat the parameter) | URL overlays made available; overlayBasemapIds (state) turns overlays on. |
| openSections | list of string (comma-separated) | Sidebar sections expanded on load (general, terrainSource, hillshade, drawing …). |
| closeSections | list of string (comma-separated) | Sidebar sections collapsed on load. |
| scrollTo | string | Section key (or element id) the side panel opens and scrolls to. |
| startTour | boolean | Starts the product walkthrough. |
| bookmarksGallery | boolean | Opens the Bookmarks gallery modal on arrival. |
| openLibrary | string | terrain | basemap - opens that Library modal (the curated dataset list) on arrival. |
| bookmarksUrl | string | A bookmarks JSON (the Bookmarks section's export format) fetched and merged into the visitor's bookmarks; pair with openSections=bookmarks. |
Stored settings (localStorage)
jotai atomWithStorage atoms: API keys, beta flags, panel folds, export preferences, the BYOD source lists. None of these is in the URL; a project export carries a curated subset.
| localStorage key | Default | Note | Defined in |
|---|---|---|---|
| mapboxKey | import.meta.env.VITE_MAPBOX_ACCESS_TOKEN ?? "" | These were hardcoded literals committed here — all moved to local, gitignored VITE_*_API_KEY / VITE_MAPBOX_ACCESS_TOKEN vars (see .env) instead, same pattern as hereKeyAtom below. | lib/settings-atoms.ts |
| googleKey | import.meta.env.VITE_GOOGLE_API_KEY ?? "" | lib/settings-atoms.ts | |
| mapzenKey | "mapzen-xxxxxxx" | lib/settings-atoms.ts | |
| maptilerKey | import.meta.env.VITE_MAPTILER_API_KEY ?? "" | lib/settings-atoms.ts | |
| hereKey | import.meta.env.VITE_HERE_API_KEY ?? "" | Unlike mapbox/maptiler/google above, no public demo key is committed here — HERE requires a paid account, so its default comes from a local, gitignored VITE_HERE_API_KEY (see .env) instead of a hardc… | lib/settings-atoms.ts |
| planetKey | import.meta.env.VITE_PLANET_API_KEY ?? "" | Same gating pattern as hereKeyAtom — Planet's monthly mosaics require a paid account, so "planet" only appears as a Basemap option (raster-basemap- section.tsx) once a real key is set, from a local V… | lib/settings-atoms.ts |
| cesiumIonKey | import.meta.env.VITE_CESIUM_ION_TOKEN ?? "" | Cesium ion access token. | lib/settings-atoms.ts |
| cesiumDetailOffset | 1 | Extra quantized-mesh levels to fetch per tile. | lib/settings-atoms.ts |
| disabledStacPresets | [] | STAC catalogs the visitor has switched OFF in the Library's Catalogs section: stored as the exclusions rather than the inclusions so a preset added in a later release is on by default instead of sile… | lib/settings-atoms.ts |
| savedStacCatalogs | [] | lib/settings-atoms.ts | |
| titilerEndpoint | "https://titiler.xyz" | lib/settings-atoms.ts | |
| customBasemapLastType | "qms" | lib/settings-atoms.ts | |
| osmBuildings3d | true | lib/settings-atoms.ts | |
| customTerrainLastType | "cog" | lib/settings-atoms.ts | |
| maxResolution | 4096 | lib/settings-atoms.ts | |
| exportResolutionMode | "screen" | lib/settings-atoms.ts | |
| exportValueFormat | "raw" | lib/settings-atoms.ts | |
| exportImageFormat | "tiff" | lib/settings-atoms.ts | |
| useCogProtocolVsTitiler | true | lib/settings-atoms.ts | |
| useClientExport | true | DTM export mode: client-side (browser range-reads/tile-mosaic, no titiler, no server-side size limit) vs the original titiler-based export — see lib/client-export.ts. | lib/settings-atoms.ts |
| colorRampType | 'classic' | lib/settings-atoms.ts | |
| licenseFilter | 'open-distribute' | lib/settings-atoms.ts | |
| highResTerrain | true | lib/settings-atoms.ts | |
| cacheVizTiles | true | Gates lib/tile-result-cache.ts — the LRU of finished viz-mode tile bytes that makes re-toggling a mode instant instead of recomputing every visible tile. | lib/settings-atoms.ts |
| terrainAnalysisAdvanced | true | Basic/Advanced toggle, one per section (Terrain Analysis / Relief Visualization — terrain-analysis-section.tsx / relief-visualization-section.tsx): in "basic" mode each sub-mode collapses to just its… | lib/settings-atoms.ts |
| reliefVisualizationAdvanced | true | lib/settings-atoms.ts | |
| lightingEffectsAdvanced | true | lib/settings-atoms.ts | |
| hasSeenTour | false | Guided product tour (components/TerrainControlPanel/product-tour.tsx). | lib/settings-atoms.ts |
| customTerrainSources | [] | getOnInit: true reads localStorage synchronously on first render instead of the jotai default (hardcoded `[]` on first paint, real value applied post-mount via onMount). | lib/settings-atoms.ts |
| isByodOpen | true | lib/settings-atoms.ts | |
| customThemes | [] | lib/settings-atoms.ts | |
| customBasemapSources | [] | lib/settings-atoms.ts | |
| isBasemapByodOpen | true | lib/settings-atoms.ts | |
| isHillshadeXYPadOpen | true | lib/settings-atoms.ts | |
| bookmarksListHeight | null | User-dragged height (px) of the Bookmarks list's scroll area (bookmarks-section.tsx's drag handle below it) — null means "use the default max-h-64 clamp". | lib/settings-atoms.ts |
| collapsedBookmarkGroups | [] | Ids of "project" (root) bookmarks whose children are currently folded away — bookmarks-section.tsx's file-tree-style collapse. | lib/settings-atoms.ts |
| bookmarksViewMode | "list" | Sidebar bookmarks list presentation — "list" (default: the original tree of project rows with children indented beneath) or "grid" (flattened thumbnail tiles, a compact in-panel cousin of the gallery… | lib/settings-atoms.ts |
| bookmarksGalleryViewMode | "grid" | Gallery MODAL presentation — "grid" (default: the original thumbnail cards) or "list" (compact rows: small thumbnail left, project + view name right). | lib/settings-atoms.ts |
| vizModePinned | true | Pins Visualization Modes open through "Fold all sections" (TerrainControlPanel.tsx) — that's the master on/off switchboard for every viz layer, so folding it away along with everything else hides the… | lib/settings-atoms.ts |
| appMode | "terrain" | lib/settings-atoms.ts | |
| isTransparentUi | true | lib/settings-atoms.ts | |
| isExportSettingsOpen | false | Download and Snapshot's own "Export settings" fold (see download-section.tsx). | lib/settings-atoms.ts |
| snapshotIncludeTimeline | false | Snapshot / Copy / Share: keep the historical timeline in the picture, or (default) crop the strip it covers off the bottom - see captureMapScreenshot. | lib/settings-atoms.ts |
| anim-render-quality | 'normal' | lib/settings-atoms.ts | |
| anim-fps | 60 | lib/settings-atoms.ts | |
| anim-target-size-mb | '' | lib/settings-atoms.ts | |
| lastSeenChangelogAt | "2026-08-01" | ISO date ("YYYY-MM-DD") of the newest changelog entry's `<!-- released -->` marker the user has already seen — see lib/changelog.ts. | lib/settings-atoms.ts |
| changelogView | "changes" | "changes" (just what's new since last visit) vs "full" (every entry) — always defaults to "changes", regardless of whether there's currently anything unseen. | lib/settings-atoms.ts |
| betaEnabled | { tells: false, sunShadow: true, historical: true, stacSear… | Mirrors of TerrainViewer's tellsBeta/sunShadowBeta/historicalBeta nuqs fields (the actual gates the app reads) — those live in the URL so a `?tellsBeta=true` link still works, but with no localStorag… | lib/settings-atoms.ts |
| galleryFlattenGroups | true | Bookmarks gallery modal: on (default) flattens every group's cards into one continuous grid (each card's label prefixed with its project name) so nothing but the real bookmark count determines how mu… | lib/settings-atoms.ts |
| galleryShowFeatured | true | Bookmarks gallery modal: whether the read-only Featured/preset strip (see lib/preset-bookmarks.ts) shows as its own section at the top of the gallery, alongside the visitor's own saved bookmarks. | lib/settings-atoms.ts |
| splitRatio | 0.5 | Draggable A/B split-screen divider — persisted (like isSidebarOpenAtom) since it's a user layout preference, not per-session UI state. | lib/layout-constants.ts |
| colorizeMapBorders | false | Outlines each active map pane in its resolved side color (SIDE_COLORS, overridden by sideColorOverridesAtom) — purely cosmetic, off by default so it doesn't surprise anyone not using the comparison/g… | lib/layout-constants.ts |
| colorizeMapBordersInset | true | Whether that border sits inset 3px from the pane edge (default, reads as a frame) or flush against it — dropping the inset doubles the stroke width (border-2 -> border-4) so a flush border doesn't re… | lib/layout-constants.ts |
| isComparisonMixAdvancedOpen | false | Compare and Blend section's own "Advanced" (capture date / colorize borders / side colors) collapsible — persisted like every other section-local collapse toggle (isBasemapByodOpenAtom etc. | lib/layout-constants.ts |
| isSidebarOpen | true | --- Persisted state --- | components/TerrainControlPanel/TerrainControlPanel.tsx |
| sectionOpen | DEFAULT_OPEN_STATE | components/TerrainControlPanel/TerrainControlPanel.tsx | |
| sidebarScroll | 0 | components/TerrainControlPanel/TerrainControlPanel.tsx | |
| drawingExportPerLayer | false | exportGeoJSON toggle (TerraDrawActions below): off (default) exports every layer's features flattened into one FeatureCollection — the existing behavior, and still the right default for "keep drawing… | components/TerrainControlPanel/TerraDrawSystem.tsx |
| drawingExportScope | null | components/TerrainControlPanel/TerraDrawSystem.tsx | |
| drawingLayers | [DEFAULT_LAYER] | Layer metadata (id/name/color/stroke width) is small enough to persist directly in localStorage — this doubles as the list of layer ids whose *geometry* gets hydrated from OPFS on mount (see hydrateP… | components/TerrainControlPanel/TerraDrawSystem.tsx |
| activeLayerId | DEFAULT_LAYER.id | components/TerrainControlPanel/TerraDrawSystem.tsx | |
| persistVectorLayers | true | components/TerrainControlPanel/TerraDrawSystem.tsx |
63 stored settings read from the app source at build time. None is in the URL; a project export carries a curated subset.