Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ A GraphQL API that provides nearby Japanese train stations and bus stops, runnin
- For system architecture and design decisions, see [docs/architecture.md](docs/architecture.md).
- For the design of the transfer route search (`connectedRoutes`, RAPTOR), see [docs/route-search.md](docs/route-search.md).
- For the source-backed operating constraints used by timetable-free search, see [docs/route-service-audit.md](docs/route-service-audit.md).
- For the Keio stopping-pattern audit and regression checks, see [docs/keio-stopping-pattern-audit.md](docs/keio-stopping-pattern-audit.md).
- For technical debt analysis and architectural concerns, see [docs/technical_debt.md](docs/technical_debt.md).
- For the record of the Cloudflare Workers migration, see [docs/cloudflare-workers-migration.md](docs/cloudflare-workers-migration.md).
- For the published GraphQL schema, see [schema/public.graphql](schema/public.graphql).
Expand All @@ -41,7 +42,7 @@ This project includes a comprehensive dataset of Japanese railway information in

## Contributors ✨

Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/emoji-key)):

<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
<!-- prettier-ignore-start -->
Expand Down Expand Up @@ -109,3 +110,4 @@ make deploy-production # production -> stationapi

The data lives inside the WASM binary, so **a data change needs a rebuild and a
redeploy**; it is not picked up at runtime.

