Use OSMnx to analyze a NetworkX street network, including routing


In [1]:
import osmnx as ox, networkx as nx, matplotlib.cm as cm, pandas as pd, numpy as np
%matplotlib inline
ox.config(log_file=True, log_console=True, use_cache=True)

Calculate basic street network measures (topological and metric)


In [2]:
# get the network for Piedmont, calculate its basic stats, then show the average circuity
stats = ox.basic_stats(ox.graph_from_place('Piedmont, California, USA'))
stats['circuity_avg']


Out[2]:
1.088462496273108

To calculate density-based metrics, you must also pass the network's bounding area in square meters (otherwise basic_stats() will just skip them in the calculation):


In [3]:
# get the street network for a place, and its area in square meters
place = 'Piedmont, California, USA'
gdf = ox.gdf_from_place(place)
area = ox.project_gdf(gdf).unary_union.area
G = ox.graph_from_place(place, network_type='drive_service')

In [4]:
# calculate basic and extended network stats, merge them together, and display
stats = ox.basic_stats(G, area=area)
extended_stats = ox.extended_stats(G, ecc=True, bc=True, cc=True)
for key, value in extended_stats.items():
    stats[key] = value
pd.Series(stats)


Out[4]:
avg_neighbor_degree                    {53090322: 3.0, 53090323: 3.33333333333, 53028...
avg_neighbor_degree_avg                                                          2.89668
avg_weighted_neighbor_degree           {53090322: 0.0235770980709, 53090323: 0.034801...
avg_weighted_neighbor_degree_avg                                               0.0328481
betweenness_centrality                 {53090322: 0.137517261549, 53090323: 0.0694908...
betweenness_centrality_avg                                                     0.0388068
center                                                                       [305541020]
circuity_avg                                                                     1.11016
closeness_centrality                   {53090322: 0.000649757188008, 53090323: 0.0006...
closeness_centrality_avg                                                     0.000593005
clustering_coefficient                 {53090322: 0.0, 53090323: 0.0, 53028190: 0.0, ...
clustering_coefficient_avg                                                     0.0744777
clustering_coefficient_weighted        {53090322: 0.0, 53090323: 0.0, 53028190: 0.0, ...
clustering_coefficient_weighted_avg                                            0.0115481
count_intersections                                                                  324
degree_centrality                      {53090322: 0.016393442623, 53090323: 0.0163934...
degree_centrality_avg                                                          0.0146811
diameter                                                                         5023.78
eccentricity                           {53090322: 3432.86384219, 53090323: 3400.89636...
edge_density_km                                                                  26867.6
edge_length_avg                                                                  118.438
edge_length_total                                                                 116779
intersection_density_km                                                          74.5432
k_avg                                                                             5.3733
m                                                                                    986
n                                                                                    367
node_density_km                                                                  84.4363
pagerank                               {53090322: 0.00376654280933, 53090323: 0.00299...
pagerank_max                                                                  0.00854043
pagerank_max_node                                                               53036558
pagerank_min                                                                 0.000587028
pagerank_min_node                                                              682931951
periphery                                                                     [53065785]
radius                                                                           2507.94
self_loop_proportion                                                          0.00608519
street_density_km                                                                13958.8
street_length_avg                                                                118.499
street_length_total                                                              60671.4
street_segments_count                                                                512
streets_per_node_avg                                                             2.90463
streets_per_node_counts                   {0: 0, 1: 43, 2: 2, 3: 272, 4: 48, 5: 1, 6: 1}
streets_per_node_proportion            {0: 0.0, 1: 0.117166212534, 2: 0.0054495912806...
dtype: object

Streets/intersection counts and proportions are nested dicts inside the stats dict. To convert these stats to a pandas dataframe (to compare/analyze multiple networks against each other), just unpack these nested dicts first:


In [5]:
# unpack dicts into individiual keys:values
stats = ox.basic_stats(G, area=area)
for k, count in stats['streets_per_node_counts'].items():
    stats['int_{}_count'.format(k)] = count
for k, proportion in stats['streets_per_node_proportion'].items():
    stats['int_{}_prop'.format(k)] = proportion

# delete the no longer needed dict elements
del stats['streets_per_node_counts']
del stats['streets_per_node_proportion']

# load as a pandas dataframe
pd.DataFrame(pd.Series(stats)).T


Out[5]:
circuity_avg count_intersections edge_density_km edge_length_avg edge_length_total int_0_count int_0_prop int_1_count int_1_prop int_2_count ... k_avg m n node_density_km self_loop_proportion street_density_km street_length_avg street_length_total street_segments_count streets_per_node_avg
0 1.110157 324.0 26867.64835 118.437601 116779.474936 0.0 0.0 43.0 0.117166 2.0 ... 5.373297 986.0 367.0 84.436301 0.006085 13958.770733 118.498834 60671.402859 512.0 2.904632

1 rows × 30 columns

Inspect betweenness centrality


In [6]:
G_projected = ox.project_graph(G)
max_node, max_bc = max(extended_stats['betweenness_centrality'].items(), key=lambda x: x[1])
max_node, max_bc


Out[6]:
(53124805, 0.30124137016491004)

In the city of Piedmont, California, the node with the highest betweenness centrality has 29.4% of all shortest paths running through it. Let's highlight it in the plot:


In [7]:
nc = ['r' if node==max_node else '#336699' for node in G_projected.nodes()]
ns = [50 if node==max_node else 8 for node in G_projected.nodes()]
fig, ax = ox.plot_graph(G_projected, node_size=ns, node_color=nc, node_zorder=2)


29.4% of all shortest paths run through the node highlighted in red. Let's look at the relative betweenness centrality of every node in the graph:


In [8]:
# get a color for each node
def get_color_list(n, color_map='plasma', start=0, end=1):
    return [cm.get_cmap(color_map)(x) for x in np.linspace(start, end, n)]

def get_node_colors_by_stat(G, data, start=0, end=1):
    df = pd.DataFrame(data=pd.Series(data).sort_values(), columns=['value'])
    df['colors'] = get_color_list(len(df), start=start, end=end)
    df = df.reindex(G.nodes())
    return df['colors'].tolist()

nc = get_node_colors_by_stat(G_projected, data=extended_stats['betweenness_centrality'])
fig, ax = ox.plot_graph(G_projected, node_color=nc, node_edgecolor='gray', node_size=20, node_zorder=2)


Above, the nodes are visualized by betweenness centrality, from low (dark violet) to high (light yellow).

Routing: calculate the network path from the centermost node to some other node

Let the origin node be the node nearest the location and let the destination node just be the last node in the network. Then find the shortest path between origin and destination, using weight='length' to find the shortest spatial path (otherwise it treats each edge as weight=1).


In [9]:
# define a lat-long point, create network around point, define origin/destination nodes
location_point = (37.791427, -122.410018)
G = ox.graph_from_point(location_point, distance=500, distance_type='network', network_type='walk')
origin_node = ox.get_nearest_node(G, location_point)
destination_node = list(G.nodes())[-1]

In [10]:
# find the route between these nodes then plot it
route = nx.shortest_path(G, origin_node, destination_node)
fig, ax = ox.plot_graph_route(G, route)



In [11]:
# project the network to UTM (zone calculated automatically) then plot the network/route again
G_proj = ox.project_graph(G)
fig, ax = ox.plot_graph_route(G_proj, route)


Routing: plot network path from one lat-long to another


In [12]:
# define origin/desination points then get the nodes nearest to each
origin_point = (37.792896, -122.412325)
destination_point = (37.790495, -122.408353)
origin_node = ox.get_nearest_node(G, origin_point)
destination_node = ox.get_nearest_node(G, destination_point)
origin_node, destination_node


Out[12]:
(850803362, 65290756)

In [13]:
# find the shortest path between origin and destination nodes
route = nx.shortest_path(G, origin_node, destination_node, weight='length')
str(route)


Out[13]:
'[850803362, 633686735, 633686762, 633686748, 633686743, 850803353, 65295320, 65295314, 65295311, 65332818, 65332815, 65319944, 65290756]'

In [14]:
# plot the route showing origin/destination lat-long points in blue
fig, ax = ox.plot_graph_route(G, route, origin_point=origin_point, destination_point=destination_point)


Demonstrate routing with one-way streets


In [15]:
G = ox.graph_from_address('N. Sicily Pl., Chandler, Arizona', distance=800, network_type='drive')
origin = (33.307792, -111.894940)
destination = (33.312994, -111.894998)
origin_node = ox.get_nearest_node(G, origin)
destination_node = ox.get_nearest_node(G, destination)
route = nx.shortest_path(G, origin_node, destination_node)
fig, ax = ox.plot_graph_route(G, route, save=True, filename='route')


Also, when there are parallel edges between nodes in the route, OSMnx picks the shortest edge to plot


In [16]:
location_point = (33.299896, -111.831638)
G = ox.graph_from_point(location_point, distance=500, clean_periphery=False)
origin = (33.301821, -111.829871)
destination = (33.301402, -111.833108)
origin_node = ox.get_nearest_node(G, origin)
destination_node = ox.get_nearest_node(G, destination)
route = nx.shortest_path(G, origin_node, destination_node)
fig, ax = ox.plot_graph_route(G, route)



In [ ]: