Spaces:
Runtime error
Runtime error
Commit
Β·
667c69c
1
Parent(s):
3d2c916
app.py
CHANGED
@@ -17,7 +17,7 @@ with gr.Blocks(
|
|
17 |
with gr.Row() as header:
|
18 |
gr.Markdown(
|
19 |
"""
|
20 |
-
##
|
21 |
""",
|
22 |
)
|
23 |
|
@@ -26,8 +26,10 @@ with gr.Blocks(
|
|
26 |
|
27 |
with gr.Row() as metainfo:
|
28 |
limit = gr.Slider(minimum=1, maximum=5, step=1, label="Limit", interactive=True, value=3)
|
29 |
-
days = gr.Textbox(label="Day", placeholder="YYYY-MM-DD")
|
30 |
-
time = gr.Textbox(label="Time", placeholder="hh:mm")
|
|
|
|
|
31 |
|
32 |
submit = gr.Button(value="Search", variant="primary")
|
33 |
|
@@ -35,17 +37,10 @@ with gr.Blocks(
|
|
35 |
with gr.Column(visible=False) as output:
|
36 |
route = gr.Markdown()
|
37 |
|
38 |
-
def search(A, B, limit, day, time):
|
39 |
"""Search for the best route from A to B"""
|
40 |
-
md = f"""
|
41 |
-
---
|
42 |
|
43 |
-
|
44 |
-
|
45 |
-
---
|
46 |
-
"""
|
47 |
-
|
48 |
-
md = get_best_path(A, B, day, time, limit)
|
49 |
|
50 |
return {
|
51 |
output: gr.Column(visible=True),
|
@@ -54,7 +49,7 @@ with gr.Blocks(
|
|
54 |
|
55 |
submit.click(
|
56 |
search,
|
57 |
-
inputs=[A, B, limit, days, time],
|
58 |
outputs=[route, output]
|
59 |
)
|
60 |
|
|
|
17 |
with gr.Row() as header:
|
18 |
gr.Markdown(
|
19 |
"""
|
20 |
+
## Search For You Next Trip
|
21 |
""",
|
22 |
)
|
23 |
|
|
|
26 |
|
27 |
with gr.Row() as metainfo:
|
28 |
limit = gr.Slider(minimum=1, maximum=5, step=1, label="Limit", interactive=True, value=3)
|
29 |
+
days = gr.Textbox(label="π
Day", placeholder="YYYY-MM-DD")
|
30 |
+
time = gr.Textbox(label="π Time", placeholder="hh:mm")
|
31 |
+
sustainable = gr.Checkbox(label="π Sustainable", value=False)
|
32 |
+
outage = gr.Checkbox(label="π§ Outage", value=False)
|
33 |
|
34 |
submit = gr.Button(value="Search", variant="primary")
|
35 |
|
|
|
37 |
with gr.Column(visible=False) as output:
|
38 |
route = gr.Markdown()
|
39 |
|
40 |
+
def search(A, B, limit, day, time, sustainable, outage):
|
41 |
"""Search for the best route from A to B"""
|
|
|
|
|
42 |
|
43 |
+
md = get_best_path(A, B, day, time, limit, outage, sustainable)
|
|
|
|
|
|
|
|
|
|
|
44 |
|
45 |
return {
|
46 |
output: gr.Column(visible=True),
|
|
|
49 |
|
50 |
submit.click(
|
51 |
search,
|
52 |
+
inputs=[A, B, limit, days, time, sustainable, outage],
|
53 |
outputs=[route, output]
|
54 |
)
|
55 |
|
utils.py
CHANGED
@@ -38,12 +38,7 @@ PENALITIES = {
|
|
38 |
|
39 |
def get_penalties(sustainability: bool):
|
40 |
if sustainability:
|
41 |
-
PENALITIES["bike"] *= 1.1
|
42 |
-
PENALITIES["train"] *= 1.2
|
43 |
PENALITIES["car"] *= 5
|
44 |
-
else:
|
45 |
-
PENALITIES["car"] *= 2
|
46 |
-
|
47 |
return PENALITIES
|
48 |
|
49 |
|
@@ -76,13 +71,6 @@ def get_args():
|
|
76 |
parser.add_argument(
|
77 |
"--limit", type=int, default=3, help="Number of journeys to return"
|
78 |
)
|
79 |
-
parser.add_argument(
|
80 |
-
"--transportations",
|
81 |
-
type,
|
82 |
-
choices=transportation_types,
|
83 |
-
default=["train"],
|
84 |
-
help=transportation_help,
|
85 |
-
)
|
86 |
parser.add_argument("--exact-travel-time", action="store_true", help=time_help)
|
87 |
parser.add_argument(
|
88 |
"--change-penalty", type=int, default=300, help="Change penalty"
|
@@ -199,6 +187,7 @@ def dijkstra(G, start, end, start_time, change_penalty=300, mode_penalties=PENAL
|
|
199 |
node: {
|
200 |
"distance": float("infinity"),
|
201 |
"time": start_time,
|
|
|
202 |
}
|
203 |
for node in list(G.nodes())
|
204 |
}
|
@@ -207,7 +196,7 @@ def dijkstra(G, start, end, start_time, change_penalty=300, mode_penalties=PENAL
|
|
207 |
edges_to = {node: None for node in list(G.nodes())}
|
208 |
|
209 |
# Set the distance from the start node to itself to 0
|
210 |
-
distances[start]
|
211 |
edges_to[start] = ("", "Start", {"type": "foot", "journey_id": None, "duration": 0})
|
212 |
|
213 |
# Priority queue to keep track of nodes with their current distances
|
@@ -218,7 +207,7 @@ def dijkstra(G, start, end, start_time, change_penalty=300, mode_penalties=PENAL
|
|
218 |
current_distance, current_node = heapq.heappop(priority_queue)
|
219 |
|
220 |
if current_node == end:
|
221 |
-
return distances
|
222 |
|
223 |
# Check if the current distance is smaller than the stored distance
|
224 |
if current_distance > distances[current_node]["distance"]:
|
@@ -247,8 +236,11 @@ def dijkstra(G, start, end, start_time, change_penalty=300, mode_penalties=PENAL
|
|
247 |
train_departed = (
|
248 |
distances[current_node]["time"] > attributes["departure"]
|
249 |
)
|
|
|
|
|
|
|
250 |
|
251 |
-
if train_departed:
|
252 |
continue
|
253 |
|
254 |
if prev_is_train:
|
@@ -263,24 +255,36 @@ def dijkstra(G, start, end, start_time, change_penalty=300, mode_penalties=PENAL
|
|
263 |
# Add max of waiting time or changing penalty to current dist
|
264 |
if changed_mode or changed_trip:
|
265 |
distance += max(wait, change_penalty)
|
|
|
|
|
266 |
|
267 |
# Overall dist (prev dist + dist to neighbor)
|
268 |
-
|
269 |
|
270 |
# If the new distance is smaller, update the distance and add to the priority queue
|
271 |
-
if
|
272 |
# Update distance and time of arrival for neighbor
|
273 |
time_of_arrival = distances[current_node]["time"] + pd.Timedelta(
|
274 |
seconds=distance
|
275 |
)
|
276 |
-
|
|
|
277 |
distances[neighbor]["time"] = time_of_arrival
|
|
|
|
|
|
|
|
|
|
|
278 |
|
279 |
# Add edge that leads to neighbor
|
280 |
-
edges_to[neighbor] = (
|
|
|
|
|
|
|
|
|
281 |
|
282 |
# Update priority queue
|
283 |
-
heapq.heappush(priority_queue, (
|
284 |
|
285 |
print(f"Probably there is no path between {start} and {end}")
|
286 |
|
@@ -396,8 +400,16 @@ def pretty_print(edges, args):
|
|
396 |
dst = args.end
|
397 |
|
398 |
duration = pretty_time_delta(attr["duration"])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
399 |
|
400 |
-
print(f"{i+1}. Go by {attr['type']} from {src} to {dst} for {duration}")
|
401 |
|
402 |
"""
|
403 |
Remove all the trains from one station to another to simulate an outage.
|
@@ -419,10 +431,12 @@ def remove_all_trains(G, from_station, to_station):
|
|
419 |
|
420 |
|
421 |
def get_final_path_md(edges, start, end, date, time, sustainability):
|
422 |
-
|
423 |
md = f"## Your Journey from {start} to {end}\n\n"
|
|
|
424 |
md += f"π
Date/ Time: {date} at {time}\n"
|
425 |
-
md += "
|
|
|
|
|
426 |
|
427 |
for i, (src, dst, attr) in enumerate(edges):
|
428 |
if src == "Start":
|
@@ -431,23 +445,44 @@ def get_final_path_md(edges, start, end, date, time, sustainability):
|
|
431 |
dst = end
|
432 |
|
433 |
duration = pretty_time_delta(attr["duration"])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
434 |
|
435 |
-
|
436 |
-
emoji = "π" if travel_type == "train" else "π" if travel_type == "car" else "πΆ" if travel_type == "foot" else "π"
|
437 |
|
438 |
-
md += f"{i+1}. {emoji} Go by {travel_type} from {src} to {dst} for {duration}\n\n"
|
439 |
-
|
440 |
return md
|
441 |
|
442 |
-
|
443 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
444 |
# Load graph
|
445 |
with open("graph.pkl", "rb") as f:
|
446 |
G = pickle.load(f)
|
447 |
|
448 |
if outage:
|
449 |
# Remove all the edges from 2 stations to simulate an outage
|
450 |
-
remove_all_trains(G, from_station="
|
451 |
|
452 |
# Convert start and destination to location (lon, lat)
|
453 |
start_loc = get_location(G, start)
|
@@ -463,36 +498,24 @@ def get_best_path(start, end, date, time, limit, exact_travel_time=False, outage
|
|
463 |
for station, attr in G.nodes(data=True):
|
464 |
try:
|
465 |
station_pos = attr["pos"]
|
466 |
-
dists_from_start.append(
|
467 |
-
(station, get_distance(start_loc, station_pos))
|
468 |
-
)
|
469 |
dists_to_end.append((station, get_distance(end_loc, station_pos)))
|
470 |
except Exception as e:
|
471 |
continue
|
472 |
|
473 |
# Sort the distances in place
|
474 |
-
start_k_closest = sorted(dists_from_start, key=lambda x: x[1])[:
|
475 |
-
end_k_closest = sorted(dists_to_end, key=lambda x: x[1])[:
|
476 |
|
477 |
# Compute travel time from start to k closest stations
|
478 |
for mode in ["foot", "bike", "car"]:
|
479 |
for station, dist in start_k_closest:
|
480 |
-
|
481 |
-
|
482 |
-
start_loc, station, method=mode
|
483 |
-
)
|
484 |
-
G.add_edge("Start", station, duration=travel_time, type=mode)
|
485 |
-
else:
|
486 |
-
travel_time = get_approx_travel_time(dist, method=mode)
|
487 |
-
G.add_edge("Start", station, duration=travel_time, type=mode)
|
488 |
|
489 |
for station, dist in end_k_closest:
|
490 |
-
|
491 |
-
|
492 |
-
G.add_edge(station, "End", duration=travel_time, type=mode)
|
493 |
-
else:
|
494 |
-
travel_time = get_approx_travel_time(dist, method=mode)
|
495 |
-
G.add_edge(station, "End", duration=travel_time, type=mode)
|
496 |
|
497 |
# Run Dijkstra on graph
|
498 |
start_time = pd.to_datetime(f"{date} {time}")
|
@@ -512,7 +535,6 @@ def get_best_path(start, end, date, time, limit, exact_travel_time=False, outage
|
|
512 |
# Postprocess path
|
513 |
path = postprocess_path(edges[:-1])
|
514 |
|
515 |
-
# Print journey
|
516 |
md = get_final_path_md(path, start, end, date, time, sustainability)
|
517 |
|
518 |
-
return md
|
|
|
38 |
|
39 |
def get_penalties(sustainability: bool):
|
40 |
if sustainability:
|
|
|
|
|
41 |
PENALITIES["car"] *= 5
|
|
|
|
|
|
|
42 |
return PENALITIES
|
43 |
|
44 |
|
|
|
71 |
parser.add_argument(
|
72 |
"--limit", type=int, default=3, help="Number of journeys to return"
|
73 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
74 |
parser.add_argument("--exact-travel-time", action="store_true", help=time_help)
|
75 |
parser.add_argument(
|
76 |
"--change-penalty", type=int, default=300, help="Change penalty"
|
|
|
187 |
node: {
|
188 |
"distance": float("infinity"),
|
189 |
"time": start_time,
|
190 |
+
"visited": False,
|
191 |
}
|
192 |
for node in list(G.nodes())
|
193 |
}
|
|
|
196 |
edges_to = {node: None for node in list(G.nodes())}
|
197 |
|
198 |
# Set the distance from the start node to itself to 0
|
199 |
+
distances[start] = {"distance": 0, "time": start_time, "visited": True}
|
200 |
edges_to[start] = ("", "Start", {"type": "foot", "journey_id": None, "duration": 0})
|
201 |
|
202 |
# Priority queue to keep track of nodes with their current distances
|
|
|
207 |
current_distance, current_node = heapq.heappop(priority_queue)
|
208 |
|
209 |
if current_node == end:
|
210 |
+
return distances, edges_to
|
211 |
|
212 |
# Check if the current distance is smaller than the stored distance
|
213 |
if current_distance > distances[current_node]["distance"]:
|
|
|
236 |
train_departed = (
|
237 |
distances[current_node]["time"] > attributes["departure"]
|
238 |
)
|
239 |
+
train_too_far_away = (
|
240 |
+
distances[current_node]["time"] - attributes["departure"]
|
241 |
+
).total_seconds() > 60 * 60 * 1
|
242 |
|
243 |
+
if train_departed or train_too_far_away:
|
244 |
continue
|
245 |
|
246 |
if prev_is_train:
|
|
|
255 |
# Add max of waiting time or changing penalty to current dist
|
256 |
if changed_mode or changed_trip:
|
257 |
distance += max(wait, change_penalty)
|
258 |
+
elif next_is_train:
|
259 |
+
distance += wait
|
260 |
|
261 |
# Overall dist (prev dist + dist to neighbor)
|
262 |
+
total_distance = current_distance + distance
|
263 |
|
264 |
# If the new distance is smaller, update the distance and add to the priority queue
|
265 |
+
if total_distance < distances[neighbor]["distance"]:
|
266 |
# Update distance and time of arrival for neighbor
|
267 |
time_of_arrival = distances[current_node]["time"] + pd.Timedelta(
|
268 |
seconds=distance
|
269 |
)
|
270 |
+
# print(f"Arrived at {neighbor} at {time_of_arrival}")
|
271 |
+
distances[neighbor]["distance"] = total_distance
|
272 |
distances[neighbor]["time"] = time_of_arrival
|
273 |
+
distances[neighbor]["visited"] = True
|
274 |
+
|
275 |
+
# for _, nneighbor, attributes in G.out_edges(neighbor, data=True):
|
276 |
+
# if nneighbor != current_node:
|
277 |
+
# distances[nneighbor]["visited"] = False
|
278 |
|
279 |
# Add edge that leads to neighbor
|
280 |
+
edges_to[neighbor] = (
|
281 |
+
current_node,
|
282 |
+
neighbor,
|
283 |
+
attributes,
|
284 |
+
)
|
285 |
|
286 |
# Update priority queue
|
287 |
+
heapq.heappush(priority_queue, (total_distance, neighbor))
|
288 |
|
289 |
print(f"Probably there is no path between {start} and {end}")
|
290 |
|
|
|
400 |
dst = args.end
|
401 |
|
402 |
duration = pretty_time_delta(attr["duration"])
|
403 |
+
departure = attr.get("departure", None)
|
404 |
+
arrival = attr.get("arrival", None)
|
405 |
+
|
406 |
+
msg = f"{i+1}. Go by {attr['type']} from {src} to {dst} for {duration}."
|
407 |
+
if attr["type"] == "train":
|
408 |
+
msg += "\n -> Take {} ({} - {})".format(
|
409 |
+
attr["trip_name"], departure, arrival
|
410 |
+
)
|
411 |
+
print(msg)
|
412 |
|
|
|
413 |
|
414 |
"""
|
415 |
Remove all the trains from one station to another to simulate an outage.
|
|
|
431 |
|
432 |
|
433 |
def get_final_path_md(edges, start, end, date, time, sustainability):
|
|
|
434 |
md = f"## Your Journey from {start} to {end}\n\n"
|
435 |
+
|
436 |
md += f"π
Date/ Time: {date} at {time}\n"
|
437 |
+
md += f"π Sustainable?: {sustainability}\n\n"
|
438 |
+
|
439 |
+
md += "\n### Travel Information\n"
|
440 |
|
441 |
for i, (src, dst, attr) in enumerate(edges):
|
442 |
if src == "Start":
|
|
|
445 |
dst = end
|
446 |
|
447 |
duration = pretty_time_delta(attr["duration"])
|
448 |
+
departure = attr.get("departure", None)
|
449 |
+
arrival = attr.get("arrival", None)
|
450 |
+
|
451 |
+
emoji = {
|
452 |
+
"foot": "πΆ",
|
453 |
+
"bike": "π΄",
|
454 |
+
"car": "π",
|
455 |
+
"train": "π",
|
456 |
+
}
|
457 |
+
|
458 |
+
msg = f"{i+1}. {emoji[attr['type']]} Go by {attr['type']} from {src} to {dst} for {duration}.\n"
|
459 |
+
if attr["type"] == "train":
|
460 |
+
msg += " -> Take {} ({} - {})\n".format(
|
461 |
+
attr["trip_name"], departure, arrival
|
462 |
+
)
|
463 |
|
464 |
+
md += msg
|
|
|
465 |
|
|
|
|
|
466 |
return md
|
467 |
|
468 |
+
|
469 |
+
def get_best_path(
|
470 |
+
start,
|
471 |
+
end,
|
472 |
+
date,
|
473 |
+
time,
|
474 |
+
limit,
|
475 |
+
outage=False,
|
476 |
+
sustainability=False,
|
477 |
+
change_penalty=300,
|
478 |
+
):
|
479 |
# Load graph
|
480 |
with open("graph.pkl", "rb") as f:
|
481 |
G = pickle.load(f)
|
482 |
|
483 |
if outage:
|
484 |
# Remove all the edges from 2 stations to simulate an outage
|
485 |
+
remove_all_trains(G, from_station="Renens VD", to_station="Lausanne")
|
486 |
|
487 |
# Convert start and destination to location (lon, lat)
|
488 |
start_loc = get_location(G, start)
|
|
|
498 |
for station, attr in G.nodes(data=True):
|
499 |
try:
|
500 |
station_pos = attr["pos"]
|
501 |
+
dists_from_start.append((station, get_distance(start_loc, station_pos)))
|
|
|
|
|
502 |
dists_to_end.append((station, get_distance(end_loc, station_pos)))
|
503 |
except Exception as e:
|
504 |
continue
|
505 |
|
506 |
# Sort the distances in place
|
507 |
+
start_k_closest = sorted(dists_from_start, key=lambda x: x[1])[:limit]
|
508 |
+
end_k_closest = sorted(dists_to_end, key=lambda x: x[1])[:limit]
|
509 |
|
510 |
# Compute travel time from start to k closest stations
|
511 |
for mode in ["foot", "bike", "car"]:
|
512 |
for station, dist in start_k_closest:
|
513 |
+
travel_time = get_approx_travel_time(dist, method=mode)
|
514 |
+
G.add_edge("Start", station, duration=travel_time, type=mode)
|
|
|
|
|
|
|
|
|
|
|
|
|
515 |
|
516 |
for station, dist in end_k_closest:
|
517 |
+
travel_time = get_approx_travel_time(dist, method=mode)
|
518 |
+
G.add_edge(station, "End", duration=travel_time, type=mode)
|
|
|
|
|
|
|
|
|
519 |
|
520 |
# Run Dijkstra on graph
|
521 |
start_time = pd.to_datetime(f"{date} {time}")
|
|
|
535 |
# Postprocess path
|
536 |
path = postprocess_path(edges[:-1])
|
537 |
|
|
|
538 |
md = get_final_path_md(path, start, end, date, time, sustainability)
|
539 |
|
540 |
+
return md
|