Skip to content

GTFSの上下ポールをグループ化しバス停の重複表示を修正#1605

Merged
TinyKitten merged 3 commits into
devfrom
fix/bus-stop-pole-grouping
Jul 12, 2026
Merged

GTFSの上下ポールをグループ化しバス停の重複表示を修正#1605
TinyKitten merged 3 commits into
devfrom
fix/bus-stop-pole-grouping

Conversation

@TinyKitten

@TinyKitten TinyKitten commented Jul 12, 2026

Copy link
Copy Markdown
Member

概要

parent_station の階層を持たない GTFS フィードで、同じバス停の上下ポールが別々の駅グループとして登録され、アプリの検索結果に同名バス停が2件表示される不具合を修正する。

変更の種類

  • バグ修正
  • 新機能
  • データの修正・追加
  • リファクタリング
  • ドキュメント
  • CI/CD
  • その他

変更内容

  • バス停は station_g_cd 単位でまとめて表示されるが、integrate_gtfs_stops_to_stationsstation_g_cdstop_id のハッシュから採番していた。parent_station を持たないフィード(西武バス・東急コミュニティバス各種・京王バス等)は上下ポールがそれぞれ別のトップレベル stop として取り込まれるため、同名でも別 station_g_cd になり、同じバス停名が2件出ていた。
  • build_bus_station_g_cd_map を追加し、同名かつ 250m 以内の stop を union-find でまとめ、クラスタ代表(最小 stop_id)の station_g_cd を共有させる。
    • 近接スコープにより、遠く離れた同名別バス停(例: 西武と都営に約92km離れて存在する「新道」)は別グループのまま維持。
    • 単独 stop は従来の generate_bus_station_g_cd と同一値になるため、parent_station を正しく持つ都営バスなど、既に正常なフィードには影響しない。
    • 閾値はローカルの GTFS 実データで検証し、150〜350m のいずれでも >600m に広がるクラスタは発生しなかったため、安全側で 250m を採用。約5765 バス停 → 約3610 グループに集約(実データ上のフィード分)。
  • 単体テスト test_build_bus_station_g_cd_map_merges_nearby_poles を追加(近接ポールの併合・遠隔同名の分離・単独stopの値不変を検証)。

テスト

  • cargo fmt --all -- --check が通ること
  • cargo clippy -- -D warnings が通ること
  • cargo testSQLX_OFFLINE=true)が通ること

補足: 本修正はインポート処理のため、反映にはバス停の再インポートが必要です。

関連Issue

スクリーンショット(任意)

Summary by CodeRabbit

  • 新機能
    • GTFSの「方向ポール」(同名で近接する停留所)を半径250m基準で同一物理停留所として統合するようになりました。
    • 統合後の停留所には、安定した共通の駅コードが割り当てられます。
  • バグ修正
    • 同名でも一定距離を超える停留所は別として扱われます。
    • 親を持つ階層の停留所は従来どおり固定され、統合の対象外になります。
  • テスト
    • 統合条件(同一化・連鎖による誤統合防止・階層除外)を回帰テストで検証しました。

parent_stationの階層を持たないフィード(西武・東急コミュニティバス・京王等)
では上下ポールが別々のトップレベルstopとして取り込まれ、同名なのに別
station_g_cdになるため同じバス停名が2件表示されていた。同名かつ250m以内の
stopをunion-findでまとめ、クラスタ代表(最小stop_id)のstation_g_cdを共有させる。
単独stopは従来値と一致するため都営など正しく動くフィードには影響しない。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@TinyKitten TinyKitten self-assigned this Jul 12, 2026
@coderabbitai

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: fb3d600a-cf1e-4fa1-96ad-0fa169b5879e

📥 Commits

Reviewing files that changed from the base of the PR and between ec08f9f and 957e617.

📒 Files selected for processing (1)
  • stationapi/src/import.rs

📝 Walkthrough

Walkthrough

GTFS停留所を名称・距離・親子関係に基づいてクラスタ化し、代表stop_id由来のstation_g_cdをstation追加処理へ反映します。統合結果のログ出力と、近接・連鎖・親停止の扱いを検証する回帰テストを追加します。

Changes

GTFS停留所グルーピング

Layer / File(s) Summary
停留所グループマップの構築
stationapi/src/import.rs
haversine_distanceと250mの半径を用い、trim後の同名停留所をcomplete-linkageで貪欲にクラスタ化します。親停止は自己グループに固定し、代表stop_idは辞書順最小とします。近接・連鎖・親停止の回帰テストも追加します。
station_g_cd割り当てへの統合
stationapi/src/import.rs
integrate_gtfs_stops_to_stationsが親停止集合から構築した統合マップを使用します。マップにないstopは従来の生成処理へフォールバックし、統合グループ数をログ出力します。

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant GTFSStops
  participant build_bus_station_g_cd_map
  participant integrate_gtfs_stops_to_stations
  participant Stations
  GTFSStops->>build_bus_station_g_cd_map: 停留所名・位置・親停止集合を渡す
  build_bus_station_g_cd_map-->>integrate_gtfs_stops_to_stations: stop_idからstation_g_cdへのマップ
  integrate_gtfs_stops_to_stations->>Stations: 統合代表に基づくstation_g_cdを割り当てる
  integrate_gtfs_stops_to_stations->>Stations: 未登録stop_idは従来方式で割り当てる
