Path computation#

In this example, we show how to perform path computation for Coquimbo, a city in La Serena Metropolitan Area in Chile.

See also

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

# Imports
from uuid import uuid4
from tempfile import gettempdir
from os.path import join
from aequilibrae.utils.create_example import create_example
# We create the example project inside our temp folder
fldr = join(gettempdir(), uuid4().hex)

project = create_example(fldr, "coquimbo")
import logging
import sys

We the project opens, we can tell the logger to direct all messages to the terminal as well

logger = project.logger
stdout_handler = logging.StreamHandler(sys.stdout)
formatter = logging.Formatter("%(asctime)s;%(levelname)s ; %(message)s")
stdout_handler.setFormatter(formatter)
logger.addHandler(stdout_handler)

Path Computation#

from aequilibrae.paths import PathResults

We build all graphs

project.network.build_graphs()
# We get warnings that several fields in the project are filled with ``NaN``s.
# This is true, but we won't use those fields.
2024-11-08 03:00:59,385;WARNING ; Field(s) speed, travel_time, capacity, osm_id, lanes has(ve) at least one NaN value. Check your computations
2024-11-08 03:00:59,458;WARNING ; Field(s) speed, travel_time, capacity, osm_id, lanes has(ve) at least one NaN value. Check your computations
2024-11-08 03:00:59,546;WARNING ; Field(s) speed, travel_time, capacity, osm_id, lanes has(ve) at least one NaN value. Check your computations
2024-11-08 03:00:59,632;WARNING ; Field(s) speed, travel_time, capacity, osm_id, lanes has(ve) at least one NaN value. Check your computations

We grab the graph for cars

graph = project.network.graphs["c"]

# we also see what graphs are available
project.network.graphs.keys()

# let's say we want to minimize the distance
graph.set_graph("distance")

# And will skim time and distance while we are at it
graph.set_skimming(["travel_time", "distance"])

# And we will allow paths to be computed going through other centroids/centroid connectors.
# We recommend you to `be extremely careful` with this setting.
graph.set_blocked_centroid_flows(False)

Let’s instantiate a path results object and prepare it to work with the graph

res = PathResults()
res.prepare(graph)

# compute a path from node 32343 to 22041, thats from near the airport to Fort Lambert,
# a popular location due to its views of the Coquimbo bay.
res.compute_path(32343, 22041)

# We can get the sequence of nodes we traverse
res.path_nodes
array([32343, 79778, 68225, 32487, 63937, 63192, 46510, 32380, 32373,
       55817, 55816, 11982, 46516, 75015, 79704, 79785, 78576, 68242,
       79144, 78635, 79784, 78748, 79082, 65861, 78343, 21311, 20312,
       21308, 78834, 79862, 79450, 63873, 79458, 78986, 78884, 79152,
       78645, 78549, 68503, 13380, 13383, 79199, 79745, 79457, 80001,
       78217, 78093, 80013, 25130, 80012, 40633, 11010, 11009, 40846,
       21827, 80056, 80055, 79481, 79486, 79485, 75142, 11448, 11446,
       11445, 67684, 60645, 11447, 11422, 11420, 11421, 13723, 10851,
       79462, 26681, 13718, 12079, 79460, 23707, 29778, 75451, 75445,
       45342, 39399, 13626, 13627, 45379, 21384, 63812, 40005, 12207,
       44243, 44241, 23405, 60002, 27114, 79431, 15148, 15146, 60000,
       75486, 55963, 55958, 59043, 59050, 59988, 39402, 59017, 59019,
       79398, 75520, 75516, 75512, 75509, 75505, 75511, 63544, 63543,
       75510, 75515, 75476, 63539, 30138, 11695, 61061, 30148, 44192,
       75556, 79364, 75534, 75552, 75548, 75321, 75532, 14802, 14823,
       71435, 65497, 64708, 64709, 64712, 64713, 40374, 40375, 77308,
       65518, 75566, 68526, 75573, 41306, 41308, 75619, 75617, 14899,
       14875, 38674, 75595, 65067, 65068, 79508, 29452, 44797, 29447,
       10065, 44798, 30552, 44783, 44808, 75612, 73617, 79653, 79651,
       73620, 73923, 79820, 14864, 69009, 22040, 22041])

We can get the link sequence we traverse

