Author: Geoff Boeing
Once you've perused the features demo notebook, this notebook demonstrates more details on querying for place boundaries and street networks, visualizing, and saving models to disk.
import geopandas as gpd
import osmnx as ox
%matplotlib inline
ox.__version__
You can configure OSMnx using the settings
module. See the documentation for the settings you can configure. For example, by default OSMnx caches all server responses to prevent repeatedly hitting the server for the same query every time you run it. This both makes our code faster on subsequent runs and helps us be a "good neighbor" to the server. But you can turn caching off (or back on again) with the use_cache
setting.
# turn response caching off
ox.settings.use_cache = False
# turn it back on and turn on/off logging to your console
ox.settings.use_cache = True
ox.settings.log_console = False
OSMnx lets you download place boundary geometries from OpenStreetMap, project them, and plot them. For a more in-depth demonstration of querying by place, see this notebook.
# get the boundary polygon for manhattan, project it, and plot it
city = ox.geocode_to_gdf("Manhattan, New York, USA")
city_proj = ox.project_gdf(city)
ax = city_proj.plot(fc="gray", ec="none")
_ = ax.axis("off")
# get boundary polygons for several cities, save as GeoPackage, project to UTM, and plot
place_names = [
"Berkeley, California, USA",
"Oakland, California, USA",
"Piedmont, California, USA",
"Emeryville, California, USA",
"Alameda, Alameda County, CA, USA",
]
east_bay = ox.geocode_to_gdf(place_names)
east_bay.to_file("./data/east_bay.gpkg", driver="GPKG")
east_bay = ox.project_gdf(east_bay)
ax = east_bay.plot(fc="gray", ec="none")
_ = ax.axis("off")
# if you know the OSM ID of the place(s) you want, you can query it directly
ox.geocode_to_gdf(["R357794", "N8170768521", "W427818536"], by_osmid=True)
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 GeoPackages for later use. The street networks are directed and preserve one-way directionality. For a more in-depth demonstration of creating street networks, see this notebook.
You can download a street network by providing OSMnx any of the following (demonstrated in the examples below):
You can also specify several different network types:
This constructs the network from all the OSM nodes and ways within the bounding box.
# define a bounding box in San Francisco
north, south, east, west = 37.79, 37.78, -122.41, -122.43
# create network from that bounding box
G = ox.graph_from_bbox(north, south, east, west, network_type="drive_service")
This creates a bounding box n meters North, South, East, and West of the point, then constructs the network from all the OSM nodes and ways within the bounding box.
# define a point at the corner of California St and Mason St in SF
location_point = (37.791427, -122.410018)
# create network from point, inside bounding box of N, S, E, W each 750m from point
G = ox.graph_from_point(location_point, dist=750, dist_type="bbox", network_type="drive")
This creates a bounding box n meters North, South, East, and West of the point, then constructs the network from all the OSM nodes and ways within the bounding box. Then it truncates the network by removing all nodes further than n meters from the point along the network.
# same point again, but create network only of nodes within 500m along the network from point
G = ox.graph_from_point(location_point, dist=500, dist_type="network")
fig, ax = ox.plot_graph(G, node_color="r")
Note the plot above shows the network within 500m (traveling distance along the network) from the location_point
. By default, the network_type
parameter value is 'all', meaning that we do not filter out paths that restrict certain types of traffic. This also means that one-way streets are honored as one-way and you cannot travel the wrong direction down them. Thus, the 500m takes into account only those nodes you can reach within 500m while only traveling in the allowed direction of the street. Instead (below), we can specify network_type='walk'
to build a street network only of paths that walking is allowed on. This also makes every path bi-directional in the directed network, because you can walk in either direction on the sidewalk of a one-way street. Thus, the 500m now takes into account those nodes you can reach within 500m while traveling in either direction (even if it's a one-way street).
# create network only of nodes within 500m walking along the network from point
G = ox.graph_from_point(location_point, dist=500, dist_type="network", network_type="walk")
fig, ax = ox.plot_graph(G, node_color="r")
This geocodes the address, creates a bounding box, downloads the network, then truncates it by network distance (if distance_type='network').
# network from address, including only nodes within 1km along the network from the address
G = ox.graph_from_address(
address="350 5th Ave, New York, NY",
dist=1000,
dist_type="network",
network_type="drive",
)
# you can project the network to UTM (zone calculated automatically)
G_projected = ox.project_graph(G)
This geocodes the place name, gets the place's boundary shape polygon and bounding box, downloads the network within the bounding box, then truncates it to the place's boundary polygon.
# create the street network within the city of Piedmont's borders
G = ox.graph_from_place("Piedmont, California, USA", network_type="drive")
# you can also pass multiple places as a mixed list of strings and/or dicts
places = [
"Los Altos, California, USA",
{"city": "Los Altos Hills", "state": "California"},
"Loyola, California",
]
G = ox.graph_from_place(places, truncate_by_edge=True)
# save to disk as GeoPackage file then plot
ox.save_graph_geopackage(G)
fig, ax = ox.plot_graph(G, node_size=0, edge_color="w", edge_linewidth=0.2)
This example loads the Mission District's polygon from a ShapeFile, then downloads the network within its bounding box, then prunes all nodes that lie outside the place's boundary polygon.
calif = gpd.read_file("input_data/ZillowNeighborhoods-CA")
mission_district = calif[(calif["CITY"] == "San Francisco") & (calif["NAME"] == "Mission")]
polygon = mission_district["geometry"].iloc[0]
G2 = ox.graph_from_polygon(polygon, network_type="drive_service")
# create graph from .osm extract file
G = ox.graph_from_xml("./input_data/West-Oakland.osm.bz2")
Simplification is normally done by OSMnx automatically under the hood, but we can break it out to see how it works. OpenStreetMap nodes are weird. They include intersections, but they also include all the points along a single block 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. There are two simplification modes, strict and non-strict. The main difference is that unlike strict mode, non-strict mode allows simplification to an "expansion graph" (ie, if the graph were undirected, nodes with degree 2 as long as the incident edges have different OSM IDs). For a more in-depth demonstration of topological simplification with OSMnx, see this notebook.
# create a network around some (lat, lng) point but do not simplify it yet
location_point = (33.299896, -111.831638)
G = ox.graph_from_point(location_point, network_type="drive_service", dist=500, simplify=False)
# turn off strict mode and see what nodes we'd remove, in yellow
nc = ["r" if ox.simplification._is_endpoint(G, node) else "y" for node in G.nodes()]
fig, ax = ox.plot_graph(G, node_color=nc)
The dots above are OSM nodes. We'll remove the nodes in yellow as they're not real network nodes (intersections/dead-ends).
# simplify the network
G = ox.simplify_graph(G)
fig, ax = ox.plot_graph(G, node_color="r")
# show the simplified network with edges colored by length
ec = ox.plot.get_edge_colors_by_attr(G, attr="length", cmap="plasma_r")
fig, ax = ox.plot_graph(
G, node_color="w", node_edgecolor="k", node_size=50, edge_color=ec, edge_linewidth=3
)
# highlight all parallel (multiple) edges
ec = ["gray" if k == 0 or u == v else "r" for u, v, k in G.edges(keys=True)]
fig, ax = ox.plot_graph(
G, node_color="w", node_edgecolor="k", node_size=50, edge_color=ec, edge_linewidth=3
)
# highlight all one-way edges in the mission district network from earlier
ec = ["r" if data["oneway"] else "w" for u, v, key, data in G2.edges(keys=True, data=True)]
fig, ax = ox.plot_graph(G2, node_size=0, edge_color=ec, edge_linewidth=1.5, edge_alpha=0.7)
For more examples of saving and loading networks to/from disk, see this notebook.
# save street network as GeoPackage to work with in GIS
ox.save_graph_geopackage(G, filepath="./data/network.gpkg")
# save street network as GraphML file to work with later in OSMnx or networkx or gephi
ox.save_graphml(G, filepath="./data/network.graphml")
# calculate basic street network metrics and display average circuity
stats = ox.basic_stats(G)
stats["circuity_avg"]
In this street network, the streets are ~16% more circuitous than the straight-lines paths would be.
For examples of analyzing street networks, see this example.