Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deprecate TripUpdate.schedule_relationship = ADDED, add TripUpdate.schedule_relationship = NEW / REPLACEMENT to specify new / replaced trips which do not run on a schedule from the GTFS static. #504

Open
wants to merge 24 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
dc181de
specify ADDED and REPLACEMENT schedule relationships, and add more fi…
miklcct Sep 25, 2024
b439cf8
remove the recommendation that ADDED is not specified
miklcct Sep 25, 2024
5a9853f
disallow TripModification on trips with TripUpdate.schedule_relations…
miklcct Sep 25, 2024
21ccde5
revert formatting
miklcct Sep 26, 2024
b6d07cc
specify that route_id is required for added trips
miklcct Sep 26, 2024
6bc14c8
Clarify the use of StopTimeUpdate to specify the whole journey in add…
miklcct Sep 26, 2024
87d28c7
Clarify the time and delay for added and replacement trips
miklcct Sep 26, 2024
991caac
Update gtfs-realtime/spec/en/reference.md
miklcct Sep 30, 2024
fea9f81
Update gtfs-realtime/spec/en/reference.md
miklcct Sep 30, 2024
b6faa23
restore formatting
miklcct Oct 2, 2024
0e62760
add scheduled time
miklcct Oct 8, 2024
896129e
allow scheduled time for duplicated trip as well
miklcct Oct 8, 2024
43a9b00
fix wording
miklcct Oct 8, 2024
e22668e
Merge remote-tracking branch 'upstream/master' into added-replacement
miklcct Dec 3, 2024
3ff34bf
remove fields that I am not going to use
miklcct Dec 3, 2024
573124c
Update gtfs-realtime/spec/en/reference.md
miklcct Dec 3, 2024
ade68ae
Update gtfs-realtime/spec/en/reference.md
miklcct Dec 3, 2024
2f019d8
remove reference to timepoint for scheduled time as it is removed fro…
miklcct Dec 3, 2024
555a619
Merge commit 'ade68aecf431db49d3bcbc55dcb43090dfd0b0dc' into added-re…
miklcct Dec 3, 2024
a2a0dd8
apply review feedback
miklcct Jan 16, 2025
aa90291
mark new fields as experimental
miklcct Jan 16, 2025
4361f12
use NEW instead of ADDED for a new trip
miklcct Jan 21, 2025
d0843d5
add migration guide
miklcct Jan 21, 2025
7804def
add some best practices
miklcct Jan 21, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions gtfs-realtime/best-practices/best-practices.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ All entities should only be removed from a GTFS Realtime feed when they are no l
General guidelines for trip cancellations:

* When canceling trips over a number of days, producers should provide TripUpdates referencing the given `trip_ids` and `start_dates` as `CANCELED` as well as an Alert with `NO_SERVICE` referencing the same `trip_ids` and `TimeRange` that can be shown to riders explaining the cancellation (e.g., detour).
* If no stops in a trip will be visited, the trip should be `CANCELED` instead of having all `stop_time_updates` being marked as `SKIPPED`.
* If no stops in a trip will be visited, the trip should be `CANCELED` instead of having all `stop_time_updates` being marked as `SKIPPED`, unless the trip was a `NEW` or `DUPLICATED` trip and was subsequently canceled.

| Field Name | Recommendation |
| --- | --- |
Expand All @@ -73,7 +73,13 @@ For example, a `VehiclePosition` entity has `vehicle_id:A` and `trip_id:4`, then

| Field Name | Recommendation |
| --- | --- |
| `schedule_relationship` | The behavior of `ADDED` trips are unspecified and the use of this enumeration is not recommended. |
| `schedule_relationship` | The behavior of `ADDED` trips are unspecified and the use of this enumeration is not recommended.<br/>If the trip is not scheduled to run originally, use `NEW` if it doesn't follow the stopping pattern of an existing trip, or `DUPLICATED` if it is a copy of an existing trip.<br/>If the trip runs on a modified schedule or calling points, but can be associated to an original scheduled trip in the GTFS static, use `REPLACEMENT` and specify the full list of calling points for the modified trip. |

### TripProperties

