Add bus journeys.

This commit is contained in:
Edward Betts 2026-02-18 09:39:51 +00:00
parent bc2c884589
commit 960b4a1fc7
3 changed files with 56 additions and 2 deletions

View file

@ -124,6 +124,38 @@ def load_coaches(
return coaches return coaches
def load_buses(
data_dir: str, route_distances: travel.RouteDistances | None = None
) -> list[StrDict]:
"""Load buses."""
items = load_travel("bus", "buses", data_dir)
stops = travel.parse_yaml("bus_stops", data_dir)
by_name = {stop["name"]: stop for stop in stops}
for item in items:
add_station_objects(item, by_name)
# Add route distance and CO2 calculation if available
if route_distances:
travel.add_leg_route_distance(item, route_distances)
if "distance" in item:
# Calculate CO2 emissions for bus (0.027 kg CO2e per passenger per km)
item["co2_kg"] = item["distance"] * 0.1
# Include GeoJSON route if defined in stations data
from_station = item.get("from_stop")
to_station = item.get("to_stop")
if from_station and to_station:
# Support scalar or mapping routes: string or dict of station name -> geojson base name
routes_val = from_station.get("routes", {})
if isinstance(routes_val, str):
geo = routes_val
else:
geo = routes_val.get(to_station.get("name"))
if geo:
item["geojson_filename"] = geo
return items
def process_flight( def process_flight(
flight: StrDict, by_iata: dict[str, Airline], airports: list[StrDict] flight: StrDict, by_iata: dict[str, Airline], airports: list[StrDict]
) -> None: ) -> None:
@ -179,7 +211,8 @@ def collect_travel_items(
load_flights(load_flight_bookings(data_dir)) load_flights(load_flight_bookings(data_dir))
+ load_trains(data_dir, route_distances=route_distances) + load_trains(data_dir, route_distances=route_distances)
+ load_ferries(data_dir, route_distances=route_distances) + load_ferries(data_dir, route_distances=route_distances)
+ load_coaches(data_dir, route_distances=route_distances), + load_coaches(data_dir, route_distances=route_distances)
+ load_buses(data_dir, route_distances=route_distances),
key=depart_datetime, key=depart_datetime,
) )

View file

@ -16,6 +16,7 @@ from agenda import format_list_with_ampersand
from . import utils from . import utils
StrDict = dict[str, typing.Any] StrDict = dict[str, typing.Any]
DateOrDateTime = datetime.datetime | datetime.date DateOrDateTime = datetime.datetime | datetime.date
@ -44,6 +45,7 @@ class TripElement:
"flight": ":airplane:", "flight": ":airplane:",
"ferry": ":ferry:", "ferry": ":ferry:",
"coach": ":bus:", "coach": ":bus:",
"bus": ":bus:",
} }
alias = emoji_map.get(self.element_type) alias = emoji_map.get(self.element_type)
@ -414,6 +416,25 @@ class Trip:
) )
) )
if item["type"] == "bus":
# Include bus journeys in trip elements
from_country = agenda.get_country(item["from_stop"]["country"])
to_country = agenda.get_country(item["to_stop"]["country"])
name = f"{item['from']}{item['to']}"
elements.append(
TripElement(
start_time=item["depart"],
end_time=item.get("arrive"),
title=name,
detail=item,
element_type="bus",
start_loc=item["from"],
end_loc=item["to"],
start_country=from_country,
end_country=to_country,
)
)
return sorted(elements, key=lambda e: utils.as_datetime(e.start_time)) return sorted(elements, key=lambda e: utils.as_datetime(e.start_time))
def elements_grouped_by_day(self) -> list[tuple[datetime.date, list[TripElement]]]: def elements_grouped_by_day(self) -> list[tuple[datetime.date, list[TripElement]]]:

View file

@ -186,7 +186,7 @@ function build_map(map_id, coordinates, routes) {
// Draw routes // Draw routes
routes.forEach(function(route) { routes.forEach(function(route) {
var color = {"train": "blue", "flight": "red", "unbooked_flight": "orange", "coach": "green"}[route.type]; var color = {"train": "blue", "flight": "red", "unbooked_flight": "orange", "coach": "green", "bus": "purple"}[route.type];
var style = { weight: 3, opacity: 0.5, color: color }; var style = { weight: 3, opacity: 0.5, color: color };
if (route.geojson) { if (route.geojson) {
L.geoJSON(JSON.parse(route.geojson), { L.geoJSON(JSON.parse(route.geojson), {