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}