Contour lines can be enabled on Map entities to visualize relief. Change line intervals, opacity, thickness and color, as well as opacity. Note that on very flat surfaces, contour lines can produce artifacts where their displayed thickness is greater than desired.
index.js
import colormap from "colormap";import { Color, DoubleSide } from "three";import { MapControls } from "three/examples/jsm/controls/MapControls.js";import XYZ from "ol/source/XYZ.js";import Extent from "@giro3d/giro3d/core/geographic/Extent.js";import Instance from "@giro3d/giro3d/core/Instance.js";import ElevationLayer from "@giro3d/giro3d/core/layer/ElevationLayer.js";import Map from "@giro3d/giro3d/entities/Map.js";import GeoTIFFFormat from "@giro3d/giro3d/formats/GeoTIFFFormat.js";import ColorMap, { ColorMapMode } from "@giro3d/giro3d/core/ColorMap.js";import TiledImageSource from "@giro3d/giro3d/sources/TiledImageSource.js";import Inspector from "@giro3d/giro3d/gui/Inspector.js";function bindNumericalDropDown(id, onChange) { const element = document.getElementById(id); if (!(element instanceof HTMLSelectElement)) { throw new Error( "invalid binding element: expected HTMLSelectElement, got: " + element.constructor.name, ); } element.onchange = () => { onChange(parseInt(element.value)); }; const callback = (v) => { element.value = v.toString(); onChange(parseInt(element.value)); }; return [callback, parseInt(element.value), element];}function bindSlider(id, onChange) { const element = document.getElementById(id); if (!(element instanceof HTMLInputElement)) { throw new Error( "invalid binding element: expected HTMLInputElement, got: " + element.constructor.name, ); } element.oninput = function oninput() { onChange(element.valueAsNumber); }; const setValue = (v, min, max, step) => { if (min != null && max != null) { element.min = min.toString(); element.max = max.toString(); if (step != null) { element.step = step; } } element.valueAsNumber = v; onChange(element.valueAsNumber); }; const initialValue = element.valueAsNumber; return [setValue, initialValue, element];}function bindToggle(id, onChange) { const element = document.getElementById(id); if (!(element instanceof HTMLInputElement)) { throw new Error( "invalid binding element: expected HTMLButtonElement, got: " + element.constructor.name, ); } element.oninput = function oninput() { onChange(element.checked); }; const callback = (v) => { element.checked = v; onChange(element.checked); }; return [callback, element.checked, element];}const x = -13602000;const y = 5812000;const halfWidth = 2500;const extent = new Extent( "EPSG:3857", x - halfWidth, x + halfWidth, y - halfWidth, y + halfWidth,);const instance = new Instance({ target: "view", crs: extent.crs,});const map = new Map({ extent, lighting: { enabled: true, hillshadeIntensity: 0.5, }, side: DoubleSide, backgroundColor: "white", contourLines: true,});instance.add(map);const source = new TiledImageSource({ source: new XYZ({ minZoom: 10, maxZoom: 16, url: "https://3d.oslandia.com/dem/MtStHelens-tiles/{z}/{x}/{y}.tif", }), format: new GeoTIFFFormat(),});const floor = 1100;const ceiling = 2500;const values = colormap({ colormap: "viridis", nshades: 256 });const colors = values.map((v) => new Color(v));const dem = new ElevationLayer({ name: "dem", source, extent, colorMap: new ColorMap({ colors, min: floor, max: ceiling, mode: ColorMapMode.Elevation, }),});map.addLayer(dem);instance.view.camera.position.set(-13594700, 5819700, 7300);const controls = new MapControls(instance.view.camera, instance.domElement);controls.target.set(-13603000, 5811000, 0);instance.view.setControls(controls);instance.notifyChange();Inspector.attach("inspector", instance);bindToggle("contourLineCheckbox", (state) => { if (state) { document.getElementById("options").removeAttribute("disabled"); } else { document.getElementById("options").setAttribute("disabled", "disabled"); } map.contourLines.enabled = state; instance.notifyChange(map);});bindNumericalDropDown("mainInterval", (v) => { map.contourLines.interval = v; instance.notifyChange(map);});bindNumericalDropDown("secondaryInterval", (v) => { map.contourLines.secondaryInterval = v; instance.notifyChange(map);});bindSlider("opacitySlider", (v) => { map.contourLines.opacity = v; instance.notifyChange(map);});bindSlider("thicknessSlider", (v) => { map.contourLines.thickness = v; instance.notifyChange(map);});