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#

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.
2025-06-17 01:06:17,580;WARNING ; Field(s) speed, travel_time, capacity, osm_id, lanes has(ve) at least one NaN value. Check your computations
/home/runner/work/aequilibrae/aequilibrae/aequilibrae/paths/graph.py:248: UserWarning: Found centroids not present in the graph!
[  1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18
  19  20  21  22  23  24  25  26  27  28  29  30  31  32  33  34  35  36
  37  38  39  40  41  42  43  44  45  46  47  48  49  50  51  52  53  54
  55  56  57  58  59  60  61  62  63  64  65  66  67  68  69  70  71  72
  73  74  75  76  77  78  79  80  81  82  83  84  85  86  87  88  89  90
  91  92  93  94  95  96  97  98  99 100 101 102 103 104 105 106 107 108
 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
 127 128 129 130 131 132 133]
  warnings.warn("Found centroids not present in the graph!\n" + str(centroids[~present_centroids]))
2025-06-17 01:06:17,653;WARNING ; Field(s) speed, travel_time, capacity, osm_id, lanes has(ve) at least one NaN value. Check your computations
2025-06-17 01:06:17,743;WARNING ; Field(s) speed, travel_time, capacity, osm_id, lanes has(ve) at least one NaN value. Check your computations
2025-06-17 01:06:17,833;WARNING ; Field(s) speed, travel_time, capacity, osm_id, lanes has(ve) at least one NaN value. Check your computations
/home/runner/work/aequilibrae/aequilibrae/aequilibrae/paths/graph.py:248: UserWarning: Found centroids not present in the graph!
[  1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18
  19  20  21  22  23  24  25  26  27  28  29  30  31  32  33  34  35  36
  37  38  39  40  41  42  43  44  45  46  47  48  49  50  51  52  53  54
  55  56  57  58  59  60  61  62  63  64  65  66  67  68  69  70  71  72
  73  74  75  76  77  78  79  80  81  82  83  84  85  86  87  88  89  90
  91  92  93  94  95  96  97  98  99 100 101 102 103 104 105 106 107 108
 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
 127 128 129 130 131 132 133]
  warnings.warn("Found centroids not present in the graph!\n" + str(centroids[~present_centroids]))

We grab the graph for cars,

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

we’ll also see what graphs are available.

project.network.graphs.keys()
dict_keys(['b', 'c', 't', 'w'])

Let’s say we want to minimise the distance,

graph.set_graph("distance")

and will skim time and distance while we are at it.

graph.set_skimming(["travel_time", "distance"])

Let’s create a path results object from the graph and compute a path from node 32343 (near the airport) to 22041 (near Fort Lambert, overlooking Coquimbo Bay).

res = graph.compute_path(32343, 22041)

Computing paths directly from the graph is more straightforward, though we could alternatively use PathComputation class to achieve the same result.

# from aequilibrae.paths import PathResults

# res = PathResults()
# res.prepare(graph)
# 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, you can also provide early_exit=True or a_star=True to compute_path to adjust its path-finding behavior.

Providing early_exit=True allows you to quit the path-finding procedure once it discovers the destination. This setup works better for topographically close origin-destination pairs. However, exiting early may cause subsequent calls to update_trace to recompute the tree in cases where it typically wouldn’t.

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

If you prefer to find a potentially non-optimal path to the destination faster, provide a_star=True to use A* with a heuristic. This method always recomputes the path’s nodes, links, skims, and mileposts with update_trace. Note that a_star takes precedence over early_exit.

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

If you are using a_star, it is possible to use different heuristics to compute the path. By default, an equirectangular heuristic is used, and we can view the available heuristics via:

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

If you prefer a more accurate but slower heuristic, you can choose “haversine”, by setting:

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

Suppose you want to adjust the path to the University of La Serena instead of Fort Lambert. It is possible to adjust the existing path computation for this alteration. The following code allows both early_exit and A* settings to persist when calling update_trace. If you’d like to adjust them for subsequent path re-computations set the res.early_exit and res.a_star attributes. Notice that this procedure is much faster when you have large networks.

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.

links = project.network.links.data.set_index("link_id")
links = links.loc[res.path]
links.explore(color="blue", style_kwds={'weight':5})
Make this Notebook Trusted to load map: File -> Trust Notebook


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

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

Gallery generated by Sphinx-Gallery