From 1dbdcd6d635147976513e6200452536edb41adb1 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 12 Jul 2026 05:59:32 +0000 Subject: [PATCH 1/2] =?UTF-8?q?=E4=BA=AC=E7=8E=8B=E3=83=90=E3=82=B9?= =?UTF-8?q?=E3=81=AEGTFS=E3=83=95=E3=82=A3=E3=83=BC=E3=83=89=E3=82=92?= =?UTF-8?q?=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ODPT 公開の京王バス (京王電鉄バス) GTFS-JP フィードを取り込み対象に追加する。 - `GTFS_FEEDS` に `keio` フィードを追加(ODPT `files/odpt/KeioBus/AllLines.zip`、 西武バスと同じく `ODPT_ACCESS_TOKEN` が必要) - `company_cd_for_gtfs_route` で `keio:` 接頭辞を京王バス (`company_cd=254`) に対応付け - `data/1!companies.csv` に京王バス (company_cd=254) を追加(lines→companies の 外部キー制約を満たすため) - ダウンロードされる GTFS ディレクトリ (KeioBus/SeibuBus) を `.gitignore` に追加 - README / AGENTS / architecture ドキュメントとデータソース表記を更新 - `test_company_cd_for_gtfs_route` / `test_gtfs_feeds` を京王バス分だけ更新 検証: cargo fmt --all -- --check / SQLX_OFFLINE=true cargo clippy -- -D warnings / SQLX_OFFLINE=true cargo test / cargo run -p data_validator いずれも成功。 Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01SfBspXi8MZnEjwrAxvbBzz --- .gitignore | 2 ++ AGENTS.md | 2 +- README.md | 1 + data/1!companies.csv | 1 + docs/architecture.md | 4 ++-- stationapi/src/import.rs | 12 +++++++++++- 6 files changed, 18 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index d2839402..1c1f88d3 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,8 @@ api_descriptor.pb tmp.sql .vscode/ data/ToeiBus-GTFS/ +data/SeibuBus-GTFS/ +data/KeioBus-GTFS/ scripts/.osm_cache/ scripts/.gtfs_cache/ __pycache__/ diff --git a/AGENTS.md b/AGENTS.md index f04add7e..5d92c24a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -54,7 +54,7 @@ This guide explains how automation agents and human contributors should work wit - **Lines** – `GetLineById`, `GetLinesByIdList`, `GetLinesByName`. Results include company data and computed line symbols based on repository helpers. - **Routes** – `GetRoutes`, `GetRoutesMinimal`. The minimal variant returns `RouteMinimalResponse` with deduplicated `LineMinimal` data; paging tokens are currently empty (pagination not implemented). - **Train types** – `GetTrainTypesByStationId`, `GetRouteTypes`. Train types aggregate by line group and include related lines plus optional train type metadata. Rail variants use `TrainTypeKind::{Default, Branch, Rapid, Express, LimitedExpress, HighSpeedRapid, CommuterRapid}` (0-6); bus variants use `BusRoute` (7), which represents a `(route_id, shape_id)` operation pattern (e.g. 循環 / 短ターン / 支線) generated automatically from the Toei Bus GTFS feed. -- **GTFS bus integration** – At startup, `src/import.rs::integrate_gtfs_to_stations()` ingests GTFS feeds into `gtfs_*` tables and then projects them onto the shared `stations` / `lines` / `types` / `station_station_types` tables. Every configured feed is imported, including Seibu Bus (downloaded from ODPT with `ODPT_ACCESS_TOKEN`). `transport_type` (0: rail, 1: bus) on both `stations` and `lines` keeps the two worlds queryable side by side. GTFS IDs are namespaced per feed before import to avoid cross-operator collisions. `line_cd` (100,000,000+), `station_cd` / `station_g_cd` (200,000,000+), and bus `type_cd` / `line_group_cd` (100,000,000+) are all deterministic fnv1a hashes that stay clear of the rail data ranges. Disable the entire bus pipeline with `DISABLE_BUS_FEATURE=true`. +- **GTFS bus integration** – At startup, `src/import.rs::integrate_gtfs_to_stations()` ingests GTFS feeds into `gtfs_*` tables and then projects them onto the shared `stations` / `lines` / `types` / `station_station_types` tables. Every configured feed is imported, including Seibu Bus and Keio Bus (both downloaded from ODPT with `ODPT_ACCESS_TOKEN`). `transport_type` (0: rail, 1: bus) on both `stations` and `lines` keeps the two worlds queryable side by side. GTFS IDs are namespaced per feed before import to avoid cross-operator collisions. `line_cd` (100,000,000+), `station_cd` / `station_g_cd` (200,000,000+), and bus `type_cd` / `line_group_cd` (100,000,000+) are all deterministic fnv1a hashes that stay clear of the rail data ranges. Disable the entire bus pipeline with `DISABLE_BUS_FEATURE=true`. - **TTS metadata** – `Station`, `StationMinimal`, `Line`, and `TrainType` expose `name_ipa` / `name_roman_ipa` plus `name_tts_segments` for multi-segment pronunciation output. Use `name_tts_segments` when clients need per-token SSML construction for mixed-language names such as `Kasai-Rinkai Park`. - **Connected routes** – `GetConnectedRoutes`. `QueryInteractor::get_connected_stations` is not implemented yet and returns an empty vector; update the use-case and infrastructure layers together when adding real logic. - Changes to the service contract require coordinated updates to `proto/stationapi.proto`, regenerated code via `tonic-build`, and corresponding adjustments in both presentation and use-case layers. diff --git a/README.md b/README.md index 31f9bc5c..a1edaae6 100644 --- a/README.md +++ b/README.md @@ -111,6 +111,7 @@ We follow Rust best practices for testing: - Bus-related data provided by [Tokyo Metropolitan Bureau of Transportation (Toei)](https://www.kotsu.metro.tokyo.jp/), licensed under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/) - Bus-related data provided by [Seibu Bus Co., Ltd. (西武バス)](https://www.seibubus.co.jp/) via the [Public Transportation Open Data Center](https://ckan.odpt.org/), licensed under the [Public Transportation Open Data Basic License](https://developer.odpt.org/terms) +- Bus-related data provided by [Keio Dentetsu Bus Co., Ltd. (京王電鉄バス)](https://www.keio-bus.com/) via the [Public Transportation Open Data Center](https://ckan.odpt.org/), licensed under the [Public Transportation Open Data Basic License](https://developer.odpt.org/terms) - Station data provided by [駅データ.jp](https://www.ekidata.jp/) - Speed calibration data (`speed_table.rs`) derived from GTFS timetables provided by Kyoto City Transportation Bureau (京都市交通局), Yokohama City Transportation Bureau (横浜市交通局), Tokyo Metro (東京メトロ), Metropolitan Intercity Railway (首都圏新都市鉄道), Tokyo Tama Intercity Monorail (多摩都市モノレール), and Tokyo Waterfront Area Rapid Transit (東京臨海高速鉄道) via the [Public Transportation Open Data Center](https://ckan.odpt.org/), licensed under the [Public Transportation Open Data Basic License](https://developer.odpt.org/terms); by [Tokyo Metropolitan Bureau of Transportation (Toei)](https://www.kotsu.metro.tokyo.jp/), licensed under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/); and by Hakodate City Enterprise Bureau Transportation Department (函館市企業局交通部), licensed under [GTFS-RUL (ODPT)](https://gtfs-jp.org/GTFS-RUL(ODPT).pdf) - Average inter-station distances (`average_distance`) computed from railway track geometry © [OpenStreetMap contributors](https://www.openstreetmap.org/copyright), licensed under [ODbL](https://opendatacommons.org/licenses/odbl/) diff --git a/data/1!companies.csv b/data/1!companies.csv index 7879e36a..f9c931f8 100644 --- a/data/1!companies.csv +++ b/data/1!companies.csv @@ -8,6 +8,7 @@ company_cd,rr_cd,company_name,company_name_k,company_name_h,company_name_r,compa 11,21,東武鉄道,トウブテツドウ,東武鉄道株式会社,東武,Tobu,Tobu Railway,http://www.tobu.co.jp/,2,0,11 12,22,西武鉄道,セイブテツドウ,西武鉄道株式会社,西武,Seibu,Seibu Railway,http://www.seibu-group.co.jp/railways/,2,0,12 253,99,西武バス,セイブバス,西武バス株式会社,西武バス,Seibu Bus,Seibu Bus,https://www.seibubus.co.jp/,0,0,12 +254,99,京王バス,ケイオウバス,京王電鉄バス株式会社,京王バス,Keio Bus,Keio Bus,https://www.keio-bus.com/,0,0,14 13,23,京成電鉄,ケイセイデンテツ,京成電鉄株式会社,京成,Keisei,Keisei Electric Railway,http://www.keisei.co.jp/,2,0,13 14,24,京王電鉄,ケイオウデンテツ,京王電鉄株式会社,京王,Keio,Keio,http://www.keio.co.jp/,2,0,14 15,25,小田急電鉄,オダキュウデンテツ,小田急電鉄株式会社,小田急,Odakyu,Odakyu Electric Railway,http://www.odakyu.jp/,2,0,15 diff --git a/docs/architecture.md b/docs/architecture.md index 8ffdf295..d3200cc3 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -166,11 +166,11 @@ CREATE INDEX idx_performance_station_name_trgm ON stations ### バス (GTFS) データの統合 -`stations` / `lines` / `types` / `station_station_types` は鉄道とバスの両方を保持し、`stations.transport_type` / `lines.transport_type` (0: 鉄道, 1: バス) でフィルタリングします。バスデータの取り込みは `src/import.rs` の `integrate_gtfs_to_stations()` が起動時に実行し、ODPT 公開の GTFS を `gtfs_*` テーブルに展開してから既存テーブルへ統合します。Seibu Bus を含む設定済みの全GTFSフィードを使用します。Seibu Bus のダウンロードには `.env.local` の `ODPT_ACCESS_TOKEN` を使用します。 +`stations` / `lines` / `types` / `station_station_types` は鉄道とバスの両方を保持し、`stations.transport_type` / `lines.transport_type` (0: 鉄道, 1: バス) でフィルタリングします。バスデータの取り込みは `src/import.rs` の `integrate_gtfs_to_stations()` が起動時に実行し、ODPT 公開の GTFS を `gtfs_*` テーブルに展開してから既存テーブルへ統合します。Seibu Bus・Keio Bus を含む設定済みの全GTFSフィードを使用します。Seibu Bus・Keio Bus のダウンロードには `.env.local` の `ODPT_ACCESS_TOKEN` を使用します。 | 鉄道側の概念 | バス側の対応 | |---|---| -| `companies` | GTFS フィードごとに会社を決定。Toei Bus は東京都交通局 (`company_cd=119`)、Seibu Bus は西武バス (`company_cd=253`) | +| `companies` | GTFS フィードごとに会社を決定。Toei Bus は東京都交通局 (`company_cd=119`)、Seibu Bus は西武バス (`company_cd=253`)、Keio Bus は京王バス (`company_cd=254`) | | `lines` | GTFS `routes` を 1:1 で `lines` に登録。`line_cd` はフィード接頭辞付き `route_id` の fnv1a ハッシュで 100,000,000+ 空間に決定的に生成 | | `stations` | GTFS `stops` (親停留所) を `(stop_id, route_id)` 単位で `stations` に登録。`station_cd` / `station_g_cd` もフィード接頭辞付き ID から 200,000,000+ 空間にハッシュ生成 | | `types` | GTFS の `(route_id, shape_id)` バリエーション (フルループ / 短ターン / 支線など) を `kind = TrainTypeKind::BusRoute (= 7)` の TrainType として登録。**停留所集合が完全に同じ shape ペア (上下方向違いのみ) は 1 つの TrainType に畳み、`direction = Both` を設定**。`type_name` は循環なら ` (循環)`、双方向ペアなら ``、片方向なら `<始発停留所> → ` | diff --git a/stationapi/src/import.rs b/stationapi/src/import.rs index 5cb5f0dd..9e6c05e1 100644 --- a/stationapi/src/import.rs +++ b/stationapi/src/import.rs @@ -286,6 +286,13 @@ const GTFS_FEEDS: &[GtfsFeed] = &[ url: "https://api.odpt.org/api/v4/files/SeibuBus/data/SeibuBus-GTFS.zip", requires_consumer_key: true, }, + GtfsFeed { + id: "keio", + name: "Keio Bus", + path: "data/KeioBus-GTFS", + url: "https://api.odpt.org/api/v4/files/odpt/KeioBus/AllLines.zip", + requires_consumer_key: true, + }, ]; const DEFAULT_GTFS_BUS_LINE_COLOR: &str = "#1f63c6"; @@ -1675,6 +1682,8 @@ fn company_cd_for_gtfs_route(route_id: &str) -> Option { Some(119) // Toei Transportation } else if route_id.starts_with("seibu:") { Some(253) // Seibu Bus + } else if route_id.starts_with("keio:") { + Some(254) // Keio Bus } else { None // Unknown/unsupported prefix } @@ -3040,6 +3049,7 @@ mod tests { fn test_company_cd_for_gtfs_route() { assert_eq!(company_cd_for_gtfs_route("toei:route_001"), Some(119)); assert_eq!(company_cd_for_gtfs_route("seibu:route_001"), Some(253)); + assert_eq!(company_cd_for_gtfs_route("keio:route_001"), Some(254)); assert_eq!(company_cd_for_gtfs_route("unknown:route_001"), None); } @@ -3128,7 +3138,7 @@ mod tests { fn test_gtfs_feeds() { assert_eq!( GTFS_FEEDS.iter().map(|feed| feed.id).collect::>(), - vec!["toei", "seibu"] + vec!["toei", "seibu", "keio"] ); } From c8c810472245a25c01ff99ca438cfbcd1260a29f Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 12 Jul 2026 07:29:30 +0000 Subject: [PATCH 2/2] =?UTF-8?q?=E3=83=AC=E3=83=93=E3=83=A5=E3=83=BC?= =?UTF-8?q?=E6=8C=87=E6=91=98=E5=AF=BE=E5=BF=9C:=20=E3=83=95=E3=82=A3?= =?UTF-8?q?=E3=83=BC=E3=83=89=E5=AE=9A=E7=BE=A9=E3=81=AE=E6=A4=9C=E8=A8=BC?= =?UTF-8?q?=E5=BC=B7=E5=8C=96=E3=81=A8AGENTS=E3=83=89=E3=82=AD=E3=83=A5?= =?UTF-8?q?=E3=83=A1=E3=83=B3=E3=83=88=E6=95=B4=E5=90=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - `test_gtfs_feeds` で京王バスフィードの name / path / url / requires_consumer_key を明示的に assert し、ID の並びだけでなく 定義値の誤りも検知できるようにする - `AGENTS.md` の TrainType 説明を Toei Bus 限定表現から設定済み全 GTFSフィード (Toei / Seibu / Keio) へ更新 - `AGENTS.md` の `ODPT_ACCESS_TOKEN` 説明に京王バスを追記 Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01SfBspXi8MZnEjwrAxvbBzz --- AGENTS.md | 4 ++-- stationapi/src/import.rs | 9 +++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 5d92c24a..d140c289 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -20,7 +20,7 @@ This guide explains how automation agents and human contributors should work wit - Environment variables: - `DATABASE_URL` – SQLx connection string (e.g., `postgres://stationapi:stationapi@localhost/stationapi`). - `DISABLE_GRPC_WEB` – `false` enables gRPC-Web; set to `true` for pure gRPC/HTTP2. - - `ODPT_ACCESS_TOKEN` – ODPT consumer key used to download authenticated GTFS feeds such as Seibu Bus. + - `ODPT_ACCESS_TOKEN` – ODPT consumer key used to download authenticated GTFS feeds such as Seibu Bus and Keio Bus. - `HOST` and `PORT` – listen address (defaults to `[::1]:50051`; Docker uses `0.0.0.0:50051`). - `.env.test` exports `TEST_DATABASE_URL`, `RUST_LOG`, `RUST_BACKTRACE`, and `RUST_TEST_THREADS=1` for integration tests. - Recommended: keep shared defaults in `.env`, copy overrides to `.env.local`, and rely on startup loading both files. @@ -53,7 +53,7 @@ This guide explains how automation agents and human contributors should work wit - **Stations** – `GetStationById`, `GetStationByIdList`, `GetStationsByGroupId`, `GetStationsByCoordinates`, `GetStationsByLineId`, `GetStationsByName`, `GetStationsByLineGroupId`. `QueryInteractor` enriches stations with lines, companies, station numbers, and train types. - **Lines** – `GetLineById`, `GetLinesByIdList`, `GetLinesByName`. Results include company data and computed line symbols based on repository helpers. - **Routes** – `GetRoutes`, `GetRoutesMinimal`. The minimal variant returns `RouteMinimalResponse` with deduplicated `LineMinimal` data; paging tokens are currently empty (pagination not implemented). -- **Train types** – `GetTrainTypesByStationId`, `GetRouteTypes`. Train types aggregate by line group and include related lines plus optional train type metadata. Rail variants use `TrainTypeKind::{Default, Branch, Rapid, Express, LimitedExpress, HighSpeedRapid, CommuterRapid}` (0-6); bus variants use `BusRoute` (7), which represents a `(route_id, shape_id)` operation pattern (e.g. 循環 / 短ターン / 支線) generated automatically from the Toei Bus GTFS feed. +- **Train types** – `GetTrainTypesByStationId`, `GetRouteTypes`. Train types aggregate by line group and include related lines plus optional train type metadata. Rail variants use `TrainTypeKind::{Default, Branch, Rapid, Express, LimitedExpress, HighSpeedRapid, CommuterRapid}` (0-6); bus variants use `BusRoute` (7), which represents a `(route_id, shape_id)` operation pattern (e.g. 循環 / 短ターン / 支線) generated automatically from the configured GTFS bus feeds (Toei Bus, Seibu Bus, Keio Bus). - **GTFS bus integration** – At startup, `src/import.rs::integrate_gtfs_to_stations()` ingests GTFS feeds into `gtfs_*` tables and then projects them onto the shared `stations` / `lines` / `types` / `station_station_types` tables. Every configured feed is imported, including Seibu Bus and Keio Bus (both downloaded from ODPT with `ODPT_ACCESS_TOKEN`). `transport_type` (0: rail, 1: bus) on both `stations` and `lines` keeps the two worlds queryable side by side. GTFS IDs are namespaced per feed before import to avoid cross-operator collisions. `line_cd` (100,000,000+), `station_cd` / `station_g_cd` (200,000,000+), and bus `type_cd` / `line_group_cd` (100,000,000+) are all deterministic fnv1a hashes that stay clear of the rail data ranges. Disable the entire bus pipeline with `DISABLE_BUS_FEATURE=true`. - **TTS metadata** – `Station`, `StationMinimal`, `Line`, and `TrainType` expose `name_ipa` / `name_roman_ipa` plus `name_tts_segments` for multi-segment pronunciation output. Use `name_tts_segments` when clients need per-token SSML construction for mixed-language names such as `Kasai-Rinkai Park`. - **Connected routes** – `GetConnectedRoutes`. `QueryInteractor::get_connected_stations` is not implemented yet and returns an empty vector; update the use-case and infrastructure layers together when adding real logic. diff --git a/stationapi/src/import.rs b/stationapi/src/import.rs index 9e6c05e1..92a9d5db 100644 --- a/stationapi/src/import.rs +++ b/stationapi/src/import.rs @@ -3140,6 +3140,15 @@ mod tests { GTFS_FEEDS.iter().map(|feed| feed.id).collect::>(), vec!["toei", "seibu", "keio"] ); + + let keio = GTFS_FEEDS.iter().find(|feed| feed.id == "keio").unwrap(); + assert_eq!(keio.name, "Keio Bus"); + assert_eq!(keio.path, "data/KeioBus-GTFS"); + assert_eq!( + keio.url, + "https://api.odpt.org/api/v4/files/odpt/KeioBus/AllLines.zip" + ); + assert!(keio.requires_consumer_key); } #[test]