Network simplifier#

In this example we use Nauru network to show how one can simplify the network, merging short links into longer ones or turning links into nodes, and saving theses changes into the project.

We use Folium to visualize the resulting network.

See also

Several functions, methods, classes and modules are used in this example:

# Imports
import branca
import folium
from uuid import uuid4
from tempfile import gettempdir
from os.path import join

from aequilibrae.utils.create_example import create_example
from aequilibrae.project.tools.network_simplifier import NetworkSimplifier

Let’s use the Nauru example project for display

fldr = join(gettempdir(), uuid4().hex)

project = create_example(fldr, "nauru")

To simplify the network, we need to create a graph. As Nauru doesn’t have any centroid in its network we have to create a centroid from an arbitrary node, otherwise we cannot create a graph.

nodes = project.network.nodes
centroid_count = nodes.data.query("is_centroid == 1").shape[0]

if centroid_count == 0:
    arbitrary_node = nodes.data["node_id"][0]
    nd = nodes.get(arbitrary_node)
    nd.is_centroid = 1
    nd.save()
# Let's analyze the mode car or 'c' in our model
mode = "c"
# Let's set the graph for computation
network = project.network
network.build_graphs(modes=[mode])
graph = network.graphs[mode]
graph.set_graph("distance")
graph.set_skimming("distance")
graph.set_blocked_centroid_flows(False)
/home/runner/work/aequilibrae/aequilibrae/aequilibrae/paths/graph.py:248: UserWarning: Found centroids not present in the graph!
[1]
  warnings.warn("Found centroids not present in the graph!\n" + str(centroids[~present_centroids]))
/home/runner/work/aequilibrae/aequilibrae/aequilibrae/paths/graph.py:248: UserWarning: Found centroids not present in the graph!
[1]
  warnings.warn("Found centroids not present in the graph!\n" + str(centroids[~present_centroids]))
# Let's revert to setting up that node as centroid in case we had to do it
if centroid_count == 0:
    nd.is_centroid = 0
    nd.save()

We check the number of links and nodes our project has initially.

links_before = project.network.links.data
nodes_before = project.network.nodes.data

print("This project initially has {} links and {} nodes".format(links_before.shape[0], nodes_before.shape[0]))
This project initially has 1389 links and 1239 nodes

Let’s call the NetworkSimplifier class. Any changes made to the database using this class are permanent. Make sure you have a backup if necessary.

net = NetworkSimplifier()
/home/runner/work/aequilibrae/aequilibrae/aequilibrae/project/tools/network_simplifier.py:29: UserWarning: This will alter your database in place. Make sure you have a backup.
  warnings.warn("This will alter your database in place. Make sure you have a backup.")

When we choose to simplify the network, we pass a graph object to the function, and the output of this operation is

net.simplify(graph)
net.rebuild_network()
Simplifying links                                 :   0%|          | 0/152 [00:00<?, ?it/s]
Simplifying links                                 :  11%|█         | 17/152 [00:00<00:00, 162.78it/s]
Simplifying links                                 :  22%|██▏       | 34/152 [00:00<00:00, 160.79it/s]
Simplifying links                                 :  34%|███▎      | 51/152 [00:00<00:00, 163.79it/s]
Simplifying links                                 :  45%|████▍     | 68/152 [00:00<00:00, 158.69it/s]
Simplifying links                                 :  56%|█████▌    | 85/152 [00:00<00:00, 159.90it/s]
Simplifying links                                 :  67%|██████▋   | 102/152 [00:00<00:00, 159.15it/s]
Simplifying links                                 :  78%|███████▊  | 119/152 [00:00<00:00, 160.79it/s]
Simplifying links                                 :  90%|█████████ | 137/152 [00:00<00:00, 164.54it/s]

/home/runner/work/aequilibrae/aequilibrae/aequilibrae/project/tools/network_simplifier.py:187: UserWarning: Geometry is in a geographic CRS. Results from 'length' are likely incorrect. Use 'GeoSeries.to_crs()' to re-project geometries to a projected CRS before this operation.

  old_dist = self.link_layer.geometry.length.sum()
/home/runner/work/aequilibrae/aequilibrae/aequilibrae/project/tools/network_simplifier.py:190: UserWarning: Geometry is in a geographic CRS. Results from 'length' are likely incorrect. Use 'GeoSeries.to_crs()' to re-project geometries to a projected CRS before this operation.

  new_dist = new_layer.data.geometry.length.sum()

Let’s plot the previous and actual networks!

links_after = net.network.links.data
nodes_after = net.network.nodes.data
fig = branca.element.Figure()

subplot1 = fig.add_subplot(1, 2, 1)
subplot2 = fig.add_subplot(1, 2, 2)

map1 = folium.Map(location=[-0.508371, 166.931142], zoom_start=17)
map1 = links_before.explore(m=map1, color="black", style_kwds={"weight": 2}, name="links_before")
map1 = nodes_before.explore(m=map1, color="red", style_kwds={"radius": 3, "fillOpacity": 1.0}, name="nodes_before")
folium.LayerControl().add_to(map1)

map2 = folium.Map(location=[-0.508371, 166.931142], zoom_start=17)
map2 = links_after.explore(m=map2, color="black", style_kwds={"weight": 2}, name="links_after")
map2 = nodes_after.explore(m=map2, color="blue", style_kwds={"radius": 3, "fillOpacity": 1.0}, name="nodes_after")
folium.LayerControl().add_to(map2)

subplot1.add_child(map1)
subplot2.add_child(map2)

fig
Make this Notebook Trusted to load map: File -> Trust Notebook


Differently we can simplify the network by collapsing links into nodes. Notice that this operation modifies the network in the neighborhood.

net.collapse_links_into_nodes([903])
net.rebuild_network()

Let’s plot the network once again and check the modifications!

links_after = net.network.links.data
nodes_after = net.network.nodes.data
fig = branca.element.Figure()

subplot1 = fig.add_subplot(1, 2, 1)
subplot2 = fig.add_subplot(1, 2, 2)

map1 = folium.Map(location=[-0.509363, 166.928563], zoom_start=18)
map1 = links_before.explore(m=map1, color="black", style_kwds={"weight": 2}, name="links_before")
map1 = nodes_before.explore(m=map1, color="red", style_kwds={"radius": 3, "fillOpacity": 1.0}, name="nodes_before")
folium.LayerControl().add_to(map1)

map2 = folium.Map(location=[-0.509363, 166.928563], zoom_start=18)
map2 = links_after.explore(m=map2, color="black", style_kwds={"weight": 2}, name="links_after")
map2 = nodes_after.explore(m=map2, color="blue", style_kwds={"radius": 3, "fillOpacity": 1.0}, name="nodes_after")
folium.LayerControl().add_to(map2)

subplot1.add_child(map1)
subplot2.add_child(map2)

fig
Make this Notebook Trusted to load map: File -> Trust Notebook


project.close()
This project at /tmp/b2c979383b414ff9b0496ab129b9472c is already closed

Total running time of the script: (0 minutes 2.819 seconds)

Gallery generated by Sphinx-Gallery