OSMnx: Python for Street Networks

OSMnx: New York City urban street network visualized and analyzed with Python and OpenStreetMap dataCheck out the journal article about OSMnx.

OSMnx is a Python package for downloading administrative boundary shapes and street networks from OpenStreetMap. It allows you to easily construct, project, visualize, and analyze complex street networks in Python with NetworkX. You can get a city’s or neighborhood’s walking, driving, or biking network with a single line of Python code. Then you can simply visualize cul-de-sacs or one-way streets, plot shortest-path routes, or calculate stats like intersection density, average node connectivity, or betweenness centrality. You can download/cite the paper here.

In a single line of code, OSMnx lets you download, construct, and visualize the street network for, say, Modena Italy:

import osmnx as ox
ox.plot_graph(ox.graph_from_place('Modena, Italy'))

OSMnx: Modena Italy networkx street network in Python from OpenStreetMap

Installing OSMnx

OSMnx is on GitHub and you can install it with conda:

conda install -c conda-forge osmnx

Or with pip:

pip install osmnx

If you are pip installing OSMnx, install geopandas and rtree first. It’s easiest to use conda-forge to get these dependencies installed. If you’re interested in OSMnx but don’t know where to begin, check out this guide to getting started with Python.

How to use OSMnx

There are several examples and tutorials in the examples repo. I’ll demonstrate 5 use cases for OSMnx in this post:

  1. Automatically download administrative place boundaries and shapefiles
  2. Download and construct street networks
  3. Correct and simplify network topology
  4. Save street networks to disk as shapefiles, GraphML, or SVG
  5. Analyze street networks: routing, visualization, and calculating network stats

1. Get administrative place boundaries and shapefiles

To acquire administrative boundary GIS data, one must typically track down shapefiles online and download them. But what about for bulk or automated acquisition and analysis? There must be an easier way than clicking through numerous web pages to download shapefiles one at a time. With OSMnx, you can download place shapes from OpenStreetMap (as geopandas GeoDataFrames) in one line of Python code – and project them to UTM (zone calculated automatically) and visualize in just one more line of code:

import osmnx as ox
city = ox.gdf_from_place('Berkeley, CA')
ox.plot_shape(ox.project_gdf(city))

OSMnx Berkeley city boundaries from OpenStreetMap

You can just as easily get other place types, such as neighborhoods, boroughs, counties, states, or nations – any place geometry in OpenStreetMap:

place1 = ox.gdf_from_place('Manhattan, New York City, New York, USA')
place2 = ox.gdf_from_place('Cook County, Illinois')
place3 = ox.gdf_from_place('Iowa, USA')
place4 = ox.gdf_from_place('Bolivia')

Or you can pass multiple places into a single query to construct a single shapefile from their geometries. You can do this with cities, states, countries or any other geographic entities and then save as a shapefile to your hard drive:

places = ox.gdf_from_places(['Botswana', 'Zambia', 'Zimbabwe'])
places = ox.project_gdf(places)
ox.save_gdf_shapefile(places)
ox.plot_shape(ox.project_gdf(places))

OSMnx: Botswana, Zambia, Zimbabwe national borders from OpenStreetMap

2. Download and construct street networks

To acquire street network GIS data, one must typically track down Tiger/Line roads from the US census bureau, or individual data sets from other countries or cities. But what about for bulk, automated analysis? And what about informal paths and pedestrian circulation that Tiger/Line ignores? And what about street networks outside the United States? OSMnx handles all of these uses.

OSMnx lets you download street network data and build topologically-corrected street networks, project and plot the networks, and save the street network as SVGs, GraphML files, or shapefiles for later use. The street networks are directed and preserve one-way directionality. You can download a street network by providing OSMnx any of the following (demonstrated in the examples below):

  • a bounding box
  • a lat-long point plus a distance
  • an address plus a distance
  • a polygon of the desired street network’s boundaries
  • a place name or list of place names

You can also specify several different network types:

  • ‘drive’ – get drivable public streets (but not service roads)
  • ‘drive_service’ – get drivable public streets, including service roads
  • ‘walk’ – get all streets and paths that pedestrians can use (this network type ignores one-way directionality)
  • ‘bike’ – get all streets and paths that cyclists can use
  • ‘all’ – download all (non-private) OSM streets and paths
  • ‘all_private’ – download all OSM streets and paths, including private-access ones

You can download and construct street networks in a single line of code using these various techniques:

