Skip to content

Support in-seat trip transfers in travel time calculations#225

Merged
polettif merged 13 commits intomasterfrom
dev/raptor_transfer_type
Jan 23, 2026
Merged

Support in-seat trip transfers in travel time calculations#225
polettif merged 13 commits intomasterfrom
dev/raptor_transfer_type

Conversation

@polettif
Copy link
Contributor

The gtfs specs support so called in-seat transfers for quite some time but I haven't come across a feed that implemented them (in my quite limited exposure to feeds). With this PR, raptor() and travel_times() considers such transfers with transfer_type 4 or 5.

There are quite a few possibilities how these transfers can be defined.

Fields from_trip_id, to_trip_id, from_route_id and to_route_id allow higher orders of specificity for transfer rules. Along with from_stop_id and to_stop_id, the ranking of specificity is as follows:

  1. Both trip_ids defined: from_trip_id and to_trip_id. ✓
  2. One trip_id and route_id set defined: (from_trip_id and to_route_id) or (from_route_id and to_trip_id).
  3. One trip_id defined: from_trip_id or to_trip_id.
  4. Both route_ids defined: from_route_id and to_route_id.
  5. One route_id defined: from_route_id or to_route_id.
  6. Only from_stop_id and to_stop_id defined: no route or trip related fields set. ✓

This PR currently only implements case 1 and 6. I'll add an issue for this work-in-progress.

Other notable changes:

  • raptor() now takes pickup_type and drop_off_type (in stop_times) into account
  • raptor() has a new param separate_starts which can lead to faster calculation times for very large feeds
  • more errors are caught with meaningful error messages

- only use departures within time_range, consider initial transfer time
- prepare for inseat transfer trips
- test departure/arrival time range
- update error messages
- initial stops are at the top of the returned table
- direct transfers contain in-seat or timed transfers (type 1, 4 and 5)
- route-route transfers (type 1) are not yet implemented, only trip-trip
- leads to faster calculations for large feeds. Should be used when stop_ids are aggregated later on anyways (like in travel_times)
- run raptor core only for unique departures, join transfers among origin stops afterwards
trim transfer tables to necessary columns
otherwise travel_times() might return from_stop_names differing from the input stop_name
@codecov
Copy link

codecov bot commented Jan 19, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (719faef) to head (a1c1f28).
⚠️ Report is 20 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##            master      #225    +/-   ##
==========================================
  Coverage   100.00%   100.00%            
==========================================
  Files           17        19     +2     
  Lines         1049      1180   +131     
==========================================
+ Hits          1049      1180   +131     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@mpadge
Copy link
Contributor

mpadge commented Jan 20, 2026

Thanks @polettif for always keeping things up to date here! I was really worried when I read your description, but then reading through the specs and looking at what gtfsrouter does, I'm pretty sure those kinds of transfers will be supported anyway. Thankfully. Great work!

@polettif
Copy link
Contributor Author

Thanks @polettif for always keeping things up to date here! I was really worried when I read your description, but then reading through the specs and looking at what gtfsrouter does, I'm pretty sure those kinds of transfers will be supported anyway. Thankfully. Great work!

Thank you 😊

Yeah, in general it's more a passenger info issue ("no need to change trains") than routing since the next trip normally departs from the same stop_id as the previous trip. It came up in raptor specifically because there's a departure_time > arrival_time (instead of >=) clause implemented for transfers.

@polettif
Copy link
Contributor Author

polettif commented Jan 23, 2026

I analysed 68 feeds with non-trivial transfer data from MobilityData, results below (number of rows in transfers.txt). The raptor() in this PR covers more than 98% of transfers which I think is close enough. There are not a lot of pure route-to-route transfers "out there".

transfer_type from_trip_id to_trip_id from_route_id to_route_id n p
2 1526096 75.0%
2 TRUE TRUE 189791 9.3%
4 TRUE TRUE TRUE TRUE 85778 4.2%
1 TRUE TRUE 85280 4.2%
0 TRUE TRUE 57033 2.8%
1 28133 1.4%
2 TRUE TRUE 21129 1.0%
1 TRUE TRUE TRUE TRUE 13741 0.7%
4 TRUE TRUE 13187 0.6%
1 TRUE TRUE 4856 0.2%
0 3475 0.2%
3 TRUE TRUE 2136 0.1%
3 2053 0.1%
3 TRUE TRUE 2046 0.1%
0 TRUE TRUE 461 0.0%
5 TRUE TRUE 12 0.0%
0 TRUE TRUE TRUE TRUE 4 0.0%
1 TRUE 4 0.0%
1 TRUE 4 0.0%
5 TRUE TRUE TRUE TRUE 4 0.0%
2 TRUE TRUE TRUE TRUE 1 0.0%

@polettif polettif merged commit 858e499 into master Jan 23, 2026
9 checks passed
@polettif polettif deleted the dev/raptor_transfer_type branch January 23, 2026 14:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants