Skip to content

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:

  1. A raster-dem source (elevation data tiles)
  2. A hillshade layer 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 ​

PropertyWhat it does
hillshade-shadow-colorColor of shadowed slopes
hillshade-highlight-colorColor of sunlit slopes
hillshade-accent-colorColor of very steep slopes
hillshade-illumination-directionDirection of the sun (0° = North)
hillshade-exaggerationShadow 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: '&copy; 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: '&copy; 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.