2a) street network from bounding box

This gets the drivable street network within some lat-long bounding box, in a single line of Python code, then projects it to UTM, then plots it:

G = ox.graph_from_bbox(37.79, 37.78, -122.41, -122.43, network_type='drive')
G_projected = ox.project_graph(G)
ox.plot_graph(G_projected)

OSMnx: San Francisco street network in Python from OpenStreetMap

You can get different types of street networks by passing a network_type argument, including driving, walking, biking networks (and more).

2b) street network from lat-long point

This gets the street network within 0.75 km (along the network) of a latitude-longitude point:

G = ox.graph_from_point((37.79, -122.41), distance=750, network_type='all')
ox.plot_graph(G)

OSMnx: San Francisco street network in Python from OpenStreetMap

You can also specify a distance in cardinal directions around the point, instead of along the network.

2c) street network from address

This gets the street network within 1 km (along the network) of the Empire State Building:

G = ox.graph_from_address('350 5th Ave, New York, NY', network_type='drive')
ox.plot_graph(G)

OSMnx: New York street network in Python from OpenStreetMap

You can also specify a distance in cardinal directions around the address, instead of along the network.

2d) street network from polygon

Just load a shapefile with geopandas, then pass its shapely geometry to OSMnx. This gets the street network of the Mission District in San Francisco:

G = ox.graph_from_polygon(mission_shape, network_type='drive')
ox.plot_graph(G)

OSMnx: San Francisco Mission District street network in Python from OpenStreetMap

2e) street network from place name

Here’s where OSMnx shines. Pass it any place name for which OpenStreetMap has boundary data, and it automatically downloads and constructs the street network within that boundary. Here, we create the driving network within the city of Los Angeles:

G = ox.graph_from_place('Los Angeles, California', network_type='drive')
ox.plot_graph(G)

OSMnx: Los Angeles street network in Python from OpenStreetMap

You can just as easily request a street network within a borough, county, state, or other geographic entity. You can also pass a list of places (such as several neighboring cities) to create a unified street network within them. This list of places can include strings and/or structured key:value place queries:

places = ['Los Altos, CA, USA',
          {'city':'Los Altos Hills', 'state':'California'},
          'Loyola, California']
G = ox.graph_from_place(places, network_type='drive')
ox.plot_graph(G)

OSMnx: Silicon Valley street network in Python from OpenStreetMap

2f) street networks from all around the world

In general, US street network data is fairly easy to come by thanks to Tiger/Line shapefiles. OSMnx makes it easier by making it available with a single line of code, and better by supplementing it with all the additional data from OpenStreetMap. However, you can also get street networks from anywhere in the world – places where such data might otherwise be inconsistent, difficult, or impossible to come by:

G = ox.graph_from_place('Modena, Italy')
ox.plot_graph(G)

OSMnx: Modena Italy networkx street network in Python from OpenStreetMap

G = ox.graph_from_place('Belgrade, Serbia')
ox.plot_graph(G)

OSMnx: Belgrade Serbia networkx street network in Python from OpenStreetMap

G = ox.graph_from_address('Maputo, Mozambique', distance=3000)
ox.plot_graph(G)

OSMnx: Maputo Mozambique networkx street network in Python from OpenStreetMap

G = ox.graph_from_address('Bab Bhar, Tunis, Tunisia', distance=3000)
ox.plot_graph(G)

OSMnx: Tunis Tunisia networkx street network in Python from OpenStreetMap

3. Correct and simplify network topology

Simplification is done by OSMnx automatically under the hood, but we can break it out to see how it works. OpenStreetMap nodes can be weird: they include intersections, but they also include all the points along a single street segment where the street curves. The latter are not nodes in the graph theory sense, so we remove them algorithmically and consolidate the set of edges between “true” network nodes into a single edge.

When we first download and construct the street network from OpenStreetMap, it looks something like this:

OSMnx: Arizona suburb before simplifying - networkx street network in Python from OpenStreetMap

We want to simplify this network to only retain the nodes that represent the junction of multiple streets. OSMnx does this automatically. First it identifies all non-intersection nodes:

OSMnx: Arizona suburb before simplifying - networkx street network in Python from OpenStreetMap

And then it removes them, but faithfully maintains the spatial geometry of the street segment between the true intersection nodes:

OSMnx: Arizona suburb after simplifying - networkx street network in Python from OpenStreetMap