1 change: 1 addition & 0 deletions data/4!types.csv
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,7 @@ DEFAULT,385,準急,ジュンキュウ,Semi Express,准急,준급,#0000FF,0,3,0,
DEFAULT,386,洛楽,ラクラク,Rakuraku,洛乐,라쿠라쿠,#DC143C,0,4,0,京阪用 快速特急,COMPLETE
DEFAULT,400,京王ライナー(京王八王子方面),ケイオウライナー,Keio Liner(for Keiō-hachiōji),京王Liner,게이오 라이너,#DD0077,0,2,0,,COMPLETE
DEFAULT,401,京王ライナー(橋本方面),ケイオウライナー,Keio Liner(for Hashimoto),京王Liner,게이오 라이너,#DD0078,0,2,0,,COMPLETE
DEFAULT,413,京王ライナー(高尾山口方面),ケイオウライナー,Keio Liner(for Takaosanguchi),京王Liner,게이오 라이너,#DD0077,0,2,0,,COMPLETE
DEFAULT,402,モーニングウィング号,モーニングウィングゴウ,"Morning Wing(Miurakaigan to Shinagawa, Sengakuji)",Morning Wing,모닝윙호,#379412,1,2,0,,COMPLETE
DEFAULT,403,イブニングウィング号,イブニングウィングゴウ,Evening Wing(Shinagawa to Misakiguchi),Evening Wing,이브닝윙호,#379412,2,2,0,,COMPLETE
DEFAULT,404,普通(方南町支線),フツウ,Local(Honancho Branch Line),慢车(方南町支线),보통(호난초 지선),#E60012,0,1,0,,COMPLETE
Expand Down
50 changes: 44 additions & 6 deletions data/5!station_station_types.csv
Original file line number Diff line number Diff line change
Expand Up @@ -2022,7 +2022,7 @@ DEFAULT,9950213,203,64,0,修善寺
DEFAULT,2400101,400,65,0,新宿
DEFAULT,2400104,400,65,1,笹塚
DEFAULT,2400105,400,65,1,代田橋
DEFAULT,2400106,400,65,1,明大前
DEFAULT,2400106,400,65,0,明大前
DEFAULT,2400107,400,65,1,下高井戸
DEFAULT,2400108,400,65,1,桜上水
DEFAULT,2400109,400,65,1,上北沢
Expand All @@ -2049,12 +2049,12 @@ DEFAULT,2400129,400,65,0,高幡不動
DEFAULT,2400130,400,65,1,南平
DEFAULT,2400131,400,65,1,平山城址公園
DEFAULT,2400132,400,65,1,長沼
DEFAULT,2400133,400,65,1,北野
DEFAULT,2400133,400,65,0,北野
DEFAULT,2400134,400,65,0,京王八王子
DEFAULT,2400101,401,66,0,新宿
DEFAULT,2400104,401,66,1,笹塚
DEFAULT,2400105,401,66,1,代田橋
DEFAULT,2400106,401,66,1,明大前
DEFAULT,2400106,401,66,0,明大前
DEFAULT,2400107,401,66,1,下高井戸
DEFAULT,2400108,401,66,1,桜上水
DEFAULT,2400109,401,66,1,上北沢
Expand Down Expand Up @@ -11375,7 +11375,7 @@ DEFAULT,2400110,382,285,1,八幡山
DEFAULT,2400111,382,285,1,芦花公園
DEFAULT,2400112,382,285,0,千歳烏山
DEFAULT,2400113,382,285,1,仙川
DEFAULT,2400114,382,285,1,つつじヶ丘
DEFAULT,2400114,382,285,0,つつじヶ丘
DEFAULT,2400115,382,285,1,柴崎
DEFAULT,2400116,382,285,1,国領
DEFAULT,2400117,382,285,1,布田
Expand Down Expand Up @@ -28232,7 +28232,7 @@ DEFAULT,2400110,382,757,1,八幡山
DEFAULT,2400111,382,757,1,芦花公園
DEFAULT,2400112,382,757,0,千歳烏山
DEFAULT,2400113,382,757,1,仙川
DEFAULT,2400114,382,757,1,つつじヶ丘
DEFAULT,2400114,382,757,0,つつじヶ丘
DEFAULT,2400115,382,757,1,柴崎
DEFAULT,2400116,382,757,1,国領
DEFAULT,2400117,382,757,1,布田
Expand Down Expand Up @@ -28336,7 +28336,7 @@ DEFAULT,2400110,382,760,1,八幡山
DEFAULT,2400111,382,760,1,芦花公園
DEFAULT,2400112,382,760,0,千歳烏山
DEFAULT,2400113,382,760,1,仙川
DEFAULT,2400114,382,760,1,つつじヶ丘
DEFAULT,2400114,382,760,0,つつじヶ丘
DEFAULT,2400115,382,760,1,柴崎
DEFAULT,2400116,382,760,1,国領
DEFAULT,2400117,382,760,1,布田
Expand Down Expand Up @@ -41996,3 +41996,41 @@ DEFAULT,3200739,306,1186,1,上古沢
DEFAULT,3200740,306,1186,1,紀伊細川
DEFAULT,3200741,306,1186,1,紀伊神谷
DEFAULT,3200742,306,1186,0,極楽橋
DEFAULT,2400101,413,1188,0,新宿
DEFAULT,2400104,413,1188,1,笹塚
DEFAULT,2400105,413,1188,1,代田橋
DEFAULT,2400106,413,1188,0,明大前
DEFAULT,2400107,413,1188,1,下高井戸
DEFAULT,2400108,413,1188,1,桜上水
DEFAULT,2400109,413,1188,1,上北沢
DEFAULT,2400110,413,1188,1,八幡山
DEFAULT,2400111,413,1188,1,芦花公園
DEFAULT,2400112,413,1188,1,千歳烏山
DEFAULT,2400113,413,1188,1,仙川
DEFAULT,2400114,413,1188,1,つつじヶ丘
DEFAULT,2400115,413,1188,1,柴崎
DEFAULT,2400116,413,1188,1,国領
DEFAULT,2400117,413,1188,1,布田
DEFAULT,2400118,413,1188,1,調布
DEFAULT,2400119,413,1188,1,西調布
DEFAULT,2400120,413,1188,1,飛田給
DEFAULT,2400121,413,1188,1,武蔵野台
DEFAULT,2400122,413,1188,1,多磨霊園
DEFAULT,2400123,413,1188,1,東府中
DEFAULT,2400124,413,1188,0,府中
DEFAULT,2400125,413,1188,0,分倍河原
DEFAULT,2400126,413,1188,1,中河原
DEFAULT,2400127,413,1188,0,聖蹟桜ヶ丘
DEFAULT,2400128,413,1188,1,百草園
DEFAULT,2400129,413,1188,0,高幡不動
DEFAULT,2400130,413,1188,1,南平
DEFAULT,2400131,413,1188,1,平山城址公園
DEFAULT,2400132,413,1188,1,長沼
DEFAULT,2400133,413,1188,0,北野
DEFAULT,2400301,413,1188,0,北野
DEFAULT,2400302,413,1188,1,京王片倉
DEFAULT,2400303,413,1188,1,山田
DEFAULT,2400304,413,1188,0,めじろ台
DEFAULT,2400305,413,1188,1,狭間
DEFAULT,2400306,413,1188,0,高尾
DEFAULT,2400307,413,1188,0,高尾山口
1 change: 1 addition & 0 deletions data/route_service_rules.csv
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,4 @@ type_cd,line_group_id,requires_timetable,boarding_wait_seconds,source,type_name,
412,1019,true,,https://www.keihan.co.jp/traffic/liner/,ライナー,京阪ライナー・平日朝夕夜限定,2026-10-13
510,,true,,https://www.keio.co.jp/zasekishitei/index_mt.html,Mt.TAKAO,土休日・少数便・方向別乗降制限,2026-10-13
519,,true,,https://www.tobu.co.jp/railway/th/timetable/,THライナー,朝夕夜限定・方向別乗降制限,2026-10-13
413,,true,,https://www.keio.co.jp/zasekishitei/index_n.html,京王ライナー(高尾山口方面),時間帯限定・方向別乗降制限,2026-10-16
54 changes: 54 additions & 0 deletions data_validator/tests/fixtures/keio_stops.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
station_number,stopping_types,source,checked_on
1,各駅停車|快速|区間急行|急行|特急|京王ライナー|Mt.TAKAO号,https://www.keio.co.jp/train/station/ko01_shinjuku/,2026-10-16
2,各駅停車|快速|区間急行|急行,https://www.keio.co.jp/train/station/ko02_hatsudai/,2026-10-16
3,各駅停車|快速|区間急行|急行,https://www.keio.co.jp/train/station/ko03_hatagaya/,2026-10-16
4,各駅停車|快速|区間急行|急行|特急,https://www.keio.co.jp/train/station/ko04_sasazuka/,2026-10-16
5,各駅停車,https://www.keio.co.jp/train/station/ko05_daitabashi/,2026-10-16
6,各駅停車|快速|区間急行|急行|特急|京王ライナー|Mt.TAKAO号,https://www.keio.co.jp/train/station/ko06in08_meidaimae/,2026-10-16
7,各駅停車|快速,https://www.keio.co.jp/train/station/ko07_shimo-takaido/,2026-10-16
8,各駅停車|快速|区間急行|急行,https://www.keio.co.jp/train/station/ko08_sakurajosui/,2026-10-16
9,各駅停車,https://www.keio.co.jp/train/station/ko09_kami-kitazawa/,2026-10-16
10,各駅停車|快速,https://www.keio.co.jp/train/station/ko10_hachiman-yama/,2026-10-16
11,各駅停車,https://www.keio.co.jp/train/station/ko11_roka-koen/,2026-10-16
12,各駅停車|快速|区間急行|急行|特急,https://www.keio.co.jp/train/station/ko12_chitose-karasuyama/,2026-10-16
13,各駅停車|快速|区間急行,https://www.keio.co.jp/train/station/ko13_sengawa/,2026-10-16
14,各駅停車|快速|区間急行|急行,https://www.keio.co.jp/train/station/ko14_tsutsujigaoka/,2026-10-16
15,各駅停車,https://www.keio.co.jp/train/station/ko15_shibasaki/,2026-10-16
16,各駅停車,https://www.keio.co.jp/train/station/ko16_kokuryo/,2026-10-16
17,各駅停車,https://www.keio.co.jp/train/station/ko17_fuda/,2026-10-16
18,各駅停車|快速|区間急行|急行|特急,https://www.keio.co.jp/train/station/ko18_chofu/,2026-10-16
19,各駅停車|快速,https://www.keio.co.jp/train/station/ko19_nishi-chofu/,2026-10-16
20,各駅停車|快速,https://www.keio.co.jp/train/station/ko20_tobitakyu/,2026-10-16
21,各駅停車|快速,https://www.keio.co.jp/train/station/ko21_musashinodai/,2026-10-16
22,各駅停車|快速,https://www.keio.co.jp/train/station/ko22_tama-reien/,2026-10-16
23,各駅停車|快速|区間急行|急行,https://www.keio.co.jp/train/station/ko23_higashi-fuchu/,2026-10-16
24,各駅停車|快速|区間急行|急行|特急|京王ライナー|Mt.TAKAO号,https://www.keio.co.jp/train/station/ko24_fuchu/,2026-10-16
25,各駅停車|快速|区間急行|急行|特急|京王ライナー|Mt.TAKAO号,https://www.keio.co.jp/train/station/ko25_bubaigawara/,2026-10-16
26,各駅停車|快速|区間急行,https://www.keio.co.jp/train/station/ko26_nakagawara/,2026-10-16
27,各駅停車|快速|区間急行|急行|特急|京王ライナー|Mt.TAKAO号,https://www.keio.co.jp/train/station/ko27_seiseki-sakuragaoka/,2026-10-16
28,各駅停車|快速|区間急行,https://www.keio.co.jp/train/station/ko28_mogusaen/,2026-10-16
29,各駅停車|快速|区間急行|急行|特急|京王ライナー|Mt.TAKAO号,https://www.keio.co.jp/train/station/ko29_takahatafudo/,2026-10-16
30,各駅停車|快速|区間急行,https://www.keio.co.jp/train/station/ko30_minamidaira/,2026-10-16
31,各駅停車|快速|区間急行,https://www.keio.co.jp/train/station/ko31_hirayamajoshi-koen/,2026-10-16
32,各駅停車|快速|区間急行,https://www.keio.co.jp/train/station/ko32_naganuma/,2026-10-16
33,各駅停車|快速|区間急行|急行|特急|京王ライナー|Mt.TAKAO号,https://www.keio.co.jp/train/station/ko33_kitano/,2026-10-16
34,各駅停車|快速|区間急行|急行|特急|京王ライナー,https://www.keio.co.jp/train/station/ko34_keio-hachioji/,2026-10-16
35,各駅停車|快速|区間急行,https://www.keio.co.jp/train/station/ko35_keio-tamagawa/,2026-10-16
36,各駅停車|快速|区間急行|急行|特急,https://www.keio.co.jp/train/station/ko36_keio-inadazutsumi/,2026-10-16
37,各駅停車|快速|区間急行,https://www.keio.co.jp/train/station/ko37_keio-yomiuri-land/,2026-10-16
38,各駅停車|快速|区間急行,https://www.keio.co.jp/train/station/ko38_inagi/,2026-10-16
39,各駅停車|快速|区間急行,https://www.keio.co.jp/train/station/ko39_wakabadai/,2026-10-16
40,各駅停車|快速|区間急行|急行|特急|京王ライナー,https://www.keio.co.jp/train/station/ko40_keio-nagayama/,2026-10-16
41,各駅停車|快速|区間急行|急行|特急|京王ライナー,https://www.keio.co.jp/train/station/ko41_keio-tama-center/,2026-10-16
42,各駅停車|快速|区間急行,https://www.keio.co.jp/train/station/ko42_keio-horinouchi/,2026-10-16
43,各駅停車|快速|区間急行|急行|特急|京王ライナー,https://www.keio.co.jp/train/station/ko43_minami-osawa/,2026-10-16
44,各駅停車|快速|区間急行,https://www.keio.co.jp/train/station/ko44_tamasakai/,2026-10-16
45,各駅停車|快速|区間急行|急行|特急|京王ライナー,https://www.keio.co.jp/train/station/ko45_hashimoto/,2026-10-16
46,各駅停車|急行|特急,https://www.keio.co.jp/train/station/ko46_fuchukeiba-seimommae/,2026-10-16
47,各駅停車|急行,https://www.keio.co.jp/train/station/ko47_tama-dobutsukoen/,2026-10-16
48,各駅停車|快速|区間急行|特急,https://www.keio.co.jp/train/station/ko48_keio-katakura/,2026-10-16
49,各駅停車|快速|区間急行|特急,https://www.keio.co.jp/train/station/ko49_yamada/,2026-10-16
50,各駅停車|快速|区間急行|急行|特急|京王ライナー|Mt.TAKAO号,https://www.keio.co.jp/train/station/ko50_mejirodai/,2026-10-16
51,各駅停車|快速|区間急行|特急,https://www.keio.co.jp/train/station/ko51_hazama/,2026-10-16
52,各駅停車|快速|区間急行|急行|特急|京王ライナー|Mt.TAKAO号,https://www.keio.co.jp/train/station/ko52_takao/,2026-10-16
53,各駅停車|快速|区間急行|急行|特急|京王ライナー|Mt.TAKAO号,https://www.keio.co.jp/train/station/ko53_takaosanguchi/,2026-10-16
111 changes: 111 additions & 0 deletions data_validator/tests/keio_stopping_patterns.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
use std::collections::{BTreeMap, BTreeSet, HashMap};
use std::path::Path;

