尚未检测到更新。GitHub 发布版本后会显示在这里。
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证Rendering and Comparison (hillshade, contours, hypso) of Global TerrainRGB, Terrarium and COG terrain DEM/DSM/DTM open-data sources based on Maplibre
翻译准备中
取自仓库 README
正在加载完整 README。 以下为已保存的摘录。
A comprehensive terrain visualization and downloading tool, built on top of MapLibre GL via react-map-gl. Explore different terrain visualization modes introduced in 2025 including hillshade, hypsometric tinting, contour lines, and more.查看完整 README ↗A comprehensive terrain visualization and downloading tool, built on top of MapLibre GL via react-map-gl. Explore different terrain visualization modes introduced in 2025 including hillshade, hypsometric tinting, contour lines, and more.
Open Terrain Viewer · Documentation · URL API · llms.txt
Free and open source, by Iconem. Hillshade, hypsometric tint, contours, slope, aspect, curvature, local relief model, sky-view factor, openness, matcap and Phong lighting, side-by-side comparison, and GeoTIFF export of the DEM and every mode, over global terrain, national LiDAR and open-data services, and your own COG, VRT, WMS, ArcGIS LERC or Cesium quantized-mesh terrain, all read in the browser. Every setting lives in the URL, so a link opens a place in a given mode.
Every setting lives in the URL, so an assistant can answer "show me the slope around the Matterhorn in 3D" with a link that opens exactly that. Install the Terrain Viewer skill for Claude Code, Codex, Cursor and other agents:
npx skills add Iconem/terrain-viewer
Assistants that read the web use llms.txt. More on the AI assistants docs page.
# Clone the repository
git clone https://github.com/iconem/elevation-terrain-visualizer.git
cd elevation-terrain-visualizer
pnpm install
pnpm run dev
pnpm run build # bundles to dist dir
Open http://localhost:5173 to view the app.
See the guide for producing a COG, with required set of parameters, for correct version, with no artifacts.
Two ways to use a COG that lives on your own disk as a BYOD terrain source, without uploading it anywhere:
The "Add New Terrain Dataset" dialog has a Local COG file (this browser only) type: pick a .tif/.tiff and the app streams it straight off disk via a blob: object URL, through the same @geomatico/maplibre-cog-protocol cog:// reader used for remote COGs — HTTP Range semantics work the same way against a blob: URL as against a real server (confirmed 206/Content-Range responses in this app's target Chromium build), so no server is involved at all.
Trade-offs:
maplibre-contour's own DEM-source machinery, independent of the cog:// protocol) — a local file can't feed it, same as it can't reach titiler over the network.blob: Range-fetch support is less consistently tested than Chromium's — if candidates hit issues there, the localhost option below is the fallback..tif. Range-based streaming only works efficiently against a file that's internally 2D-tiled (e.g. produced with gdal_translate -of COG src.tif out_cog.tif, or TILED=YES + gdaladdo overviews) — a plain strip-organized GeoTIFF (GDAL's default when no COG/tiling option is passed) forces the reader to decode large chunks of the file repeatedly on every pan/zoom, which can manifest as anything from sluggishness to a hard crash (RangeError: Array buffer allocation failed). The "Add New Terrain Dataset" dialog checks this on pick and warns if the file isn't tiled or has no overviews, but can't fix the file for you — re-export it as a real COG first.@geomatico/maplibre-cog-protocol's zoom/bounds math (lib/read/math.js) hardcodes every COG as EPSG:3857 with no reprojection step — it feeds the raw pixel resolution straight into a mercator-meters zoom formula and inverse-mercator-projects the raw bounding box as if it were already in mercator meters. A geographic (EPSG:4326, degree-based) source gets its degree-sized pixels misread as meter-sized ones, producing a wildly inflated "native zoom" (this is what fed z27+ into the terrain-elevation crash fixed above); a different projected CRS (e.g. a UTM zone) has correct units but the wrong origin, so the detected bounds land nowhere near the real data. Almost no publicly published DSM/DTM data is natively in EPSG:3857 (national/agency DEMs are typically geographic or UTM) — reproject first with gdalwarp -t_srs EPSG:3857 -of COG src.tif out_3857.tif, or use the remote COG type in titiler mode instead, which reprojects server-side via rio-tiler and isn't affected by this. The BYOD dialog checks the picked file's CRS and warns if it isn't 3857.Browsers treat http://localhost as a "potentially trustworthy" origin, exempt from mixed-content blocking — so this HTTPS app can fetch() a plain http://localhost:PORT/your.tif COG even without a certificate. Serve the directory containing your COG with any static file server that supports Range requests, e.g.:
npx serve --cors -p 8080 .
Then add it as a normal COG BYOD source with URL http://localhost:8080/your-file.tif. Unlike the built-in local-file picker, this survives reloads (the URL is a real, stable address) and works in any browser, at the cost of needing a terminal command running alongside the app.
Watching vinayakkulkarni/tileserver-rs#1008 (proposed native Terrarium/Mapbox-RGB DEM tile encoding straight from a COG/GeoTIFF source) — if it lands, it'd be a much lighter self-hosted alternative to titiler for this exact "serve a local COG as real terrain-rgb tiles" case, without needing titiler's fuller COG/mosaic/STAC feature set.
Most of these modes are supported by — and inspired by — gdaldem and the RVT (Relief Visualization Toolbox) QGIS plugin. Neighborhood usually refers to a 3×3 kernel centered on the pixel.
k (levels up the pyramid, exposed as the "Smoothing Radius" control via k = round(log2(radiusPx))), fetch the ancestor tile at z-k plus its 8 same-zoom neighbors, decode, and stitch into a padded elevation grid — the same 3×3-stitch logic the other neighborhood-based modes use, just parameterized to run at z-k instead of z.2^k) and bilinearly sample the padded coarse grid — this is what avoids the "boxy" hard edges a naive nearest-neighbor pyramid lookup would give.LRM = fine[pixel] - bilinear(coarseGrid, mappedCoord), re-encoded via the standard terrain-rgb elevation encoding.Visibility-analysis modes, both built on the same "horizon angle" ray march (see lib/horizon-angle.ts): march outward from each pixel in 8 compass directions up to a configurable "Search Radius" (same-zoom pixels), and in each direction find the steepest elevation angle to any point along the ray — the horizon angle in that direction.
SVF ≈ 1 - mean(sin(max(0, horizonAngle))) across the 8 directions, scaled ×100.Deliberately the simplest viable version of both — not the literature's full accuracy: fixed 8 directions (not RVT's usual 16-32), integer-pixel ray steps (no bilinear sampling along the ray), and SVF's simplified mean-of-sines estimator rather than a per-sector solid-angle-weighted integral. A later pass can raise direction count, switch to bilinear ray sampling, or adopt a more precise SVF integral without changing the overall approach.
Computes a Difference-of-Gaussians of the LRM (DoG-of-LRM) as the primary bump signal, keeps only its local maxima (non-maximum suppression scaled to the configured tell size), then vetoes candidates that fail any of three shape filters: Blobness (structure-tensor peak/pit detector), Plan Curvature / Divergence (rejects saddles and ridges where flow diverges outward across contours), and Det-Hessian (rejects saddle points, keeps bowl/dome shapes). Opt in via Settings, or directly with the ?tellsBeta=true URL param.
The detector's low-pass component is built from "ancestor" tiles fetched several pyramid levels coarser than the current view (up to 6 levels for the largest smoothing radius). For pre-tiled terrarium/mapbox-encoded sources, each zoom level is pre-baked by the tile server with real downsampling, so an ancestor tile is a genuine smoothed version of the terrain. For a source streamed live through @geomatico/maplibre-cog-protocol, every tile read — including these coarse ancestor reads — is hardcoded to resampleMethod: 'nearest' deep inside the library's CogReader (confirmed in node_modules/@geomatico/maplibre-cog-protocol/dist/esm/read/CogReader.js), with no option exposed to override it. That means ancestor tiles for a COG source are nearest-neighbor-decimated rather than area-averaged, which can introduce aliasing into the low-pass signal — most noticeably at the coarser pyramid levels — rather than a true smoothed regional trend. This affects every terrain-derivative mode that fetches ancestor tiles for a COG source (LRM, Tells), not just Tells.
Confirmed via the "GRE - Amphipolis COG clamped" DSM source: the tile-pyramid arithmetic itself (fetchAncestorScale in lib/tells-protocol.ts) is standard XYZ tiling and is correct regardless of upstream type — the COG protocol answers any {z}/{x}/{y} request the same way a real tile server would. The actual limitation is resampling quality, not level/index misalignment.
Options considered, not yet implemented:
patch-package, not currently set up in this repo) to force resampleMethod: 'bilinear' — the underlying geotiff.js resampler only supports 'nearest' or 'bilinear' (no true box/average filter), so this reduces aliasing but still isn't a real low-pass.geotiff package directly for cog:// ancestor reads in lib/normal-derived-protocol.ts, requesting at native resolution and doing a proper box-average downsample ourselves. More correct, but touches a shared code path used by every terrain-derivative protocol, not just Tells.Curvature/blobness-based detection is only as good as the underlying DEM's real (not just nominal) resolution. Mapterhorn's global coverage, and most other freely-available global tilesets, are built from Copernicus GLO-30 — TanDEM-X-derived (infilled with SRTM/ASTER/ALOS), ~30 m posting. A 3×3–5×5 derivative kernel there has an effective footprint of 90–150 m. Mound-type sites in Bronze/Iron-Age arid-zone settlement landscapes (e.g. the Balkh/Helmand/Sistan basins) commonly run 50–300 m across and 3–20 m tall — the smaller half of that range is close to or below what a curvature operator can reliably resolve at 30 m, and TanDEM-X's own correlated stripe/mosaic-seam noise has spatial-frequency content that can masquerade as small domes. Realistically this pipeline reliably catches the larger mega-tells and misses or false-positives on smaller ones — a real ceiling to flag before investing further in this detector, and a good reason to prefer a finer-resolution source (LidarHD, a local high-res COG, etc.) wherever one is available for the area of interest.
Mound/tell detection specifically — related approaches and how they compare to this project's curvature/blobness pipeline:
| Study | Region | Data source | Method | Notes |
|---|---|---|---|---|
| Menze & Ur, PNAS 2012 | NE Syria (Fragile Crescent) | ASTER multispectral time series, 15 m | Random Forest on multi-temporal "anthrosol" spectral signature | ~14,000 sites; spectral, not terrain |
| Tapete, Traviglia, Delpozzo & Cigna, RS 2021 | Near/Middle East tell landscapes | COSMO-SkyMed SAR-derived DEM + imagery, 3 m | Regional systematic mound mapping + looting-pit detection | Closest DEM-based analog at usable resolution for tells |
| Trier et al., PNAS 2020 | Cholistan, Pakistan (Indus) | Multisensor/multitemporal SAR + multispectral | ML classifier → mound probability field | Same arid mounded-settlement morphology as Afghanistan; not DEM-curvature |
| Kokalj & Hesse, 2017 / RVT toolbox | General (methodology) | Any LiDAR/DEM | Hillshade-from-multiple-directions, sky-view factor, local relief, openness, slope | The standard "combine several derivative visualizations" reference; repo |
| Gallwey et al., RS 2018 | Carnac/Morbihan, France | Airborne LiDAR | Multi-scale Topographic Position (micro/meso/macro) + Random Forest | Methodologically closest to what this project proposes — literally multi-scale TPI extrema + ML, κ=0.98 |
| PMC7070870, "Geomorphometric Methods for Burial Mound Recognition" | Europe (general) | High-res LiDAR DEM | Curvature/geomorphometric feature extraction for mound recognition | Direct precedent for curvature-based (not spectral) mound extraction |
This project was inspired by:
Know a good terrain tileset (Terrain-RGB or Terrarium encoded) that isn't in the built-in source list? Suggest it.
MIT License - feel free to use this project for any purpose.
| Pistola, Orrù, Marchetti, Roccetti & Gordin, PLOS ONE 2025 | Abu Ghraib, Mesopotamia (Iraq) | Declassified CORONA imagery | CNN retrained on historical imagery for vanished/destroyed sites | Most recent (2025); imagery not DEM, but same region family |
| Rajani (Kandahar survey), PLOS ONE 2021 | Kandahar, Afghanistan | Public satellite imagery (visual survey) | Manual identification, not automated | Only Afghanistan-specific study found — not curvature/ML-based |