Above, all the non-intersection nodes have been removed, all the true intersections (junctions of multiple streets) remain in blue, and self-loop nodes are in purple. There are two simplification modes: strict and non-strict. In strict mode (above), OSMnx considers two-way intersections to be topologically identical to a single street that bends around a curve. If you want to retain these intersections when the incident edges have different OSM IDs, use non-strict mode:

OSMnx: Arizona suburb after simplifying - networkx street network in Python from OpenStreetMap

4. Save street networks to disk

OSMnx can save the street network to disk as a GraphML file to work with later in Gephi or networkx. Or it can save the network (such as this one, for the New York urbanized area) as ESRI shapefiles to work with in any GIS:

QGIS: New York street network from OpenStreetMap

OSMnx can also save street networks as SVG files for design work in Adobe Illustrator:

Adobe Illustrator: Piedmont California street network SVG from OpenStreetMap

You can then load any network you saved as GraphML back into OSMnx to calculate network stats, solve routes, or visualize it.

5. Analyze street networks

Since this is the whole purpose of working with street networks, I’ll break analysis out in depth in a future dedicated post. But I’ll summarize some basic capabilities briefly here. OSMnx is built on top of NetworkX, geopandas, and matplotlib, so you can easily analyze networks and calculate spatial network statistics:

G = ox.graph_from_place('Santa Monica, CA', network_type='walk')
basic_stats = ox.basic_stats(G)
print(basic_stats['circuity_avg'])
extended_stats = ox.extended_stats(G, bc=True)
print(extended_stats['betweenness_centrality_avg'])

The stats are broken up into two functions: basic and extended. The extended stats function also has optional parameters to run additional advanced measures. You can also calculate and plot shortest-path routes between points, taking one-way streets into account:

G = ox.graph_from_address('N. Sicily Pl., Chandler, Arizona', distance=800, network_type='drive')
route = nx.shortest_path(G, origin, destination)
ox.plot_graph_route(G, route)

OSMnx: routing along a street network with networkx and OpenStreetMap

OSMnx can visualize street segments by length to provide a sense of where a city’s longest and shortest blocks are distributed:

ec = ox.get_edge_colors_by_attr(G, attr='length')
ox.plot_graph(G, edge_color=ec)

OSMnx: visualizing a street network colored by street segment block length with networkx and OpenStreetMap

OSMnx can easily visualize one-way vs two-way edges to provide a sense of where a city’s one-way streets and divided roads are distributed:

ec = ['r' if data['oneway'] else 'b' for u, v, key, data in G.edges(keys=True, data=True)]
ox.plot_graph(G, node_size=0, edge_color=ec)

OSMnx: visualizing San Francisco Mission District street network colored by one-way vs two-way street segments with networkx and OpenStreetMap

You can also quickly visualize all the cul-de-sacs (or intersections of any other type) in a city to get a sense of these points of low network connectivity:

culdesacs = [key for key, value in G.graph['streets_per_node'].items() if value==1]
nc = ['r' if node in culdesacs else 'none' for node in G.nodes()]
ox.plot_graph(G, node_color=nc)

OSMnx: visualizing and analyzing cul-de-sacs in the street network of Piedmont California with Python and OpenStreetMap

Allan Jacobs famously compared several cities’ urban forms through figure-ground diagrams of 1 square mile of each’s street network in his book Great Streets. We can re-create this automatically and computationally with OSMnx:

OSMnx: Figure-ground diagrams of one square mile of Portland, San Francisco, Irvine, and Rome shows the street network, urban form, and urban design in these cities

These Jacobsesque figure-ground diagrams are created completely with OSMnx.

Conclusion

OSMnx lets you download spatial geometries and construct, project, visualize, and analyze complex street networks. It allows you to automate the collection and computational analysis of street networks for powerful and consistent research, transportation engineering, and urban design. OSMnx is built on top of NetworkX, matplotlib, and geopandas for rich network analytic capabilities, beautiful and simple visualizations, and fast spatial queries with R-tree.

OSMnx is open-source and on GitHub. You can download/cite the paper here.

