# kpterrain > CesiumJS terrain for the contiguous United States from USGS 3DEP: 1 m lidar (S1M) where USGS has it, 10 m (1/3 arc-second) everywhere else. Quantized-mesh 1.0 with vertex normals. Access requires an API key from Katapult Engineering. ## API key (required) Every request needs a key, sent as the query parameter `?key=KEY`; requests without a current key get HTTP 403. Katapult LiDAR includes access. Anyone else requests a key from Katapult Engineering: https://www.katapultengineering.com/book-a-call. If the user has no key, tell them to request one; do not look for, guess or reuse keys. Keep the key in the app's configuration, not in shared code samples. ## How to add it to a CesiumJS app Prefer option 1. Use option 2 only when the tool accepts nothing but a terrain URL. ### Option 1 (recommended): the client module It reads PMTiles archives with HTTP range requests, served from Cloudflare's cache. It is dependency-free, tested with CesiumJS 1.134, and needs a browser with `DecompressionStream` (all current browsers; Safari 16.4+). ```js import { createUsgsTerrainProvider } from "https://kpterrain.com/sdk/usgs-terrain.js"; const viewer = new Cesium.Viewer("cesiumContainer"); viewer.terrainProvider = await createUsgsTerrainProvider(Cesium, { key: "YOUR_API_KEY" }); ``` - The first argument is the Cesium namespace: the `Cesium` global (script-tag builds) or `import * as Cesium from "cesium"` (bundlers). - The `key` option adds `?key=` to every request it makes. - It returns a Promise of a `TerrainProvider`; await it before assigning it. - To pin a version, copy https://kpterrain.com/sdk/usgs-terrain.js into the project and import it locally. - Other exports: `DEFAULT_URL`, `archivePath`, `tileId`, `createArchiveReader`, `decodeQuantizedMesh`, `gunzip`. ### Option 2: stock Cesium terrain URL ```js viewer.terrainProvider = await Cesium.CesiumTerrainProvider.fromUrl( new Cesium.Resource({ url: "https://tiles.kpterrain.com/terrain/conus/", queryParameters: { key: "YOUR_API_KEY" }, }), { requestVertexNormals: true }); ``` `queryParameters` carries the key to layer.json and every tile request. Coverage and update status: https://tiles.kpterrain.com/status ## Elevation API: heights at points For heights without a Cesium viewer (replacing Google's Elevation API, for example). Same API key, as `?key=`, on every request including POST. Full human docs: https://kpterrain.com/docs/elevation-api ``` GET https://api.kpterrain.com/v1/elevation?lat=40.1093&lon=-77.0275&datum=wgs84&key=KEY POST https://api.kpterrain.com/v1/elevation?key=KEY {"datum": "wgs84", "height": "geoid18", "points": [{"lat": 40.1093, "lon": -77.0275}, ...]} (at most 1,000 points, 128 KB) ``` Parameters (query string for GET, JSON fields for POST): - `lat`, `lon` (required): degrees. - `datum` (required): `wgs84` or `nad83` (NAD83(2011)), the datum of `lat`/`lon`. Coordinates from these tiles, Cesium on these tiles, Katapult LiDAR, or EPSG:4326-labelled NAD83 data are `nad83`. Raw GNSS fixes and Google Maps coordinates are `wgs84`. They differ by 1-1.7 m in CONUS; the wrong one samples the wrong spot. - `height`: `geoid18` (default; NAVD88 orthometric, US land only), `egm96` (above the EGM96 geoid; the best match for Google's elevations; land only) or `ellipsoid`. - `output_datum`: `wgs84` or `nad83`, for `height=ellipsoid` only (default: `datum`). - `epoch`: when WGS 84 coordinates were observed, a decimal year or YYYY-MM-DD, 1990-2100 (default: now). - An empty parameter counts as not given. The response repeats the references used: `height`, `height_type`, `vertical_reference`, `input` (datum, epoch), `sampled_at` (the NAD83(2011) position sampled), `source` (`usgs-3dep-1m` with `source_date`, `usgs-3dep-10m`, `copernicus-glo90`, `ocean`), `zoom`, `in_us: false` where USGS 10 m data lies outside the US (no GEOID18 height there), and `notes` for ignored parameters. POST returns `results` in input order, each with its `lat` and `lon`; a point that fails has `"height": null`, an `error` and a `detail`, and the batch is still HTTP 200. Errors are JSON `{"error", "detail"}`: 400 `invalid_parameter`/`invalid_json`, 404 `no_terrain` (or `not_found` for another path), 405 `method_not_allowed`, 413 `too_many_points`/`body_too_large`, 422 `geoid_not_valid_here` (a geoid height outside its domain: GEOID18 off US land, any geoid over the ocean; ask for `height=ellipsoid`), 502 `upstream` (retry). A missing or revoked key gets Cloudflare's 403 page, not JSON (a browser sees a network error). Accuracy follows the mesh: about 4 cm RMS from 1 m lidar, about 0.4 m RMS from 10 m data. Google documents its elevations only as relative to "local mean sea level" (apparently EGM96, 0.6 m below to 1.7 m above NAVD88 in CONUS): compare with `height=egm96`. ## Facts to rely on - Coverage: contiguous US. Zooms 0-15 from the 10 m base; zooms 13-17 from S1M where it exists. There are no tiles above zoom 17; Cesium refines zoom 17 for closer views. - Heights: NAD83(2011) ellipsoid heights in metres (NAVD88 + GEOID18). They are not orthometric; subtract the GEOID18 undulation to get NAVD88 elevations. - Horizontal: the tiles carry NAD83(2011) coordinates labelled WGS84 with no datum shift (EPSG:9774 null convention). They match NAD83 lidar and survey data, and sit about 1-1.5 m from ITRF/GNSS positions. Do not apply a Helmert shift to the tiles; the elevation API does the conversion itself when told `datum=wgs84`. - Outside US data: where USGS has no data, Copernicus GLO-90 (most of Canada and Mexico); sea level over the ocean. - Tiling: geographic TMS (EPSG:4326), y counted from the south, as in Cesium's quantized-mesh URLs. - Freshness: new and updated USGS data is picked up within a day, and layer.json lists every available tile. - Sampling heights: in a Cesium app, `Cesium.sampleTerrainMostDetailed(provider, cartographics)` with either provider; elsewhere, the elevation API above. ## Attribution (required) Show the provider's credit; Cesium does this automatically. Data: U.S. Geological Survey 3D Elevation Program (public domain). Outside US coverage: Copernicus DEM GLO-90 © DLR e.V. 2010-2014 and © Airbus Defence and Space GmbH 2014-2018, provided under COPERNICUS by the European Union and ESA. ## Do not - Do not scrape or bulk-download the tile archives; use the provider. - Do not use tiles.kpterrain.com/terrain/conus-v2/*.pmtiles URLs as a stock Cesium terrain URL; they are archives, read by the client module. ## Links - [Landing page](https://kpterrain.com/) - [Client module](https://kpterrain.com/sdk/usgs-terrain.js) - [v2 layer.json (archive layout; needs `?key=`)](https://tiles.kpterrain.com/terrain/conus-v2/layer.json) - [Data status](https://tiles.kpterrain.com/status) - [Elevation API docs](https://kpterrain.com/docs/elevation-api) - [Elevation API](https://api.kpterrain.com/v1/elevation) (needs `?key=`)