From 24e89c97cbbdd89348b1f02497a129ac8ac0a14f Mon Sep 17 00:00:00 2001 From: isabelle-dr Date: Tue, 22 Mar 2022 19:33:40 -0400 Subject: re arrange repo --- gtfs-realtime-book/ch-08-consuming-trip-updates.md | 380 +++++++++++++++++++++ 1 file changed, 380 insertions(+) create mode 100644 gtfs-realtime-book/ch-08-consuming-trip-updates.md (limited to 'gtfs-realtime-book/ch-08-consuming-trip-updates.md') diff --git a/gtfs-realtime-book/ch-08-consuming-trip-updates.md b/gtfs-realtime-book/ch-08-consuming-trip-updates.md new file mode 100644 index 0000000..867e30f --- /dev/null +++ b/gtfs-realtime-book/ch-08-consuming-trip-updates.md @@ -0,0 +1,380 @@ +## 8. Consuming Trip Updates + +Of the three message types in GTFS-realtime, trip updates are the most +complex. A single trip update can contain a large quantity of data and +is used to transform the underlying schedule. Trips can be modified in a +number of ways: trips can be canceled, stops can be skipped, and arrival +times can be updated. + +This chapter will show you how to consume trip updates and will discuss +real-world scenarios and how they can be represented using trip updates. + +Similar to service alerts and vehicle positions, you can loop over the +`FeedEntity` objects from `getEntityList()` to access and then +process `TripUpdate` objects. + +```java +for (FeedEntity entity : fm.getEntityList()) { + if (entity.hasTripUpdate()) { + TripUpdate tripUpdate = entity.getTripUpdate(); + + processTripUpdate(tripUpdate); + } +} +``` + +### Timestamp + +Just like with vehicle position updates, trip updates include a +timestamp value. This indicates when the real-time data was updated, +including any subsequent arrival/departure estimates contained within +the trip update. + +You can read this value into a native `java.util.Date` object as +follows: + +```java +if (tripUpdate.hasTimestamp()) { + Date timestamp = new Date(tripUpdate.getTimestamp() * 1000); + +} +``` + +**Note:** The value is multiplied by 1,000 because the java.util.Date +class accepts milliseconds, whereas GTFS-realtime uses whole seconds. + +Two of the ways you can use the timestamp value are: + +* When telling your users arrival estimates, you can also show the + timestamp so they know when the estimate was made. A newer estimate + (e.g. one minute old) is likely to be more reliable than an older + one (e.g. ten minutes old). +* You can also use the timestamp to decide whether or not to keep the + estimate. For instance, if for some reason a feed did not refresh in + a timely manner and all of the estimates were hours old, you could + simply skip over them as they would no longer provide meaningful + information. + +### Trip Information + +Each trip update contains necessary data so the included estimates can +be linked to a trip in the corresponding GTFS feed. + +There are three ways estimate data can be provided in a trip update: + +* The trip update contains estimates for all stops. +* The trip update contains estimates for some stops. +* The trip has been canceled, so there is no stop information. + +If the trip has been added (that is, it is not a part of the schedule in +the GTFS feed), then the trip descriptor has no use in resolving the +trip back to the GTFS feed. + +However, if a trip update only contains updates for some of the stops, +you must be able to find the entire trip in the GTFS feed so you can +propagate the arrival delay to subsequent stops. + +The following code shows how to extract values such as the GTFS trip ID +and route ID from the `TripDescriptor` object. + +```java +if (tripUpdate.hasTrip()) { + TripDescriptor trip = tripUpdate.getTrip(); + + if (trip.hasTripId()) { + String tripId = trip.getTripId(); + + // ... + } + + if (trip.hasRouteId()) { + String routeId = trip.getRouteId(); + + // ... + } + + if (trip.hasStartDate()) { + String startDate = trip.getStartDate(); + + // ... + } + + if (trip.hasStartTime()) { + String startTime = trip.getStartTime(); + + // ... + } + + if (trip.hasScheduleRelationship()) { + ScheduleRelationship sr = trip.getScheduleRelationship(); + + // ... + } +} +``` + +### Trip Delay + +Although only an experimental part of the GTFS-realtime specification at +the time of writing, a trip update can also contain a delay value. A +positive number indicates the number of seconds the vehicle is late, +while a negative value indicates the number of seconds early. A value of +`0` means the vehicle is on-time. + +This value can be used for any stop along the trip that does not +otherwise have an associated `StopTimeEvent` element. + +```java +if (tripUpdate.hasDelay()) { + int delay = tripUpdate.getDelay(); + + if (delay == 0) { + // on time + + } + else if (delay < 0) { + // early + + } + else if (delay > 0) { + // late + + } +} +``` + +### Vehicle Identifiers + +The `VehicleDescriptor` object contained in a trip update provides a +number of values by which to identify a vehicle. You can access an +internal identifier (not for public display), a label (such as a vehicle +number painted on to a vehicle), or a license plate. + +The following code shows how to access these values: + +```java +if (tripUpdate.hasVehicle()) { + VehicleDescriptor vehicle = tripUpdate.getVehicle(); + + if (vehicle.hasId()) { + String id = vehicle.getId(); + + } + + if (vehicle.hasLabel()) { + String label = vehicle.getLabel(); + + } + + if (vehicle.hasLicensePlate()) { + String licensePlate = vehicle.getLicensePlate(); + + } +} +``` + +The vehicle descriptor and the values contained within are all optional. +In the case where this information is not available, you can use the +trip descriptor information provided with each vehicle position to match +up vehicle positions across multiple updates. + +### Stop Time Updates + +Each trip update contains a number of stop time updates, each of which +is a `StopTimeUpdate` object. You can access each of the +`StopTimeUpdate` objects by calling **getStopTimeUpdateList()**. + +```java +for (StopTimeUpdate stopTimeUpdate : tripUpdate.getStopTimeUpdateList()) { + // ... +} +``` + +Alternatively, you can loop over each `StopTimeUpdate` object as +follows: + +```java +for (int i = 0; i < tripUpdate.getStopTimeUpdateCount(); i++) { + StopTimeUpdate stopTimeUpdate = tripUpdate.getStopTimeUpdate(i); + + // ... +} +``` + +Each `StopTimeUpdate` object contains a schedule relationship value +(using the `ScheduleRelationship` class, which is different to that +contained in `TripDescriptor` objects). + +The schedule relationship dictates how to use the rest of the data in +the stop time update, as well as which data will be present. If the +schedule relationship value is not present, the value is assumed to be +`SCHEDULED`. + +```java +ScheduleRelationship sr; + +if (stopTimeUpdate.hasScheduleRelationship()) { + sr = stopTimeUpdate.getScheduleRelationship(); + +} +else { + sr = ScheduleRelationship.SCHEDULED; + +} + +if (sr.getNumber() == ScheduleRelationship.SCHEDULED_VALUE) { + // An arrival and/or departure estimate is provided + +} +else if (sr.getNumber() == ScheduleRelationship.NO_DATA_VALUE) { + // No real-time data available in this update + +} +else if (sr.getNumber() == ScheduleRelationship.SKIPPED_VALUE) { + // The vehicle will not stop at this stop + +} +``` + +### Stop Information + +In order to determine which stop a stop time update corresponds to, +either the stop ID or stop sequence (or both) must be specified. You can +then look up the stop based on its entry in `stops.txt`, or determine +the stop based on the corresponding entry in `stop_times.txt`. + +```java +if (stopTimeUpdate.hasStopId()) { + String stopId = stopTimeUpdate.getStopId(); + +} + +if (stopTimeUpdate.hasStopSequence()) { + int sequence = stopTimeUpdate.getStopSequence(); + +} +``` + +### Arrival/Departure Estimates + +If the `ScheduleRelationship` value is `SKIPPED` there must either +be an arrival or departure object specified. Both the arrival and +departure use the `StopTimeEvent` class, which can be accessed as +follows. + +The following code shows how to access these values: + +```java +if (sr.getNumber() == ScheduleRelationship.SCHEDULED_VALUE) { + if (stopTimeUpdate.hasArrival()) { + StopTimeEvent arrival = stopTimeUpdate.getArrival(); + + // Process the arrival + } + + if (stopTimeUpdate.hasDeparture()) { + StopTimeEvent departure = stopTimeUpdate.getDeparture(); + + // Process the departure + } +} +``` + +Each `StopTimeEvent` object contains either an absolute timestamp for +the arrival or departure, or it contains a delay value. The delay value +is relative to the scheduled time in the corresponding GTFS feed. + +```java +if (stopTimeUpdate.hasArrival()) { + StopTimeEvent arrival = stopTimeUpdate.getArrival(); + + if (arrival.hasDelay()) { + int delay = arrival.getDelay(); + + // ... + } + + if (arrival.hasTime()) { + Date time = new Date(arrival.getTime() * 1000); + + // ... + } + + if (arrival.hasUncertainty()) { + int uncertainty = arrival.getUncertainty(); + + // ... + } +} +``` + +### Trip Update Scenarios + +This chapter has shown you how to handle trip update information as it +appears in a GTFS-realtime feed. It is also important to understand the +intent of the data provider when reading these updates. Consider the +following scenarios that can occur frequently in large cities: + +1. **A train needs to skip one or more stops.** Perhaps the station has + been closed temporarily due to unforeseen circumstances. +2. **A train that was scheduled to bypass a station will now stop at + it.** Perhaps there is a temporary delay on the line so the train + will wait at a stop while the track is cleared. +3. **A bus is rerouted down a different street.** Perhaps there was a + car accident earlier and police have redirected traffic. +4. **A train will stop at a different platform.** For example, instead + of a train stopping at platform 5 at a given station, it will now + stop at platform 6. This happens frequently on large train networks + such as in Sydney. +5. **A bus trip is completely canceled.** Perhaps the bus has broken + down and there is no replacement vehicle. +6. **An unplanned trip is added.** Perhaps there is an unexpectedly + large number of passengers so extra buses are brought in to clear + the backlog. + +While each provider may represent these scenarios differently in +GTFS-realtime, it is likely each of them would be represented as +follows. + +1. **Modify the existing trip.** Include a `StopTimeUpdate` for each + stop that is to be canceled with a `ScheduleRelationship` value of + `SKIPPED`. +2. **Cancel the trip and add a new one.** Unfortunately, there is no + way in GTFS-realtime to insert a stop into an existing trip. The + `ScheduleRelationship` value for the trip would be set to + `CANCELED` (in the `TripDescriptor`, not in the + `StopTimeUpdate` elements). +3. If a bus is rerouted down a different street, how it is handled + depends on which stops are missed: + + * If no stops are to be made on the new street, cancel the stops + impacted by the detour, similar to scenario 1. + * If the bus will stop on the new street to drop passengers off or + pick them up, then cancel the trip and add a new one, similar to + scenario 2. + +4. **Cancel the trip and add a new one.** Since it is not possible to + insert a stop using GTFS-realtime, the existing trip must be + canceled and a new trip added to replace it if you want the platform + to be reflected accurately. Note, however, that many data providers + do not differentiate between platforms in their feeds, so they only + refer to the parent station instead. In this instance a platform + change should be communicated using service alerts if it could + otherwise cause confusion. +5. **Cancel the trip.** In this instance you would not need to include + any `StopTimeUpdate` elements for the trip; rather, you would + specify `CANCELED` in the `ScheduleRelationship` field of + `TripDescriptor`. +6. **Add a new trip.** When adding a new trip, all of the stops in the + trip should also be included (not just the next one). The + `ScheduleRelationship` for the trip would be set to `ADDED`. + +The important takeaway from this section is that if you are telling your +users that a trip has been canceled, you need to make it clear to them +if a new trip has replaced it, otherwise they may not correctly +understand the intent of the data. + +In these instances, hopefully the data provider also provides a +corresponding service alert so the reason for the change can be +communicated to passengers. + -- cgit v1.2.3