Add a Hillshade Layer ​
A hillshade layer simulates how sunlight would fall on terrain — mountains cast shadows, valleys stay dark. This makes elevation visible on an otherwise flat 2D map.
No three.js or external libraries needed. Hillshade is a built-in layer type in MapMetrics GL.
What is a Hillshade Layer? ​
Imagine shining a light from the top-left corner of the map. Mountains facing the light become bright; valleys and north-facing slopes fall into shadow. That's what a hillshade layer does — it gives a flat 2D map a sense of depth and elevation.
Adding the Hillshade Layer ​
You need two things:
- A
raster-demsource (elevation data tiles) - A
hillshadelayer that references it
javascript
// Step 1: Add the elevation data source (free AWS terrain tiles — no API key needed)
map.addSource('dem', {
type: 'raster-dem',
tiles: ['https://s3.amazonaws.com/elevation-tiles-prod/terrarium/{z}/{x}/{y}.png'],
tileSize: 256,
encoding: 'terrarium',
maxzoom: 15,
});
// Step 2: Add the hillshade layer
map.addLayer({
id: 'hillshade',
type: 'hillshade',
source: 'dem',
paint: {
'hillshade-shadow-color': '#473B24', // shadow color
'hillshade-highlight-color': '#ffffff', // lit color
'hillshade-illumination-direction': 335, // sun angle 0–360°
'hillshade-exaggeration': 0.5, // 0 = flat, 1 = maximum contrast
},
});Paint Properties ​
| Property | What it does |
|---|---|
hillshade-shadow-color | Color of shadowed slopes |
hillshade-highlight-color | Color of sunlit slopes |
hillshade-accent-color | Color of very steep slopes |
hillshade-illumination-direction | Direction of the sun (0° = North) |
hillshade-exaggeration | Shadow intensity (0–1) |
Show/Hide at Runtime ​
javascript
// Show
map.setLayoutProperty('hillshade', 'visibility', 'visible');
// Hide
map.setLayoutProperty('hillshade', 'visibility', 'none');Complete Example ​
html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<link href="https://cdn.mapmetrics-atlas.net/versions/latest/mapmetrics-gl.css" rel="stylesheet" />
<script src="https://cdn.mapmetrics-atlas.net/versions/latest/mapmetrics-gl.js"></script>
<style>body { margin: 0; } #map { height: 100vh; width: 100%; }</style>
</head>
<body>
<div id="map"></div>
<script>
const map = new mapmetricsgl.Map({
container: 'map',
zoom: 7,
center: [11.39085, 47.27574],
style: {
version: 8,
sources: {
osm: {
type: 'raster',
tiles: ['https://a.tile.openstreetmap.org/{z}/{x}/{y}.png'],
tileSize: 256,
attribution: '© OpenStreetMap Contributors',
maxzoom: 19,
},
dem: {
type: 'raster-dem',
tiles: ['https://s3.amazonaws.com/elevation-tiles-prod/terrarium/{z}/{x}/{y}.png'],
tileSize: 256,
encoding: 'terrarium',
maxzoom: 15,
},
},
layers: [
{ id: 'osm', type: 'raster', source: 'osm' },
{
id: 'hillshade',
type: 'hillshade',
source: 'dem',
paint: {
'hillshade-shadow-color': '#473B24',
'hillshade-highlight-color': '#ffffff',
'hillshade-illumination-direction': 335,
'hillshade-exaggeration': 0.5,
},
},
],
},
});
map.addControl(new mapmetricsgl.NavigationControl(), 'top-right');
</script>
</body>
</html>jsx
import React, { useEffect, useRef } from 'react';
import mapmetricsgl from '@mapmetrics/mapmetrics-gl';
import '@mapmetrics/mapmetrics-gl/dist/mapmetrics-gl.css';
const HillshadeLayer = () => {
const mapContainer = useRef(null);
const map = useRef(null);
useEffect(() => {
if (map.current) return;
map.current = new mapmetricsgl.Map({
container: mapContainer.current,
zoom: 7,
center: [11.39085, 47.27574],
style: {
version: 8,
sources: {
osm: {
type: 'raster',
tiles: ['https://a.tile.openstreetmap.org/{z}/{x}/{y}.png'],
tileSize: 256,
attribution: '© OpenStreetMap Contributors',
maxzoom: 19,
},
dem: {
type: 'raster-dem',
tiles: ['https://s3.amazonaws.com/elevation-tiles-prod/terrarium/{z}/{x}/{y}.png'],
tileSize: 256,
encoding: 'terrarium',
maxzoom: 15,
},
},
layers: [
{ id: 'osm', type: 'raster', source: 'osm' },
{
id: 'hillshade',
type: 'hillshade',
source: 'dem',
paint: {
'hillshade-shadow-color': '#473B24',
'hillshade-highlight-color': '#ffffff',
'hillshade-illumination-direction': 335,
'hillshade-exaggeration': 0.5,
},
},
],
},
});
map.current.addControl(new mapmetricsgl.NavigationControl(), 'top-right');
return () => { map.current?.remove(); map.current = null; };
}, []);
return <div ref={mapContainer} style={{ height: '100vh', width: '100%' }} />;
};
export default HillshadeLayer;For more information, visit the MapMetrics GitHub repository.