res.path
array([34709, 34710, 34711, 34712, 34713, 34714, 34715, 34716, 34717,
       34718, 34719, 34720, 34721, 34722,  3321,  3322,  3323,  3324,
        3325,  3326,  3327,  3328,  3329,  3330,  3331,  3332,  2970,
        2971,  2969, 19995,  1434,  1435,  1436, 19326, 19327, 19328,
       19329, 19330, 33674, 33675, 33676, 33677, 26525, 20765, 20746,
       20747, 20748, 20749, 20750, 20751, 20752,   496,   497,   498,
         499,   500,   501, 10380, 15408,   553,   552,   633,   634,
         635,   630,   631,   632,   623,   624,   625,   626,   471,
        5363, 34169, 34170, 34171, 34785,  6466,  6465, 29938, 29939,
       29940, 29941,  1446,  1447,  1448,  1449,  1450,   939,   940,
         941,  9840,  9841, 26314, 26313, 26312, 26311, 26310, 26309,
       26308, 26307, 26306, 26305, 26304, 26303, 26302, 26301, 26300,
       34079, 34147, 29962, 26422, 26421, 26420,   765,   764,   763,
         762,   761,   760,   736, 10973, 10974, 10975,   725, 10972,
         727,   728, 26424,   733,   734, 29899, 20970, 20969, 20968,
       20967, 20966, 20965, 20964, 20963, 20962,  9584,  9583, 20981,
       21398, 20982, 20983, 20984, 20985, 10030, 10031, 10032, 10033,
       10034, 10035, 10036,    64,    65, 21260, 21261, 21262, 21263,
       21264, 21265, 21266,    33, 11145, 11146,    71,    72, 34529,
       34530, 34531, 28691, 28692, 28693,  3574])

We can get the mileposts for our sequence of nodes

res.milepost
array([    0.        ,   161.94834565,   252.51996291,   390.08508135,
         549.82648658,   561.81026027,   581.36871507,   593.21987605,
         630.08836889,  1030.57121686,  1052.34444478,  1112.07906484,
        1165.81004929,  1267.22602763,  1624.43103234,  1924.50863633,
        1972.01917098,  2021.6997169 ,  2062.34315111,  2109.67655695,
        2155.98823775,  2196.91106309,  2221.69061004,  2249.01761535,
        2298.72337036,  2363.21417492,  2375.9615406 ,  2392.22488207,
        2426.81462733,  2675.27978499,  3632.15818275,  3699.27505758,
        3823.65932479,  3956.65040737,  4017.46560312,  4072.01402297,
        4129.64308736,  4163.12532905,  4187.96101397,  4224.21499938,
        4307.91646806,  4323.79479478,  4458.86701963,  4569.20833913,
        4692.25740782,  4930.34266637,  4997.45500599,  5068.23739204,
        5120.12897437,  5187.77254139,  5207.89952503,  5327.95612724,
        5368.62533167,  5378.38940327,  5385.33334293,  5426.19568418,
        5468.3231786 ,  5523.97170089,  5548.40671886,  5559.07102378,
        5592.3855075 ,  5757.7535581 ,  5923.39441593,  5950.82694781,
        5956.69374937,  5982.88386915,  6047.00805103,  6254.07203583,
        6284.94026694,  6297.98296824,  7047.68761904,  7322.48973364,
        7410.01354224,  7554.81816989,  7654.30645223,  8000.95440184,
        8009.6579379 ,  8048.90437039,  8056.1819337 ,  8230.32269321,
        8476.75016293,  8620.89727688,  9010.45521832,  9274.61196368,
        9280.12960244,  9383.74516275,  9496.88846171,  9711.90807592,
       10093.92389013, 10097.37343293, 10098.79806269, 10100.74846208,
       10158.97660612, 10308.02183308, 11038.25297634, 11043.82678472,
       11184.07536528, 11241.90536698, 11251.4005022 , 11507.50220541,
       11734.64237287, 11891.6028426 , 11914.05783947, 11931.91664218,
       11955.2823231 , 12032.22401165, 12107.39910016, 12119.5125075 ,
       12136.71574687, 12210.28469894, 12389.41094897, 12417.43348286,
       12718.35052704, 12730.00818724, 12843.38553893, 12911.9429086 ,
       12921.34664177, 13071.40008271, 13080.33320947, 13161.6119503 ,
       13335.34223859, 13388.53619865, 13412.08272452, 13574.14596808,
       13738.18613296, 13798.71647839, 14036.00947087, 14102.56840025,
       14197.10918933, 14292.09203161, 14315.76699626, 14434.43917168,
       14484.88708304, 14491.84542125, 14851.61908868, 14868.34527227,
       14877.57764797, 15028.49457611, 15037.54620347, 15204.94094821,
       15215.00009437, 15351.10413143, 15623.06737615, 15672.61684115,
       15722.54738786, 15807.97421917, 15901.71819152, 15937.27964123,
       16179.52060712, 16191.20903769, 16203.56934613, 16308.11327422,
       16479.15664668, 16536.53372128, 16548.12481843, 16559.65540426,
       16680.70266884, 16720.69837006, 16796.68692111, 16841.49843813,
       16877.97092451, 16889.16327603, 16920.08515864, 16976.52291632,
       17076.47607269, 17094.22058834, 17134.27959626, 17183.85951634,
       17472.72121764, 17554.85677394, 17659.06248146, 17923.55576674,
       17933.3892542 , 17942.66958063, 18164.10699022, 18421.89973826,
       18597.64939792, 18599.05175306])

