gistools/readers/gtfs/schedule/stop_times.rs
1use crate::readers::csv::parse_csv_as_record;
2use alloc::{string::String, vec::Vec};
3use s2json::MValueCompatible;
4
5/// Pickup method.
6/// - 0 or empty = Regularly scheduled pickup
7/// - 1 = No pickup available
8/// - 2 = Must phone agency to arrange pickup
9/// - 3 = Must coordinate with driver to arrange pickup
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash)]
11pub enum GTFSPickupDropOffType {
12 /// Regularly scheduled pickup/drop off
13 Regular = 0,
14 /// No pickup/drop off available
15 None = 1,
16 /// Must phone agency to arrange pickup/drop off
17 PhoneAgency = 2,
18 /// Must coordinate with driver to arrange pickup/drop off
19 CoordinateDriver = 3,
20}
21impl From<i8> for GTFSPickupDropOffType {
22 fn from(value: i8) -> Self {
23 match value {
24 0 => GTFSPickupDropOffType::Regular,
25 2 => GTFSPickupDropOffType::PhoneAgency,
26 3 => GTFSPickupDropOffType::CoordinateDriver,
27 _ => GTFSPickupDropOffType::None,
28 }
29 }
30}
31
32/// Continuous pickup behavior from this stop_time to the next.
33/// - 0 = Continuous stopping pickup/drop off
34/// - 1 or empty = No continuous stopping pickup/drop off
35/// - 2 = Must phone agency
36/// - 3 = Must coordinate with driver
37#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash)]
38pub enum GTFSContinuousPickupDropOff {
39 /// Continuous stopping pickup
40 Continuous = 0,
41 /// No continuous stopping pickup
42 None = 1,
43 /// Must phone agency
44 PhoneAgency = 2,
45 /// Must coordinate with driver
46 CoordinateDriver = 3,
47}
48impl From<i8> for GTFSContinuousPickupDropOff {
49 fn from(value: i8) -> Self {
50 match value {
51 0 => GTFSContinuousPickupDropOff::Continuous,
52 2 => GTFSContinuousPickupDropOff::PhoneAgency,
53 3 => GTFSContinuousPickupDropOff::CoordinateDriver,
54 _ => GTFSContinuousPickupDropOff::None,
55 }
56 }
57}
58
59/// Indicates if arrival/departure times are exact or approximate.
60/// - 0 = Approximate times
61/// - 1 = Exact times
62#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash)]
63pub enum GTFSTimepoint {
64 /// Approximate times
65 Approximate = 0,
66 /// Exact times
67 Exact = 1,
68}
69impl From<i8> for GTFSTimepoint {
70 fn from(value: i8) -> Self {
71 match value {
72 0 => GTFSTimepoint::Approximate,
73 _ => GTFSTimepoint::Exact,
74 }
75 }
76}
77
78/// # Stop Time Information
79///
80/// **Required** - Times that a vehicle arrives at and departs from stops for each trip.
81#[derive(Debug, Default, Clone, PartialEq, MValueCompatible)]
82pub struct GTFSStopTime {
83 /// **Required**
84 /// Identifies a trip (`trips.trip_id`).
85 pub trip_id: String,
86 /// **Conditionally Required**
87 /// Arrival time at the stop in HH:MM:SS (local) or possibly > 24:00:00 after midnight.
88 /// Required for the first/last stop of the trip or if `timepoint=1`.
89 /// Forbidden if `start_pickup_drop_off_window` or `end_pickup_drop_off_window` are defined.
90 pub arrival_time: Option<String>,
91 /// **Conditionally Required**
92 /// Departure time at the stop in HH:MM:SS (local) or possibly > 24:00:00 after midnight.
93 /// Required if `timepoint=1`.
94 /// Forbidden if `start_pickup_drop_off_window` or `end_pickup_drop_off_window` are defined.
95 pub departure_time: Option<String>,
96 /// **Conditionally Required**
97 /// References a stop (`stops.stop_id`). Must be a location_type of 0 or empty.
98 /// Required if neither `location_group_id` nor `location_id` is used.
99 /// Forbidden if `location_group_id` or `location_id` is defined.
100 pub stop_id: Option<String>,
101 /// **Conditionally Forbidden**
102 /// References a location group (`location_groups.location_group_id`).
103 /// Forbidden if `stop_id` or `location_id` is defined.
104 pub location_group_id: Option<String>,
105 /// **Conditionally Forbidden**
106 /// References a GeoJSON location ID (`locations.geojson`).
107 /// Forbidden if `stop_id` or `location_group_id` is defined.
108 pub location_id: Option<String>,
109 /// **Required**
110 /// Order of stops (or location groups, or GeoJSON locations) for this trip.
111 /// Must increase along the trip, but need not be consecutive.
112 pub stop_sequence: usize,
113 /// **Optional**
114 /// Overrides the trip’s headsign at this specific stop.
115 pub stop_headsign: Option<String>,
116 /// **Conditionally Required**
117 /// Time on-demand service becomes available at this location/stop/location group.
118 /// Required if `end_pickup_drop_off_window` is defined, or if `location_group_id` or `location_id` is used.
119 /// Forbidden if `arrival_time` or `departure_time` is defined.
120 pub start_pickup_drop_off_window: Option<String>,
121 /// **Conditionally Required**
122 /// Time on-demand service ends at this location/stop/location group.
123 /// Required if `start_pickup_drop_off_window` is defined, or if `location_group_id` or `location_id` is used.
124 /// Forbidden if `arrival_time` or `departure_time` is defined.
125 pub end_pickup_drop_off_window: Option<String>,
126 /// **Conditionally Forbidden**
127 /// Pickup method:
128 /// 0 or empty = Regular, 1 = None, 2 = Phone Agency, 3 = Coordinate with Driver
129 /// Forbidden if `start_pickup_drop_off_window` or `end_pickup_drop_off_window` are defined (for 0 or 3).
130 pub pickup_type: Option<i8>, // ?: GTFSPickupType;
131 /// **Conditionally Forbidden**
132 /// Drop-off method:
133 /// 0 or empty = Regular, 1 = None, 2 = Phone Agency, 3 = Coordinate with Driver
134 /// Forbidden if `start_pickup_drop_off_window` or `end_pickup_drop_off_window` are defined (for 0).
135 pub drop_off_type: Option<i8>, // ?: GTFSDropOffType;
136 /// **Conditionally Forbidden**
137 /// Continuous pickup from this stop_time to the next.
138 /// 0 = Continuous, 1 or empty = None, 2 = Phone Agency, 3 = Coordinate with Driver
139 /// Forbidden if `start_pickup_drop_off_window` or `end_pickup_drop_off_window` are defined.
140 pub continuous_pickup: Option<i8>, // ?: ContinuousPickup;
141 /// **Conditionally Forbidden**
142 /// Continuous drop-off from this stop_time to the next.
143 /// 0 = Continuous, 1 or empty = None, 2 = Phone Agency, 3 = Coordinate with Driver
144 /// Forbidden if `start_pickup_drop_off_window` or `end_pickup_drop_off_window` are defined.
145 pub continuous_drop_off: Option<i8>, // ?: ContinuousDropOff;
146 /// **Optional**
147 /// Distance traveled along the associated shape from the first stop to this record’s stop.
148 /// Must be in the same units used in shapes.txt.
149 pub shape_dist_traveled: Option<usize>,
150 /// **Optional**
151 /// 0 = Times are approximate, 1 = Times are exact.
152 pub timepoint: Option<i8>, // ?: Timepoint;
153 /// **Optional**
154 /// Boarding booking rule reference (`booking_rules.booking_rule_id`).
155 /// Recommended if `pickup_type=2`.
156 pub pickup_booking_rule_id: Option<String>,
157 /// **Optional**
158 /// Alighting booking rule reference (`booking_rules.booking_rule_id`).
159 /// Recommended if `drop_off_type=2`.
160 pub drop_off_booking_rule_id: Option<String>,
161}
162impl GTFSStopTime {
163 /// Create a new GTFSStopTime
164 pub fn new(source: &str) -> Vec<GTFSStopTime> {
165 let mut res = Vec::new();
166 for record in parse_csv_as_record::<GTFSStopTime>(source, None, None) {
167 res.push(record);
168 }
169 res
170 }
171 /// Get the pickup_type
172 pub fn pickup_type(&self) -> Option<GTFSPickupDropOffType> {
173 self.pickup_type.map(GTFSPickupDropOffType::from)
174 }
175 /// Get the drop_off_type
176 pub fn drop_off_type(&self) -> Option<GTFSPickupDropOffType> {
177 self.drop_off_type.map(GTFSPickupDropOffType::from)
178 }
179 /// Get the continuous_pickup
180 pub fn continuous_pickup(&self) -> Option<GTFSContinuousPickupDropOff> {
181 self.continuous_pickup.map(GTFSContinuousPickupDropOff::from)
182 }
183 /// Get the continuous_drop_off
184 pub fn continuous_drop_off(&self) -> Option<GTFSContinuousPickupDropOff> {
185 self.continuous_drop_off.map(GTFSContinuousPickupDropOff::from)
186 }
187 /// Get the timepoint
188 pub fn timepoint(&self) -> Option<GTFSTimepoint> {
189 self.timepoint.map(GTFSTimepoint::from)
190 }
191}