import { addProtocol } from "/vendor/maplibre-gl/maplibre-gl.mjs"; import { ONLINE_TERRAIN_TILES } from "/js/overlays.js"; import { firstSymbolLayerId } from "/js/radar.js"; import { CONTOUR_ICON } from "/js/icons.js"; const SOURCE_ID = "contour-lines"; const LINE_LAYER_ID = "contour-lines-line"; // Topographic contour lines, generated client-side by the vendored // maplibre-contour plugin (public/vendor/maplibre-contour/) from the same // online Terrarium DEM tiles overlays.js's HillshadeControl already // fetches. Online only, unlike Terrain shading/Terrain shadow: the // plugin's DemSource has no public hook to route its own tile fetch // through the pmtiles:// protocol HillshadeControl uses for the offline // basemap, so — same story as the Wikipedia layer and Radar — this just // quietly renders nothing without a network. export class ContourControl { constructor(map) { this._map = map; this._active = false; this._demSource = new window.mlcontour.DemSource({ url: ONLINE_TERRAIN_TILES[0], encoding: "terrarium", maxzoom: 15, worker: true, cacheSize: 100, }); this._demSource.setupMaplibre({ addProtocol }); // setStyle() (switching Online/Offline) tears down all custom sources/ // layers — put contour lines back if they were on, same as every other // Overlays control. map.on("style.load", () => { if (this._active) this._addLayer(); }); } mount(li) { li.innerHTML = ` `; this._checkbox = li.querySelector(".overlays-panel__toggle"); this._checkbox.addEventListener("change", (event) => this._toggle(event.target.checked)); } _toggle(active) { this._active = active; if (active) this._addLayer(); else this._removeLayer(); } _addLayer() { if (this._map.getSource(SOURCE_ID)) return; // Starting-point thresholds (README's own generic example) — meters, // more/tighter contours at higher zoom. Expect to eyeball-tune against // real Tatras-scale terrain. const tiles = [ this._demSource.contourProtocolUrl({ thresholds: { 11: [200, 1000], 12: [100, 500], 14: [50, 200], 15: [20, 100], }, contourLayer: "contours", elevationKey: "ele", levelKey: "level", }), ]; this._map.addSource(SOURCE_ID, { type: "vector", tiles, maxzoom: 15 }); this._map.addLayer( { id: LINE_LAYER_ID, type: "line", source: SOURCE_ID, "source-layer": "contours", paint: { // Roughly the same brown as the mid-slope band of overlays.js's // own ELEVATION_COLOR_RAMP, so this reads as part of the same // terrain treatment rather than a bolted-on layer. "line-color": "#8a6d3b", "line-width": ["match", ["get", "level"], 1, 1.2, 0.6], "line-opacity": 0.6, }, }, firstSymbolLayerId(this._map), ); } _removeLayer() { if (this._map.getLayer(LINE_LAYER_ID)) this._map.removeLayer(LINE_LAYER_ID); if (this._map.getSource(SOURCE_ID)) this._map.removeSource(SOURCE_ID); } }