Buildings Missing from OSM

On this page

Problem statement

The building density map shows a dense city centre, a sparse ring around it, and suburbs that look almost empty. On satellite imagery the suburbs are packed with houses. OpenStreetMap building coverage is excellent in much of Europe and patchy in much of the world, and incompleteness is not random: it follows where mappers live, which imports happened, and which humanitarian mapping campaigns chose to work. A density or morphology analysis on incomplete footprints measures mapping effort.

The fix is to measure completeness against an independent source before analysing, and to switch to β€” or fill from β€” a more complete layer where OSM falls short.

Quick answer

Compare OSM with Overture's buildings, which conflate OSM with machine-learned footprints, and look at which source each Overture building came from:

import duckdb, pandas as pd, geopandas as gpd, osmnx as ox

ox.settings.http_user_agent = "spatialworkflow-examples/1.0"
con = duckdb.connect()
con.execute("INSTALL spatial; LOAD spatial; INSTALL httpfs; LOAD httpfs; SET s3_region='us-west-2';")

def overture(w, s, e, n):
    df = con.execute(f"""
        SELECT sources[1].dataset AS source, ST_AsWKB(geometry) AS wkb
        FROM read_parquet('s3://overturemaps-us-west-2/release/2026-09-23.0/theme=buildings/type=building/*',
                          hive_partitioning = 1)
        WHERE bbox.xmin > {w} AND bbox.xmax < {e} AND bbox.ymin > {s} AND bbox.ymax < {n}""").df()
    return gpd.GeoDataFrame(df.drop(columns="wkb"),
                            geometry=gpd.GeoSeries.from_wkb(df.wkb.apply(bytes)), crs=4326)

AREAS = {"Den Haag centre": (4.300, 52.072, 4.320, 52.084),
         "Nairobi, Kibera and surroundings": (36.770, -1.325, 36.800, -1.300)}
for name, (w, s, e, n) in AREAS.items():
    osm = ox.features_from_bbox((w, s, e, n), {"building": True})
    osm = osm[osm.geom_type.isin(["Polygon", "MultiPolygon"])]
    ov = overture(w, s, e, n)
    crs = ov.estimate_utm_crs()
    print(f"{name}: OSM {len(osm):,} buildings ({osm.to_crs(crs).area.sum() / 1e6:.3f} kmΒ²), "
          f"Overture {len(ov):,} ({ov.to_crs(crs).area.sum() / 1e6:.3f} kmΒ²)")
    print("   Overture by source:", ov.source.value_counts().to_dict())
Den Haag centre: OSM 4,415 buildings (0.887 kmΒ²), Overture 4,208 (0.820 kmΒ²)
   Overture by source: {'OpenStreetMap': 4208}
Nairobi, Kibera and surroundings: OSM 18,903 buildings (1.862 kmΒ²), Overture 26,233 (2.315 kmΒ²)
   Overture by source: {'OpenStreetMap': 18785, 'Google Open Buildings': 6303, 'Microsoft ML Buildings': 1145}

In Den Haag every Overture building comes from OSM: OSM is the complete layer, and the two differ only by edge effects of the box. In Nairobi, 7,448 of Overture's 26,233 buildings β€” more than a quarter β€” come from Google's and Microsoft's machine-learned footprints, and those are buildings OSM does not have.

An independent reference, area comparisons, completeness maps

1. Pick an independent reference

A cadastre where one exists; otherwise machine-learned footprints (Microsoft, Google) or Overture's conflated layer, which records the source of each building.

2. Compare footprint area, not only counts

Counts depend on how attached buildings are divided. Covered area is the better completeness measure.

3. Map completeness, not just a total

Completeness varies block by block. A grid of OSM/reference area ratios shows where the gaps are and whether they correlate with the phenomenon being studied.

