Skip to content

3D Terrain ​

Make your map look like a real landscape by enabling 3D terrain. The map reads elevation data from a special tile source (raster-dem) and pushes mountains up into 3D.

No external libraries needed. This is built directly into MapMetrics GL — no three.js, no plugins.

Drag to pan · Hold right-click and drag to tilt · Use the terrain button (mountain icon) to toggle 3D

How It Works ​

3D terrain needs two things:

  1. A raster-dem source — tiles containing elevation data for every point on the map
  2. The terrain style property — tells the map to use that elevation data to push the land surface up into 3D
javascript
const map = new mapmetricsgl.Map({
  container: 'map',
  // 1. Your regular base map — here a MapMetrics vector style
  style: 'https://gateway.mapmetrics-atlas.net/styles/?fileName=YOUR_STYLE.json&token=YOUR_TOKEN',
  center: [11.39085, 47.27574],
  zoom: 12,
  pitch: 70,      // Tilt the camera to see the 3D effect
  maxPitch: 85,
});

// 2. Wait for the vector style to finish loading, then add elevation
map.on('load', () => {
  // Elevation data source (free AWS terrain tiles — no API key needed)
  map.addSource('terrainSource', {
    type: 'raster-dem',
    tiles: ['https://s3.amazonaws.com/elevation-tiles-prod/terrarium/{z}/{x}/{y}.png'],
    tileSize: 256,
    encoding: 'terrarium',
    maxzoom: 15,
  });

  // 3. Enable 3D terrain
  map.setTerrain({ source: 'terrainSource', exaggeration: 1.5 });
});

Exaggeration ​

exaggeration controls how dramatic the 3D effect looks:

ValueEffect
0Flat map (terrain disabled)
1Real-world scale
1.5Slightly dramatic (good default)
3Very exaggerated mountains

Add a Terrain Toggle Button ​

javascript
map.addControl(new mapmetricsgl.TerrainControl({
  source: 'terrainSource',
  exaggeration: 1.5,
}), 'top-right');

This adds a mountain icon button that toggles 3D terrain on/off.

Tips ​

  • Set pitch: 70 or higher to see the 3D effect clearly
  • Set maxPitch: 85 to allow steep camera angles
  • Add a sky layer for a realistic atmospheric background
  • Mountains and valleys look best at zoom 8–14

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: 12,
        center: [11.39085, 47.27574],
        pitch: 70,
        maxPitch: 85,
        // Replace with your own MapMetrics style URL + token
        style: 'https://gateway.mapmetrics-atlas.net/styles/?fileName=YOUR_STYLE.json&token=YOUR_TOKEN',
      });

      map.on('load', () => {
        map.addSource('terrainSource', {
          type: 'raster-dem',
          tiles: ['https://s3.amazonaws.com/elevation-tiles-prod/terrarium/{z}/{x}/{y}.png'],
          tileSize: 256,
          encoding: 'terrarium',
          maxzoom: 15,
        });
        map.setTerrain({ source: 'terrainSource', exaggeration: 1.5 });
        map.addLayer({ id: 'sky', type: 'sky', paint: { 'sky-type': 'atmosphere' } });
      });

      map.addControl(new mapmetricsgl.NavigationControl({ visualizePitch: true }), 'top-right');
      map.addControl(new mapmetricsgl.TerrainControl({ source: 'terrainSource', exaggeration: 1.5 }), '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';

// Replace with your own MapMetrics style URL + token
const STYLE_URL = 'https://gateway.mapmetrics-atlas.net/styles/?fileName=YOUR_STYLE.json&token=YOUR_TOKEN';

const Terrain3D = () => {
  const mapContainer = useRef(null);
  const map = useRef(null);

  useEffect(() => {
    if (map.current) return;
    map.current = new mapmetricsgl.Map({
      container: mapContainer.current,
      style: STYLE_URL,
      center: [11.39085, 47.27574],
      zoom: 12,
      pitch: 70,
      maxPitch: 85,
    });

    map.current.on('load', () => {
      map.current.addSource('terrainSource', {
        type: 'raster-dem',
        tiles: ['https://s3.amazonaws.com/elevation-tiles-prod/terrarium/{z}/{x}/{y}.png'],
        tileSize: 256,
        encoding: 'terrarium',
        maxzoom: 15,
      });
      map.current.setTerrain({ source: 'terrainSource', exaggeration: 1.5 });
      map.current.addLayer({ id: 'sky', type: 'sky', paint: { 'sky-type': 'atmosphere' } });
    });

    map.current.addControl(new mapmetricsgl.NavigationControl({ visualizePitch: true }), 'top-right');
    map.current.addControl(new mapmetricsgl.TerrainControl({ source: 'terrainSource', exaggeration: 1.5 }), 'top-right');

    return () => { map.current?.remove(); map.current = null; };
  }, []);

  return <div ref={mapContainer} style={{ height: '100vh', width: '100%' }} />;
};

export default Terrain3D;

For more information, visit the MapMetrics GitHub repository.