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

Enabling separation of trips and runs via trip_service_ids #80

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
143 changes: 143 additions & 0 deletions docs/spec/examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,3 +168,146 @@ weekday,10000,20,BLOCK-A,deadhead ,,garage,08:50:00,stop-1,09:00:00
weekday,10000,30,BLOCK-A,run-as-directed,,stop-1,09:00:00,stop-1,12:00:00
weekday,10000,30,BLOCK-A,deadhead ,,stop-1,12:00:00,garage,12:10:00
```

## Jobs of entirely nonrevenue operations

A track inspection train operates once per week, with a separate crew. It's scheduled and operated separately from other service, so is given its own service ID separate from any trips in the public GTFS file. In this example, the route and stops are assumed to be defined in the public GTFS.

### `calendar_supplement.txt`

```csv
service_id,monday,tuesday,wednesday,thursday,friday,saturday,sunday,start_date,end_date
inspection_train,0,0,0,0,0,0,1,20240601,20241231
```

### `trips_supplement.txt`

```csv
route_id,service_id,trip_id,TODS_trip_type,direction_id
line1,inspection_train,inspection_line1_ob,deadhead,0
line1,inspection_train,inspection_line1_ib,deadhead,1
```

### `stop_times_supplement.txt`

```csv
trip_id,stop_id,arrival_time
inspection_line1_ob,downtown,01:00:00
inspection_line1_ob,anytown,01:45:00
inspection_line1_ib,anytown,02:00:00
inspection_line1_ib,downtown,02:45:00
```

### `run_events.txt`

This file references the service ID and trip ID defined in the other supplement files.

```csv
service_id,run_id,event_sequence,event_type,trip_id,start_location,start_time,end_location,end_time
inspection_train ,1 ,1 ,sign-in , ,main_terminal ,00:45:00 ,main_terminal ,00:45:00
inspection_train ,1 ,2 ,operator ,inspection_line1_ob ,downtown ,01:00:00 ,anytown ,01:45:00
inspection_train ,1 ,3 ,operator ,inspection_line1_ib ,anytown ,02:00:00 ,downtown ,02:45:00
inspection_train ,1 ,4 ,sign-off , ,main_terminal ,03:00:00 ,main_terminal ,03:00:00
```

## Distinct Crew and Trip schedule scenarios

These examples show situations where the crew schedules in `run_events.txt` use different service IDs than the trips they work on, as is allowed by [the spec](/docs/spec/#service_id-crew-schedules-and-trip-schedules). Most agencies will not need to model a situation like this.

In all these cases, the trips and service IDs in the public GTFS file are not modified. New service IDs are created in the calendar supplement files, and runs that operate on those dates are described in `run_events.txt`.

### Extra staffing for a special event

Due to a baseball game, an additional ticket collector will be assigned to supplement the existing crew on train 101, serving the ballpark.

#### `calendar.txt`

```csv
service_id,monday,tuesday,wednesday,thursday,friday,saturday,sunday,start_date,end_date
weekday,1,1,1,1,1,0,0,20240101,20241231
```

#### `trips.txt`

```csv
route_id,service_id,trip_id,block_id
route,weekday,101,BLOCK-A
```

#### `calendar_dates_supplement.txt`

```csv
service_id,date,exception_type
gameday,20240820,1
gameday,20240821,1
gameday,20240827,1
gameday,20240903,1
```

#### `run_events.txt`

```csv
service_id,run_id,event_sequence,block_id,job_type,event_type,trip_id,start_location,start_time,end_location,end_time
weekday,1,1, ,collector,sign-in , ,main_terminal,14:00:00,main_terminal,14:15:00
weekday,1,2,BLOCK-A,collector,collector ,101,main_terminal,14:45:00,ballpark ,15:30:00
gameday,2,1, ,collector,sign-in , ,main_terminal,14:00:00,main_terminal,14:15:00
gameday,2,2,BLOCK-A,collector,extra collector,101,main_terminal,14:45:00,ballpark ,15:30:00
```

### Trip worked by different runs on different dates

Consider a bus network between West City, Eastland, and Northingdon. Route 1 runs between West City and Eastland via Northingdon, whereas Route 2 runs directly between the two cities.

To lengthen a short layover and improve on-time performance, the driver working the 10:45am Route 1 departure and the driver of the 11am Route 2 departure will exchange these trips effective September 2024.

The public-facing schedule will not change. The trips remain on the service `weekday`. The runs are scheduled on new services `summer` and `fall`, which together cover all of the dates in `weekday`.

![Diagram showing four trips on two runs, with the assignments rearranged in the fall.](different-runs-same-trips.png)

#### `calendar.txt`

```csv
service_id,monday,tuesday,wednesday,thursday,friday,saturday,sunday,start_date,end_date
weekday,1,1,1,1,1,0,0,20240601,20241231
```

#### `trips.txt`

```csv
route_id,service_id,trip_id,trip_headsign,direction_id
1,weekday,101,Eastland via Northingdon,1
2,weekday,201,Eastland,1
1,weekday,102,West City via Northingdon,0
2,weekday,202,West City,0
```

#### `calendar_supplement.txt`

To detail the presence of the new `service_id`s and assign them to their applicable days of the week, the runs can be added to the calendar via `calendar_supplement.txt`:

```csv
service_id,monday,tuesday,wednesday,thursday,friday,saturday,sunday,start_date,end_date
summer,1,1,1,1,1,0,0,20240601,20240831
fall ,1,1,1,1,1,0,0,20240901,20241231
```

#### `run_events.txt`

The current runs can be modeled with service_id `summer`, and mapped to the existing `weekday` trips. The future runs can be modeled with service_id `fall`, also mapped to the existing `weekday` trips. The service_id `weekday` is already defined in `calendar.txt`, but neither `summer` nor `fall` are.

```csv
service_id ,run_id ,event_sequence ,block_id ,event_type ,trip_id ,start_location ,start_time ,end_location ,end_time
summer ,1 ,20 ,A ,drive ,101 ,westcity ,09:00:00 ,eastland ,10:30:00
summer ,1 ,30 ,A ,drive ,102 ,eastland ,10:45:00 ,westcity ,12:15:00

