Skip to main content

gtfs_structures/
raw_gtfs.rs

1use crate::Error;
2use crate::GtfsReader;
3use crate::objects::*;
4use std::path::Path;
5use web_time::Duration;
6
7/// Data structure that map the GTFS csv with little intelligence
8///
9/// This is used to analyze the GTFS and detect anomalies
10/// To manipulate the transit data, maybe [crate::Gtfs] will be more convienient
11#[derive(Debug)]
12pub struct RawGtfs {
13    /// Time needed to read and parse the archive
14    pub read_duration: Duration,
15    /// All Calendar, None if the file was absent as it is not mandatory
16    pub calendar: Option<Result<Vec<Calendar>, Error>>,
17    /// All Calendar dates, None if the file was absent as it is not mandatory
18    pub calendar_dates: Option<Result<Vec<CalendarDate>, Error>>,
19    /// All Stops
20    pub stops: Result<Vec<Stop>, Error>,
21    /// All Routes
22    pub routes: Result<Vec<Route>, Error>,
23    /// All Trips
24    pub trips: Result<Vec<RawTrip>, Error>,
25    /// All Agencies
26    pub agencies: Result<Vec<Agency>, Error>,
27    /// All shapes points, None if the file was absent as it is not mandatory
28    pub shapes: Option<Result<Vec<Shape>, Error>>,
29    /// All FareAttributes, None if the file was absent as it is not mandatory
30    pub fare_attributes: Option<Result<Vec<FareAttribute>, Error>>,
31    /// All FareRules, None if the file was absent as it is not mandatory
32    pub fare_rules: Option<Result<Vec<FareRule>, Error>>,
33    /// All FareProducts, None if the file was absent as it is not mandatory
34    pub fare_products: Option<Result<Vec<FareProduct>, Error>>,
35    /// All FareMedias, None if the file was absent as it is not mandatory
36    pub fare_media: Option<Result<Vec<FareMedia>, Error>>,
37    /// All RiderCategories, None if the file was absent as it is not mandatory
38    pub rider_categories: Option<Result<Vec<RiderCategory>, Error>>,
39    /// All Frequencies, None if the file was absent as it is not mandatory
40    pub frequencies: Option<Result<Vec<RawFrequency>, Error>>,
41    /// All Transfers, None if the file was absent as it is not mandatory
42    pub transfers: Option<Result<Vec<RawTransfer>, Error>>,
43    /// All Pathways, None if the file was absent as it is not mandatory
44    pub pathways: Option<Result<Vec<RawPathway>, Error>>,
45    /// All FeedInfo, None if the file was absent as it is not mandatory
46    pub feed_info: Option<Result<Vec<FeedInfo>, Error>>,
47    /// All StopTimes
48    pub stop_times: Result<Vec<RawStopTime>, Error>,
49    /// All files that are present in the feed
50    pub files: Vec<String>,
51    /// Format of the data read
52    pub source_format: SourceFormat,
53    /// sha256 sum of the feed
54    pub sha256: Option<String>,
55    /// All translations, None if the file was absent as it is not mandatory
56    pub translations: Option<Result<Vec<RawTranslation>, Error>>,
57    /// Base urls to ticket shops
58    pub ticketing_deep_links: Option<Result<Vec<TicketingDeepLink>, Error>>,
59    /// Identifiers to pass to ticket shops
60    pub ticketing_identifiers: Option<Result<Vec<TicketingIdentifier>, Error>>,
61    /// Attribution for the dataset
62    pub attributions: Option<Result<Vec<Attribution>, Error>>,
63}
64
65impl RawGtfs {
66    /// Prints on stdout some basic statistics about the GTFS file (numbers of elements for each object). Mostly to be sure that everything was read
67    pub fn print_stats(&self) {
68        println!("GTFS data:");
69        println!("  Read in {:?}", self.read_duration);
70        println!("  Stops: {}", mandatory_file_summary(&self.stops));
71        println!("  Routes: {}", mandatory_file_summary(&self.routes));
72        println!("  Trips: {}", mandatory_file_summary(&self.trips));
73        println!("  Agencies: {}", mandatory_file_summary(&self.agencies));
74        println!("  Stop times: {}", mandatory_file_summary(&self.stop_times));
75        println!("  Shapes: {}", optional_file_summary(&self.shapes));
76        println!("  Fares: {}", optional_file_summary(&self.fare_attributes));
77        println!(
78            "  Frequencies: {}",
79            optional_file_summary(&self.frequencies)
80        );
81        println!("  Transfers: {}", optional_file_summary(&self.transfers));
82        println!("  Pathways: {}", optional_file_summary(&self.pathways));
83        println!("  Feed info: {}", optional_file_summary(&self.feed_info));
84        println!(
85            "  Translations: {}",
86            optional_file_summary(&self.translations)
87        );
88        println!(
89            "  Ticketing deep links: {}",
90            optional_file_summary(&self.ticketing_deep_links)
91        );
92        println!(
93            "  Ticketing identifiers: {}",
94            optional_file_summary(&self.ticketing_identifiers)
95        );
96        println!(
97            "  Attributions: {}",
98            optional_file_summary(&self.attributions)
99        );
100    }
101
102    /// Reads from an url (if starts with http), or a local path (either a directory or zipped file)
103    ///
104    /// To read from an url, build with read-url feature
105    /// See also [RawGtfs::from_url] and [RawGtfs::from_path] if you don’t want the library to guess
106    #[cfg(not(target_arch = "wasm32"))]
107    pub fn new(gtfs: &str) -> Result<Self, Error> {
108        GtfsReader::default().raw().read(gtfs)
109    }
110
111    /// Reads the raw GTFS from a local zip archive or local directory
112    pub fn from_path<P>(path: P) -> Result<Self, Error>
113    where
114        P: AsRef<Path>,
115    {
116        GtfsReader::default().raw().read_from_path(path)
117    }
118
119    /// Reads the raw GTFS from a remote url
120    ///
121    /// The library must be built with the read-url feature. Not available on WASM targets.
122    #[cfg(all(feature = "read-url", not(target_arch = "wasm32")))]
123    pub fn from_url<U: reqwest::IntoUrl>(url: U) -> Result<Self, Error> {
124        GtfsReader::default().raw().read_from_url(url)
125    }
126
127    /// Non-blocking read the raw GTFS from a remote url
128    ///
129    /// The library must be built with the read-url feature
130    #[cfg(feature = "read-url")]
131    pub async fn from_url_async<U: reqwest::IntoUrl>(url: U) -> Result<Self, Error> {
132        GtfsReader::default().raw().read_from_url_async(url).await
133    }
134
135    /// Reads for any object implementing [std::io::Read] and [std::io::Seek]
136    ///
137    /// Mostly an internal function that abstracts reading from an url or local file
138    pub fn from_reader<T: std::io::Read + std::io::Seek>(reader: T) -> Result<Self, Error> {
139        GtfsReader::default().raw().read_from_reader(reader)
140    }
141
142    pub(crate) fn unknown_to_default(&mut self) {
143        if let Ok(stops) = &mut self.stops {
144            for stop in stops.iter_mut() {
145                if let LocationType::Unknown(_) = stop.location_type {
146                    stop.location_type = LocationType::default();
147                }
148                if let Availability::Unknown(_) = stop.wheelchair_boarding {
149                    stop.wheelchair_boarding = Availability::default();
150                }
151            }
152        }
153        if let Ok(stop_times) = &mut self.stop_times {
154            for stop_time in stop_times.iter_mut() {
155                if let PickupDropOffType::Unknown(_) = stop_time.pickup_type {
156                    stop_time.pickup_type = PickupDropOffType::default();
157                }
158                if let PickupDropOffType::Unknown(_) = stop_time.drop_off_type {
159                    stop_time.drop_off_type = PickupDropOffType::default();
160                }
161                if let ContinuousPickupDropOff::Unknown(_) = stop_time.continuous_pickup {
162                    stop_time.continuous_pickup = ContinuousPickupDropOff::default();
163                }
164                if let ContinuousPickupDropOff::Unknown(_) = stop_time.continuous_drop_off {
165                    stop_time.continuous_drop_off = ContinuousPickupDropOff::default();
166                }
167            }
168        }
169        if let Ok(trips) = &mut self.trips {
170            for trip in trips.iter_mut() {
171                if let Availability::Unknown(_) = trip.wheelchair_accessible {
172                    trip.wheelchair_accessible = Availability::default();
173                }
174                if let BikesAllowedType::Unknown(_) = trip.bikes_allowed {
175                    trip.bikes_allowed = BikesAllowedType::default();
176                }
177            }
178        }
179    }
180}
181
182fn mandatory_file_summary<T>(objs: &Result<Vec<T>, Error>) -> String {
183    match objs {
184        Ok(vec) => format!("{} objects", vec.len()),
185        Err(e) => format!("Could not read {e}"),
186    }
187}
188
189fn optional_file_summary<T>(objs: &Option<Result<Vec<T>, Error>>) -> String {
190    match objs {
191        Some(objs) => mandatory_file_summary(objs),
192        None => "File not present".to_string(),
193    }
194}