OpenStreetMap#

This dataset is external to the Digital Earth Africa platform.

Background#

OpenStreetMap is a free and open geographic database, providing location information of roads, buildings, and landmarks. It is built, maintained and supported by a world-wide geospatial community.

OpenStreetMap data is licensed under the Open Data Commons Open Database License (ODbL) by the OpenStreetMap Foundation (OSMF). It is free to copy, distribute, transmit and adapt the data, providing credit to OpenStreetMap and its contributors. Full licence information and attribution guidelines can be found at the OpenStreetMap’s Copyright and License page.

Description#

This notebook will demonstrate how to access OpenStreetMap data using Python package `osmnx <https://osmnx.readthedocs.io/en/stable/osmnx.html>`__.

Getting started#

To run this analysis, run all the cells in the notebook, starting with the “Load packages” cell.

Load packages#

Import Python packages that are used for the analysis.

[1]:
try:
    import duckdb
except ImportError:
    !pip install duckdb
    import duckdb

from shapely import from_wkb
import geopandas as gpd

We will use the duckdb package to access OpenStreetMap data and visualise using geopandas. We need to connect to DuckDB and set some parameters first.

[2]:
# Connect to DuckDB and load spatial/http modules
con = duckdb.connect()
con.execute("INSTALL spatial; LOAD spatial;")
con.execute("INSTALL httpfs; LOAD httpfs;")

con.execute("SET s3_region='us-west-2';")
con.execute("SET s3_access_key_id='';")
con.execute("SET s3_secret_access_key='';")

# Fetch latest release string automatically
latest_release = con.execute(
    "SELECT latest FROM 'https://stac.overturemaps.org/catalog.json'"
).fetchone()[0]

Analysis parameters#

This section defines the analysis parameters, including:

  • central_lat, central_lon, buffer: center lat/lon and analysis window size for the area of interest

The default location is in Johannesburg, South Africa.

[3]:
# Set the central latitude and longitude.
central_lat = -26.2041
central_lon = 28.0473

# Set the buffer to load around the central coordinates.
buffer = 0.05

# Compute the bounding box for the study area.
xmin, xmax = central_lon - buffer, central_lon + buffer
ymin, ymax = central_lat - buffer, central_lat + buffer

bbox = (xmin, ymin, xmax, ymax)

Retrieve building geometries from OpenStreetMap#

Different types of geometries can be queried by altering the class in the query and the URL. In this case, we are selecting all building footprints in the area.

Using the location and tags, geometries will be retrieved from OpenStreetMap and the results are returned as a Geopandas GeoDataFrame.

[4]:
# Query OpenStreetMap vector data hosted on AWS S3
query = f"""
SELECT
    id,
    names.primary AS name,
    class AS building_type,
    height,
    geometry
FROM read_parquet('s3://overturemaps-us-west-2/release/{latest_release}/theme=buildings/type=building/*')
WHERE bbox.xmin >= {xmin} AND bbox.xmax <= {xmax}
  AND bbox.ymin >= {ymin} AND bbox.ymax <= {ymax}
"""

df = con.execute(query).fetchdf()

# Convert WKB bytes to Shapely geometry objects
df['geometry'] = df['geometry'].apply(lambda g: from_wkb(bytes(g)) if g else None)

# Build GeoDataFrame
geometries = gpd.GeoDataFrame(df, geometry='geometry', crs="EPSG:4326")

Next, we check the first few rows of the geometries we’ve loaded for the building footprints.

[5]:
# Display the first five building records.
geometries.head()
[5]:
id name building_type height geometry
0 aafca274-d2c3-4f1c-874a-848ef1f33244 None None NaN POLYGON ((27.99768 -26.24087, 27.99771 -26.240...
1 9be17437-90a4-4129-ad05-63e335844c8c None None NaN POLYGON ((27.99768 -26.24169, 27.99757 -26.241...
2 50ea7281-0f41-4d0f-b5a0-671d60606f0a None None NaN POLYGON ((27.99765 -26.24198, 27.99764 -26.242...
3 7ea6bfe2-e9af-40a2-bd8a-9659c37df5b1 None None NaN POLYGON ((27.99744 -26.24225, 27.99737 -26.242...
4 912e634e-4e71-4a05-8549-70b926d45ff3 None None NaN POLYGON ((27.99764 -26.24231, 27.99752 -26.242...
[6]:
#fill building_type = None
geometries['building_type_filled'] = geometries['building_type'].fillna('unspecified')

#visualize the geometries retrieved
geometries.plot(figsize=(10,12), cmap='tab20', column='building_type_filled', legend=True, legend_kwds={'bbox_to_anchor':(1.25,1)});
../../../_images/sandbox_notebooks_Datasets_OpenStreetMap_14_0.png

Retrieve road geometries from OpenStreetMap#

Road networks can be retrieved using the highway tag. A list of tag values can be used to select specific types of geometries.

Tag values for highway and their descriptions can be found at https://wiki.openstreetmap.org/wiki/OpenStreetMap_Carto/Lines

[7]:
# Query OpenStreetMap vector data hosted on AWS S3
query = f"""
SELECT
    id,
    names.primary AS name,
    class AS highway,
    geometry
FROM read_parquet('s3://overturemaps-us-west-2/release/{latest_release}/theme=transportation/type=segment/*')
WHERE bbox.xmin >= {xmin} AND bbox.xmax <= {xmax}
  AND bbox.ymin >= {ymin} AND bbox.ymax <= {ymax}
"""

df = con.execute(query).fetchdf()

# Convert WKB bytes to Shapely geometry objects
df['geometry'] = df['geometry'].apply(lambda g: from_wkb(bytes(g)) if g else None)

# Build GeoDataFrame
geometries = gpd.GeoDataFrame(df, geometry='geometry', crs="EPSG:4326")
[8]:
#print the first few rows
geometries.head()
[8]:
id name highway geometry
0 98bb3b8c-5d1c-4462-90a3-164af738c8d5 None service LINESTRING (28.0072 -26.25237, 28.00731 -26.25...
1 3661d160-4fe4-4590-b95b-6076fd2e4d5a None service LINESTRING (28.0076 -26.25192, 28.0076 -26.252...
2 8b4b56ef-9d38-4233-baa6-a5942aeecb5d Caro Street residential LINESTRING (28.00719 -26.25194, 28.0076 -26.25...
3 00aaa097-4405-405c-b716-de45b439a6a4 Tilrae Drive residential LINESTRING (28.00643 -26.25196, 28.00689 -26.2...
4 b244db64-ae95-49f2-ac0c-2f87d013e12b Limerick Road residential LINESTRING (28.00635 -26.25015, 28.00639 -26.2...
[9]:
#visualize the geometries retrieved

highway_types = geometries['highway'].dropna().unique().tolist()

geometries.dropna(subset=['highway']).plot(figsize=(10, 12), cmap='tab10', column='highway', categorical=True, categories=highway_types, legend=True);
../../../_images/sandbox_notebooks_Datasets_OpenStreetMap_18_0.png

Additional information#

License: The code in this notebook is licensed under the Apache License, Version 2.0. Digital Earth Africa data is licensed under the Creative Commons by Attribution 4.0 license.

This notebook accesses OpenStreetMap data, therefore when sharing analysis results, attribution should follow guidellines from the OpenStreetMap’s Copyright and License page.

Contact: If you need assistance, please post a question on the Open Data Cube Slack channel or on the GIS Stack Exchange using the open-data-cube tag (you can view previously asked questions here). If you would like to report an issue with this notebook, you can file one on Github.

Compatible datacube version:

[10]:
import datacube
print(datacube.__version__)
1.9.13

Last Tested:

[11]:
from datetime import datetime
datetime.today().strftime('%Y-%m-%d')
[11]:
'2026-07-23'