Terrain module
The Terrain Module draws the relief of the map from the elevation data of the map style, in two ways that combine freely:
- Hillshade shades slopes with light and shadow, so hills, valleys and mountains read at any camera pitch.
- 3D elevation raises the map surface itself, which a pitched camera shows.
What this module owns: whether the hillshade is shown and how the shading looks — see Appearance below — and whether and how high the surface is raised. See Who owns what.
Initialization
import { TomTomMap, TerrainModule } from '@tomtom-org/maps-sdk/map';
const map = new TomTomMap({ mapLibre: { container: 'map' } });
const terrain = await TerrainModule.get(map, { hillshade: { visible: true }, elevation: { visible: true } });
get() adds the terrain style part, which carries the elevation data, when the style does not include it yet. The SDK keeps one terrain module per map, so a second get() returns the same instance.
TerrainModule.get(map) on its own changes nothing on screen. The style parts the SDK adds — traffic flow, traffic incidents and the hillshade — are hidden until you show them, and the surface stays as the style draws it. On a TomTom style that is flat, which is also where resetConfig() returns it; a terrain your own style or MapLibre code raised stays until you set elevation.visible.
Hillshade
Show the shading at initialization, or toggle it later:
const terrain = await TerrainModule.get(map, { hillshade: { visible: true } });
terrain.setHillshadeVisible(false);
terrain.isHillshadeVisible(); // false
isHillshadeVisible() reads the hillshade.visible setting back, false until something shows it.
Appearance
The shading's method, light direction, strength, zoom range and colours sit beside visible under hillshade: method, lightDirection, exaggeration, maxZoom, shadowColor, highlightColor, accentColor. They restyle the style's own hillshade layer, read its own value while unset, and show once the shading does.
The standard styles ramp the shading's exaggeration to zero by zoom 13 and stop drawing it there, so on a city-zoom map the shading shows nothing until both exaggeration and maxZoom are set. The light comes from 335° (north-west) by cartographic convention: relief lit from the south reads inside-out to most people.
const terrain = await TerrainModule.get(map, {
// Keep the shading at every zoom:
hillshade: { visible: true, method: 'multidirectional', exaggeration: 0.5, maxZoom: 22 },
});
setKnob(terrain, terrainKnobCatalogue, 'hillshade.shadowColor', '#3b2f2f');
hillshade.exaggeration is how strongly the slopes are shaded and raises nothing; to exaggerate the height of the 3D surface, use elevation.exaggeration below.
3D elevation
Raise the surface with elevation.visible, and scale the relief with elevation.exaggeration (1 is true elevation). It scales height only; the shading's strength is the hillshade.exaggeration knob:
const map = new TomTomMap({
mapLibre: { container: 'map', center: [7.66, 45.98], zoom: 12.5, pitch: 70, maxPitch: 85 },
});
const terrain = await TerrainModule.get(map, {
elevation: { visible: true, exaggeration: 1.5 },
hillshade: { visible: true },
});
setKnob(terrain, terrainKnobCatalogue, 'elevation.exaggeration', 2); // keeps the surface raised
terrain.setElevationVisible(false); // flat again, still shaded
terrain.isElevationVisible(); // false
isElevationVisible() reads the elevation.visible setting back. Until something sets it, it reports
whether the map's own terrain raises the surface.
- Pitch the camera. Seen from straight above, a raised surface looks flat. MapLibre's default
maxPitchis 60°; raise it to look along a valley. - Combine it with the hillshade. The shading keeps a slope readable where the camera flattens it.
- The exaggeration is kept while elevation is off, so turning it back on restores the relief you had.
- The surface reads its own elevation source,
TERRAIN_SOURCE_ID: a copy of the hillshade'sHILLSHADE_SOURCE_IDthat the module adds the first time elevation is enabled, since MapLibre draws both at a lower quality from one source. With both on, each elevation tile loads twice.
The 3D buildings of the base map (BaseMapModule layer group buildings3D) and the Landmarks 3D plugin stand on the raised surface.
Every setting as data
terrainKnobCatalogue lists every setting of the module's configuration with its kind, a
description, the range of the number and, where one value holds everywhere, its default. Each id is
the setting's path in the config, so a UI can build its controls from it, with no map loaded:
import { knobEntryOf, terrainKnobCatalogue } from '@tomtom-org/maps-sdk/map';
const exaggeration = knobEntryOf(terrainKnobCatalogue, 'elevation.exaggeration');
// { id: 'elevation.exaggeration', kind: 'number', description: '…', range: { min: 0.5, max: 3, step: 0.1, bounds: 'hard-min' }, default: 1 }
| Id | Kind | Default |
|---|---|---|
hillshade.visible |
toggle | false |
hillshade.method |
enum: standard, basic, igor, combined, multidirectional |
none: the style's own |
hillshade.lightDirection |
number, 0–359 | none: the style's own |
hillshade.exaggeration |
number, 0–1 | none: the style's own |
hillshade.maxZoom |
number, 10–22 | none: the style's own |
hillshade.shadowColor, hillshade.highlightColor, hillshade.accentColor |
colour | none: the style's own |
elevation.visible |
toggle | none: the terrain your style or MapLibre code raised; flat on TomTom styles |
elevation.exaggeration |
number, at least 0.5 (hard-min) |
1 |
terrainKnobIdslists the ids in catalogue order,TerrainKnobIdis their union, andTerrainKnobValueOf<ID>the plain value one takes. The entries share their shape,KnobEntry, with every module's catalogue, andsetKnobsets one by id.getKnobreads a look knob left unset as the loaded style draws it, andgetStyleColorreads the three colours by their map-wide ids,terrain.hillshade.shadowColorand its siblings, with or without the module.
Style changes
Both parts are map configuration, not style content: the module re-applies them on top of every style that setStyle loads, and a clean switch (resetState: true) turns them off. The sky above a tilted horizon is a styling knob, view.sky, which a style switch keeps too. The playground below switches styles under a tilted camera, with terrain and sky kept:
Events
The hillshade is a raster layer with no features to click or hover, so the module has lifecycle events only:
terrain.events.on('config-change', (config) => {
console.log('Terrain config changed:', config);
});
API reference
For complete documentation of all Terrain Module properties, methods, and types, see the TerrainModule API Reference.
Related guides and examples
Related examples
- 3D terrain map - Terrain, hillshade, 3D buildings and Ljubljana Castle on its hill
- Hillshade - Hillshade shading on a flat map
- 3D terrain playground - Terrain, sky and exaggeration that outlive a style switch
- Globe and 3D terrain - A globe that zooms into 3D terrain
- Route fly-over playground - A camera flying a mountain route, clear of the relief it crosses
Related map modules
- Map styling - Sizes and map colours across the whole map, and the globe and sky for a tilted view
- Base map module - 3D buildings on top of the terrain