gistools/readers/gtfs/schedule/
transfers.rs

1use crate::readers::csv::parse_csv_as_record;
2use alloc::{string::String, vec::Vec};
3use s2json::MValueCompatible;
4
5/// TransferType enumerates how a rider can transfer between routes/trips/stops:
6/// - 0 or empty = Recommended transfer
7/// - 1 = Timed transfer
8/// - 2 = Requires a minimum transfer time
9/// - 3 = Transfers not possible
10/// - 4 = In-seat transfer (stay onboard, same vehicle)
11/// - 5 = In-seat transfers not allowed
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash)]
13pub enum GTFSTransferType {
14    /// 0 = Recommended transfer
15    Recommended = 0,
16    /// 1 = Timed transfer
17    Timed = 1,
18    /// 2 = Requires a minimum transfer time
19    MinTimeRequired = 2,
20    /// 3 = Transfers not possible
21    NotPossible = 3,
22    /// 4 = In-seat transfer (stay onboard, same vehicle)
23    InSeatTransfer = 4,
24    /// 5 = In-seat transfers not allowed
25    InSeatNotAllowed = 5,
26}
27impl From<i8> for GTFSTransferType {
28    fn from(value: i8) -> Self {
29        match value {
30            1 => GTFSTransferType::Timed,
31            2 => GTFSTransferType::MinTimeRequired,
32            3 => GTFSTransferType::NotPossible,
33            4 => GTFSTransferType::InSeatTransfer,
34            5 => GTFSTransferType::InSeatNotAllowed,
35            _ => GTFSTransferType::Recommended,
36        }
37    }
38}
39
40/// # Transfers
41///
42/// **Optional**
43/// Defines additional rules/overrides for transfers between routes/trips/stops.
44/// The level of specificity is determined by which fields are present:
45/// - from_trip_id & to_trip_id (most specific)
46/// - route vs. trip combos
47/// - only from_stop_id & to_stop_id (least specific)
48///
49/// **Primary Key**: (from_stop_id, to_stop_id, from_trip_id, to_trip_id, from_route_id, to_route_id)
50#[derive(Debug, Default, Clone, PartialEq, MValueCompatible)]
51pub struct GTFSTransfer {
52    /// **Conditionally Required**
53    /// Identifies where a connection begins (`stops.stop_id`, location_type=0 or 1).
54    /// Required if transfer_type is 1, 2, or 3. Optional if transfer_type is 4 or 5.
55    pub from_stop_id: Option<String>,
56    /// **Conditionally Required**
57    /// Identifies where a connection ends (`stops.stop_id`, location_type=0 or 1).
58    /// Required if transfer_type is 1, 2, or 3. Optional if transfer_type is 4 or 5.
59    pub to_stop_id: Option<String>,
60    /// **Optional**
61    /// Identifies a route on which the arriving trip is running.
62    /// If both `fromTripId` and `fromRouteId` are defined, the trip must belong to that route,
63    /// but `fromTripId` takes precedence.
64    pub from_route_id: Option<String>,
65    /// **Optional**
66    /// Identifies a route on which the departing trip is running.
67    /// If both `toTripId` and `toRouteId` are defined, the trip must belong to that route,
68    /// but `toTripId` takes precedence.
69    pub to_route_id: Option<String>,
70    /// **Conditionally Required**
71    /// Identifies the arriving trip (`trips.trip_id`).
72    /// Required if transfer_type is 4 or 5; optional otherwise.
73    pub from_trip_id: Option<String>,
74    /// **Conditionally Required**
75    /// Identifies the departing trip (`trips.trip_id`).
76    /// Required if transfer_type is 4 or 5; optional otherwise.
77    pub to_trip_id: Option<String>,
78    /// **Required**
79    /// Indicates the type of connection:
80    /// - 0 = Recommended
81    /// - 1 = Timed
82    /// - 2 = Requires min_transfer_time
83    /// - 3 = Not possible
84    /// - 4 = In-seat transfer
85    /// - 5 = In-seat transfer not allowed
86    pub transfer_type: i8,
87    /// **Optional**
88    /// Time in seconds required for a rider to complete the transfer. If `transfer_type=2`,
89    /// this is the minimum transfer time.
90    pub min_transfer_time: Option<u32>,
91}
92impl GTFSTransfer {
93    /// Create a new GTFSTransfer
94    pub fn new(source: &str) -> Vec<GTFSTransfer> {
95        let mut res = Vec::new();
96        for record in parse_csv_as_record::<GTFSTransfer>(source, None, None) {
97            res.push(record);
98        }
99        res
100    }
101    /// Get the transfer_type
102    pub fn transfer_type(&self) -> GTFSTransferType {
103        GTFSTransferType::from(self.transfer_type)
104    }
105}