Add a 3D pitch toggle, with raised terrain following it
This commit is contained in:
1 parent
f5eb9a4a8d
commit
5c3151c2f5
3 files changed
+122
No files matched your search
@@ -16,6 +16,16 @@ const WIKI_BADGE_COLOR = "#795548";
|
||||
|
||||
const HILLSHADE_ID = "hillshade-dem";
|
||||
const COLOR_RELIEF_ID = "elevation-tint";
|
||||
// MapLibre's own default light ({anchor:"viewport", intensity:0.5,
|
||||
// position:[1.15,210,30]}) sits the sun almost overhead (polar 30°) — fine
|
||||
// for flat 2D, but it barely rakes across 3D terrain at all, which is
|
||||
// most of why real relief still looked flat/washed out even once the mesh
|
||||
// itself was raised. Lower polar angle = lower sun = longer, more visible
|
||||
// slope shadows; azimuth 315 is the classic NW hillshade convention.
|
||||
// anchor:"map" (not "viewport") keeps the sun fixed to geography rather
|
||||
// than spinning with the camera as you look around a tilted view.
|
||||
const TERRAIN_LIGHT = { anchor: "map", color: "#fff", intensity: 0.6, position: [1.5, 315, 65] };
|
||||
const DEFAULT_LIGHT = { anchor: "viewport", color: "#fff", intensity: 0.5, position: [1.15, 210, 30] };
|
||||
// Hypsometric tint reusing this app's own palette (wood_a's green at
|
||||
// valley floor, the road casing brown through the mid-slopes) rather than
|
||||
// a generic rainbow ramp, so it reads as part of the same map rather than
|
||||
@@ -52,6 +62,7 @@ class HillshadeControl {
|
||||
// sources/layers — put hillshade back if it was on.
|
||||
map.on("style.load", () => {
|
||||
if (this._active) this._addLayer();
|
||||
this.syncTerrain(this._map.getPitch() > 0);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -73,8 +84,39 @@ class HillshadeControl {
|
||||
|
||||
_toggle(active) {
|
||||
this._active = active;
|
||||
// Clear first: setTerrain may still be pointing at HILLSHADE_ID from
|
||||
// before, and removing that source out from under it is worth avoiding
|
||||
// even for the instant before syncTerrain below re-evaluates it.
|
||||
if (this._map.getTerrain()) this._map.setTerrain(null);
|
||||
if (active) this._addLayer();
|
||||
else this._removeLayer();
|
||||
this.syncTerrain(this._map.getPitch() > 0);
|
||||
}
|
||||
|
||||
// Real terrain displacement (map.setTerrain), not just the flat
|
||||
// hillshade/color-relief paint above — called both when this checkbox
|
||||
// changes and when the 3D pitch toggle changes (map.js's
|
||||
// PitchToggleControl, via OverlaysControl.syncTerrain below), since
|
||||
// showing raised terrain needs both "tilted enough to see it" and "the
|
||||
// DEM source this checkbox controls is actually loaded".
|
||||
syncTerrain(pitched) {
|
||||
const terrainOn = pitched && this._active && this._map.getSource(HILLSHADE_ID);
|
||||
if (terrainOn) {
|
||||
this._map.setTerrain({ source: HILLSHADE_ID, exaggeration: 3 });
|
||||
} else if (this._map.getTerrain()) {
|
||||
this._map.setTerrain(null);
|
||||
}
|
||||
this._map.setLight(terrainOn ? TERRAIN_LIGHT : DEFAULT_LIGHT);
|
||||
// The raster hillshade layer fakes relief by shading a flat texture —
|
||||
// once real 3D geometry is doing that job (with its own lit shading
|
||||
// for free), the raster version just looks like a blurry, low-res
|
||||
// texture pasted on top of it. It's the only way to suggest relief in
|
||||
// flat 2D though, so only hide it once real terrain is actually up.
|
||||
// The color-relief elevation tint stays regardless — that's
|
||||
// colour-by-height information, not a shading trick.
|
||||
if (this._map.getLayer(HILLSHADE_ID)) {
|
||||
this._map.setLayoutProperty(HILLSHADE_ID, "visibility", terrainOn ? "none" : "visible");
|
||||
}
|
||||
}
|
||||
|
||||
_addLayer() {
|
||||
@@ -194,6 +236,13 @@ export class OverlaysControl {
|
||||
this._hillshade.setOfflineAvailable(available);
|
||||
}
|
||||
|
||||
// Called by map.js's PitchToggleControl whenever the 3D pitch toggle
|
||||
// changes, so raised terrain follows the camera tilt rather than needing
|
||||
// the "Terrain shading" checkbox re-clicked to notice.
|
||||
syncTerrain(pitched) {
|
||||
this._hillshade.syncTerrain(pitched);
|
||||
}
|
||||
|
||||
setShadowOfflineAvailable(available) {
|
||||
this._shadow.setOfflineAvailable(available);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user