OpenStreetMap quickstart — current-state features (live)¶
This notebook teaches the live download type of the earthlens.osm backend: how to pull
current-state OpenStreetMap features for a small area and work with the result. By the
end you will be able to run a named query, read the returned
FeatureCollection, and map it.
earthlens.osm is a vector backend: download() returns a pyramids FeatureCollection
(a geopandas.GeoDataFrame subclass, CRS EPSG:4326) and also writes one file. OSM data is
ODbL (share-alike), so every download emits a LicenseWarning — credit
'© OpenStreetMap contributors' when you redistribute.
Setup¶
The imports for the whole notebook. EarthLens is the unified entry point; matplotlib
draws the maps. We also pick a small output directory — keep OSM bboxes small, the public
Overpass service is shared infrastructure.
import time
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)
!!! note
The public Overpass API allows only 2 concurrent slots per client. This notebook runs
several queries, so we pause briefly (time.sleep) between them to stay a good citizen of
the shared service — bursting would earn an HTTP 429.
The area of interest¶
A tiny bounding box over central Heidelberg, Germany — dense, well-mapped OSM coverage so the
queries return features quickly. The box is lat_lim / lon_lim in degrees (WGS84).
| argument | meaning | value here |
|---|---|---|
lat_lim |
[south, north] |
[49.40, 49.42] |
lon_lim |
[west, east] |
[8.67, 8.71] |
LAT_LIM = [49.40, 49.42]
LON_LIM = [8.67, 8.71]
Quickstart — hospitals as a FeatureCollection¶
The shortest end-to-end example: pick the live:hospitals named query via
variables=, give it the bbox, and download(). The backend builds the Overpass QL, runs
the live query, and returns the features. (The LicenseWarning you see is the ODbL notice —
expected on every result.)
hospitals = EarthLens(
data_source='osm',
variables=['live:hospitals'],
lat_lim=LAT_LIM,
lon_lim=LON_LIM,
path=OUT_DIR,
).download()
len(hospitals), type(hospitals).__name__
Each row is one OSM element. The osm_id / osm_type columns identify it, the element's OSM
tags ride as their own columns, and geometry is a shapely Point (a node) or Polygon (a
building footprint). Let's peek at a few identifying columns.
cols = [
c
for c in ['osm_id', 'osm_type', 'amenity', 'name', 'geometry']
if c in hospitals.columns
]
hospitals[cols].head()
Map the result¶
A FeatureCollection is a GeoDataFrame, so .plot() maps it directly. Point and polygon
hospitals are coloured by their geometry type.
ax = hospitals.plot(
column='osm_type',
categorical=True,
legend=True,
markersize=40,
alpha=0.8,
figsize=(7, 6),
)
ax.set_title('Hospitals in central Heidelberg (OSM via Overpass)')
ax.set_xlabel('longitude')
ax.set_ylabel('latitude')
plt.show()
Another named query — the road network¶
live:roads returns every highway way as a LineString. The named queries are listed
by EarthLens.list_datasets('osm'); the <type>: prefix selects live / history / bulk.
EarthLens.list_datasets('osm')
time.sleep(12) # let the Overpass slots recover before the next query
roads = EarthLens(
data_source='osm',
variables=['live:roads'],
lat_lim=LAT_LIM,
lon_lim=LON_LIM,
path=OUT_DIR,
).download()
ax = roads.plot(linewidth=0.6, color='#444', figsize=(7, 6))
ax.set_title(f'{len(roads)} road segments (live:roads)')
ax.set_xlabel('longitude')
ax.set_ylabel('latitude')
plt.show()
The rest of the live named queries¶
list_datasets('osm') showed five live: queries. We mapped hospitals and roads; the other three are buildings (polygons), cafes (points), and schools (points + polygons). Each is the same call shape with a different tag filter. We fetch all three — pausing between them to respect Overpass's shared slots — and show them side by side.
live_queries = ['live:buildings', 'live:cafes', 'live:schools']
live_results = {}
for q in live_queries:
time.sleep(12) # respect the shared Overpass slots
live_results[q] = EarthLens(
data_source='osm',
variables=[q],
lat_lim=LAT_LIM,
lon_lim=LON_LIM,
path=OUT_DIR,
).download()
{q: len(fc) for q, fc in live_results.items()}
fig, axes = plt.subplots(1, 3, figsize=(15, 5))
for ax, (q, fc) in zip(axes, live_results.items()):
if len(fc):
fc.plot(ax=ax, markersize=8, color='#1f77b4')
ax.set_title(f'{q}\n{len(fc)} features')
ax.set_xlabel('longitude')
ax.set_ylabel('latitude')
fig.tight_layout()
plt.show()
Raw Overpass QL (power users)¶
When the named queries aren't enough, pass your own Overpass QL via query=. A {bbox}
placeholder is filled with the request bbox; the query must request JSON output
([out:json]). Here we fetch museums (tourism=museum).
time.sleep(12) # be gentle with the shared Overpass service
museums = EarthLens(
data_source='osm',
variables=['live:hospitals'], # still required to route to Overpass
lat_lim=LAT_LIM,
lon_lim=LON_LIM,
query='[out:json][timeout:180];(node["tourism"="museum"]({bbox}););out geom;',
path=OUT_DIR,
).download()
museums[[c for c in ['osm_id', 'name', 'geometry'] if c in museums.columns]].head()
Takeaway¶
EarthLens('osm', variables=['live:<query>'], lat_lim=…, lon_lim=…).download()returns aFeatureCollectionof current-state OSM features.- Named queries cover the common cases; a raw
query=(JSON output) is the escape hatch. - Keep the bbox small, and honour the ODbL attribution / share-alike obligation.
Next: ohsome_history.ipynb shows the history-aware history download type.