summer ,2 ,20 ,B ,drive ,201 ,westcity ,09:00:00 ,eastland ,10:00:00
summer ,2 ,30 ,B ,drive ,202 ,eastland ,11:00:00 ,westcity ,12:00:00

fall ,1 ,20 ,A ,drive ,101 ,westcity ,09:00:00 ,eastland ,10:30:00
fall ,1 ,30 ,A ,drive ,202 ,eastland ,11:00:00 ,westcity ,12:00:00

fall ,2 ,20 ,B ,drive ,201 ,westcity ,09:00:00 ,eastland ,10:00:00
fall ,2 ,30 ,B ,drive ,102 ,eastland ,10:45:00 ,westcity ,12:15:00
```

(In this example, block IDs are listed in `run_events.txt` but not `trips.txt` because the blocks would also change with the schedule change.)
Binary file added docs/spec/examples/different-runs-same-trips.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
17 changes: 16 additions & 1 deletion docs/spec/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ There are two types of files used in the TODS standard:
| stops_supplement.txt | Supplement | Supplements and modifies GTFS [stops.txt](https://github.com/google/transit/blob/master/gtfs/spec/en/reference.md#stopstxt) with internal stop locations, waypoints, and other non-public stop information.|
| stop_times_supplement.txt | Supplement | Supplements and modifies GTFS [stop_times.txt](https://github.com/google/transit/blob/master/gtfs/spec/en/reference.md#stop_timestxt) with non-public times at which trips stop at locations, `stop_times` entries for non-public trips, and related information. |
| routes_supplement.txt | Supplement | Supplements and modifies GTFS [routes.txt](https://github.com/google/transit/blob/master/gtfs/spec/en/reference.md#routestxt) with internal route identifiers and other non-public route identification. |
| calendar_supplement.txt | Supplement | Supplements and modifies GTFS [calendar.txt](https://github.com/google/transit/blob/master/gtfs/spec/en/reference.md#calendartxt) to adjust days for which existing `service_id`s are in effect, and add additional `service_id`s where crew and/or deadhead information is distinct from revenue trips. |
| calendar_dates_supplement.txt | Supplement | Supplements and modifies GTFS [calendar_dates.txt](https://github.com/google/transit/blob/master/gtfs/spec/en/reference.md#calendar_datestxt) to adjust dates for which existing `service_id`s are in effect, and add additional `service_id`s where crew and/or deadhead information is distinct from revenue trips. |
| run_events.txt | TODS-Specific | Lists all trips and other scheduled activities to be performed by a member of personnel during a run. |

_The use of the Supplement standard to modify other GTFS files is not yet formally adopted into the specification and remains subject to change. Other files may be formally adopted in the future._
Expand Down Expand Up @@ -113,14 +115,26 @@ Primary Key: (`service_id`, `run_id`, `event_sequence`)
| `block_id` | ID referencing `trips.block_id` | Optional | Identifies the block to which the run event belongs.<br /><br />This field is always optional. May exist even if `trip_id` does not (e.g. if an event represents a run-as-directed block with no scheduled trips). May exist even if `trip_id` exists and the associated trip in `trips.txt` doesn't have a `block_id`. May be omitted even if `trip_id` exists and the associated trip in `trips.txt` has a `block_id`.<br /><br />If `block_id` is set, `trip_id` is set, and the associated trip in `trips.txt` has a `block_id`, then the two `block_id`s must not be different. |
| `job_type` | Text | Optional | The type of job that the employee is doing, in a human-readable format. e.g. "Assistant Conductor". Producers may use any values, but should be consistent.<br /><br />A single run may include more than one `job_type` throughout the day if the employee has multiple responsibilities, e.g. an "Operator" in the morning and a "Shifter" in the afternoon. |
| `event_type` | Text | Required | The type of event that the employee is doing, in a human-readable format. e.g. "Sign-in". Producers may use any values, but should be consistent. Consumers may ignore events with an `event_type` that they don't recognize. |
| `trip_id` | ID referencing `trips.trip_id` | Optional | If this run event corresponds to working on a trip, identifies that trip. |
| `trip_id` | ID referencing `trips.trip_id` | Optional | If this run event corresponds to working on a trip, identifies that trip. Note that the trip may be on a different `service_id` than the run, see [`service_id`, Crew Schedules, and Trip Schedules](#service_id-crew-schedules-and-trip-schedules). |
| `start_location` | ID referencing `stops.stop_id` | Required | Identifies where the employee starts working this event.<br /><br />If `trip_id` is set (and `mid_trip_start` is not `1`), this should be the `stop_id` of the first stop of the trip in `stop_times.txt` (after applying any trip supplement). If `start_mid_trip` is `1`, this should be the location where the employee starts working, matching a `stop_id` in the middle of the supplemented trip. |
| `start_time` | Time | Required | Identifies the time when the employee starts working this event.<br /><br />If `trip_id` is set (and `mid_trip_start` is not `1`), this corresponds to the time of the first stop of the trip in `stop_times.txt` (after applying any trip supplement). If `start_mid_trip` is `1`, this time corresponds to a stop time in the middle of the supplemented trip, when the employee starts working on the trip. Note that this time may not exactly match `stop_times.txt` `arrival_time` or `departure_time` if the employee is considered to be working for a couple minutes before the trip departs. This field is about when the employee is working, and consumers who care about the the trip times should check `stop_times.txt` instead. |
| `start_mid_trip` | Enum | Optional | Indicates whether the event begins at the start of the trip or in the middle of the trip (after applying any trip supplement).<br /><br />`0` (or blank) - Run event is not associated with a trip, or no information about whether the run event starts mid-trip<br />`1` - Run event starts mid-trip<br />`2` - Run event does not start mid-trip |
| `end_location` | ID referencing `stops.stop_id` | Required | Identifies where the employee stops working this event.<br /><br />If `trip_id` is set (and `mid_trip_end` is not `1`), this should be the `stop_id` of the last stop of the trip in `stop_times.txt` (after applying any trip supplement). If `end_mid_trip` is `1`, this should be the location where the employee stops working, matching a `stop_id` in the middle of the supplemented trip. |
| `end_time` | Time | Required | Identifies the time when the employee stops working this event.<br /><br />If `trip_id` is set (and `mid_trip_end` is not `1`), this corresponds to the time of the last stop of the trip in `stop_times.txt` (after applying any trip supplement). If `end_mid_trip` is `1`, this time corresponds to a stop time in the middle of the supplemented trip, when the employee stops working on the trip. Note that this time may not exactly match `stop_times.txt` `arrival_time` or `departure_time` if the employee is considered to be working for a couple minutes after the trip finishes. This field is about when the employee is working, and consumers who care about the the trip times should check `stop_times.txt` instead. |
| `end_mid_trip` | Enum | Optional | Indicates whether the event ends at the end of the trip or in the middle of the trip (after applying any trip supplement).<br /><br />`0` (or blank) - Run event is not associated with a trip, or no information about whether the run event ends mid-trip<br />`1` - Run event ends mid-trip<br />`2` - Run event does not end mid-trip |

#### `service_id`, Crew Schedules, and Trip Schedules

For most agencies, crew schedules will use the same schedule as trips do. The `service_id` in `run_events.txt` will be the same `service_id` as is in `trips.txt` for all the trips that run works on, and the TODS feed won't need any additional entries in `calendar_supplement.txt`.

Some agencies schedule crew schedules separately from vehicle/trip schedules, and runs and trips may occur on different `service_id`s. For example, a trip that occurs on service ID `weekday` may be worked by run `1` on service ID `monday`, and by run `2` on service ID `tuesday`. See the [examples](/spec/examples) for some situations where this may happen. In that case, `trips.txt:service_id` refers to dates when the trip occurs, and `run_events.txt:service_id` refers to the dates where the run works that trip. This means that producers do not need to rewrite the calendar for their public GTFS trip schedules in order to publish complex crew schedules in TODS.

Consumers who care about the dates that a trip occurs should use the `trip_id` (which is unique even across different services) to look up the trip in `trips.txt`, and get the `service_id` there.

Producers who create separate services for their crew schedules will likely need to add those services into `calendar_supplement.txt` and `calendar_dates_supplement.txt`.

If the run's `service_id` and the trip's `service_id` are different, the run's `service_id` must not occur on any dates that the trip's `service_id` doesn't (after combining data from `calendar.txt`, `calendar_supplement.txt`, `calendar_dates.txt`, and `calendar_dates_supplement.txt`).

#### `event_sequence` and Event Times

`event_sequence` is required and unique within a run so it can be used in the Primary Key to uniquely identify events.
Expand All @@ -135,6 +149,7 @@ Because some events may overlap in time, it may not be possible to choose a sing

#### `run_events` Notes

- Run IDs may be reused between different service IDs. A run is uniquely determined by a `service_id`, `run_id` pair. Runs with the same `run_id` on different `service_id`s are considered different unrelated runs.
- Multiple `run_event`s may refer to the same `trip_id`, if multiple employees work on that trip.
- Events may have gaps between the end time of one event and the start time of the next. e.g. if an operator's layovers aren't represented by an event.
- `start_time` may equal `end_time` for an event that's a single point in time (such as a report time) without any duration.
Expand Down
Loading