Skip to content

Commit 66ef4d7

Browse files
committed
Update materials for creating a directed graph from street network
1 parent dc6228b commit 66ef4d7

2 files changed

Lines changed: 739 additions & 4 deletions

File tree

source/part2/chapter-08/md/00-introduction-to-spatial-network-analysis.md

Lines changed: 144 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ In this first example, we will construct a simple graph using the `networkx` lib
4949

5050
```python
5151
import networkx as nx
52+
import matplotlib.pyplot as plt
5253

5354
G = nx.Graph()
5455
G
@@ -348,7 +349,7 @@ What is the path length and route from `e` to `a` using the directed graph?
348349
<!-- #endregion -->
349350

350351
<!-- #region editable=true slideshow={"slide_type": ""} -->
351-
Data was obtained from
352+
Data was obtained from Digiroad
352353
<!-- #endregion -->
353354

354355
```python editable=true slideshow={"slide_type": ""}
@@ -358,5 +359,147 @@ from contextily import add_basemap
358359
```
359360

360361
```python editable=true slideshow={"slide_type": ""}
362+
fp = "data/digiroad_helsinki.gpkg"
363+
streets = gpd.read_file(fp)
364+
streets.head()
365+
```
366+
367+
```python
368+
streets.plot();
369+
```
370+
371+
The `direction` column includes information about the allowed direction of the traffic flow, i.e. whether the traffic is permitted in both directions or whether it is a oneway street. In this street network dataset the values are coded as shown in Table 8.1.
372+
373+
374+
375+
: _**Table 8.1**. The rules for directed graph in terms of permitted direction of traffic._
376+
377+
| Value | Direction of traffic flow |
378+
|-------|---------------------------------------------------------------------------------------|
379+
| 2 | Traffic is permitted in both directions |
380+
| 3 | Traffic is permitted against the direction of digitalisation (end-node to start-node) |
381+
| 4 | Traffic is permitted in the direction of digitalisation (start-node to end-node) |
382+
383+
```python
384+
def gdf_to_directed_graph(gdf, direction='direction', both_ways=2, against=3, along=4):
385+
"""Creates a NetworkX MultiDiGraph from road network GeoDataFrame.
386+
387+
Parameters
388+
----------
389+
390+
gdf : GeoDataFrame
391+
GeoDataFrame containing the road network data.
392+
393+
direction : str
394+
Name for column that contains information about the allowed driving directions
395+
396+
both_ways : int
397+
Value specifying that the road is drivable to both directions.
398+
399+
against : int
400+
Value specifying that the road is drivable against the digitizing direction.
401+
402+
along : int
403+
Value specifying that the road is drivable along the digitizing direction.
404+
405+
"""
406+
import networkx as nx
407+
408+
# Create the NetworkX graph
409+
graph = nx.MultiDiGraph()
410+
411+
columns = list(gdf.columns)
412+
413+
# Generate edge dictionary
414+
for edge in gdf.itertuples():
415+
coords = edge.geometry.coords
416+
417+
# Get first and last coordinates (drop possible Z information)
418+
first, last = coords[0][:2], coords[-1][:2]
419+
420+
# Edge attributes
421+
edge_attr = dict(edge._asdict())
422+
423+
# Create edges according the direction rules
424+
if edge_attr[direction] == both_ways:
425+
426+
# If road is bi-directional add it in both ways
427+
graph.add_edge(first, last, **edge_attr)
428+
graph.add_edge(last, first, **edge_attr)
429+
430+
elif edge_attr[direction] == along:
431+
432+
# Add the edge along digitization direction
433+
graph.add_edge(first, last, **edge_attr)
434+
435+
elif edge_attr[direction] == against:
436+
437+
# Add the edge against digitization direction
438+
graph.add_edge(last, first, **edge_attr)
439+
440+
# Generate node attributes
441+
node_attrs = {node: {"coords": node, "x": node[0], "y": node[1]} for node in graph.nodes}
442+
nx.set_node_attributes(graph, node_attrs)
443+
444+
# Relabel the indices
445+
graph = nx.convert_node_labels_to_integers(graph)
446+
447+
# Add some useful attributes
448+
graph.graph['crs'] = gdf.crs
449+
450+
return graph
451+
```
452+
453+
```python
454+
G = gdf_to_directed_graph(streets)
455+
```
456+
457+
```python
458+
positions = {node: attrs["coords"] for node, attrs in G.nodes.data()}
459+
```
460+
461+
```python
462+
edge_colors = ["blue" if attrs["direction"] == 2 else "red" for u, v, attrs in G.edges.data()]
463+
```
464+
465+
```python
466+
fig, ax = plt.subplots(figsize=(10,10))
467+
468+
nx.draw(G,
469+
ax=ax,
470+
pos=positions,
471+
node_color="black",
472+
node_size=0.5,
473+
edge_color=edge_colors,
474+
arrows=False,
475+
)
476+
```
477+
478+
```python
479+
import osmnx as ox
480+
```
481+
482+
```python
483+
nodes, edges = ox.graph_to_gdfs(G)
484+
```
485+
486+
```python
487+
nodes.head()
488+
```
489+
490+
```python
491+
edges.head()
492+
```
493+
494+
As many Python libraries related to working with have been
495+
496+
```python
497+
import neatnet
498+
499+
streets_cleaned = neatnet.remove_interstitial_nodes(streets)
500+
streets_cleaned.shape
501+
```
502+
503+
```python
361504

362505
```

source/part2/chapter-08/nb/00-introduction-to-spatial-network-analysis.ipynb

Lines changed: 595 additions & 3 deletions
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)