opentripplanner/OpenTripPlanner — pull request #1647 by Mona77 (2014-12-04 20:57:07+00:00)
Title: Fix #1347 - Add support for GTFS-rt TripUpdates for frequency-based (exact_times=0) trips

This pull request adds support for GTFS-rt TripUpdates for frequency-based (exact_times=0) trips, as discussed in https://github.com/opentripplanner/OpenTripPlanner/issues/1347.

* Sample frequency-based GTFS data - http://usf242601.cutr.usf.edu/clearota/otp/Bull_Runner.zip
* Sample frequency-based GTFS-rt TripUpdates feed - http://mobullity.forest.usf.edu:8088/trip-updates?debug (remove `?debug` for protobuf format)
* You can download OSM data to be used with the above [here](https://dl.dropboxusercontent.com/u/46443835/OpenTripPlanner/Frequency-based%20GTFS-rt%20PR/usf-small.osm)

Excerpt from above GTFS-rt feed, as example input to OTP:

~~~
entity {
id: "1"
trip_update {
trip {
trip_id: "8"
start_time: "14:41:04"
schedule_relationship: UNSCHEDULED
route_id: "D"
}
stop_time_update {
stop_sequence: 13
arrival {
time: 1417722769
}
stop_id: "701"
}
stop_time_update {
stop_sequence: 14
arrival {
time: 1417722949
}
stop_id: "241"
}
~~~

Below are details:

* GTFS-rt TripUpdate arrival/departure times for frequency-based trips should only be provided as absolute times, not delay (since there is no schedule to reference). Also, because frequency-based trips don't have a schedule, the method for estimating missing arrival/departure time is different. In addition to `trip_id` (which is used for normal schedule-based real-time updates), `vehicle_id` is used to differentiate trip instances on the same route. For every tripUpdate with different `vehicle_id` a new tripTimes is created and is added to the timetable of that route. The logic for consuming the real-time updates of frequency-based trips can be seen [here](https://github.com/CUTR-at-USF/OpenTripPlanner/blob/frequency-rt-5/src/main/java/org/opentripplanner/routing/edgetype/Timetable.java#L518).

* Real-time updates for frequency based trips should have `schedule_relationship: UNSCHEDULED` header and is consumed differently ([code](https://github.com/CUTR-at-USF/OpenTripPlanner/blob/frequency-rt-5/src/main/java/org/opentripplanner/routing/edgetype/TimetableResolver.java#L117))

* The `tripTimes` has been added to `scheduledTimetable` so that ìfrequency-based trips with real time updateî have the same interface as scheduled-based trips. ([code](https://github.com/CUTR-at-USF/OpenTripPlanner/blob/frequency-rt-5/src/main/java/org/opentripplanner/routing/edgetype/TripPattern.java#L327)).

* All data structures of real time updates of frequency based trips should be deleted/reset on every refresh interval ([code](https://github.com/CUTR-at-USF/OpenTripPlanner/blob/frequency-rt-5/src/main/java/org/opentripplanner/updater/stoptime/TimetableSnapshotSource.java#L121)). This means that only the trip instances reflected in the most recent GTFS-rt update will be used in OTP.

* No changes have been made to how OTP handles routing for frequency-based trips when no real-time data is available (see [code](https://github.com/CUTR-at-USF/OpenTripPlanner/blob/frequency-rt-5/src/main/java/org/opentripplanner/routing/edgetype/Timetable.java#L186)).

* The web UI has been changed so that the interface for itineraries stays the same in both scheduled based and frequency-based trips (with or without real time updates). Also, the delay value wonít be displayed for frequency-based trips, since delay is not relevant for trips without a schedule ([code](https://github.com/CUTR-at-USF/OpenTripPlanner/blob/frequency-rt-5/src/client/js/otp/modules/planner/ItinerariesWidget.js#L349)).

* For frequency-based trips, OTP assumes each GTFS-rt TripUpdate entity is a distinct trip instance. It is the responsibility of the GTFS-rt feed to make sure that each trip instance is represented correctly. The same `trip_id` may appear in more than one TripUpdate entity for one route, if there is more than one vehicle serving a route simultaneously (in which case `trip_id` + `vehicle_id` would uniquely identify the trip instance). Or, if the route is a loop (in which case `trip_id` + `vehicle_id` + `start_time` would uniquely identify the trip instance - see below).

* While it doesn't directly affect OTP trip planning, we suggest that the field `start_time` is included in frequency-based GTFS-rt feeds, in addition to `trip_id` and `vehicle_id`. `start_time` is primarily included to differentiate trip instances on loop routes, where predictions may simultaneously exist for the end of one trip instance and the beginning of the next trip instance for the same vehicle. In this case, the `vehicle_id` alone may not be sufficient to uniquely identify a trip instance. See [this thread](https://github.com/opentripplanner/OpenTripPlanner/issues/1347#issuecomment-52500250) for details. `start_time` should be immutable for a trip instance, meaning that when a new vehicle is running a route, a `start_time` is generated for that trip instance, and remains the same for the entire lifetime of that trip instance. As mentioned above, because OTP assumes each GTFS-rt TripUpdate entity is a distinct trip instance, OTP does not examine the value of the `start_time` field in each TripUpdate entity. The GTFS-rt feed is responsible for ensuring that each entity is a distinct trip instance.

cc @barbeau