Loading

Poem

ぴょんと並んだポールたち
近けりゃひとつ、連鎖は別々
小さなIDを旗にして
親停留所は動かない
テストも跳ねて、確認完了!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed 変更内容の主眼であるGTFS停留所のグループ化と重複表示修正を端的に表しています。
Description check ✅ Passed 必要な各セクションが揃っており、概要・変更内容・テストも具体的に記載されています。
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/bus-stop-pole-grouping

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added fix 直した deploy-dev and removed fix 直した labels Jul 12, 2026
…ping

# Conflicts:
#	stationapi/src/import.rs
@github-actions github-actions Bot added the fix 直した label Jul 12, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@stationapi/src/import.rs`:
- Around line 2288-2300: Replace the pairwise union logic in the stop-grouping
loop with deterministic cluster assignment that only adds a stop when its
distance to every stop already in the candidate group is at most
BUS_STOP_GROUPING_RADIUS_METERS, preserving a maximum cluster diameter of 250m
rather than transitive connectivity. Update the surrounding grouping function to
use this assignment, and add a regression test covering three stops where A-B
and B-C are within the radius but A-C exceeds it, asserting A and C remain in
separate groups.
- Around line 2288-2302: Replace the all-pairs comparison loop over each
same-name stop bucket with a 250-meter spatial grid index, assigning stops to
grid cells and comparing each stop only with candidates in its own or adjacent
cells. Preserve the existing haversine-distance threshold and union-find updates
using find, parent, and BUS_STOP_GROUPING_RADIUS_METERS, while avoiding
duplicate candidate comparisons.
- Around line 3002-3004: Update the stop-loading SELECT and
build_bus_station_g_cd_map flow to retain whether each stop is referenced as a
parent_station. Use that metadata to keep parent stops mapped to their own
station_g_cd, while only grouping nearby or same-name poles for stops without a
parent hierarchy. Ensure independent hierarchy-less stops remain eligible for
the existing collapse behavior without merging with parent stops.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: eac7f1bb-69f6-4e77-9cfa-bc8ca0a5ee14

📥 Commits

Reviewing files that changed from the base of the PR and between 8251c5a and ec08f9f.

📒 Files selected for processing (1)
  • stationapi/src/import.rs

Comment thread stationapi/src/import.rs Outdated
Comment thread stationapi/src/import.rs Outdated
Comment thread stationapi/src/import.rs Outdated
バス停グループ化を決定的 greedy complete-linkage に変更しクラスタ直径を
250m以内に制限(推移閉包による遠距離統合を防止)。parent_stationとして参照
される親stopはグループ化対象外にして自身のstation_g_cdへ固定し、parent
階層を持つフィード(都営等)への影響をなくす。回帰テストを追加。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@TinyKitten

Copy link
Copy Markdown
Member Author

Fixes Applied Successfully

Fixed 1 file based on 2 CodeRabbit feedback item(s) (1 item deferred with rationale).

Applied

  • 近接の連鎖(single-linkage)で250mを超えるstopが統合されうる問題 → 決定的な greedy complete-linkage に変更し、クラスタ直径を BUS_STOP_GROUPING_RADIUS_METERS(250m)以内に制限。DB行順に依存しない。3点連鎖(A-B・B-C が半径内、A-C が半径外)の回帰テストを追加。
  • 親stopと階層なしstopを区別せずグループ化していた問題 → parent_station として参照される stop を特定し、親stopは自身の station_g_cd に固定してグループ化対象外に。parent_station 階層を持つフィード(都営バス等)は完全に不変。回帰テストを追加。

Deferred

  • 同名bucketの全ペア走査 O(k²) の空間インデックス化: 走査は同名bucket内(実データ上ほぼ2〜3件)に限定され、一度きりのオフラインインポートでは二乗コストは無視できるため、複雑さに見合わないと判断し見送り。

Files modified

  • stationapi/src/import.rs

Commit: 957e617

検証: cargo fmt --all -- --check / cargo clippy -- -D warnings / SQLX_OFFLINE=true cargo test(import 41件)すべてパス。

The latest autofix changes are on the fix/bus-stop-pole-grouping branch.

@TinyKitten TinyKitten merged commit 267aed5 into dev Jul 12, 2026
11 checks passed
@TinyKitten TinyKitten deleted the fix/bus-stop-pole-grouping branch July 12, 2026 14:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant