Note
Go to the end to download the full example code.
Active transport assignment#
We can also assign the resulting active transport trips to the walk and bike networks using AequilibraE
For this integration we have used [AequilibraE](https://www.aequilibrae.com/), which is an open source modelling package with incredibly fast static assignment capabilities, but similar integrations are possible with other packages.
sphinx_gallery_thumbnail_path = ‘../../examples/modelling_like_the_old_days/active_transport.png’
import numpy as np
import pandas as pd
from aequilibrae.matrix import AequilibraeMatrix
from aequilibrae.paths import TrafficAssignment, TrafficClass
from scipy.sparse import coo_matrix
from polaris import Polaris
from polaris.runs.static_assignment.static_active_graph import ActiveGraph
from polaris.utils.testing.temp_model import TempModel
We create a new model directory with a grid network that has already been run
project_dir = TempModel("Grid")
pol = Polaris.from_dir(project_dir)
Before anything else, we build the graphs
ag = ActiveGraph(pol.supply_file)
walk_graph = ag.walkgraph
bike_graph = ag.bikegraph
/venv-py312/lib/python3.12/site-packages/geopandas/array.py:1770: UserWarning: CRS not set for some of the concatenation inputs. Setting output's CRS as NAD83 / UTM zone 16N (the single non-null crs provided).
return GeometryArray(data, crs=_get_common_crs(to_concat))
/venv-py312/lib/python3.12/site-packages/geopandas/array.py:1770: UserWarning: CRS not set for some of the concatenation inputs. Setting output's CRS as NAD83 / UTM zone 16N (the single non-null crs provided).
return GeometryArray(data, crs=_get_common_crs(to_concat))
Cleaning the network#
The bike and walk networks may not be fully connected, so let’s make sure we only assign trips between nodes that are part of the main network
walk_graph.set_graph("time")
walk_graph.set_skimming(["time"])
all_nodes = ag.nodes.node.to_numpy()
res = walk_graph.compute_path(all_nodes[0], all_nodes[1])
res.predecessors[walk_graph.nodes_to_indices[all_nodes[0]]] = 0
connected = walk_graph.all_nodes[np.where(res.predecessors >= 0)]
missing_nodes = np.setdiff1d(all_nodes, connected)
# So we got the actual "main island" of the network
assert connected.shape[0] > 0.95 * all_nodes.shape[0]
print(f"Disconnected nodes: {missing_nodes.shape[0]:,}")
Disconnected nodes: 0
locs = pol.network.tables.get("Location")
nodes = ag.nodes[ag.nodes.node.isin(connected)]
Grab the trips#
walk_trips = pol.demand.tables.get("Trip", filter='mode in (8)')
bike_trips = pol.demand.tables.get("Trip", filter='mode in (7)')
Functions to process trips intro matrices and assign them to the graph#
def transform_trips(locs, nodes, df_trips, graph):
valid_nodes = np.hstack([graph.network.a_node, graph.network.b_node])
nodes =nodes[nodes.node.isin(valid_nodes)]
node_loc = locs[["location", "geo"]].sjoin_nearest(nodes[["node", "geo"]])[["node", "location"]]
node_loc1 = node_loc.rename(columns={"location": "origin", "node":"orig_node"})
node_loc2 = node_loc.rename(columns={"location": "destination", "node":"dest_node"})
df_trips2 = df_trips[["origin", "destination"]].merge(node_loc1, on="origin").merge(node_loc2, on="destination")
df_trips2 = df_trips2.groupby(["orig_node", "dest_node"]).size().reset_index()
df_trips2.columns = ["origin", "destination", "trips"]
centroids = np.unique(np.hstack([df_trips2.origin.unique(), df_trips2.destination.unique()]))
df1 = pd.DataFrame({"origin": centroids, "orig_node": np.arange(centroids.shape[0])})
df2 = pd.DataFrame({"destination": centroids, "dest_node": np.arange(centroids.shape[0])})
df = df_trips2.merge(df1, on="origin").merge(df2, on="destination")
coo_ = coo_matrix((df.trips.values, (df.orig_node.values, df.dest_node.values)))
demand_mat = AequilibraeMatrix()
demand_mat.create_empty(zones=centroids.shape[0], matrix_names=["trips"], memory_only=True)
demand_mat.index[:] = centroids[:]
demand_mat.matrices[:, :] = 0
demand_mat.matrices[:, :, 0] = coo_.todense()[:, :]
return demand_mat
def assign(graph, matrix):
matrix.computational_view(["trips"])
graph.prepare_graph(matrix.index)
graph.set_blocked_centroid_flows(False)
graph.set_skimming([])
# Create the assignment class
assigclass = TrafficClass(name="all_trips", graph=graph, matrix=matrix)
assig = TrafficAssignment()
# We start by adding the list of traffic classes to be assigned
assig.add_class(assigclass)
# This stuff we don't need, but the API requires
# --------------------------------------
assig.set_capacity_field("distance")
assig.set_vdf("BPR") # This is not case-sensitive
assig.set_vdf_parameters({"alpha": 0.15, "beta": 4.0})
# --------------------------------------
assig.set_time_field("time")
# And the algorithm we want to use to assign
assig.set_algorithm("all-or-nothing")
# Let's set parameters that make this example run very fast
assig.max_iter = 1
assig.rgap_target = 0.01
# we then execute the assignment
assig.execute()
return assig.results().reset_index()
Assign Walk trips#
cols = []
walk_trips_mat = transform_trips(locs, nodes, walk_trips, walk_graph)
walk_link_loads = assign(walk_graph, walk_trips_mat)[["link_id", "trips_ab", "trips_ba", "trips_tot"]]
walk_links = pol.network.tables.get("Transit_Walk").rename(columns={"walk_link":"link_id"})[["link_id", "geo"]]
walk_links = walk_links.merge(walk_link_loads, on="link_id")
factor = 30 / walk_links.trips_tot.max()
walk_links.to_crs(epsg=4326).explore(
color="blue",
style_kwds={
"style_function": lambda x: {
"weight": x["properties"]["trips_tot"] * factor,
}
},
)
all_trips : 0%| | 0/173 [00:00<?, ?it/s]
Equilibrium Assignment : 0%| | 0/1 [00:00<?, ?it/s]
Assign Bike trips#
bike_trips_mat = transform_trips(locs, nodes, bike_trips, bike_graph)
bike_link_loads = assign(bike_graph, bike_trips_mat)[["link_id", "trips_ab", "trips_ba", "trips_tot"]]
bike_links = pol.network.tables.get("Transit_Bike").rename(columns={"bike_link":"link_id"})[["link_id", "geo"]]
bike_links = bike_links.merge(bike_link_loads, on="link_id")
factor = 30 / bike_links.trips_tot.max()
bike_links.explore(
color="red",
style_kwds={
"style_function": lambda x: {
"weight": x["properties"]["trips_tot"] * factor,
}
},
)
all_trips : 0%| | 0/175 [00:00<?, ?it/s]
Equilibrium Assignment : 0%| | 0/1 [00:00<?, ?it/s]
Total running time of the script: (0 minutes 5.768 seconds)