Network analysis in Python¶
Finding a shortest path using a specific street network is a common GIS problem that has many practical applications. For example navigators are one of those “every-day” applications where routing using specific algorithms is used to find the optimal route between two (or multiple) points.
It is also possible to perform network analysis such as tranposrtation routing in Python. Networkx is a Python module that provides a lot tools that can be used to analyze networks on various different ways. It also contains algorithms such as Dijkstras algorithm or A* algoritm that are commonly used to find shortest paths along transportation network.
To be able to conduct network analysis, it is, of course, necessary to have a network that is used for the analyses.
Osmnx package that we just explored in previous tutorial, makes it really easy to
retrieve routable networks from OpenStreetMap with different transport modes (walking, cycling and driving). Osmnx also
combines some functionalities from networkx
module to make it straightforward to conduct routing along OpenStreetMap data.
Next we will test the routing functionalities of osmnx by finding a shortest path between two points based on drivable roads.
Let’s first download the OSM data from Kamppi but this time include only such street segments that are walkable.
In omsnx it is possible to retrieve only such streets that are drivable by specifying 'drive'
into network_type
parameter that can be used to
specify what kind of streets are retrieved from OpenStreetMap (other possibilities are walk
and bike
).
In [1]: import osmnx as ox
In [2]: import networkx as nx
In [3]: import geopandas as gpd
In [4]: import matplotlib.pyplot as plt
In [5]: import pandas as pd
In [6]: place_name = "Kamppi, Helsinki, Finland"
In [7]: graph = ox.graph_from_place(place_name, network_type='drive')
In [8]: fig, ax = ox.plot_graph(graph)
Okey so now we have retrieved only such streets where it is possible to drive with a car. Let’s confirm this by taking a look at the attributes of the street network. Easiest way to do this is to convert the graph (nodes and edges) into GeoDataFrames.
In [9]: edges = ox.graph_to_gdfs(graph, nodes=False, edges=True)
- Let’s check what columns do we have in our data
In [10]: edges.columns
Out[10]:
Index(['bridge', 'geometry', 'highway', 'key', 'lanes', 'length', 'maxspeed',
'name', 'oneway', 'osmid', 'u', 'v'],
dtype='object')
Okey, so we have quite many columns in our GeoDataFrame. Most of the columns are fairly self-exploratory but the following table describes all of them.
Column | Description | Data type |
---|---|---|
bridge | Bridge feature | boolean |
geometry | Geometry of the feature | Shapely.geometry |
highway | Tag for roads, paths (road type) | str (list if multiple) |
lanes | Number of lanes | int (or nan) |
lenght | The length of a feature in meters | float |
maxspeed | maximum legal speed limit | int (list if multiple) |
name | Name of the (street) element | str (or nan) |
oneway | Street is usable only in one direction | boolean |
osmid | Unique node ids of the element | list |
u | The first node of networkx edge tuple | int |
v | The last node of networkx edge tuple | int |
Most of the attributes comes directly from the OpenStreetMap, however, columns u
and v
are networkx specific ids.
- Let’s take a look what kind of features we have in
highway
column.
In [11]: edges['highway'].value_counts()
Out[11]:
residential 110
tertiary 82
primary 25
secondary 18
unclassified 11
living_street 4
primary_link 1
Name: highway, dtype: int64
In [12]: print("Coordinate system:", edges.crs)