Working with street networks and geospatial data used to require expensive proprietary software or painful manual clean-up. If you're building routing models, analyzing urban accessibility, or mapping spatial distribution, OSMnx changes how you acquire and process data. Created by Geoff Boeing, this Python library interacts directly with OpenStreetMap to download, analyze, and visualize spatial networks without tedious preprocessing.
This guide breaks down how OSMnx datasets work, how to download and structure them efficiently, and how to avoid common performance pitfalls when scaling your geospatial projects.
What Are OSMnx Datasets?
OSMnx datasets aren't static files stored on a server. Instead, they are dynamic graph objects generated on the fly from OpenStreetMap data. When you query a city or coordinate bounding box, the library queries the OpenStreetMap Overpass API, parses the XML response, and builds a directed graph network using NetworkX.
The resulting dataset contains two core components:
- Nodes: Represent intersections and dead-ends, containing latitude, longitude, and internal IDs.
- Edges: Represent the street segments connecting nodes, storing attributes like street names, highway types, max speeds, and one-way status.
Unlike traditional GIS shapefiles that require heavy cleaning for network routing, OSMnx structures these elements into topological graphs ready for shortest-path calculations and spatial analysis out of the box.
Getting Started with OSMnx Installation
Setting up your environment correctly saves hours of debugging later, especially since OSMnx relies on complex geospatial dependencies like Fiona, GDAL, and Shapely.
The safest way to install OSMnx is via Conda-forge to ensure binary dependencies match correctly. Run these commands in your terminal:
conda config --prepend channels conda-forge
conda create -n ox_env python=3.10
conda activate ox_env
conda install osmnx
If you prefer using pip, install it alongside its core geospatial stack, though keeping your virtual environment isolated is critical to prevent version conflicts with GDAL:
pip install osmnx
Downloading and Structuring Network Datasets
You can download street networks using several geographic boundaries. Whether you want an entire country, a specific city, or a custom polygon, the syntax remains straightforward.
Downloading by Place Name
To download a street network for a named administrative boundary, use graph_from_place:
import osmnx as ox
# Download walk network for downtown Chicago
place_name = "Chicago, Illinois, USA"
G = ox.graph_from_place(place_name, network_type="walk")
The network_type parameter accepts values like "drive", "walk", "bike", or "all", filtering the underlying OpenStreetMap tags automatically.
Downloading by Bounding Box or Polygon
When administrative boundaries don't fit your study area, pass coordinate bounds or custom Shapely polygons:
# Using a bounding box: north, south, east, west
G_box = ox.graph_from_bbox(
north=45.53, south=45.50, east=-122.65, west=-122.68, network_type="drive"
)
Once downloaded, you can project the graph to local UTM coordinates for accurate metric distance calculations:
G_projected = ox.project_graph(G)
Converting Graphs to GeoDataFrames
While NetworkX graphs excel at routing algorithms, many mapping and spatial analysis workflows require standard geospatial vector formats. OSMnx lets you extract nodes and edges into separate GeoDataFrames instantly:
nodes, edges = ox.graph_to_gdfs(G_projected)
# Inspect the first few edge records
print(edges[["name", "length", "highway"]].head())
These GeoDataFrames integrate smoothly with standard geospatial libraries like Geopandas, enabling you to export data directly to GeoJSON, Shapefiles, or GeoPackage formats for GIS software like QGIS.
Practical Applications for Spatial Analysis
Armed with clean network datasets, you can tackle complex spatial problems efficiently.
1. Shortest Path Routing
Finding the fastest or shortest path between two points doesn't require third-party routing engines for small to medium-sized regions. You can find nearest nodes by coordinates and compute paths directly:
# Define origin and destination coordinates
orig_lat, orig_lon = 41.8781, -87.6298
dest_lat, dest_lon = 41.8950, -87.6400
orig_node = ox.distance.nearest_nodes(G, X=orig_lon, Y=orig_lat)
dest_node = ox.distance.nearest_nodes(G, X=dest_lon, Y=dest_lat)
# Calculate shortest path by length
route = ox.shortest_path(G, orig_node, dest_node, weight="length")
# Plot the route on a map
ox.plot_graph_route(G, route)
2. Isochrone and Accessibility Mapping
Urban planners frequently analyze service areas—how far someone can travel within a specific time limit. OSMnx handles travel time calculations by estimating speeds based on highway tags or custom speed columns:
# Add travel times to edges based on speed limits
G = ox.routing.add_edge_speeds(G)
G = ox.routing.add_edge_travel_times(G)
center_node = ox.distance.nearest_nodes(G, X=-87.6298, Y=41.8781)
# Generate a 15-minute walking isochrone polygon
import networkx as nx
travel_time = 15 * 60 # seconds
subgraph = nx.ego_graph(
G, center_node, distance="travel_time", to_dst=True
)
node_points = [
(data["x"], data["y"]) for node, data in subgraph.nodes(data=True)
]
# Convert nodes to a convex hull or polygon boundary
Common Mistakes to Avoid
Working with live API data introduces several common traps. Avoid these missteps to keep your code running smoothly:
- Querying overly large boundaries: Requesting an entire state or country via
graph_from_placewill exhaust memory or trigger Overpass API timeouts. Clip your queries to cities, neighborhoods, or custom bounding boxes. - Forgetting coordinate reference systems (CRS): Calculating lengths or buffers on unprojected latitude/longitude degrees results in distorted geometric measurements. Always project your graph before computing distances.
- Ignoring API usage policies: Heavy, automated scripts that bombard the public Overpass API can get your IP blocked. Cache your downloaded graphs locally using
ox.save_graphml()to avoid repeated downloads.
Frequently Asked Questions
How do I save and load an OSMnx graph locally?
You can serialize your graph dataset to disk using GraphML format:
ox.save_graphml(G, filepath="my_street_network.graphml")
G_loaded = ox.load_graphml("my_street_network.graphml")
This prevents you from hammering the OpenStreetMap API every time you run your script.
Why are some streets missing from my downloaded dataset?
OpenStreetMap relies on community contributions. If a path lacks proper tagging (like highway=residential or highway=service), OSMnx excludes it based on your network filter. Check your network_type parameter or inspect the raw OSM tags for that area.
Can OSMnx calculate routes considering traffic congestion?
No. OSMnx relies on static OpenStreetMap metadata, such as speed limits and road types. It doesn't incorporate real-time traffic feeds or live congestion data, making it best for structural accessibility and theoretical routing analysis.
How does OSMnx handle multi-modal routing?
By default, network types like walk or drive filter out incompatible edges. For multi-modal analysis (combining walking and public transit), you'll typically need specialized transport graph libraries, though setting network_type="all" captures both pedestrian and vehicle infrastructure.