| Field Name | Recommendation |
| --- | --- |
| `trip_headsign` | Should always be provided for a trip with `TripDescriptor.schedule_relationship` = `NEW`, and provided for a trip with `TripDescriptor.schedule_relationship` = `REPLACEMENT` if the trip is diverted. |

### VehicleDescriptor

Expand All @@ -100,6 +106,7 @@ For example, a `VehiclePosition` entity has `vehicle_id:A` and `trip_id:4`, then
| Field Name | Recommendation |
| --- | --- |
| `delay` | If only `delay` is provided in a `stop_time_update` `arrival` or `departure` (and not `time`), then the GTFS [`stop_times.txt`](https://gtfs.org/reference/static#stopstxt) should contain `arrival_times` and/or `departure_times` for these corresponding stops. A `delay` value in the realtime feed is meaningless unless you have a clock time to add it to in the GTFS `stop_times.txt` file. |
| `scheduled_time` | If the trip is a new or replacement trip, and the trip will run according to a schedule (which can be a modified schedule in case of a replacement trip), `scheduled_time` should be provided for all timepoints. If a duplicated trip runs have different run or dwell times compared to the original, `scheduled_time` can also be used to specify them. |

### VehiclePosition

Expand Down
70 changes: 55 additions & 15 deletions gtfs-realtime/proto/gtfs-realtime.proto
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,11 @@ message TripUpdate {
// To specify a completely certain prediction, set its uncertainty to 0.
optional int32 uncertainty = 3;

// Scheduled time for a new or replacement trip.
// In Unix time (i.e., number of seconds since January 1st 1970 00:00:00
// UTC).
optional int64 scheduled_time = 4;

// The extensions namespace allows 3rd-party developers to extend the
// GTFS Realtime Specification in order to add and evaluate new features
// and modifications to the spec.
Expand Down Expand Up @@ -272,6 +277,32 @@ message TripUpdate {
// NOTE: This field is still experimental, and subject to change. It may be formally adopted in the future.
optional string assigned_stop_id = 1;

// The updated headsign of the vehicle at the stop.
// NOTE: This field is still experimental, and subject to change. It may be formally adopted in the future.
optional string stop_headsign = 2;

enum DropOffPickupType {
// Regularly scheduled pickup/dropoff.
REGULAR = 0;

// No pickup/dropoff available
NONE = 1;

// Must phone agency to arrange pickup/dropoff.
PHONE_AGENCY = 2;

// Must coordinate with driver to arrange pickup/dropoff.
COORDINATE_WITH_DRIVER = 3;
}

// The updated pickup of the vehicle at the stop.
// NOTE: This field is still experimental, and subject to change. It may be formally adopted in the future.
optional DropOffPickupType pickup_type = 3;

// The updated drop off of the vehicle at the stop.
// NOTE: This field is still experimental, and subject to change. It may be formally adopted in the future.
optional DropOffPickupType drop_off_type = 4;
miklcct marked this conversation as resolved.
Show resolved Hide resolved

// The extensions namespace allows 3rd-party developers to extend the
// GTFS Realtime Specification in order to add and evaluate new features
// and modifications to the spec.
Expand Down Expand Up @@ -379,6 +410,14 @@ message TripUpdate {
// NOTE: This field is still experimental, and subject to change. It may be formally adopted in the future.
optional string shape_id = 4;

// Specifies the headsign for this trip when it differs from the original.
// NOTE: This field is still experimental, and subject to change. It may be formally adopted in the future.
optional string trip_headsign = 5;

// Specifies the name for this trip when it differs from the original.
// NOTE: This field is still experimental, and subject to change. It may be formally adopted in the future.
optional string trip_short_name = 6;

// The extensions namespace allows 3rd-party developers to extend the
// GTFS Realtime Specification in order to add and evaluate new features
// and modifications to the spec.
Expand Down Expand Up @@ -793,14 +832,10 @@ message TripDescriptor {
// enough to the scheduled trip to be associated with it.
SCHEDULED = 0;

// An extra trip that was added in addition to a running schedule, for
// example, to replace a broken vehicle or to respond to sudden passenger
// load.
// NOTE: Currently, behavior is unspecified for feeds that use this mode. There are discussions on the GTFS GitHub
// [(1)](https://github.com/google/transit/issues/106) [(2)](https://github.com/google/transit/pull/221)
// [(3)](https://github.com/google/transit/pull/219) around fully specifying or deprecating ADDED trips and the
// documentation will be updated when those discussions are finalized.
ADDED = 1;
// This value has been deprecated as the behavior was unspecified.
// Use DUPLICATED for an extra trip that is the same as a scheduled trip except the start date or time,
// or NEW for an extra trip that is unrelated to an existing trip.
ADDED = 1 [deprecated = true];

// A trip that is running with no schedule associated to it (GTFS frequencies.txt exact_times=0).
// Trips with ScheduleRelationship=UNSCHEDULED must also set all StopTimeUpdates.ScheduleRelationship=UNSCHEDULED.
Expand All @@ -809,17 +844,18 @@ message TripDescriptor {
// A trip that existed in the schedule but was removed.
CANCELED = 3;

// Should not be used - for backwards-compatibility only.
REPLACEMENT = 5 [deprecated = true];
// A trip that replaces an existing trip in the schedule.
REPLACEMENT = 5;
miklcct marked this conversation as resolved.
Show resolved Hide resolved
miklcct marked this conversation as resolved.
Show resolved Hide resolved

// An extra trip that was added in addition to a running schedule, for example, to replace a broken vehicle or to
// respond to sudden passenger load. Used with TripUpdate.TripProperties.trip_id, TripUpdate.TripProperties.start_date,
// and TripUpdate.TripProperties.start_time to copy an existing trip from static GTFS but start at a different service
// date and/or time. Duplicating a trip is allowed if the service related to the original trip in (CSV) GTFS
// (in calendar.txt or calendar_dates.txt) is operating within the next 30 days. The trip to be duplicated is
// identified via TripUpdate.TripDescriptor.trip_id. This enumeration does not modify the existing trip referenced by
// TripUpdate.TripDescriptor.trip_id - if a producer wants to cancel the original trip, it must publish a separate
// TripUpdate with the value of CANCELED or DELETED. Trips defined in GTFS frequencies.txt with exact_times that is
// TripUpdate.TripDescriptor.trip_id - if a producer wants to replace the original trip, a value of `REPLACEMENT` should be used instead.
//
// Trips defined in GTFS frequencies.txt with exact_times that is
// empty or equal to 0 cannot be duplicated. The VehiclePosition.TripDescriptor.trip_id for the new trip must contain
// the matching value from TripUpdate.TripProperties.trip_id and VehiclePosition.TripDescriptor.ScheduleRelationship
// must also be set to DUPLICATED.
Expand All @@ -839,6 +875,10 @@ message TripDescriptor {
// real-time predictions.
// NOTE: This field is still experimental, and subject to change. It may be formally adopted in the future.
DELETED = 7;

// An extra trip unrelated to any existing trips, for example, to respond to sudden passenger load.
// NOTE: This field is still experimental, and subject to change. It may be formally adopted in the future.
NEW = 8;
}
optional ScheduleRelationship schedule_relationship = 4;

Expand All @@ -864,8 +904,8 @@ message TripDescriptor {
extensions 9000 to 9999;
}

// Linkage to any modifications done to this trip (shape changes, removal or addition of stops).
// If this field is provided, the `trip_id`, `route_id`, `direction_id`, `start_time`, `start_date` fields of the `TripDescriptor` MUST be left empty, to avoid confusion by consumers that aren't looking for the `ModifiedTripSelector` value.
// Linkage to any modifications done to this trip (shape changes, removal or addition of stops).
// If this field is provided, the `trip_id`, `route_id`, `direction_id`, `start_time`, `start_date` fields of the `TripDescriptor` MUST be left empty, to avoid confusion by consumers that aren't looking for the `ModifiedTripSelector` value.
optional ModifiedTripSelector modified_trip = 7;

// The extensions namespace allows 3rd-party developers to extend the
Expand Down Expand Up @@ -1134,7 +1174,7 @@ message TripModifications {
}

message SelectedTrips {
// A list of trips affected with this replacement that all have the same new `shape_id`.
// A list of trips affected with this replacement that all have the same new `shape_id`. A `TripUpdate` with `schedule_relationship=REPLACEMENT` must not already exist for the trip.
repeated string trip_ids = 1;
// The ID of the new shape for the modified trips in this SelectedTrips.
// May refer to a new shape added using a GTFS-RT Shape message, or to an existing shape defined in the GTFS-Static feed’s shapes.txt.
Expand Down
71 changes: 67 additions & 4 deletions gtfs-realtime/spec/en/examples/migration-duplicated.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,76 @@
## Migration Guide - Transition from ADDED to DUPLICATED trips
## Migration Guide - Transition from ADDED to NEW or DUPLICATED trips

The GTFS-realtime `trip.schedule_relationship` of `NEW` represents a new trip that runs on a schedule unrelated to any existing scheduled trip.

The GTFS-realtime `trip.schedule_relationship` of `DUPLICATED` represents a new trip that is the same as an existing scheduled trip except for service start date and time.

This migration guide defines how existing producers and consumers that were using the `ADDED` enumeration to represent duplicated trips should transition to the `DUPLICATED` enumeration. The goal is to minimize disruption to producers and consumers during the transition.
This migration guide defines how existing producers and consumers that were using the `ADDED` enumeration should transition to either the `NEW` and the `DUPLICATED` enumeration. The goal is to minimize disruption to producers and consumers during the transition.

*If you are a producer or consumer that has **not** used the `ADDED` enumeration, no action is required - you can produce/consume `NEW` and/or `DUPLICATED` trips without producing/consuming any `ADDED` entities.*

*If you are a producer or consumer that has **not** used the `ADDED` enumeration to describe duplicated trips, no action is required - you can produce/consume `DUPLICATED` trips without producing/consuming any `ADDED` entities.*
For a full history of the `NEW` enumeration, see the [`NEW` and `REPLACEMENT` proposal on GitHub](https://github.com/google/transit/pull/504).

For a full history of the `DUPLICATED` enumeration, see the [`DUPLICATED` proposal on GitHub](https://github.com/google/transit/pull/221).

### Which one to migrate to

Both `NEW` and `DUPLICATED` enumeration are used to specify a trip which was not originally scheduled to run in the GTFS static.

Use `NEW` if your trip cannot be described using any scheduled trips as a template. For example, if the trip calls at different stops from the regular trips of the route, or if the extra trip is pickup only at the beginning of the route despite that the regular trips allow both pickup and drop off at all stops.

Use `DUPLICATED` if your trip is a copy of a scheduled trip, which may run at the same, or at different times of the original scheduled trip.

### Using ADDED and NEW entities in the same feed

If you are a producer who has been using the `ADDED` enumeration to specify trips which are unrelated to the schedule, to avoid disruption to existing consumers it is recommended that you continue to produce `ADDED` entitles for these trips but also add `NEW` entitles for the same trip.

Choose a reason for hiding this comment

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

I don't like this approach, there for I think it's good to seriously consider a version bump where the 'ADDED' enumeration is completely removed as @skinkie suggested.

Copy link
Author

Choose a reason for hiding this comment

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

I'm sorry, but as Google mentioned, we can't break backward compatibility.

A decade ago, when REPLACEMENT was removed from the spec, there was a version of .proto file without that value published, and it horribly broke consumers on existing feeds when they were compiled with an updated .proto . Therefore the value has been put back and marked as deprecated.

Therefore the value ADDED has to remain even if we don't want to use it anymore, and those consumers who are using ADDED for the purpose I am migrating to NEW (like OpenTripPlanner) can treat it the same as NEW instead.

Copy link
Contributor

Choose a reason for hiding this comment

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

We don't break backwards compatibility when bumping a version.

Copy link
Collaborator

@tzujenchanmbd tzujenchanmbd Jan 23, 2025

Choose a reason for hiding this comment

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

@sven4all @skinkie
May I confirm what you expect for versioning here? (I saw option A in the beginning of this conversation, just to double check...)

A. Bump version number, add "NEW" and completely deprecate "ADDED"
B. Bump version number, add "NEW", "ADDED" coexist (with transition guide)
C. Bump version number, no "NEW" but make changes on "ADDED"

Perhaps some of these look acceptable or ideal for you?

Copy link
Contributor

@skinkie skinkie Jan 23, 2025

Choose a reason for hiding this comment

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

I am preferring C. I absolutely don't want a situation which is suggested by B where a bumped version exists, and data ends up in both NEW and ADDED under that higher version.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Let me be up front that if we are considering a version bump to GTFS-Realtime (either major or minor), then I don't think we'd do it for just this proposal. We've never done a version bump of GTFS-Realtime, so this would need a broader discussion of whether we'd actually do it, what the process looks like, expectations around support for old and new versions, and if we come to terms with all that, then an extended phase where we discuss any other breaking-changes that we'd want to roll into the new version (think something akin to the GBFS process).

Which is to say, I don't want you to be surprised if/when I vote -1 on any single proposal here with a version bump.

Copy link
Author

Choose a reason for hiding this comment

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

The GTFS-realtime version is now 2.0, where it was 1.0 in the past, so it has been done already.

Copy link
Collaborator

Choose a reason for hiding this comment

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

True, but my concern still stands.

Copy link
Author

Choose a reason for hiding this comment

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

Also, when REPLACEMENT was removed a decade ago (because it was never fully specified, like ADDED right now), breaking changes to the .proto was actually released into the wild as well.

Copy link
Contributor

Choose a reason for hiding this comment

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

I wouldn't consider 2.0 a real breaking change. In practice, it only formalized that some states were invalid. In the past, those invalid state were valid per the specification but no one really considered those state valid. #64

In other words, I don't think anyone has special code handling gtfs-rt 1.0 and 2.0.


However, to prevent consumers from accidentally adding the same trip twice, the entities referencing the same trip **must** be linked using the same `trip_id`, `route_id` and `start_date`.
In addition, the contents of the `stop_time_update` must also be the same as well.

#### Producers

~~~
entity {
id: "ei0"
trip_update {
trip: {
trip_id: "1" // <-- a trip_id not found in the static GTFS
route_id: "A"
schedule_relationship: ADDED
start_date: "20200821" // <-- New trip date
start_time: "11:30:00" // <-- New trip time
}
stop_time_update {
... // The full list of the calling points of the trip
}
}
}

entity {
id: "ei10"
trip_update {
trip: {
trip_id: "1" // <-- The same trip_id as the above
route_id: "A" // <-- The same route_id as the above
schedule_relationship: NEW
start_date: "20200821" // <-- The same date as the above
start_time: "11:30:00" // <-- The same time as the above
}
stop_time_update {
... // <-- The same content as the above
}
}
}
~~~

It is suggested that you notify existing consumers (e.g., via a developer mailing list) that the use of `ADDED` is being deprecated by a set deadline and that consumers should start consuming the `NEW` trips instead. The above strategy being used to match `ADDED` and `NEW` trip entities should also be mentioned and a link to this migration guide should be included. After the deadline passes, you can remove the `NEW` entities from your feed and publish only the `NEW` entities for newly-added trips.

#### Consumers

As mentioned above, producers will transition from `ADDED` to `NEW` enumerations by initially publishing two entities for each new trip using the same `trip_id`.

Therefore, when a consumer implements support for `NEW` trips, it is important that consumers ignore any `ADDED` trips that have the same `trip_id` as a `NEW` trip `trip_id`.

### Using ADDED and DUPLICATED entities in same feed

#### Producers
Expand Down Expand Up @@ -93,7 +156,7 @@ entity {
}
~~~

It is suggested that you notify existing consumers (e.g., via a developer mailing list) that the use of `ADDED` for duplicated trips is being deprecated by a set deadline and that consumers should start consuming the `DUPLICATED` trips instead. The above strategy being used to match `ADDED` and `DUPLICATED` trip entities should also be mentioned and a link to this migration guide should be included. After the deadline passes, you can remove the `ADDED` entities from your feed and publish only the `DUPLICATED` entities for duplicated trips.
It is suggested that you notify existing consumers (e.g., via a developer mailing list) that the use of `ADDED` is being deprecated by a set deadline and that consumers should start consuming the `DUPLICATED` trips instead. The above strategy being used to match `ADDED` and `DUPLICATED` trip entities should also be mentioned and a link to this migration guide should be included. After the deadline passes, you can remove the `ADDED` entities from your feed and publish only the `DUPLICATED` entities for duplicated trips.

#### Consumers

Expand Down
Loading