3D Buildings
Add a fill-extrusion layer on the Maptoolkit building source layer and tilt the map with pitch to see buildings in 3D. This view looks south from inside Central Park toward the Midtown Manhattan skyline.
The Street style already ships this layer and needs no code at all. Use this example for the other six styles, which draw buildings flat. See 3D Buildings and Terrain for what each library supports.
- Filter on
extrude. Thebuildinglayer carries a booleanextrudeflag marking the footprints meant to stand up. The Street style filters on the same flag. At this Midtown view it excludes about 120 of roughly 16,700 footprints, mostly building parts that would otherwise double up on the building they belong to. - Set
fill-extrusion-basefrommin_height. Without it, anything that starts above ground level, such as a storey bridging a street, sinks into the ground. - Insert below the first symbol layer. Otherwise labels render behind the buildings and the map becomes hard to read at pitch.
Buildings are present from zoom 13 to 15, which is the maximum zoom of the tileset. Beyond 15 the tiles overzoom, so the geometry stays but no new detail arrives.
import * as maplibregl from 'https://cdn.jsdelivr.net/npm/maplibre-gl@6.9.0/dist/maplibre-gl.mjs';
import { MaptoolkitLogoControl } from 'https://cdn.jsdelivr.net/npm/@maptoolkit/maplibre-gl-logo/+esm';
const map = new maplibregl.Map({ container: 'map', attributionControl: { customAttribution: '', compact: false }, style: 'https://styles.maptoolkit.org/summer.json', center: [-73.9770, 40.7645], zoom: 14.3, pitch: 62, bearing: 200 }); // Midtown Manhattan, looking out from Central Park South
map.on('load', function () {
// Insert the extrusion under the first symbol layer, so street and place
// labels stay readable on top of the buildings instead of behind them.
const firstSymbol = map.getStyle().layers.find(function (l) { return l.type === 'symbol'; });
map.addLayer({
id: 'buildings-3d',
type: 'fill-extrusion',
source: 'mtk',
'source-layer': 'building',
minzoom: 14,
// Not every footprint is meant to stand up. Building parts and similar
// carry extrude=false, and the Street style filters on the same flag.
filter: ['==', ['get', 'extrude'], true],
paint: {
'fill-extrusion-color': '#cfd8dc',
'fill-extrusion-height': ['get', 'height'],
'fill-extrusion-base': ['get', 'min_height'],
'fill-extrusion-opacity': 0.9
}
}, firstSymbol && firstSymbol.id);
});
map.addControl(new MaptoolkitLogoControl());<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/maplibre-gl@6.9.0/dist/maplibre-gl.css" />
<style>
html, body { width: 100%; height: 100%; margin: 0; padding: 0; }
#map { width: 100%; height: 100%; }
</style>
</head>
<body>
<div id="map"></div>
<script type="module">
import * as maplibregl from 'https://cdn.jsdelivr.net/npm/maplibre-gl@6.9.0/dist/maplibre-gl.mjs';
import { MaptoolkitLogoControl } from 'https://cdn.jsdelivr.net/npm/@maptoolkit/maplibre-gl-logo/+esm';
const map = new maplibregl.Map({ container: 'map', attributionControl: { customAttribution: '', compact: false }, style: 'https://styles.maptoolkit.org/summer.json', center: [-73.9770, 40.7645], zoom: 14.3, pitch: 62, bearing: 200 }); // Midtown Manhattan, looking out from Central Park South
map.on('load', function () {
// Insert the extrusion under the first symbol layer, so street and place
// labels stay readable on top of the buildings instead of behind them.
const firstSymbol = map.getStyle().layers.find(function (l) { return l.type === 'symbol'; });
map.addLayer({
id: 'buildings-3d',
type: 'fill-extrusion',
source: 'mtk',
'source-layer': 'building',
minzoom: 14,
// Not every footprint is meant to stand up. Building parts and similar
// carry extrude=false, and the Street style filters on the same flag.
filter: ['==', ['get', 'extrude'], true],
paint: {
'fill-extrusion-color': '#cfd8dc',
'fill-extrusion-height': ['get', 'height'],
'fill-extrusion-base': ['get', 'min_height'],
'fill-extrusion-opacity': 0.9
}
}, firstSymbol && firstSymbol.id);
});
map.addControl(new MaptoolkitLogoControl());
</script>
</body>
</html>