MapMath

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.

3 min read

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

optional — skip if familiarrefresher

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:

  1. Triangulate the floor polygon (ear-clipping or Delaunay)
  2. Lift each vertex by the height to create the roof
  3. Generate wall quads for each footprint edge
  4. 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

SourceHeight attributeCoverage
OpenStreetMapbuilding:height (metres)Patchy but growing
Overture Mapsheight fieldGlobal, curated
Microsoft Building Footprintsheight60+ countries
Lidar-derivedActual roof heightUrban areas
Default fallback10 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,
  },
});
Chapter 26 · Paid content

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.