3D Buildings
Buildings stand up on a Maptoolkit map in one of two ways: as 3D bodies with their roof shapes, together with official 3D city models and 3D trees, or as footprints extruded into blocks with flat tops. For a 3D map surface, see 3D Terrain.
For buildings like the ones on www.maptoolkit.org, you need two plugins. A style alone gives you extruded blocks at most.
@maptoolkit/maplibre-scene3d-controlreads the 3D scene from the style and adds its layers to the map.@maptoolkit/maplibre-3d-layersdraws the buildings with roof shapes and the trees. It uses three.js.
Both work with MapLibre GL JS 6 or later.
Quick start
Install the two plugins together with three.js and MapLibre GL JS:
npm install @maptoolkit/maplibre-scene3d-control @maptoolkit/maplibre-3d-layers three maplibre-glAdd the control to a map with the Street or Summer style:
import * as maplibregl from "maplibre-gl";
import { Scene3dControl } from "@maptoolkit/maplibre-scene3d-control";
import "@maptoolkit/maplibre-scene3d-control/style.css";
const map = new maplibregl.Map({
container: "map",
style: "https://styles.maptoolkit.org/street.json",
center: [16.3731, 48.2085], // Vienna, St. Stephen's Cathedral
zoom: 16,
pitch: 70,
maxPitch: 85,
canvasContextAttributes: { antialias: true }, // smooth edges on distant trees
});
map.addControl(new Scene3dControl());The control loads @maptoolkit/maplibre-3d-layers itself, so the code imports only the control. The community buildings source needs no API key, so leave out the control’s apiKey option. Without a bundler, load both packages from a CDN with an import map, as the 3D Buildings with Roof Shapes example shows.
Two ways
| Extruded blocks | Buildings with roof shapes | |
|---|---|---|
| What you see | Footprints raised to their height, with flat tops | 3D bodies with roof shapes from zoom 15. Where an official 3D city model exists, as in Vienna, its buildings replace the OpenStreetMap ones |
| Data | building layer of the mtk tiles | https://tiles.maptoolkit.org/buildings.json |
| Drawn with | A standard fill-extrusion layer | Fill3DLayer from @maptoolkit/maplibre-3d-layers (with three.js) for the roof shapes, a fill-extrusion for the other buildings |
| Set up by | The style (Street, Summer) or a layer you add | @maptoolkit/maplibre-scene3d-control, which reads the scene from the style |
| Libraries | Every library with fill-extrusion, see What each library supports | MapLibre GL JS 6 or later |
| Styles | Street and Summer ship the layer. For the other styles, switch it on in MapMaker or add it yourself | Street and Summer carry the scene that the Scene3D Control reads. For the other styles, switch it on in MapMaker |
The two work together. Street and Summer show extruded blocks on their own. Add the two plugins, and the buildings with roof shapes replace them once they have loaded.
Buildings with roof shapes
Street and Summer, and their -3d and language versions, carry a 3D scene in the style metadata under maptoolkit:scene3d. The scene holds the buildings from buildings.json and 3D tree models, placed on single trees, along tree rows and in woods from zoom 15. Winter, Hiking, Cycling, Light and Dark have no scene.
MapLibre ignores style metadata, so the scene stays invisible until you add @maptoolkit/maplibre-scene3d-control. The control reads the scene and adds its layers; @maptoolkit/maplibre-3d-layers draws the buildings with roof shapes and the trees.
What the control does:
- It adds the buildings source and puts the buildings and trees below the style’s labels.
- It loads
@maptoolkit/maplibre-3d-layers, which uses three.js, only once the map reaches zoom 12. The buildings source has no tiles below zoom 12. - Once the buildings source has loaded, it hides the style’s own extruded buildings (
building_3d_multicolored), so no building is drawn twice. If the source cannot load, the style keeps them. - It reads the scene again on every style change. Switch to Winter and the scene goes away; switch back to Street and it returns.
The buildings get more opaque as you tilt the map. Looking straight down at zoom 15 and below, they are hidden, so tilt the map to see them. On a -3d style the buildings stand on the terrain.
For a style without a scene, switch the scene on in MapMaker (see Extruded blocks), pass one with the control’s scene option, or add the Fill3DLayer of @maptoolkit/maplibre-3d-layers yourself. Both READMEs list the options.
The buildings tileset
https://tiles.maptoolkit.org/buildings.json needs no API key. It has tiles from zoom 12 to 15. Beyond 15 the tiles overzoom: the geometry stays, no new detail arrives.
| Source layer | Zoom | Content | Draw with |
|---|---|---|---|
buildings2d | 12–15 | OpenStreetMap footprints with the height, extrude and roof fields of the building layer | fill-extrusion |
buildings3d | 15 | 3D bodies with roof and wall surfaces, built from the OpenStreetMap roof tags (roof:shape and others). Where an official 3D city model exists, as in Vienna, its buildings replace the OpenStreetMap ones | Fill3DLayer from @maptoolkit/maplibre-3d-layers |
Every buildings3d feature carries type (roof or wall), source (osm or citygml) and colour_index. Buildings from OpenStreetMap also carry roof_shape, height and min_height, and roof_colour, roof_material and building_colour where OpenStreetMap records them.
buildings3dis not standard vector tile geometry. It uses a Maptoolkit extension of the Mapbox Vector Tile (MVT) format that stores a height for every vertex. Standard vector tile renderers read its fields but not its geometry, and afill-extrusiononbuildings3ddraws broken shapes. Usefill-extrusiononbuildings2donly.buildings2dalone has gaps from zoom 15. From zoom 15 it leaves out every building thatbuildings3ddraws, so no building appears in both layers. For extruded blocks without the plugins, use thebuildinglayer of themtktiles instead.
Extruded blocks: fill-extrusion on the building layer
Street and Summer contain a fill-extrusion layer. Their building_3d_multicolored layer draws from zoom 14, filters on extrude, and takes its geometry from the height and min_height attributes. The -3d and language versions of both have it too; the other five styles draw buildings flat.
This is what Street and Summer show without the two plugins, and what the libraries without the plugins can draw. With the plugins, the style’s layer is hidden once the buildings with roof shapes have loaded.
To add 3D buildings to one of the other styles, switch them on in MapMaker, no code needed: in Easy Mode, open Edit Layer Groups › Buildings › Contained Layers and make 3D Buildings visible. The style then has the extruded blocks and also the buildings of the 3D scene for the plugins. 3D Trees under Nature adds the trees to the scene. Export the style and use it as described in Customize a Map Style.
To add the layer in code instead, the 3D Buildings example is a complete, runnable page.
The building layer
The building source layer exists from zoom 13 to 15. Zoom 15 is the maximum zoom of the tileset, so beyond it tiles overzoom: the geometry stays, no new detail arrives.
| Field | Type | Use |
|---|---|---|
extrude | Boolean | Whether the footprint is meant to stand up |
height | Number | Building height in metres |
min_height | Number | Height at which the building starts, for parts above ground |
colour_index | Number | Index for varying the fill across neighbouring buildings |
building_colour | String | Colour where OpenStreetMap records one |
roof_shape, roof_colour, roof_angle, roof_direction, roof_height, roof_levels, roof_material, roof_orientation | String | Roof attributes |
Filter on extrude. Not every footprint is meant to be extruded, and the Street style filters on the same flag. At a Midtown Manhattan view it excludes roughly 120 of about 16,700 footprints, mostly building parts that would otherwise stack on top of the building they belong to.
What each library supports
| Library | 3D buildings | Example |
|---|---|---|
| MapLibre GL JS | Yes: with roof shapes through the two plugins, as extruded blocks without them | Buildings, Buildings with roof shapes |
| deck.gl | Extruded blocks, through a MapLibre basemap | None |
| MapLibre Native Android | Extruded blocks | Buildings |
| MapLibre Native iOS | Extruded blocks | Buildings |
| Flutter | Extruded blocks | Buildings |
| React Native | Extruded blocks | Buildings |
| kepler.gl | Whatever the style you load carries | None |
| Leaflet | No | None, not possible |
| OpenLayers | No | None, not possible |
| CesiumJS | No | None, not possible |
Buildings with roof shapes need MapLibre GL JS 6 or later and the two plugins. The buildings3d layer uses a Maptoolkit extension of the vector tile format that the other libraries cannot draw, so they show extruded blocks from the building layer. kepler.gl has no setting of its own: it loads a style URL and renders what that style contains, so the Street and Summer styles give extruded buildings.
Where 3D buildings are not possible, and why
Leaflet. The maplibre-gl-leaflet plugin renders a MapLibre style inside Leaflet, but its README states plainly that there is no rotation, bearing or pitch support. Without pitch the map is always viewed from directly above, where extrusions have no visible effect. Use MapLibre GL JS directly for 3D.
OpenLayers. There is no fill-extrusion equivalent. OpenLayers is a 2D renderer; 3D requires pairing it with a separate engine such as Cesium.
CesiumJS. Our CesiumJS setup uses the basemap as an imagery layer, and Cesium’s 3D buildings come from 3D Tiles rather than vector tile footprints. You get a 3D globe with our cartography painted on it, not 3D buildings from our data.