Terrain Viewer
Features

Embedding & URL parameters

Everything the app shows is in its URL — how to compose a link or an iframe that loads your own COGs and vector data, the parameters that are instructions rather than state, and the project JSON preset format

The whole application state lives in the query string: viewport, sources, every visualization toggle, split layout, historical dates. A link reproduces a view exactly, and an iframe is just that link in a src. Inside an iframe the product walkthrough never starts by itself (startTour=true still opens it). The app sets no frame restrictions and is already embedded by the Heritage Watch and Anchise wrappers.

Live: the app embedded in this page, with a national COG as terrain and a Natural Earth lakes GeoJSON loaded from a URL.

Builder

Fill in a terrain (and optionally a second one, a basemap and vector data), copy the iframe. Or paste a link copied from the app's Share dialog to start from an existing view. The Share dialog itself also offers the iframe snippet, with a switch to hide the side panel.

<iframe
  src="https://terrain-viewer.iconem.com/?terrainSourceA=custom-ca-nrcan-mrdem30&drawingUrl=https%3A%2F%2Fraw.githubusercontent.com%2Fnvkelso%2Fnatural-earth-vector%2Fmaster%2Fgeojson%2Fne_110m_lakes.geojson&viewMode=3d"
  width="100%" height="560"
  style="border: 0"
  allow="fullscreen; clipboard-write"
  loading="lazy"
></iframe>

Any view's terrain or basemap can be given as a URL instead of an id. The URL becomes the source's id, so the link is self-contained: a visitor whose browser has never seen the source gets it registered on load.

ParameterMeaning
terrainSourceA=https://host/dem.tif … terrainSourceH=Terrain of a view. A URL with {z} is read as Terrarium tiles, anything else as a COG.
basemapSource= / basemapSourceA= … basemapSourceH=Basemap of a view (the unsuffixed one is the shared basemap when per-view basemaps are off). {z} means XYZ tiles, otherwise a COG.
terrainType= / basemapType=Override the type guess (terrarium, terrainrgb, tms, wms, cog, …).
viaTitiler=1Route URL COGs through titiler. Needed when the file is not in Web Mercator; the in-browser reader assumes EPSG:3857.
drawingUrl=Vector data loaded into the Drawing tool, one layer per URL; several URLs comma-separated (a comma inside a URL as %2C). GeoJSON, KML, GPX, FlatGeobuf or Shapefile. Re-fetched on every load, never stored; the layer's name and colours are remembered. This one is state: importing from a URL with "Keep in the link" adds to it, deleting the layer removes from it.

A link without lat/lng is framed on view A's COG (its bounds are read from the file). The server hosting the data must allow cross-origin requests, and range requests for COGs. URLs inside the query string must be percent-encoded.

Active source versus available sources

The parameters above select a source for a view. To make sources available in the Bring-Your-Own-Data lists without activating any, so a visitor can switch between them:

ParameterMeaning
addSources=id1,id2Library entries by id, terrain or basemap.
addTerrainUrl= / addBasemapUrl= / addOverlayUrl=URL sources, repeatable. viaTitiler, terrainType and basemapType apply to them too.

So a typical embed points terrainSourceA at one COG and lists the alternatives with addTerrainUrl. Overlays, basemaps stacked on top of the active one, follow the same pattern: addOverlayUrl= makes one available, and the state field overlayBasemapIds= (a comma-separated list, which may hold URLs directly) is what turns overlays on.

The older terrainUrl= / basemapUrl= parameters still work; they only target view A and register the source under a fixed id.

Parameters that are instructions, not state

These are read once on load and are never written by the app. They stay in the URL, so the link remains shareable.

ParameterMeaning
project=<id>A named preset (see below): hidden panels, initial state, bundled sources.
openSections=a,b / closeSections=a,bSidebar sections to expand or collapse on load (general, terrainSource, hillshade, drawing, …).
scrollTo=<section>Opens that section and scrolls the side panel to it, so a link can land on the hillshade light pad: scrollTo=hillshade. Section keys as above, or any element id.
startTour=trueStarts the product walkthrough.
bookmarksGallery=trueOpens the Bookmarks gallery modal on arrival.
openLibrary=terrain / basemapOpens that Library modal, the curated list of national and global datasets, on arrival. One or the other: they are both dialogs.