60 thoughts on “OSMnx: Python for Street Networks”

  1. Hi Geoff,
    thank you for this great resource!

    How can we export the data as a GeoJSON file? Is this possible?
    Do you know how can we use this data to build the city blocks from the street network?

    Thanks!

    1. Thanks. I’ll be adding GeoJSON export in an upcoming version. It’ll be a simple feature to add. City blocks is a good question, and something I am currently researching. The ambiguity lies in the definition of a city block. One thing you can do is polygonize your set of edge lines and treat the polygons as blocks. There will be some strange artifacts there, but it’s a proxy. Looking forward to your suggestions as you delve into this.

  2. Hi Geoff,

    First of all, great work! Congrats! Second, I’d like to ask you how the tool manages multiple parallel lines (e.g., big roads with multiple lanes, footpaths, cycle ways etc.) and roundabouts. Does it shrink everything in single neat lines and intersections?

    Thanks in advance,
    Alessandro

    1. Thanks. OSMnx handles edges however OSM provides them. That said, roads with multiple lanes are represented as a single edge (and the edge has optional attributes of number of lines, width, road type, etc.). Divided roads are represented as directed edges going in reciprocal directions, but offset from each other such that they are (approximately) center-lines for each direction. Roundabouts are represented as roundabouts, as they are on OSM. Reducing parallel, nearby paths to single edges is a non-trivial computational task.

  3. Hi Geoff,

    this tool is amazing. You’ve done a really great job.

    I’d like to ask you if there is any way to “disable” nodes, which are at the ends of roads in order to extract “true” intersections only?

    Thanks a lot!

    1. Yes you can easily identify which nodes are actual intersections, vs dead-ends. OSMnx creates a graph attribute that states how many physical streets are connected to each node. Any node with >1 is an intersection. For example:
      G = ox.graph_from_place('Piedmont, California, USA', network_type='drive')
      streets_per_node = G.graph['streets_per_node']
      node_ids = set(G.nodes())
      intersections = [node for node, count in streets_per_node.items() if count>1]
      dead_ends = [node for node, count in streets_per_node.items() if count==1]

      1. cool. This seems like a good solution to the large networks issue i raised (and i think you suggested the same once?)

  4. This looks awesome. I just started playing with it and noticed your example for `ox.graph_from_bbox()` you pass a list and a network_type, however when I try that it gives me a “need at least 4” parameters error. Seems to work fine if I pass each of the bbox coordinates separately, eg like so: `ox.graph_from_bbox(bbox[0], bbox[1], bbox[2], bbox[3], network_type=’drive_service’)`.
    Cheers.

  5. Wow! Amazing work! For real… awesome!
    Congrats on such a useful and simple-to-use library!
    Rafael Pereira mentioned your work on a post of his blog… I will definitely be following you from now on!

    #ThanksGeoff

  6. Hi Geoff,

    Congratulations for this neat piece of code. I’ll try to use it for my studies. I do exactly what this tool is meant for on a daily basis. Will hopefully come up with more to discuss.

    Zia
    PhD in Geomatics Engg.

  7. Hi Geoff,

    Could you share the code you used to make the Jacobsesque figure-ground diagrams? Again, this is a fabulous tool for urban designers everywhere.

    Best,
    Yasir

  8. Hello
    Nice work !
    I found your article because I am looking for a way to extract contours from OSM and draw them on a tiles. For example, using folium, you can easily add markers or draw polygons on the map.
    But it is sometimes not easy to find the coordinates of administrative regions (out of USA). With your module, with simple requests you can easily get it.
    Is there a way to do plot on a tiles with OSMnx ? or Do you know another module which can be used to do this ?
    Thanks

    1. You might check out smopy. I’ll be integrating it into an upcoming OSMnx release. It lets you plot OSM tiles in Python and draw geometries on top of them.

  9. Hey, I’d love to try this but I’m having trouble with an rtree libspatialindex_c dependency, I’ve installed via conda-forge to no avail, and after looking at the rtree library it looks like a pretty common thing.

    Is it possible the rtree dependency could be removed? Have you dealt with rtree c lib dependencies before?

    Also the project looks really cool!

    Thanks in advance!

    1. What platform are you on? If you’re on Linux, you can install libspatialindex with apt-get. If you’re on Windows, you can install rtree (and its C extensions) easily with Gohlke’s binaries. I don’t have a Mac, but my students who are Mac users got rtree installed and working using conda and either the conda-forge or the IOOS channel. Make sure you’re using the latest versions of rtree (0.8.3) and libspatialindex. If you have any other problems, you can open an issue in the GitHub repo (or the relevant repo if someone’s dependency is broken for your platform).

  10. Hello,
    Fantastic work, thank you for sharing!

    I got experience in Python but I am pretty new to the whole geospatial world – so sorry for the newbie question.

    I would like to ask you how could I use osmnx to load my locally saved (road)-network and parse it correctly into networkx in order to be able perform the operations that you show (plotting, routing, and computing network metrics).
    The locally saved road network is optimally in shapefile or geojson format. But probably I could also convert it prior (with command line operation or QGIS) to OSM format or into Spatial Database.

    Can this be implemented with little effort? Are you planning to make this type of operation part of the main package?

    Thanks

  11. Hi Geoff – this is pretty great! Any way to integrate transit networks into this – e.g. bus and subway routes?
    Thanks!

  12. Hello
    Fantastic work !
    I would like to ask you, how could I load back the saved shapefile into a networkx graph object. Is this operation feasible?

    Alternatively could I load locally saved files in OSM or spatial database format as graph, and then perform routing, network simplification in osmnx ?

  13. Hi,
    Thanks so much for the information. Seems like this could be very helpful. Is it possible for you to do an follow up explanation for someone with GIS experience but no coding experience? This will help agencies who want to perform such analysis but currently do not have the coding experience.

    Thank you

  14. Great project!
    A general question – What would be the most efficient way to load graph into osmnx? what format is optimal?
    loading data for an entire country using graph_from_place() can be a bit time consuming.

  15. When I run:
    ox.plot_graph(ox.graph_from_place(‘Modena, Italy’))

    I get this error:
    File “C:\Python27\lib\site-packages\rtree\core.py”, line 116, in
    raise OSError(“could not find or load spatialindex_c.dll”)
    OSError: could not find or load spatialindex_c.dll

    1. OSMnx depends on the rtree package, which depends on libspatialindex, per its documentation. I suggest installing OSMnx with conda as it handles the dependencies gracefully for you. If you have trouble with OSMnx, please open an issue at its GitHub repo.

  16. Thanks for sharing this great tool!
    Have you ever considered implementing isolines computation on top of it? I’m thinking to adopt OSMnx to calculate all the routes starting from a list of locations (their projections on the network geometries) and going X meters far.

    PS: in my case the routes have no one-way or turn restrictions being pedestrian routes.

      1. It sounds great! Today I’ve been working on it, trying various solutions based on OSMnx output (even one with the spatial output imported in a spatial DB).
        Now I’m testing another path with GeoPandas and NetworkX.

          1. Right, ego graph is the key method. Thanks for sharing this notebook.
            Though I’m working on something a little bit different (I will complete it today hopefully): I need the exact (interpolated) point X distance far from the starting, projected, positions. At the end I will have the set of linestrings of the exact paths, not aproximated to the network nodes.

  17. hi,gobeing,i am a chinese students, i have some mistakes in installing osmnx,can you help me, i am so upset.it always show me ” TypeError: not enough arguments for format string”

  18. Hi, gobeing, when I run the following setences:
    G = ox.graph_from_place(‘Los Angeles, California’, network_type=’drive’)
    ox.plot_graph(G)
    it shows nothing, I am confused

  19. Hello I am from Wuhan, China students, my name is Wu Yong, I would like to ask the administrative boundaries in some areas of China can not? Osmnx is to call that geocoding service?

  20. What does the key attribute in the shapefile export reference. In most cases there are two overlapping line segments with Key 0 and Key 1. These segments usually have a reference each other’s end nodes. Key 0 may have a to:1 from 0 and Key 1 may have a to:0 from 1.

      1. This seems like a mechanism to identify different edges that have the same start and stop nodes. I am guessing the Key=0 and Key=1 are bi-directions ? The Key values 2 and greater are a little confusing.

  21. Hi Geoff,

    Indeed a very useful and well put together code. I wish I had similar skills! Congratulations!

    I have noticed you hard coded the Overpass API URL and there is no helper function to set the target API to a different instance except the official one. I have hosted Overpass API and OSM database with full global network to avoid overloading the official service and allow for more demanding queries. So far we use it with much success on full-scale POI’s city analysis and multi-agent transport simulations.

    I was wondering if you would consider enabling custom setup of Overpass API URL via some utility function or setting variable.

    Greate work!

    With respect,

    Catalin

  22. Great tool! The code for visualising cul-de-sacs is not 100% correct anymore, it seems, on the version I am using. Should be:
    streets_per_node, not streets_per_intersection.

Leave a Reply

Your email address will not be published. Required fields are marked *