gistools/readers/gtfs/realtime/trip/
descriptor.rs

1use crate::{readers::parse_gtfs_date, util::Date};
2use alloc::string::{String, ToString};
3use pbf::{BitCast, ProtoRead, ProtoWrite, Protobuf};
4
5/// The relation between this trip and the static schedule. If a trip is done
6/// in accordance with temporary schedule, not reflected in GTFS, then it
7/// shouldn't be marked as SCHEDULED, but likely as ADDED.
8#[repr(u8)]
9#[derive(Copy, Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd, BitCast)]
10pub enum GTFSRealtimeScheduleRelationship {
11    /// Trip that is running in accordance with its GTFS schedule, or is close
12    /// enough to the scheduled trip to be associated with it.
13    #[default]
14    Scheduled = 0,
15    /// An extra trip that was added in addition to a running schedule, for
16    /// example, to replace a broken vehicle or to respond to sudden passenger
17    /// load.
18    /// NOTE: Currently, behavior is unspecified for feeds that use this mode. There are discussions on the GTFS GitHub
19    /// [(1)](https://github.com/google/transit/issues/106) [(2)](https://github.com/google/transit/pull/221)
20    /// [(3)](https://github.com/google/transit/pull/219) around fully specifying or deprecating ADDED trips and the
21    /// documentation will be updated when those discussions are finalized.
22    Added = 1,
23    /// A trip that is running with no schedule associated to it (GTFS frequencies.txt exact_times=0).
24    /// Trips with ScheduleRelationship=UNSCHEDULED must also set all StopTimeUpdates.ScheduleRelationship=UNSCHEDULED.
25    Unscheduled = 2,
26    ///  A trip that existed in the schedule but was removed.
27    Cancelled = 3,
28    /// Should not be used - for backwards-compatibility only.
29    Replacement = 5, // deprecated
30    /// An extra trip that was added in addition to a running schedule, for example, to replace a broken vehicle or to
31    /// respond to sudden passenger load. Used with TripUpdate.TripProperties.trip_id, TripUpdate.TripProperties.start_date,
32    /// and TripUpdate.TripProperties.start_time to copy an existing trip from static GTFS but start at a different service
33    /// date and/or time. Duplicating a trip is allowed if the service related to the original trip in (CSV) GTFS
34    /// (in calendar.txt or calendar_dates.txt) is operating within the next 30 days. The trip to be duplicated is
35    /// identified via TripUpdate.TripDescriptor.trip_id. This enumeration does not modify the existing trip referenced by
36    /// TripUpdate.TripDescriptor.trip_id - if a producer wants to cancel the original trip, it must publish a separate
37    /// TripUpdate with the value of CANCELED or DELETED. Trips defined in GTFS frequencies.txt with exact_times that is
38    /// empty or equal to 0 cannot be duplicated. The VehiclePosition.TripDescriptor.trip_id for the new trip must contain
39    /// the matching value from TripUpdate.TripProperties.trip_id and VehiclePosition.TripDescriptor.ScheduleRelationship
40    /// must also be set to DUPLICATED.
41    /// Existing producers and consumers that were using the ADDED enumeration to represent duplicated trips must follow
42    /// the migration guide <https://github.com/google/transit/tree/master/gtfs-realtime/spec/en/examples/migration-duplicated.md>
43    /// to transition to the DUPLICATED enumeration.
44    /// NOTE: This field is still experimental, and subject to change. It may be formally adopted in the future.
45    Duplicated = 6,
46    /// A trip that existed in the schedule but was removed and must not be shown to users.
47    /// DELETED should be used instead of CANCELED to indicate that a transit provider would like to entirely remove
48    /// information about the corresponding trip from consuming applications, so the trip is not shown as cancelled to
49    /// riders, e.g. a trip that is entirely being replaced by another trip.
50    /// This designation becomes particularly important if several trips are cancelled and replaced with substitute service.
51    /// If consumers were to show explicit information about the cancellations it would distract from the more important
52    /// real-time predictions.
53    /// NOTE: This field is still experimental, and subject to change. It may be formally adopted in the future.
54    Deleted = 7,
55}
56
57/// A descriptor that identifies an instance of a GTFS trip, or all instances of
58/// a trip along a route.
59/// - To specify a single trip instance, the trip_id (and if necessary,
60///   start_time) is set. If route_id is also set, then it should be same as one
61///   that the given trip corresponds to.
62/// - To specify all the trips along a given route, only the route_id should be
63///   set. Note that if the trip_id is not known, then stop sequence ids in
64///   TripUpdate are not sufficient, and stop_ids must be provided as well. In
65///   addition, absolute arrival/departure times must be provided.
66#[derive(Debug, Default, Clone, PartialEq)]
67pub struct GTFSRealtimeTripDescriptor {
68    /// The trip_id from the GTFS feed that this selector refers to.
69    /// For non frequency-based trips, this field is enough to uniquely identify
70    /// the trip. For frequency-based trip, start_time and start_date might also be
71    /// necessary. When schedule_relationship is DUPLICATED within a TripUpdate, the trip_id identifies the trip from
72    /// static GTFS to be duplicated. When schedule_relationship is DUPLICATED within a VehiclePosition, the trip_id
73    /// identifies the new duplicate trip and must contain the value for the corresponding TripUpdate.TripProperties.trip_id.
74    pub trip_id: Option<String>, // 1 [string]
75    /// The initially scheduled start time of this trip instance.
76    /// When the trip_id corresponds to a non-frequency-based trip, this field
77    /// should either be omitted or be equal to the value in the GTFS feed. When
78    /// the trip_id correponds to a frequency-based trip, the start_time must be
79    /// specified for trip updates and vehicle positions. If the trip corresponds
80    /// to exact_times=1 GTFS record, then start_time must be some multiple
81    /// (including zero) of headway_secs later than frequencies.txt start_time for
82    /// the corresponding time period. If the trip corresponds to exact_times=0,
83    /// then its start_time may be arbitrary, and is initially expected to be the
84    /// first departure of the trip. Once established, the start_time of this
85    /// frequency-based trip should be considered immutable, even if the first
86    /// departure time changes -- that time change may instead be reflected in a
87    /// StopTimeUpdate.
88    /// Format and semantics of the field is same as that of
89    /// GTFS/frequencies.txt/start_time, e.g., 11:15:35 or 25:15:35.
90    pub start_time: Option<String>, // 2 [string]
91    /// The scheduled start date of this trip instance.
92    /// Must be provided to disambiguate trips that are so late as to collide with
93    /// a scheduled trip on a next day. For example, for a train that departs 8:00
94    /// and 20:00 every day, and is 12 hours late, there would be two distinct
95    /// trips on the same time.
96    /// This field can be provided but is not mandatory for schedules in which such
97    /// collisions are impossible - for example, a service running on hourly
98    /// schedule where a vehicle that is one hour late is not considered to be
99    /// related to schedule anymore.
100    /// In YYYYMMDD format.
101    pub start_date: Option<Date>, // 3 [string]
102    /// The relation between this trip and the static schedule. If a trip is done
103    /// in accordance with temporary schedule, not reflected in GTFS, then it
104    /// shouldn't be marked as SCHEDULED, but likely as ADDED.
105    pub schedule_relationship: Option<GTFSRealtimeScheduleRelationship>, // 4 [enum]
106    /// The route_id from the GTFS that this selector refers to.
107    pub route_id: Option<String>, // 5 [string]
108    /// The direction_id from the GTFS feed trips.txt file, indicating the
109    /// direction of travel for trips this selector refers to.
110    pub direction_id: Option<u32>, // 6 [uint32]
111    /// Linkage to any modifications done to this trip (shape changes, removal or addition of stops).
112    /// 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.
113    pub modified_trip: Option<GTFSRealtimeModifiedTripSelector>, // 7 [message]
114}
115/// Read in the contents of the GTFSRealtimeTripDescriptor
116impl ProtoRead for GTFSRealtimeTripDescriptor {
117    fn read(&mut self, tag: u64, pb: &mut Protobuf) {
118        match tag {
119            1 => self.trip_id = Some(pb.read_string()),
120            2 => self.start_time = Some(pb.read_string()),
121            3 => self.start_date = Some(parse_gtfs_date(&pb.read_string()).unwrap_or_default()),
122            4 => self.schedule_relationship = Some(pb.read_varint()),
123            5 => self.route_id = Some(pb.read_string()),
124            6 => self.direction_id = Some(pb.read_varint()),
125            7 => {
126                let mut modified_trip = GTFSRealtimeModifiedTripSelector::default();
127                pb.read_message(&mut modified_trip);
128                self.modified_trip = Some(modified_trip);
129            }
130            _ => panic!("unknown tag {}", tag),
131        }
132    }
133}
134
135/// A descriptor that identifies an instance of a GTFS trip, or all instances of
136/// a trip along a route.
137/// - To specify a single trip instance, the trip_id (and if necessary,
138///   start_time) is set. If route_id is also set, then it should be same as one
139///   that the given trip corresponds to.
140/// - To specify all the trips along a given route, only the route_id should be
141///   set. Note that if the trip_id is not known, then stop sequence ids in
142///   TripUpdate are not sufficient, and stop_ids must be provided as well. In
143///   addition, absolute arrival/departure times must be provided.
144#[derive(Debug, Default, Clone, PartialEq)]
145pub struct GTFSRealtimeModifiedTripSelector {
146    /// The 'id' from the FeedEntity in which the contained TripModifications object affects this trip.
147    pub modifications_id: Option<String>, // 1 [string]
148    /// The trip_id from the GTFS feed that is modified by the modifications_id
149    pub affected_trip_id: Option<String>, // 2 [string]
150    /// The initially scheduled start time of this trip instance, applied to the frequency based
151    /// modified trip. Same definition as start_time in TripDescriptor.
152    pub start_time: Option<String>, // 3 [string]
153    /// The start date of this trip instance in YYYYMMDD format, applied to the modified trip. Same
154    /// definition as start_date in TripDescriptor.
155    pub start_date: Option<Date>, // 4 [string]
156}
157/// Write in the contents of the GTFSRealtimeModifiedTripSelector
158impl ProtoWrite for GTFSRealtimeModifiedTripSelector {
159    fn write(&self, pb: &mut Protobuf) {
160        if let Some(modifications_id) = &self.modifications_id {
161            pb.write_string_field(1, modifications_id.as_ref());
162        }
163        if let Some(affected_trip_id) = &self.affected_trip_id {
164            pb.write_string_field(2, affected_trip_id.as_ref());
165        }
166        if let Some(start_time) = &self.start_time {
167            pb.write_string_field(3, start_time.as_ref());
168        }
169        if let Some(start_date) = &self.start_date {
170            pb.write_string_field(4, &start_date.to_string());
171        }
172    }
173}
174/// Read in the contents of the GTFSRealtimeModifiedTripSelector
175impl ProtoRead for GTFSRealtimeModifiedTripSelector {
176    fn read(&mut self, tag: u64, pb: &mut Protobuf) {
177        match tag {
178            1 => self.modifications_id = Some(pb.read_string()),
179            2 => self.affected_trip_id = Some(pb.read_string()),
180            3 => self.start_time = Some(pb.read_string()),
181            4 => self.start_date = Some(parse_gtfs_date(&pb.read_string()).unwrap_or_default()),
182            _ => panic!("unknown tag {}", tag),
183        }
184    }
185}