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:
- A
raster-demsource — tiles containing elevation data for every point on the map - The
terrainstyle 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',
style: {
version: 8,
sources: {
// 1. Your regular base map (OSM, satellite, etc.)
osm: {
type: 'raster',
tiles: ['https://a.tile.openstreetmap.org/{z}/{x}/{y}.png'],
tileSize: 256,
},
// 2. Elevation data source (free AWS terrain tiles — no API key needed)
terrainSource: {
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' },
],
// 3. Enable 3D terrain
terrain: {
source: 'terrainSource',
exaggeration: 1.5,
},
},
pitch: 60, // Tilt the camera to see the 3D effect
maxPitch: 85,
});Exaggeration ​
exaggeration controls how dramatic the 3D effect looks:
| Value | Effect |
|---|---|
0 | Flat map (terrain disabled) |
1 | Real-world scale |
1.5 | Slightly dramatic (good default) |
3 | Very 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: 60or higher to see the 3D effect clearly - Set
maxPitch: 85to allow steep camera angles - Add
sky: {}to the style for a realistic sky 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,
style: {
version: 8,
sources: {
osm: {
type: 'raster',
tiles: ['https://a.tile.openstreetmap.org/{z}/{x}/{y}.png'],
tileSize: 256,
attribution: '© OpenStreetMap Contributors',
maxzoom: 19,
},
terrainSource: {
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' }],
terrain: { source: 'terrainSource', exaggeration: 1.5 },
sky: {},
},
});
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';
const terrainStyle = {
version: 8,
sources: {
osm: {
type: 'raster',
tiles: ['https://a.tile.openstreetmap.org/{z}/{x}/{y}.png'],
tileSize: 256,
attribution: '© OpenStreetMap Contributors',
maxzoom: 19,
},
terrainSource: {
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' }],
terrain: { source: 'terrainSource', exaggeration: 1.5 },
sky: {},
};
const Terrain3D = () => {
const mapContainer = useRef(null);
const map = useRef(null);
useEffect(() => {
if (map.current) return;
map.current = new mapmetricsgl.Map({
container: mapContainer.current,
style: terrainStyle,
center: [11.39085, 47.27574],
zoom: 12,
pitch: 70,
maxPitch: 85,
});
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.