CHAPTER 26
3D buildings and feature extrusion
Turning 2D building footprints into 3D meshes with Mapbox fill-extrusion, LOD strategies, rendering order, and the math behind extrusion.
Building extrusion takes a 2D polygon footprint and a height value and turns them into a 3D mesh. The math draws directly on polygon triangulation from Chapter 11 and the rendering pipeline from Chapter 23.
3D building extrusion · drag to orbit
A 3D building is just a 2D polygon (the footprint) with height. The extrusion step is mechanical: copy the polygon upward by the height value, then connect the edges with wall quads. Every 3D building renderer — Mapbox, CesiumJS, deck.gl — does exactly this.
From footprint to mesh
A building is a 2D polygon (the footprint) + a height. Extruding it means:
- Triangulate the floor polygon (ear-clipping or Delaunay)
- Lift each vertex by the height to create the roof
- Generate wall quads for each footprint edge
- Flip normals outward so lighting works correctly
function extrudePolygon(ring, height) {
const floor = triangulate(ring); // floor triangles (facing down)
const roof = floor.map(v => ({...v, z: v.z + height })); // same, raised
const walls = [];
for (let i = 0; i < ring.length - 1; i++) {
const [a, b] = [ring[i], ring[i + 1]];
// Two triangles per wall quad
walls.push(
{ a, b, c: { ...b, z: b.z + height } },
{ a, b: { ...b, z: b.z + height }, c: { ...a, z: a.z + height } }
);
}
return { floor, roof, walls };
}
Each wall edge becomes two triangles (a quad split along the diagonal). The normal direction — pointing outward from the building — determines which direction receives lighting. A reversed normal makes a wall appear lit when it should be in shadow.
Height data sources
| Source | Height attribute | Coverage |
|---|---|---|
| OpenStreetMap | building:height (metres) | Patchy but growing |
| Overture Maps | height field | Global, curated |
| Microsoft Building Footprints | height | 60+ countries |
| Lidar-derived | Actual roof height | Urban areas |
| Default fallback | 10 m (2–3 storeys) | Anywhere |
Level of Detail (LOD)
Rendering 500,000 buildings as full 3D meshes tanks performance. LOD strategies:
- Zoom < 14: show 2D footprints only (no extrusion)
- Zoom 14–15: simplified boxes, no roof detail
- Zoom > 15: full mesh with roof geometry, textures
map.addLayer({
id: "buildings-3d",
type: "fill-extrusion",
source: "composite",
"source-layer": "building",
minzoom: 14,
filter: ["has", "height"],
paint: {
"fill-extrusion-height": ["get", "height"],
"fill-extrusion-base": ["get", "min_height"], // for multi-level buildings
"fill-extrusion-color": "#1C1C28",
"fill-extrusion-opacity": 0.9,
},
});
Continue reading "3D buildings and feature extrusion"
You've reached the end of the free preview. Unlock all 22 paid chapters, including distance math, bearings, polygons, spatial indexing, and 3D map rendering — plus a downloadable PDF and the companion code repo.
- All 22 paid chapters with worked examples
- Downloadable PDF for offline reading
- Companion GitHub repo (JavaScript + Python)
- Free updates for life
Multiple payment options including Wise, PayPal, and bank transfer.
Related chapters
- Terrain and elevation data — buildings sit on terrain
- Camera and perspective math — the camera through which buildings are viewed
- How maps render — tiles, vectors, and the GPU pipeline — buildings in the rendering pipeline