A map of Kibera and its surroundings in Nairobi with every building footprint coloured by source: most are OpenStreetMap, with Google and Microsoft machine-learned footprints concentrated in some neighbourhoods. A map of Kibera and its surroundings in Nairobi with every building footprint coloured by source: most are OpenStreetMap, with Google and Microsoft machine-learned footprints concentrated in some neighbourhoods.
Overture’s 26,233 buildings in the guide’s Kibera box, coloured by the dataset each one came from. OpenStreetMap supplies 18,785. The 6,303 from Google Open Buildings and 1,145 from Microsoft are buildings OpenStreetMap does not have, and they are not spread evenly: some neighbourhoods are almost entirely machine-learned footprints, while in others OpenStreetMap is close to complete. Overture release 2026-09-23.0.

4. Choose: switch, fill or restrict

Use the reference instead; add reference buildings where OSM has none nearby; or restrict the analysis to areas where OSM is near complete. State which.

5. Re-check after filling

Combining sources can double-count buildings drawn in both. Remove reference footprints that overlap OSM ones before merging.

Overture buildings by source in central Den Haag and in Nairobi, against the OSM count for the same box. where Overture adds buildings, OSM has gapsOSMOverture from OSMmachine-learnedDen Haag centre4,4154,2080Nairobi, Kibera18,90318,7857,448 (28%)Google Open Buildings 6,303 and Microsoft 1,145 in the Nairobi box spatialworkflow.io
Where Overture adds machine-learned buildings, OSM has gaps.

Completeness on a grid, and filling the gaps

The data used on this page

OpenStreetMap buildings fetched live through the Overpass API with OSMnx, and Overture Maps buildings (release 2026-09-23.0) read straight from Overture's public S3 bucket with DuckDB. overpass-api.de refuses connections from here, so the queries go to the maps.mail.ru mirror of the same API. OpenStreetMap is edited continuously, so its counts drift a little between runs; Overture's are fixed by the release. The OpenStreetMap answers are read as the Overpass mirror gave them on 10 October 2026.

import io
import os
import osmnx as ox
import requests
import zipfile

# OpenStreetMap as Overpass returned it on 10 October 2026, saved as osmnx's own cache so the counts
# below do not drift as OSM is edited; delete the cache folder to query live instead
if not os.path.exists("cache"):
    reply = requests.get("https://data.spatialworkflow.io/osm-cache-osm-buildings-missing-2026-10-10.zip", timeout=300)
    reply.raise_for_status()
    with zipfile.ZipFile(io.BytesIO(reply.content)) as archive:
        archive.extractall("cache")

ox.settings.overpass_url = "https://maps.mail.ru/osm/tools/overpass/api"
print("Overpass:", ox.settings.overpass_url)
Overpass: https://maps.mail.ru/osm/tools/overpass/api

Example 1 β€” completeness on a grid

import numpy as np
from shapely.geometry import box

w, s, e, n = AREAS["Nairobi, Kibera and surroundings"]
osm = ox.features_from_bbox((w, s, e, n), {"building": True})
osm = osm[osm.geom_type.isin(["Polygon", "MultiPolygon"])]
ov = overture(w, s, e, n)
crs = ov.estimate_utm_crs()
osm_m, ov_m = osm.to_crs(crs), ov.to_crs(crs)
x0, y0, x1, y1 = ov_m.total_bounds
grid = gpd.GeoDataFrame(geometry=[box(x, y, x + 500, y + 500) for x in np.arange(x0, x1, 500)
                                  for y in np.arange(y0, y1, 500)], crs=crs)
def area_in(g):
    parts = gpd.overlay(g[["geometry"]], grid.reset_index(), how="intersection")
    return parts.assign(a=parts.area).groupby("index").a.sum()
ratio = (area_in(osm_m) / area_in(ov_m)).reindex(grid.index)
print(f"{len(grid)} cells of 500 m; OSM/Overture footprint area ratio: "
      f"median {ratio.median():.2f}, cells under 0.5: {(ratio < 0.5).mean():.0%}")
42 cells of 500 m; OSM/Overture footprint area ratio: median 0.85, cells under 0.5: 12%

Most cells are fairly complete β€” Kibera has been mapped intensively by community projects β€” but the median cell still has 15% less footprint area in OSM, and one cell in eight has less than half. A density map built from OSM here would show those cells as sparse neighbourhoods, when what is sparse is the mapping.

Example 2 β€” what the gap does to a density measure

