gistools/readers/gtfs/schedule/
mod.rs

1// https://gtfs.org/documentation/schedule/reference/#agencytxt
2mod agency;
3mod areas;
4mod attributions;
5mod booking_rules;
6mod calendar;
7mod calendar_dates;
8mod fare_attributes;
9mod fare_leg_join_rules;
10mod fare_leg_rules;
11mod fare_media;
12mod fare_products;
13mod fare_rules;
14mod fare_transfer_rules;
15mod feed_info;
16mod frequencies;
17mod levels;
18mod location_group_stops;
19mod location_groups;
20mod networks;
21mod pathways;
22mod route_networks;
23mod routes;
24mod shapes;
25mod stop_areas;
26mod stop_times;
27mod stops;
28mod timeframes;
29mod transfers;
30mod translations;
31mod trips;
32
33use crate::{
34    parsers::FeatureReader,
35    readers::json::{JSONCollectionReader, ToGisJSON},
36    util::iter_zip_folder,
37};
38pub use agency::*;
39use alloc::{collections::BTreeMap, string::String, vec, vec::Vec};
40pub use areas::*;
41pub use attributions::*;
42pub use booking_rules::*;
43pub use calendar::*;
44pub use calendar_dates::*;
45pub use fare_attributes::*;
46pub use fare_leg_join_rules::*;
47pub use fare_leg_rules::*;
48pub use fare_media::*;
49pub use fare_products::*;
50pub use fare_rules::*;
51pub use fare_transfer_rules::*;
52pub use feed_info::*;
53pub use frequencies::*;
54pub use levels::*;
55pub use location_group_stops::*;
56pub use location_groups::*;
57pub use networks::*;
58pub use pathways::*;
59pub use route_networks::*;
60pub use routes::*;
61use s2json::{MValue, MValueCompatible, Properties, VectorFeature};
62use serde::{Deserialize, Serialize};
63pub use shapes::*;
64pub use stop_areas::*;
65pub use stop_times::*;
66pub use stops::*;
67pub use timeframes::*;
68pub use transfers::*;
69pub use translations::*;
70pub use trips::*;
71
72// TODO: postprocess all interactions like `Trips -> shape_id [Link]` & `StopTime -> On-demand Service Routing Behavior [Link]`
73
74/// A piece of the GTFS schedule
75#[derive(Debug, Clone, PartialEq)]
76pub struct Piece {
77    /// The name of the file
78    pub filename: String,
79    /// The contents of the file
80    pub data: String,
81}
82
83/// `locations.geojson` data properties
84/// Defines zones where riders can request either pickup or drop off by on-demand services.
85/// These zones are represented as GeoJSON polygons.
86#[derive(Debug, Default, Clone, PartialEq, MValueCompatible, Serialize, Deserialize)]
87pub struct GTFSLocationsProperties {
88    /// The name of the stop
89    pub stop_name: String,
90    /// The description of the stop
91    pub stop_desc: String,
92}
93
94/// # GTFS Schedule Reader
95///
96/// ## Description
97/// Schedule class that pulls in all of the GTFS schedule files and parses them into a single object
98///
99/// implements the [`FeatureReader`] trait
100///
101/// ## Usage
102///
103/// The methods you have access to:
104/// - [`GTFSScheduleReader::new`]: Create a new GTFSScheduleReader
105/// - [`GTFSScheduleReader::from_folder`]: Create a new GTFSScheduleReader from a standard folder
106/// - [`GTFSScheduleReader::from_gzip`]: Create a new GTFSScheduleReader from a gzip file
107/// - [`GTFSScheduleReader::collect_vector_features`]: Collect vector features
108/// - [`GTFSScheduleReader::iter`]: Iterate over the features
109/// - [`GTFSScheduleReader::par_iter`]: Iterate over the features
110///
111/// ```rust
112/// use gistools::{parsers::FeatureReader, readers::GTFSScheduleReader};
113/// use std::{fs, path::PathBuf};
114/// use s2json::VectorFeature;
115///  
116/// let gzip_data = fs::read(
117///     PathBuf::from(env!("CARGO_MANIFEST_DIR"))
118///         .join("tests/readers/gtfs/fixtures/caltrain_20160406.zip"),
119/// ).unwrap();
120///
121/// let reader = GTFSScheduleReader::from_gzip(&gzip_data);
122/// assert_eq!(reader.stops.len(), 95);
123///
124/// let features: Vec<VectorFeature> = reader.iter().collect();
125/// assert_eq!(features.len(), 103);
126/// ```
127///
128/// ## Links
129/// - <https://mobilitydatabase.org>
130/// - <https://developers.google.com/transit/gtfs/examples/overview>
131/// - <https://gtfs.org/documentation/schedule/reference/#tripstxt>
132/// - <https://mobilitydata.github.io/>
133/// - <https://www.transit.land>
134#[derive(Debug, Default, Clone)]
135pub struct GTFSScheduleReader {
136    /// Agencies
137    pub agencies: BTreeMap<String, GTFSAgency>,
138    /// Areas
139    pub areas: BTreeMap<String, GTFSArea>,
140    /// Attributions
141    pub attributions: BTreeMap<String, GTFSAttribution>,
142    /// Booking Rules
143    pub booking_rules: BTreeMap<String, GTFSBookingRule>,
144    /// Calendar
145    pub calendar: Vec<GTFSCalendar>,
146    /// Calendar Dates
147    pub calendar_dates: BTreeMap<String, GTFSCalendarDate>,
148    /// Fare Attributes
149    pub fare_attributes: BTreeMap<String, GTFSFareAttribute>,
150    /// Fare Leg Join Rules
151    pub fare_leg_join_rules: Vec<GTFSFareLegJoinRule>,
152    /// Fare Leg Rules
153    pub fare_leg_rules: Vec<GTFSFareLegRule>,
154    /// Fare Media
155    pub fare_media: BTreeMap<String, GTFSFareMedia>,
156    /// Fare Products
157    pub fare_products: BTreeMap<String, GTFSFareProduct>,
158    /// Fare Rules
159    pub fare_rules: Vec<GTFSFareRule>,
160    /// Fare Transfer Rules
161    pub fare_transfer_rules: Vec<GTFSFareTransferRule>,
162    /// Feed Info
163    pub feed_info: BTreeMap<String, GTFSFeedInfo>,
164    /// Frequencies
165    pub frequencies: Vec<GTFSFrequency>,
166    /// Levels
167    pub levels: BTreeMap<String, GTFSLevel>,
168    /// Location Groups
169    pub location_groups: BTreeMap<String, GTFSLocationGroup>,
170    /// Location Group Stops
171    pub location_group_stops: Vec<GTFSLocationGroupStop>,
172    /// Networks
173    pub networks: BTreeMap<String, GTFSNetwork>,
174    /// Pathways
175    pub pathways: BTreeMap<String, GTFSPathway>,
176    /// Route Networks
177    pub route_networks: Vec<GTFSRouteNetwork>,
178    /// Routes
179    pub routes: BTreeMap<String, GTFSRoute>,
180    /// Shapes
181    pub shapes: BTreeMap<String, Vec<GTFSShape>>,
182    /// Stop Areas
183    pub stop_areas: Vec<GTFSStopArea>,
184    /// Stop Areas
185    pub stops: BTreeMap<String, GTFSStop>,
186    /// Stop Times
187    pub stop_times: Vec<GTFSStopTime>,
188    /// Timeframes
189    pub timeframes: BTreeMap<String, GTFSTimeframe>,
190    /// Transfers
191    pub transfers: Vec<GTFSTransfer>,
192    /// Translations
193    pub translations: Vec<GTFSTranslation>,
194    /// Trips
195    pub trips: BTreeMap<String, GTFSTrip>,
196    /// Geojson
197    pub geojson: Option<JSONCollectionReader>,
198}
199impl GTFSScheduleReader {
200    /// Create a new GTFSScheduleReader
201    pub fn new(pieces: &[Piece]) -> Self {
202        let mut res = GTFSScheduleReader::default();
203
204        for Piece { filename, data } in pieces {
205            let stem = filename.split('.').next().unwrap_or("");
206            match stem {
207                "agency" => res.agencies = GTFSAgency::new(data),
208                "areas" => res.areas = GTFSArea::new(data),
209                "attributions" => res.attributions = GTFSAttribution::new(data),
210                "booking_rules" => res.booking_rules = GTFSBookingRule::new(data),
211                "calendar" => res.calendar = GTFSCalendar::new(data),
212                "calendar_dates" => res.calendar_dates = GTFSCalendarDate::new(data),
213                "fare_attributes" => res.fare_attributes = GTFSFareAttribute::new(data),
214                "fare_leg_join_rules" => res.fare_leg_join_rules = GTFSFareLegJoinRule::new(data),
215                "fare_leg_rules.txt" => res.fare_leg_rules = GTFSFareLegRule::new(data),
216                "fare_media" => res.fare_media = GTFSFareMedia::new(data),
217                "fare_products" => res.fare_products = GTFSFareProduct::new(data),
218                "fare_rules" => res.fare_rules = GTFSFareRule::new(data),
219                "fare_transfer_rules" => res.fare_transfer_rules = GTFSFareTransferRule::new(data),
220                "feed_info" => res.feed_info = GTFSFeedInfo::new(data),
221                "frequencies" => res.frequencies = GTFSFrequency::new(data),
222                "levels" => res.levels = GTFSLevel::new(data),
223                "location_groups" => res.location_groups = GTFSLocationGroup::new(data),
224                "location_group_stops" => {
225                    res.location_group_stops = GTFSLocationGroupStop::new(data)
226                }
227                "networks" => res.networks = GTFSNetwork::new(data),
228                "pathways" => res.pathways = GTFSPathway::new(data),
229                "route_networks" => res.route_networks = GTFSRouteNetwork::new(data),
230                "routes" => res.routes = GTFSRoute::new(data),
231                "shapes" => res.shapes = GTFSShape::new(data),
232                "stop_areas" => res.stop_areas = GTFSStopArea::new(data),
233                "stops" => res.stops = GTFSStop::new(data),
234                "stop_times" => res.stop_times = GTFSStopTime::new(data),
235                "timeframes" => res.timeframes = GTFSTimeframe::new(data),
236                "transfers" => res.transfers = GTFSTransfer::new(data),
237                "translations" => res.translations = GTFSTranslation::new(data),
238                "trips" => res.trips = GTFSTrip::new(data),
239                "locations" => {
240                    if let Ok(mut feature_collection) = data.as_str().to_feature_collection() {
241                        res.geojson = Some(JSONCollectionReader::from(&mut feature_collection));
242                    }
243                }
244                _ => {}
245            }
246        }
247
248        res
249    }
250
251    /// Builds a GTFS Schedule Reader from a gzip folder
252    ///
253    /// ## Parameters
254    /// - `gzip_data`: The gzip folder to parse
255    ///
256    /// ## Returns
257    /// A Schedule class
258    pub fn from_gzip(gzip_data: &[u8]) -> Self {
259        let mut pieces: Vec<Piece> = vec![];
260
261        for item in iter_zip_folder(gzip_data).unwrap() {
262            if let Ok(read_data) = (item.read)() {
263                pieces.push(Piece {
264                    filename: item.filename,
265                    data: String::from_utf8_lossy(&read_data).into(),
266                });
267            }
268        }
269
270        GTFSScheduleReader::new(&pieces)
271    }
272
273    /// Build a GTFS Schedule Reader from a standard folder
274    #[cfg(feature = "std")]
275    pub fn from_folder(folder_path: &str) -> Self {
276        let mut pieces: Vec<Piece> = vec![];
277
278        for entry in std::fs::read_dir(folder_path).unwrap().flatten() {
279            if let Ok(read_data) = std::fs::read(entry.path()) {
280                pieces.push(Piece {
281                    filename: entry.file_name().to_str().unwrap().into(),
282                    data: String::from_utf8_lossy(&read_data).into(),
283                });
284            }
285        }
286
287        GTFSScheduleReader::new(&pieces)
288    }
289
290    /// Collect all known vector features
291    pub fn collect_vector_features(&self) -> Vec<VectorFeature> {
292        let mut res = vec![];
293        // add stops
294        for stop in self.stops.values() {
295            if let Some(feature) = stop.to_feature() {
296                res.push(feature);
297            }
298        }
299        // add geojson
300        if let Some(geojson) = &self.geojson {
301            for feature in geojson.iter() {
302                res.push(feature);
303            }
304        }
305        // add shapes
306        for shape in self.shapes.values() {
307            let gtfs_shapes: GTFSShapes = shape.into();
308            res.push(gtfs_shapes.into());
309        }
310
311        res
312    }
313}
314
315/// The GTFS Schedule Iterator tool
316#[derive(Debug)]
317pub struct GTFSScheduleIterator {
318    features: Vec<VectorFeature>,
319    index: usize,
320}
321impl Iterator for GTFSScheduleIterator {
322    type Item = VectorFeature;
323
324    fn next(&mut self) -> Option<Self::Item> {
325        self.index += 1;
326        self.features.get(self.index - 1).cloned()
327    }
328}
329/// A feature reader trait with a callback-based approach
330impl FeatureReader<(), Properties, MValue> for GTFSScheduleReader {
331    type FeatureIterator<'a> = GTFSScheduleIterator;
332
333    fn iter(&self) -> Self::FeatureIterator<'_> {
334        GTFSScheduleIterator { features: self.collect_vector_features(), index: 0 }
335    }
336
337    fn par_iter(&self, pool_size: usize, thread_id: usize) -> Self::FeatureIterator<'_> {
338        let start = self.collect_vector_features().len() * thread_id / pool_size;
339        let end = self.collect_vector_features().len() * (thread_id + 1) / pool_size;
340        let features = self.collect_vector_features()[start..end].to_vec();
341        GTFSScheduleIterator { features, index: 0 }
342    }
343}