coverageOverlays is ordinary state, not an instruction — but it is worth knowing that it folds whole groups to a key, so ?coverageOverlays=mapterhorn,library is a complete, hand-writable link rather than a wall of ids. See Coverage overlays. | bookmarksUrl=<url> | A bookmarks JSON (the Bookmarks section's export format) fetched and merged into the visitor's bookmarks: a curated selection handed out by link. Pair with bookmarksGallery=true, or openSections=bookmarks / scrollTo=bookmarks, to land on it. | | addSources, addTerrainUrl, addBasemapUrl, addOverlayUrl, drawingUrl, viaTitiler, terrainType, basemapType | See above. |

The side panel and the timeline are ordinary state: sidebarCollapsed=true and historicalTimelineCollapsed=true. Which sections are folded, and the panel's scroll position, are not in the URL beyond openSections / closeSections / scrollTo: they change constantly in a session and would churn the address bar.

Everything else in the URL is state: lat, lng, zoom, pitch, bearing, viewMode (2d/3d/globe), appMode (terrain/historical), splitStyle (off/overlay/side-by-side), gridLayout, showHillshade, showRasterBasemap, date/dateA…, and so on. The quickest way to learn a parameter's name is to set it in the sidebar and read the address bar.

Project presets

?project=<id> applies a preset from the app's bundled lib/projects.json. A preset can hide parts of the interface, seed state and carry the sources it needs, which is how a curated embed avoids dozens of query parameters. See also Projects for the user-facing import/export bundles, which share the source and drawing formats.

{
  "my-site": {
    "id": "my-site",
    "name": "My site",
    "description": "One COG, hillshade on, no source pickers",
    "initialViewMode": "3d",
    "disableViewModes": ["globe"],
    "initialSidebarOpen": true,
    "hideSourcePanels": true,
    "hiddenSections": ["contour", "bookmarks", "projectImportExport"],
    "hideMapControls": ["geocoder", "geolocate"],
    "initialSections": { "general": false, "visualizationModes": true },
    "initialState": { "sourceA": "my-dem", "showHillshade": true, "showColorRelief": false },
    "customTerrainSources": [
      { "id": "my-dem", "name": "My DEM", "url": "https://host/dem.tif", "type": "cog", "cogViaTitiler": true, "maxzoom": 14 }
    ],
    "customBasemapSources": [
      { "id": "my-ortho", "name": "Orthophoto", "url": "https://host/ortho.tif", "type": "cog", "role": "basemap", "opacity": 100 }
    ],
    "autoZoomToSource": "sourceA"
  }
}
FieldMeaning
initialViewMode, disableViewModesStarting view mode, and modes removed from the toggle.
initialSidebarOpenWhether the sidebar starts open.
hideSourcePanelsHides the terrain and basemap source pickers, for embeds that pin their sources.
hiddenSectionsSidebar sections removed entirely (contour, background, drawing, bookmarks, sourceInfo, openIn, projectImportExport, hillshadeAdvanced, shadows, …).
hideMapControlsAny of geocoder, zoom, geolocate, fullscreen, minimap, scale. The fullscreen button (under geolocate) is there mainly for embeds.
initialSectionsSections expanded or collapsed on load.
initialStateAny state parameter, applied only where the URL does not already say otherwise.
customTerrainSources, customBasemapSourcesSources merged by id into the visitor's lists. Same fields as a BYOD source: url, type, maxzoom, bounds, cogViaTitiler, nodataFloor, role, opacity, …
initialBounds, autoZoomToSourceFly to a known bbox, or to the bbox read from a COG (sourceA or basemapSource).

The exact interface, generated from lib/project-config.ts at build time:

Prop

Type

Presets are part of the deployed app, so adding one is a pull request. For a one-off, the URL parameters above cover most of the same ground.

All parameters that are not state

Everything the app reads from the URL that it never writes back, in one place. Read once on load and left in the URL, so the link stays shareable.

ParameterTypeEffect
projectstringA named preset from lib/projects.json: hidden panels, initial state, bundled sources.
terrainUrlstringView A's terrain as a URL, registered under a fixed id (older form of terrainSourceA=https://…).
basemapUrlstringView A's basemap as a URL (older form of basemapSource=https://…).
terrainTypestringType of URL terrain sources (cog, terrarium, terrainrgb, wms-raw, tilejson …) instead of the {z} guess.
basemapTypestringType of URL basemap sources (cog, tms, wms, wmts, tilejson) instead of the {z} guess.
viaTitilerbooleanRoute URL COGs through titiler (files not in Web Mercator).
addSourceslist of string (comma-separated)Library entries (by id) made available in the BYOD lists without being selected.
addTerrainUrllist of string (repeat the parameter)URL terrain sources made available without being selected (repeat the parameter).
addBasemapUrllist of string (repeat the parameter)URL basemaps made available without being selected (repeat the parameter).
addOverlayUrllist of string (repeat the parameter)URL overlays made available; overlayBasemapIds (state) turns overlays on.
openSectionslist of string (comma-separated)Sidebar sections expanded on load (general, terrainSource, hillshade, drawing …).
closeSectionslist of string (comma-separated)Sidebar sections collapsed on load.
scrollTostringSection key (or element id) the side panel opens and scrolls to.
startTourbooleanStarts the product walkthrough.
bookmarksGallerybooleanOpens the Bookmarks gallery modal on arrival.
openLibrarystringterrain | basemap - opens that Library modal (the curated dataset list) on arrival.
bookmarksUrlstringA bookmarks JSON (the Bookmarks section's export format) fetched and merged into the visitor's bookmarks; pair with openSections=bookmarks.

Everything else is state, mirrored to the address bar by nuqs as you use the app. The complete generated list, with types and defaults, and the settings that live in localStorage instead, are on the State reference page; the same parameters as an interactive form are on URL as an API.

On this page