Skip to content

Commit 2e82cb6

Browse files
committed
Add infrastructure for generating a "meta station" GTFS feed
Meta stations allow using e.g. "London" or "Paris" as destination while referring to a set of (main) stations in the city. This affects sufficiently few cities that we can maintain this mapping manually, as this only really produces the result for multiple terminus stations with limited connectivity between each other on long-distance modes.
1 parent d4ce49d commit 2e82cb6

2 files changed

Lines changed: 121 additions & 0 deletions

File tree

metastations/metastations.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# SPDX-FileCopyrightText: Volker Krause <vkrause@kde.org>
2+
# SPDX-License-Identifier: CC0-1.0
3+
4+
# London
5+
Q84:
6+
- Q219867 # King's Cross
7+
- Q720102 # St Pancras
8+
- Q795691 # Waterloo Station
9+
- Q800751 # Euston Station
10+
- Q801124 # Liverpool Street

src/generate-metastations.py

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
#!/usr/bin/env python3
2+
# SPDX-FileCopyrightText: Volker Krause <vkrause@kde.org>
3+
# SPDX-License-Identifier: AGPL-3.0-or-later
4+
5+
import argparse
6+
import csv
7+
from ruamel.yaml import YAML
8+
import pathlib
9+
import requests
10+
import os
11+
12+
parser = argparse.ArgumentParser(description='Transitous meta-station GTFS feed generator.')
13+
parser.add_argument('--config', type=str, help="Configuration file describing the meta station mapping")
14+
parser.add_argument('--output', type=str, help="GTFS feed output folder")
15+
arguments = parser.parse_args()
16+
17+
18+
if __name__ == "__main__":
19+
20+
stops = []
21+
stop_groups = []
22+
translations = []
23+
with open(arguments.config) as f:
24+
yaml = YAML(typ="rt")
25+
config = yaml.load(f)
26+
27+
headers = {"User-Agent": "org.transitous.metastation-generator"}
28+
for metastation in config:
29+
print(metastation)
30+
metareq = requests.get(f"https://www.wikidata.org/w/rest.php/wikibase/v1/entities/items/{metastation}/labels", headers=headers)
31+
metadata = metareq.json()
32+
stop = {
33+
"stop_id": metastation,
34+
"stop_name": metadata["en"],
35+
"stop_lat": 0,
36+
"stop_lon": 0,
37+
"location_type": 0
38+
}
39+
stops.append(stop)
40+
41+
for lang, name in metadata.items():
42+
if lang == "en" or name == metadata["en"]:
43+
continue
44+
translation = {
45+
"table_name": "stops",
46+
"field_name": "stop_name",
47+
"language": lang,
48+
"record_id": metastation,
49+
"translation": name
50+
}
51+
translations.append(translation)
52+
53+
for station in config[metastation]:
54+
print(station)
55+
stationreq = requests.get(f"https://www.wikidata.org/w/rest.php/wikibase/v1/entities/items/{station}", headers=headers)
56+
stationdata = stationreq.json()
57+
coord = stationdata["statements"]["P625"][0]["value"]["content"]
58+
stop = {
59+
"stop_id": station,
60+
"stop_name": stationdata["labels"]["en"],
61+
"stop_lat": coord["latitude"],
62+
"stop_lon": coord["longitude"],
63+
"location_type": 0
64+
}
65+
stops.append(stop)
66+
stop_group = {
67+
"stop_group_id": metastation,
68+
"stop_id": station,
69+
}
70+
stop_groups.append(stop_group)
71+
72+
os.makedirs(arguments.output, exist_ok=True)
73+
74+
# create dummy files we need for MOTIS to recognize this as a valid GTFS feed
75+
pathlib.Path(os.path.join(arguments.output, "calendar_dates.txt")).touch(exist_ok=True)
76+
pathlib.Path(os.path.join(arguments.output, "routes.txt")).touch(exist_ok=True)
77+
pathlib.Path(os.path.join(arguments.output, "stop_times.txt")).touch(exist_ok=True)
78+
pathlib.Path(os.path.join(arguments.output, "trips.txt")).touch(exist_ok=True)
79+
80+
# dummy agency data to define a timezone
81+
agency = {
82+
"agency_id": "transitous",
83+
"agency_name": "Transitous",
84+
"agency_url": "https://transitous.org",
85+
"agency_timezone": "Etc/UTC",
86+
}
87+
with open(os.path.join(arguments.output, "agency.txt"), 'w') as f:
88+
writer = csv.DictWriter(f, fieldnames=agency.keys())
89+
writer.writeheader()
90+
writer.writerow(agency)
91+
92+
# write stops
93+
with open(os.path.join(arguments.output, "stops.txt"), 'w') as f:
94+
writer = csv.DictWriter(f, fieldnames=["stop_id", "stop_name", "stop_lat", "stop_lon", "location_type"])
95+
writer.writeheader()
96+
for stop in stops:
97+
writer.writerow(stop)
98+
99+
# write stop_group_elements
100+
with open(os.path.join(arguments.output, "stop_group_elements.txt"), 'w') as f:
101+
writer = csv.DictWriter(f, fieldnames=["stop_group_id", "stop_id"])
102+
writer.writeheader()
103+
for group in stop_groups:
104+
writer.writerow(group)
105+
106+
# write translations
107+
with open(os.path.join(arguments.output, "translations.txt"), 'w') as f:
108+
writer = csv.DictWriter(f, fieldnames=["table_name", "field_name", "language", "record_id", "translation"])
109+
writer.writeheader()
110+
for t in translations:
111+
writer.writerow(t)

0 commit comments

Comments
 (0)