cov_osm = osm_m.area.sum() / grid.area.sum()
cov_ov = ov_m.area.sum() / grid.area.sum()
print(f"building coverage from OSM:      {cov_osm:.1%}")
print(f"building coverage from Overture: {cov_ov:.1%}")
building coverage from OSM:      17.7%
building coverage from Overture: 22.0%

OSM implies a fifth less building coverage than Overture over the same 42 cells. Every downstream measure β€” coverage, tessellation cell sizes, fabric clusters β€” inherits the gap.

Example 3 β€” fill without double counting

extra = ov_m[ov_m.source != "OpenStreetMap"]
overlaps = gpd.sjoin(extra[["geometry"]], osm_m[["geometry"]], predicate="intersects")
fill = extra.drop(index=overlaps.index.unique())
merged = gpd.GeoDataFrame(pd.concat([osm_m[["geometry"]].assign(source="OpenStreetMap"),
                                     fill[["geometry", "source"]]]), crs=crs)
print(f"added {len(fill):,} non-overlapping machine-learned footprints; "
      f"merged coverage {merged.area.sum() / grid.area.sum():.1%}")
added 7,448 non-overlapping machine-learned footprints; merged coverage 22.5%

Overture has already done this conflation; the example shows the logic for combining any two sources. Keep the source column so later analyses can test sensitivity to it.

Building coverage of the Nairobi study area computed from OSM, from Overture and from OSM filled with non-overlapping machine-learned footprints. building coverage, Nairobi, 42 cells of 500 mOSM17.7%Overture22.0%OSM + ML, no overlap22.5%010%20%OSM implies a fifth less coverage; the median cell is at 0.85 of the reference spatialworkflow.io
The same neighbourhoods, the coverage each source implies.

Why OSM completeness is uneven

Why OSM completeness is uneven

OSM is mapped by people. Countries with active communities and large imports of official data are near complete; many cities in the Global South were mapped in bursts by humanitarian projects that covered some districts thoroughly and left others empty. The gaps are systematic and often correlate with poverty and informality β€” exactly the places many analyses care about.

Why machine-learned footprints fill the gap

Models trained on satellite imagery can detect most buildings everywhere imagery is recent. They merge attached buildings, miss some small structures and occasionally hallucinate, but their coverage is consistent. Overture combines them with OSM, preferring OSM where it exists.

Why the source must stay in the data

A morphological measure computed partly on hand-mapped and partly on machine-learned footprints mixes two definitions of "a building". Keeping the source per footprint lets you check whether a pattern is real or an artefact of where each source dominates.

Checks to run on OSM building completeness before a density or morphology analysis. before trusting OSM footprintscompare area, not countsdivision differs; ground does notmap completeness per cellNairobi: 1 cell in 8 under half the referenceread Overture’s source columnmachine-learned share = OSM’s gapfill without double countingdrop ML footprints that touch OSM onestrust a clean-looking mapgaps look like sparse areasIncompleteness follows mapping effort, so it correlates with what you are studying spatialworkflow.io
Measure completeness before measuring the city.

Imports, merged footprints and recent construction

  • Import quality: some imported footprints are old or offset; completeness is not correctness.
  • Machine-learned merges: terraces become one polygon, which lowers counts and raises median sizes.
  • Recent construction may be in neither source; imagery dates matter in fast-growing cities.
  • Overture releases update the conflation monthly; pin the release.
  • Licences: OSM is ODbL; machine-learned datasets have their own licences β€” Overture records them.

FAQ

How complete is OpenStreetMap building data?

Near complete in much of Europe and North America; highly variable elsewhere. Measure it for your area.

How do I check OSM completeness?

Compare footprint area with an independent source β€” a cadastre, machine-learned footprints or Overture β€” on a grid.

What is Overture's building layer?

A conflation of OSM with machine-learned and other building datasets, published as GeoParquet with a source for each building.

Can I merge OSM with machine-learned footprints?

Yes, adding only reference footprints that do not overlap OSM ones, and keeping a source column.

Does missing data bias density maps?

Yes β€” systematically towards where mapping has happened.

Which source should I use outside Europe?

Usually Overture or a machine-learned dataset, checked against imagery for your area.