use csv::StringRecord;

fn records(path: &Path) -> Vec<StringRecord> {
csv::Reader::from_path(path)
.unwrap()
.records()
.collect::<Result<_, _>>()
.unwrap()
}

// Line-specific station IDs at junctions must appear on both sides, in order.
fn sections(group: u32) -> Vec<(u32, u32, u32)> {
match group {
65 | 67..=71 => vec![(24001, 1, 34)],
66 | 252 | 416 | 760..=762 => vec![(24001, 1, 18), (24002, 18, 45)],
253 | 412..=415 | 758 | 759 | 1188 => vec![(24001, 1, 33), (24003, 33, 53)],
254 | 285..=287 | 757 => {
vec![(24007, 1, 4), (24001, 4, 18), (24002, 18, 45)]
}
255 => vec![(24007, 1, 4), (24001, 4, 33), (24003, 33, 53)],
756 => vec![(24007, 1, 4), (24001, 4, 34)],
763 | 764 => vec![(24001, 1, 23), (24004, 23, 46)],
765 => vec![(24004, 23, 46)],
766 => vec![(24001, 1, 29), (24005, 29, 47)],
767 => vec![(24005, 29, 47)],
_ => panic!("unaudited Keio group: {group}"),
}
}

#[test]
fn existing_keio_patterns_match_official_stops_and_complete_station_sequences() {
let root = Path::new(env!("CARGO_MANIFEST_DIR")).join("..");
let data = root.join("data");
let stations = records(&data.join("3!stations.csv"));
let station_by_id: HashMap<&str, &StringRecord> = stations.iter().map(|r| (&r[0], r)).collect();
let types = records(&data.join("4!types.csv"));
let type_names: HashMap<&str, &str> = types.iter().map(|r| (&r[1], &r[2])).collect();
let fixture = records(&root.join("data_validator/tests/fixtures/keio_stops.csv"));
let official: HashMap<u32, &str> = fixture
.iter()
.map(|r| (r[0].parse().unwrap(), &r[1]))
.collect();
assert_eq!(official.len(), 53);

let mut actual: BTreeMap<u32, Vec<String>> = BTreeMap::new();
for row in records(&data.join("5!station_station_types.csv")) {
let station = station_by_id[&row[1]];
let line: u32 = station[13].parse().unwrap();
if !matches!(line, 24001..=24005 | 24007) {
continue;
}
let number: u32 = station[8].parse().unwrap();
let group: u32 = row[3].parse().unwrap();
let name = type_names[&row[2]];
let category = if name.starts_with("京王ライナー") {
"京王ライナー"
} else if name == "Mt.TAKAO" {
"Mt.TAKAO号"
} else {
name
};
// Per-row type is essential: Toei locals become Keio express services.
let mut expected = u32::from(!official[&number].split('|').any(|t| t == category));
if group == 758 {
// Mt.TAKAO down: Shinjuku, Meidaimae, Takaosanguchi only.
expected = u32::from(!matches!(number, 1 | 6 | 53));
}
if number == 23 && category == "特急" {
// Racecourse through service stops; main-line services partly stop.
expected = if group == 764 { 0 } else { 5 };
}
assert_eq!(
row[4].parse::<u32>().unwrap(),
expected,
"group {group}, station {} ({}), type {name}",
&station[2],
&station[0]
);
actual.entry(group).or_default().push(row[1].to_owned());
}

let audited: BTreeSet<u32> = [
65, 66, 67, 68, 69, 70, 71, 252, 253, 254, 255, 285, 286, 287, 412, 413, 414, 415, 416,
756, 757, 758, 759, 760, 761, 762, 763, 764, 765, 766, 767, 1188,
]
.into_iter()
.collect();
assert_eq!(actual.keys().copied().collect::<BTreeSet<_>>(), audited);
for (group, station_ids) in actual {
let mut expected = Vec::new();
for (line, first, last) in sections(group) {
let mut section: Vec<&StringRecord> = stations
.iter()
.filter(|r| {
r[13].parse::<u32>().unwrap() == line
&& &r[21] == "0"
&& (first..=last).contains(&r[8].parse::<u32>().unwrap())
})
.collect();
section.sort_by_key(|r| (r[22].parse::<u32>().unwrap(), r[0].to_owned()));
expected.extend(section.into_iter().map(|r| r[0].to_owned()));
}
assert_eq!(
station_ids, expected,
"incomplete/out-of-order group {group}"
);
}
}
57 changes: 57 additions & 0 deletions data_validator/tests/keio_takao_liner.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
use std::path::Path;

