OpenStreetMap history — building footprints over time (history)¶
This notebook teaches the history download type of the earthlens.osm backend: OSM is
history-aware, so you can ask what the map looked like at a point in time. We pull building
footprints for two yearly snapshots of the same area and compare them.
Like the live path, the history type returns a vector FeatureCollection (EPSG:4326) and emits the
ODbL LicenseWarning. Unlike live, a history query needs a time (start= / end=).
Setup¶
Imports and a small output directory. The same tiny Heidelberg bbox as the quickstart.
from pathlib import Path
import matplotlib.pyplot as plt
from earthlens.core import EarthLens
OUT_DIR = Path('osm_output')
OUT_DIR.mkdir(exist_ok=True)
LAT_LIM = [49.40, 49.42]
LON_LIM = [8.67, 8.71]
BUILDINGS = "history:buildings" # the named query reused across the snapshot / range examples below
A single historical snapshot¶
history:buildings returns building footprints as they existed at the start date. ohsome's
own columns ride along: @osmId (the element id) and @snapshotTimestamp (the snapshot
instant). Here we ask for the state on 1 January 2018.
buildings_2018 = EarthLens(
data_source='osm',
variables=[BUILDINGS],
lat_lim=LAT_LIM,
lon_lim=LON_LIM,
start='2018-01-01',
path=OUT_DIR,
).download()
len(buildings_2018), buildings_2018['@snapshotTimestamp'].iloc[0]
Map the 2018 footprints¶
The footprints are polygons; plot them filled.
ax = buildings_2018.plot(
facecolor='#9ecae1', edgecolor='#3182bd', linewidth=0.3, figsize=(7, 6)
)
ax.set_title(f'{len(buildings_2018)} building footprints — 2018-01-01 (ohsome)')
ax.set_xlabel('longitude')
ax.set_ylabel('latitude')
plt.show()
Compare two snapshots — how the map grew¶
Because ohsome is history-aware, the same query at a later date shows how mapping progressed. We pull the 2023 snapshot and compare the building counts — OSM coverage grows over time as contributors add detail.
buildings_2023 = EarthLens(
data_source='osm',
variables=[BUILDINGS],
lat_lim=LAT_LIM,
lon_lim=LON_LIM,
start='2023-01-01',
path=OUT_DIR,
).download()
counts = {'2018': len(buildings_2018), '2023': len(buildings_2023)}
counts
A quick bar chart makes the growth between the two snapshots legible.
fig, ax = plt.subplots(figsize=(4, 4))
ax.bar(list(counts), list(counts.values()), color=['#9ecae1', '#3182bd'])
ax.set_title('Mapped building footprints, same bbox')
ax.set_ylabel('feature count')
plt.show()
The other history named queries¶
Beside buildings, the catalog ships history:amenities (POI points) and history:highways (road lines). Same call shape, at a single snapshot.
history_other = {}
for q in ['history:amenities', 'history:highways']:
history_other[q] = EarthLens(
data_source='osm',
variables=[q],
lat_lim=LAT_LIM,
lon_lim=LON_LIM,
start='2023-01-01',
path=OUT_DIR,
).download()
{q: len(fc) for q, fc in history_other.items()}
A time range, not just a snapshot¶
Passing start= alone asks for one snapshot. Passing start= and end= asks ohsome for the interval start/end: it returns each feature at both boundary snapshots, so @snapshotTimestamp carries more than one instant. Here, buildings across 2018 and 2023.
buildings_range = EarthLens(
data_source='osm',
variables=[BUILDINGS],
lat_lim=LAT_LIM,
lon_lim=LON_LIM,
start='2018-01-01',
end='2023-01-01',
path=OUT_DIR,
).download()
sorted(buildings_range['@snapshotTimestamp'].unique())
Raw ohsome filter (power users)¶
Pass your own ohsome filter via
filter= to query any tag combination — here, parks (leisure=park) as polygons at the
2023 snapshot.
parks = EarthLens(
data_source='osm',
variables=[BUILDINGS], # still required to route to ohsome
lat_lim=LAT_LIM,
lon_lim=LON_LIM,
start='2023-01-01',
filter='leisure=park and geometry:polygon',
path=OUT_DIR,
).download()
len(parks)
Takeaway¶
- the
historytype adds a time axis to OSM: passstart=(and optionallyend=) to get the map as it was, or a range. - The result carries
@osmId/@snapshotTimestampso you can track features across time. - A raw
filter=queries any tag combination; the ODbL obligation applies to ohsome data too.
See quickstart.ipynb for the current-state live type.