Additionally we could also provide early_exit=True or a_star=True to compute_path to adjust its path finding behaviour. Providing early_exit=True will allow the path finding to quit once it’s discovered the destination, this means it will perform better for ODs that are topographically close. However, exiting early may cause subsequent calls to update_trace to recompute the tree in cases where it usually wouldn’t. a_star=True has precedence of early_exit=True.

res.compute_path(32343, 22041, early_exit=True)

If you’d prefer to find a potentially non-optimal path to the destination faster provide a_star=True to use A* with a heuristic. With this method update_trace will always recompute the path.

res.compute_path(32343, 22041, a_star=True)

By default a equirectangular heuristic is used. We can view the available heuristics via

res.get_heuristics()
['haversine', 'equirectangular']

If you’d like the more accurate, but slower, but more accurate haversine heuristic you can set it using

res.set_heuristic("haversine")

or

res.compute_path(32343, 22041, a_star=True, heuristic="haversine")

If we want to compute the path for a different destination and the same origin, we can just do this. It is way faster when you have large networks. Here we’ll adjust our path to the University of La Serena. Our previous early exit and A* settings will persist with calls to update_trace. If you’d like to adjust them for subsequent path re-computations set the res.early_exit and res.a_star attributes.

res.a_star = False
res.update_trace(73131)
res.path_nodes
array([32343, 79778, 68225, 32487, 63937, 63192, 46510, 32380, 32373,
       55817, 55816, 11982, 46516, 75015, 79704, 79785, 78576, 68242,
       79144, 78635, 79784, 78748, 79082, 65861, 78343, 21311, 20312,
       21308, 78834, 79862, 79450, 63873, 79458, 78986, 78884, 79152,
       78645, 78549, 68503, 13380, 13383, 79199, 79745, 79457, 80001,
       78217, 78093, 80013, 25130, 80012, 40633, 11010, 11009, 40846,
       21827, 80056, 80055, 79481, 79486, 79485, 75142, 11448, 11446,
       11445, 67684, 60645, 11447, 11422, 11420, 11421, 13723, 10851,
       79462, 26681, 13718, 12079, 79460, 23707, 29778, 75451, 75445,
       45342, 39399, 13626, 13627, 45379, 21384, 63812, 40005, 12207,
       44243, 44241, 23405, 60002, 27114, 79431, 15148, 15146, 60000,
       75486, 55963, 55958, 59043, 59050, 59988, 39402, 59017, 59019,
       79398, 75520, 75516, 75512, 75509, 75505, 75511, 63544, 63543,
       75510, 75515, 75476, 63539, 30138, 11695, 61061, 30148, 44192,
       75556, 79364, 75534, 75552, 75548, 75321, 75532, 14802, 14823,
       71435, 65497, 64708, 64709, 64712, 64713, 40374, 40375, 77308,
       65518, 75566, 68526, 79517, 51754, 77189, 65059, 10093, 65058,
       30491, 66966, 66863, 30492, 77190, 77191, 79366, 77417, 79368,
       77406, 77421, 77425, 77393, 77398, 53993, 77394, 70959, 77395,
       27752, 65293, 73131])

If you want to show the path in Python.

We do NOT recommend this, though… It is very slow for real networks.

import matplotlib.pyplot as plt
from shapely.ops import linemerge
links = project.network.links

# We plot the entire network
curr = project.conn.cursor()
curr.execute("Select link_id from links;")

for lid in curr.fetchall():
    geo = links.get(lid[0]).geometry
    plt.plot(*geo.xy, color="red")

path_geometry = linemerge(links.get(lid).geometry for lid in res.path)
plt.plot(*path_geometry.xy, color="blue", linestyle="dashed", linewidth=2)
plt.show()
plot path computation
project.close()

Total running time of the script: (1 minutes 20.792 seconds)

Gallery generated by Sphinx-Gallery