Search
Network analysis

Graph analysis offers three modes, of which the first two are used within momepy (as per v0.2):

  • node-based
    • value per node
  • edge-based
    • value per edge
  • network-based
    • single value per network
import momepy
import geopandas as gpd
import osmnx as ox
import matplotlib.pyplot as plt

In this notebook, we will look at Písek, Czechia. We retrieve its network from OSM and convert it to GeoDataFrame:

streets_graph = ox.graph_from_place('Pisek, Czechia', network_type='drive')
streets_graph = ox.project_graph(streets_graph)

streets = ox.save_load.graph_to_gdfs(streets_graph, nodes=False, edges=True,
                                   node_geometry=False, fill_edge_geometry=True)

Note: See the detailed explanation of these steps in the centrality notebook.

f, ax = plt.subplots(figsize=(10, 10))
streets.plot(ax=ax, linewidth=0.2)
ax.set_axis_off()
plt.show()

We can generate networkX.MultiGraph, which is used within momepy for network analysis, using gdf_to_nx.

graph = momepy.gdf_to_nx(streets)

Node-based analysis

Once we have the graph, we can use momepy functions, like the one measuring clustering:

graph = momepy.clustering(graph, name='clustering')

Using sub-graph

Momepy includes local characters measured on the network within a certain radius from each node, like meshedness. The function will generate ego_graph for each node so that it might take a while for more extensive networks. Radius can be defined topologically:

graph = momepy.meshedness(graph, radius=5, name='meshedness')
100%|██████████| 530/530 [00:01<00:00, 465.71it/s]

Or metrically, using distance which has been saved as an edge argument by gdf_to_nx (or any other weight).

graph = momepy.meshedness(graph, radius=400, name='meshedness400',
                          distance='mm_len')
100%|██████████| 530/530 [00:00<00:00, 1118.07it/s]

Once we have finished the graph-based analysis, we can go back to GeoPandas. In this notebook, we are interested in nodes only:

nodes = momepy.nx_to_gdf(graph, points=True, lines=False, spatial_weights=False)

Now we can plot our results in a standard way, or link them to other elements (using get_node_id).

Clustering:

f, ax = plt.subplots(figsize=(10, 10))
nodes.plot(ax=ax, column='clustering', markersize=100, legend=True, cmap='viridis',
           scheme='quantiles', alpha=0.5, zorder=2)
streets.plot(ax=ax, color='lightgrey', alpha=0.5, zorder=1)
ax.set_axis_off()
plt.show()
/Users/martin/anaconda3/envs/momepy_guide/lib/python3.7/site-packages/mapclassify/classifiers.py:138: UserWarning: Warning: Not enough unique values in array to form k classes
  "Warning: Not enough unique values in array to form k classes", UserWarning
/Users/martin/anaconda3/envs/momepy_guide/lib/python3.7/site-packages/mapclassify/classifiers.py:140: UserWarning: Warning: setting k to 3
  Warn("Warning: setting k to %d" % k_q, UserWarning)

Meshedness based on topological distance:

f, ax = plt.subplots(figsize=(10, 10))
nodes.plot(ax=ax, column='meshedness', markersize=100, legend=True, cmap='viridis',
           alpha=0.5, zorder=2, scheme='quantiles')
streets.plot(ax=ax, color='lightgrey', alpha=0.5, zorder=1)
ax.set_axis_off()
plt.show()

And meshedness based on 400 metres:

f, ax = plt.subplots(figsize=(10, 10))
nodes.plot(ax=ax, column='meshedness400', markersize=100, legend=True, cmap='viridis',
           alpha=0.5, zorder=2, scheme='quantiles')
streets.plot(ax=ax, color='lightgrey', alpha=0.5, zorder=1)
ax.set_axis_off()
plt.show()