Note
Converting from GeoDataFrame to Graph and back#
The model situation expects to have all input data for analysis in GeoDataFrames
, including street network (e.g. from shapefile).
[1]:
import momepy
import geopandas as gpd
import matplotlib.pyplot as plt
import networkx as nx
[2]:
streets = gpd.read_file(momepy.datasets.get_path('bubenec'), layer='streets')
[3]:
f, ax = plt.subplots(figsize=(10, 10))
streets.plot(ax=ax)
ax.set_axis_off()
plt.show()

We have to convert this LineString GeoDataFrame to a networkx.Graph
. We use momepy.gdf_to_nx
and later momepy.nx_to_gdf
as a pair of interconnected functions. gdf_to_nx
supports both primal and dual graphs. The primal approach will save the length of each segment to be used as a weight later, while dual will save the angle between segments (allowing angular centrality).
[4]:
graph = momepy.gdf_to_nx(streets, approach='primal')
[14]:
f, ax = plt.subplots(1, 3, figsize=(18, 6), sharex=True, sharey=True)
streets.plot(color='#e32e00', ax=ax[0])
for i, facet in enumerate(ax):
facet.set_title(("Streets", "Primal graph", "Overlay")[i])
facet.axis("off")
nx.draw(graph, {n:[n[0], n[1]] for n in list(graph.nodes)}, ax=ax[1], node_size=15)
streets.plot(color='#e32e00', ax=ax[2], zorder=-1)
nx.draw(graph, {n:[n[0], n[1]] for n in list(graph.nodes)}, ax=ax[2], node_size=15)

[6]:
dual = momepy.gdf_to_nx(streets, approach='dual')
[16]:
f, ax = plt.subplots(1, 3, figsize=(18, 6), sharex=True, sharey=True)
streets.plot(color='#e32e00', ax=ax[0])
for i, facet in enumerate(ax):
facet.set_title(("Streets", "Dual graph", "Overlay")[i])
facet.axis("off")
nx.draw(dual, {n:[n[0], n[1]] for n in list(dual.nodes)}, ax=ax[1], node_size=15)
streets.plot(color='#e32e00', ax=ax[2], zorder=-1)
nx.draw(dual, {n:[n[0], n[1]] for n in list(dual.nodes)}, ax=ax[2], node_size=15)

At this moment (almost) any networkx
method can be used. For illustration, we will measure the node degree. Using networkx
, we can do:
[7]:
degree = dict(nx.degree(graph))
nx.set_node_attributes(graph, degree, 'degree')
However, node degree is implemented in momepy so we can use directly:
[8]:
graph = momepy.node_degree(graph, name='degree')
Once we have finished our network-based analysis, we want to convert the graph back to a geodataframe. For that, we will use momepy.nx_to_gdf
, which gives us several options of what to export.
lines
original LineString geodataframe
points
point geometry representing street network intersections (nodes of primal graph)
spatial_weights
spatial weights for nodes capturing their relationship within a network
Moreover, edges
will contain node_start
and node_end
columns capturing the ID of both nodes at its ends.
[9]:
nodes, edges, sw = momepy.nx_to_gdf(graph, points=True, lines=True,
spatial_weights=True)
[10]:
f, ax = plt.subplots(figsize=(10, 10))
nodes.plot(ax=ax, column='degree', cmap='tab20b', markersize=(nodes['degree'] * 100), zorder=2)
edges.plot(ax=ax, color='lightgrey', zorder=1)
ax.set_axis_off()
plt.show()

[11]:
nodes.head(3)
[11]:
degree | nodeID | geometry | |
---|---|---|---|
0 | 3 | 1 | POINT (1603585.640 6464428.774) |
1 | 5 | 2 | POINT (1603413.206 6464228.730) |
2 | 3 | 3 | POINT (1603268.502 6464060.781) |
[12]:
edges.head(3)
[12]:
geometry | mm_len | node_start | node_end | |
---|---|---|---|---|
0 | LINESTRING (1603585.640 6464428.774, 1603413.2... | 264.103950 | 1 | 2 |
1 | LINESTRING (1603561.740 6464494.467, 1603564.6... | 70.020202 | 1 | 9 |
2 | LINESTRING (1603585.640 6464428.774, 1603603.0... | 88.924305 | 1 | 7 |