use csv::StringRecord;
use stationapi::domain::service_rules::service_rule;

fn records(path: &Path) -> Vec<StringRecord> {
csv::Reader::from_path(path)
.unwrap()
.records()
.collect::<Result<_, _>>()
.unwrap()
}

#[test]
fn takao_liner_has_its_own_complete_pattern_and_requires_timetable() {
let data = Path::new(env!("CARGO_MANIFEST_DIR")).join("../data");
let types = records(&data.join("4!types.csv"));
let liner_types: Vec<_> = types.iter().filter(|r| &r[1] == "413").collect();
assert_eq!(liner_types.len(), 1);
let liner = liner_types[0];
assert_eq!(&liner[2], "京王ライナー(高尾山口方面)");
assert_eq!(&liner[8], "0"); // Both directions, like the other Keio Liners.
assert_eq!(&liner[9], "2");

let all_rows = records(&data.join("5!station_station_types.csv"));
let rows: Vec<_> = all_rows.iter().filter(|r| &r[3] == "1188").collect();
let expected_stations: Vec<u32> = std::iter::once(2400101)
.chain(2400104..=2400133)
.chain(2400301..=2400307)
.collect();
let actual_stations: Vec<u32> = rows.iter().map(|r| r[1].parse().unwrap()).collect();
assert_eq!(actual_stations, expected_stations);

// Official up/down stopping lists, checked 2026-10-16:
// https://www.keio.co.jp/zasekishitei/
// https://www.keio.co.jp/zasekishitei/index_n.html
// Both Kitano IDs are intentional; Katakura, Yamada and Hazama pass.
let stops = [
2400101, 2400106, 2400124, 2400125, 2400127, 2400129, 2400133, 2400301, 2400304, 2400306,
2400307,
];
for row in rows {
let station: u32 = row[1].parse().unwrap();
assert_eq!(&row[2], "413");
assert_eq!(
row[4].parse::<u32>().unwrap(),
u32::from(!stops.contains(&station))
);
}
assert!(all_rows
.iter()
.filter(|r| &r[2] == "413")
.all(|r| &r[3] == "1188"));
let rule = service_rule(Some(413), Some(1188)).expect("Takao Liner operating rule");
assert!(rule.requires_timetable);
assert_eq!(rule.boarding_wait_seconds, None);
}
2 changes: 1 addition & 1 deletion docs/gtfs-bus-integration-research.md
Original file line number Diff line number Diff line change
Expand Up @@ -563,4 +563,4 @@ distance -- 駅間距離(メートル)

### 国土交通省

- [静的バス情報フォーマット(GTFS-JP)仕様書](https://www.mlit.go.jp/sogoseisaku/transport/sosei_transport_tk_000112.html)
- [公共交通運行情報標準データ仕様(GTFS-JP)v4](https://www.mlit.go.jp/commmmons/document/007/commmmons_doc_007-02_ver01.pdf)
Loading
Loading