Landmarks 3D plugin
Important note 3D Landmarks are in Private Preview and unavailable on a Freemium or Pay As You Grow (PAYG) basis. Click the Request Access button above to contact our Sales team. With a key that lacks the access, the map draws no landmarks and the console warns, per tile, that the key needs it.
Use cases
- City-scale 3D visualization - Recognizable buildings (stadiums, cathedrals, towers) appear as detailed meshes instead of plain extrusions.
- Blend with the base map - Landmarks take the look of the base map's 3D buildings and follow it as the map colours change, or wear a look of your own.
- Show landmarks as they are - Landmarks wear the colours of their own stone, brick and roofs, under the map's light.
How it works
- A MapLibre custom layer fetches the landmark tiles covering the viewport and renders them with Three.js, lit to match the map style.
- The layer draws when the map draws and asks for a frame only when a tile arrives, so a still map stays idle. If you change a field on
landmarks.layeryourself, callmap.mapLibreMap.triggerRepaint(). - The layer sits below the POI layer, so POI labels and icons draw over the meshes and roads stay below.
- Layers you add to a module with
beforeLayerConfig: 'poi'go under the meshes too, even when you add them later. To draw a label over a landmark, place its layer at'top'. Landmarks3Dleaves the base map's 3D buildings shown or hidden as they are. Basemap buildings that overlap a landmark carryhas_landmark, and the plugin filters them out so they don't clip through the meshes.- After
setStyle, the plugin restores the layer, the filter and the look. - The plugin builds the tile URL from the global
TomTomConfig(apiKey,commonBaseURL). Behind a credentialed proxy, tile requests send cookies like every other SDK request.withCredentialsoverrides that. - Three.js decodes KTX2 textures with the Basis Universal transcoder, two files the plugin ships beside its bundle:
basis_transcoder.jsandbasis_transcoder.wasm. The plugin loads them with the first landmark tile, so no request goes to a CDN.- By default, Vite and webpack 5 emit the two files with your build, so they load from your app's host. A bundler that doesn't emit the files a
new URL('./file', import.meta.url)names, such as Rollup or esbuild on its own, needstranscoderPath. - The Vite 7 dev server's pre-bundler moves the plugin away from the two files. Upgrade to Vite 8, or add the plugin to the
optimizeDeps.excludethat the project setup gives for the SDK:exclude: ['@tomtom-org/maps-sdk', '@tomtom-org/maps-sdk-plugin-landmarks-3d']. - Set
transcoderPathto serve the Basis Universal transcoder from your own host. Copy the two files from the plugin'sdistfolder, as the configuration shows. - A strict Content Security Policy needs
blob:inworker-src, and the transcoder's host inconnect-src:'self'by default, or the hosttranscoderPathnames.
- By default, Vite and webpack 5 emit the two files with your build, so they load from your app's host. A bundler that doesn't emit the files a
Installation
This plugin uses
@tomtom-org/maps-sdk,maplibre-glandthreeas peer dependencies. Install all three.
npm install @tomtom-org/maps-sdk maplibre-gl three @tomtom-org/maps-sdk-plugin-landmarks-3d
- Follow the SDK Project Setup to create a
TomTomMap. Pitch the camera. From above, the meshes look flat. - Show the base map's 3D buildings, so the landmarks stand in a 3D city. Most standard styles ship the
buildings3Dgroup hidden. Leave this step out for landmarks alone. - Create the plugin. Rendering starts once the map is ready, so there is nothing to await.
import { BaseMapModule } from '@tomtom-org/maps-sdk/map';
import { Landmarks3D } from '@tomtom-org/maps-sdk-plugin-landmarks-3d';
await BaseMapModule.get(map, { groups: { buildings3D: { visible: true } } });
const landmarks = new Landmarks3D(map);
Usage examples
Set the look
The look of the landmarks is a set of knobs. Set the ones you want, and leave the others to the base map:
import { resetKnob, setKnob } from '@tomtom-org/maps-sdk/map';
import { landmarks3DKnobCatalogue } from '@tomtom-org/maps-sdk-plugin-landmarks-3d';
landmarks.updateConfig({ color: '#c8b28a', opacity: 0.9 }); // the rest still follows the base map
setKnob(landmarks, landmarks3DKnobCatalogue, 'verticalGradient', false); // a value held as data
resetKnob(landmarks, landmarks3DKnobCatalogue, 'color'); // back to the base map's colour
landmarks.resetConfig(); // every knob back to the base map
- Each knob you leave unset follows the style's 3D buildings, so landmarks blend in. They keep following them through
setStyleand whatever restyles them: the map colourscolors.artificialandcolors.land, as a borrowed look does, or a paint property you set on3D - Buildingyourself. - While a
Buildings3Don the same map shows, the unsetcolor,opacityandminZoomfollow its buildings instead, which take the place of the style's. The unsetmaxZoomis then 22,textureisdetail, andverticalGradientreportsfalse, since its walls have no gradient. - Every standard style but
streetSatellitehas 3D buildings to follow. OnstreetSatellite, and on a style of your own without them or with a value that isn't a single one, the knob takes the 3D building look ofstreetLight. Setcolorwhere that doesn't suit a dark style. Landmarks3Dfollows the same rules as a map module.applyConfig(config)replaces every knob andupdateConfig(config)the ones you name. For values held as data,setKnobandsetKnobscheck each one againstlandmarks3DKnobCataloguefirst.getKnobreturns the value in force: the one you set, otherwise the base map's.getConfig()returns only the knobs you set.
Knobs
| Knob | Values | Unset |
|---|---|---|
visible |
true or false |
true |
color |
a CSS colour | the colour of the base map's 3D buildings |
texture |
mask, detail or color: what each landmark's own texture adds to its outline. mask adds nothing, detail adds its light and shade over color, and color draws its own colours in place of color |
mask, or detail while a Buildings3D shows |
opacity |
0 to 1 | the opacity of the base map's 3D buildings |
verticalGradient |
true or false: whether the walls darken towards the ground |
as the base map's 3D buildings |
minZoom, maxZoom |
0 to 22: the lowest and highest whole zoom levels the landmarks are drawn at | the zoom range of the base map's 3D buildings |
Show the landmarks' own colours
Each landmark comes with a texture of its own stone, brick, roofs and windows. By default, the texture only carves the landmark's outline, and the color knob paints it. To draw the landmarks in their own colours, set texture to color:
landmarks.updateConfig({ texture: 'color' });
colordraws each landmark with its own textures, lit by the style light's colour and intensity, as the 3D buildings around it are.opacityandverticalGradientstill apply.- The textures keep their daylight colours on a dark style. To dim or tint them for the night, set the styling module's
view.lightknobs, which light the 3D buildings too. detailkeeps thecolorknob and adds the texture's light and shade over it, so the landmarks match the city around them and still show their facades.- Under a
Buildings3D, the textures take its sun, its sky and its shadows instead. There,detailis as strong asfacade.intensity.
Terrain
Landmarks stand on 3D terrain without any configuration. The plugin lowers each one onto the lowest ground under its footprint, so no wall floats above a slope. Landmarks follow the terrain as it loads or its exaggeration changes.
import { TerrainModule } from '@tomtom-org/maps-sdk/map';
import { Landmarks3D } from '@tomtom-org/maps-sdk-plugin-landmarks-3d';
await TerrainModule.get(map, { elevation: { visible: true }, hillshade: { visible: true } });
const landmarks = new Landmarks3D(map);
Toggle visibility
landmarks.setVisible(false); // the same as landmarks.updateConfig({ visible: false })
landmarks.isVisible(); // false
Several maps
Create one Landmarks3D per map. Each one loads the tiles of its own map's view:
const landmarks = maps.map((map) => new Landmarks3D(map));
- Each map takes a WebGL context, and a browser allows only so many per page (16 in Chrome). To show more landmarks than maps, move the maps you have instead of creating new ones.
- With more than one map, Three.js warns about
Multiple active KTX2 loaders. The landmarks still render.
To frame a tall landmark, aim the camera at its mid-height instead of its foot. Set centerClampedToGround: false on the map and give the camera an elevation in metres. MapLibre's flyTo and easeTo leave the elevation where it was, so move with jumpTo:
const map = new TomTomMap({
mapLibre: {
container: 'map',
centerClampedToGround: false,
center: [2.2945, 48.8584],
elevation: 165, // halfway up the Eiffel Tower
zoom: 15.4,
pitch: 60,
},
});
new Landmarks3D(map);
map.mapLibreMap.jumpTo({ center: [-122.3493, 47.6205], elevation: 92 }); // on to the Space Needle
Aim at the ground again before the reader zooms in. MapLibre picks how detailed its tiles are from the camera's distance to the point it aims at, not to the streets. Closing in on a point halfway up a tower draws the streets in thousands of tiles, and the map stalls. On pointerdown, wheel and keydown, jump to the same view aimed at the ground under the middle of the screen, as the demo below does. Listen in the capture phase, so the jump comes before the gesture starts: a jump during a gesture cancels it.
The demo below shows four landmarks of one kind, one per map:
Configuration
Pass the knobs to start with in the constructor, alongside withCredentials and transcoderPath. The first sends cookies with each tile request, and follows the SDK's proxy configuration unless you set it.
const landmarks = new Landmarks3D(map, { minZoom: 14, opacity: 1 });
To load the texture transcoder from your own host, copy basis_transcoder.js and basis_transcoder.wasm from node_modules/@tomtom-org/maps-sdk-plugin-landmarks-3d/dist there. Then set transcoderPath to the path you serve them at, ending in /:
const landmarks = new Landmarks3D(map, { transcoderPath: 'https://assets.example.com/basis/' });
Landmark tiles exist at zoom 15 only. Below it, each view needs more of them: at zoom 13, 16 times as many, and more again on a pitched map. Keep minZoom close to 15, above all with several maps on a page.
3D buildings
Buildings3D draws the base map's buildings in 3D with Three.js. Their walls get windows, a sun or a moon casts shadows across the buildings and the streets, and the dark street and mono presets give some windows a glow from inside. It extrudes the buildings of the vector tiles your map already loads and makes no requests of its own. Unlike the landmarks, it's in Public Preview and works with any API key. Shadows reach 5 km from the map's centre and fade out on the streets before that, so in a steeply tilted view the farthest buildings stand unshadowed.
The playground below draws Manhattan's towers, with controls for their windows, colours, light and shadows, a style switch and a sun that crosses the day.
To add the buildings, create the class, with the knobs to start with if you like. Like Landmarks3D, it starts drawing once the map is ready:
import { Buildings3D } from '@tomtom-org/maps-sdk-plugin-landmarks-3d';
const buildings = new Buildings3D(map, { facade: { pattern: 'office' } });
- While it shows, it takes the place of the style's own 3D buildings, so you don't need the
buildings3Dgroup.buildings.setVisible(false)gives the style's buildings their place back. The filter leaves3D - Buildingwith nothing to query, so abuildings3Dscope onBaseMapModulenever fires, and the Three.js buildings are not interactive either. - It comes back after
setStyle, in the look of the new style. - While a
Landmarks3Don the same map draws its landmarks, it leaves out the buildings they replace. - It draws those landmarks among its buildings, so they stand in the same city. They share its sun and sky, cast their shadows on the buildings and the streets and catch the buildings' ones, and take its wall colour, opacity and
minZoomfor the landmark knobs you leave unset. Their own stonework and windows show as facade detail, as strong asfacade.intensity, with no gradient down the walls. Set the landmarks'texturetocolorto draw them in their own colours instead. Their walls mirror a little of the street and the sky, their darker windows the most, as much asreflections.intensitysets. - Its light is its own
lightandambientLightknobs. The styling module'sview.lightknobs light the style's 3D buildings, which it hides, so they light nothing while it shows. - It draws at sea level. On 3D terrain, hills cover the lower floors of the buildings that stand on them.
Tune the buildings
The look of the buildings is a set of knobs, and a preset gives each one you leave unset its value. By default, the preset is auto, which follows the map style. Light styles and streetSatellite get a sun and glass that mirrors the street and the sky, and the dark street and mono styles get a moon and lit windows.
import { setKnob } from '@tomtom-org/maps-sdk/map';
import { buildings3DKnobCatalogue } from '@tomtom-org/maps-sdk-plugin-landmarks-3d';
buildings.updateConfig({ facade: { pattern: 'glass' } }); // every building in glass
setKnob(buildings, buildings3DKnobCatalogue, 'light.altitude', 12); // a low sun with long shadows
buildings.updateConfig({ preset: 'streetDark' }); // a night city on any map
buildings.resetConfig(); // every knob back to the map style
- Walls take the style's 3D building colour of each kind of building, and follow the map colours that restyle them, until you set
walls.color. Roofs take a shade darker than the walls until you setroofs.color. - Unset,
walls.colorreports the colour of the buildings of no named kind, androofs.colorthe walls' colour, not the darker shade drawn. streetSatellitehas no 3D buildings to take colours from, so its preset brings walls and roofs of its own.Buildings3Dfollows the same rules as a map module.getKnobreturns the value in force, the preset's one when unset, andgetConfig()returns only the knobs you set.
The knobs belong to Buildings3D and draw its own layer. A few reach past it, into the style's 3D buildings and the landmarks:
Buildings knobs
| Knob | Values | Unset |
|---|---|---|
visible |
true or false |
true |
preset |
auto or a standard style ID |
auto |
opacity |
0 to 1 | 1 |
minZoom |
13 to 22 | 15 |
facade.pattern |
mixed, residential, office, glass or plain. mixed picks one by the building's height |
mixed |
facade.intensity |
0 (plain walls) to 1 | the preset's |
facade.floorHeightMeters |
2.5 to 6: the height of one storey, which spaces the rows of windows | 3.4 |
walls.color, roofs.color |
a CSS colour | the style's 3D building colours |
windows.color |
a CSS colour | the preset's |
reflections.intensity |
0 (matte) to 1: how much the window glass mirrors the street, the facades across it with their windows, and the sky, and catches the sun. The reflection slides across the glass as the camera moves. The street it mirrors takes the same sun and sky light, the sky glows on the sun's side, and panes in shadow lose the sun. Glass curtain walls reflect the most, and ambientLight.color is the sky they mirror |
0.7 |
litWindows.color, litWindows.fraction, litWindows.intensity |
a CSS colour, then the share and brightness of the windows lit from inside, 0 to 1 | the preset's, no lit windows on light styles |
light.direction, light.altitude |
0 to 359 degrees clockwise from north, then 5 to 90 degrees above the horizon | the preset's |
light.color, light.intensity |
a CSS colour, then 0 to 1 | the preset's |
ambientLight.color, ambientLight.intensity |
the light from the sky, which lights the walls in shade: a CSS colour, then 0 to 1 | the preset's |
shadows.visible, shadows.intensity |
true or false, then 0 to 1 |
the preset's |
roadLabels.occluded, pois.occluded |
true draws the road names, or the style's POIs, under the buildings, so a building that covers a label hides it. A hidden POI still keeps other labels off its place, and the labels moved under give way to the ones above them where they collide |
false |
With map effects and a map theme
The buildings and the landmarks pair with two other plugins. The map effects plugin makes lit windows glow and softens the city with distance, and the map-theme plugin gives the map colours the walls follow. The playground below circles the Empire State Building in eight looks, each setting the style, the colours, the buildings, the landmarks and the effects together:
- Bloom brings out the lit windows of a dark preset.
- The depth of field treats the map as flat, so each part of a tower takes the depth of the ground behind it on screen, and its top blurs with the far ground. To keep a landmark sharp, raise
depthOfField.focustowards the far ground. - Leave
walls.colorunset to have the walls follow the theme'sartificialcolour, and set the landmarks'colorto make them stand out from the city.
API reference
For complete documentation see the Landmarks3D API Reference and the Buildings3D API Reference.
Related guides and examples
- Landmarks 3D plugin - Landmarks in seven cities, with a control per look knob, their own colours among them, and a built-up colour they follow
- Buildings 3D playground - Manhattan's towers with windows, shadows, a day passing and the look's main knobs live
- Cinematic city orbit - The Empire State Building circled in eight looks, mixing landmarks, buildings, bloom, depth of field and a map theme
- Cinematic city fly-by - A bird's-eye flight round the landmarks of eight cities and down their avenues, over 3D buildings
- 3D terrain map - Landmarks standing on the relief
- 3D landmarks of one kind - Four towers, cathedrals, stadiums or monuments, each on its own map
- Terrain - Hillshade and 3D elevation