3D Terrain
This map shows how to use 3D terrain with hillshading on the map. Data from MapLibre Demo Tiles. Tutorial based on MapLibre GL JS 3D Terrain.
<script lang="ts">
import MapLibre from 'svelte-maplibre/MapLibre.svelte';
import NavigationControl from 'svelte-maplibre/NavigationControl.svelte';
import AttributionControl from 'svelte-maplibre/AttributionControl.svelte';
import RasterTileSource from 'svelte-maplibre/RasterTileSource.svelte';
import RasterDEMTileSource from 'svelte-maplibre/RasterDEMTileSource.svelte';
import RasterLayer from 'svelte-maplibre/RasterLayer.svelte';
import HillshadeLayer from 'svelte-maplibre/HillshadeLayer.svelte';
import Terrain from 'svelte-maplibre/Terrain.svelte';
import TerrainControl from 'svelte-maplibre/TerrainControl.svelte';
import CodeSample from '$site/CodeSample.svelte';
import code from './+page.svelte?raw';
import type { PropertyValueSpecification } from 'maplibre-gl';
let terrainExaggeration: number = $state(1.0);
let hillshadeExaggeration: number = $state(0.5);
let illuminationAnchor: PropertyValueSpecification<'map' | 'viewport'> = $state('map');
</script>
<p>
This map shows how to use 3D terrain with hillshading on the map. Data from <a
href="https://github.com/maplibre/demotiles"
target="_blank">MapLibre Demo Tiles</a
>. Tutorial based on
<a href="https://maplibre.org/maplibre-gl-js/docs/examples/3d-terrain/" target="_blank"
>MapLibre GL JS 3D Terrain</a
>.
</p>
<fieldset class="flex gap-x-4">
<legend>Hillshade illumination anchor</legend>
<label><input type="radio" bind:group={illuminationAnchor} value="map" /> Map</label>
<label
><input type="radio" bind:group={illuminationAnchor} value="viewport" /> Viewport (default)</label
>
</fieldset>
<fieldset class="flex gap-x-4">
<legend>Exaggeration</legend>
<label>
Hillshade: {hillshadeExaggeration.toFixed(2)}
<input
type="range"
min="0.0"
max="1.0"
step="0.01"
bind:value={hillshadeExaggeration}
id="hillshade-exaggeration"
/>
</label>
<label>
Terrain: {terrainExaggeration.toFixed(1)}
<input
type="range"
min="0.0"
max="5.0"
step="0.1"
bind:value={terrainExaggeration}
id="terrain-exaggeration"
/>
</label>
</fieldset>
<MapLibre
style={{
version: 8,
center: [11.39085, 47.3],
zoom: 12,
pitch: 52,
sources: {},
layers: [],
}}
class="relative aspect-[9/16] max-h-[70vh] w-full sm:aspect-video sm:max-h-full"
attributionControl={false}
diffStyleUpdates={true}
>
<NavigationControl visualizePitch={true} position={'top-right'} />
<AttributionControl
customAttribution={`Map data © <a href=https://www.openstreetmap.org/copyright>OpenStreetMap</a> Contributors | Terrain data <a href="https://earth.jaxa.jp/en/data/policy/">AW3D30 (JAXA)</a> | <a href=https://maplibre.org>MapLibre</a>`}
/>
<RasterTileSource tiles={['https://a.tile.openstreetmap.org/{z}/{x}/{y}.png']} tileSize={256}>
<RasterLayer paint={{}} />
</RasterTileSource>
<!-- Use a different source for terrain and hillshade layers, to improve render quality -->
<RasterDEMTileSource
tiles={['https://demotiles.maplibre.org/terrain-tiles/{z}/{x}/{y}.png']}
tileSize={256}
id="terrainSource"
/>
<RasterDEMTileSource
tiles={['https://demotiles.maplibre.org/terrain-tiles/{z}/{x}/{y}.png']}
tileSize={256}
id="hillshadeSource"
>
<HillshadeLayer
id={'hills'}
layout={{ visibility: 'visible' }}
paint={{
'hillshade-exaggeration': hillshadeExaggeration,
'hillshade-illumination-anchor': illuminationAnchor,
'hillshade-shadow-color': '#473B24',
}}
/>
</RasterDEMTileSource>
<Terrain source={'terrainSource'} exaggeration={terrainExaggeration} />
<TerrainControl
source={'terrainSource'}
exaggeration={terrainExaggeration}
position={'top-right'}
/>
</MapLibre>