1use crate::{Error, RawGtfs, objects::*};
2use jiff::civil::Date;
3use std::collections::{HashMap, HashSet};
4use std::convert::TryFrom;
5use std::sync::Arc;
6use web_time::{Duration, Instant};
7
8#[derive(Default)]
23pub struct Gtfs {
24 pub read_duration: Duration,
26 pub calendar: HashMap<String, Calendar>,
28 pub calendar_dates: HashMap<String, Vec<CalendarDate>>,
30 pub stops: HashMap<String, Arc<Stop>>,
32 pub routes: HashMap<String, Route>,
34 pub trips: HashMap<String, Trip>,
36 pub agencies: Vec<Agency>,
38 pub shapes: HashMap<String, Vec<Shape>>,
40 pub fare_attributes: HashMap<String, FareAttribute>,
42 pub fare_rules: HashMap<String, Vec<FareRule>>,
44 pub fare_products: HashMap<String, Vec<FareProduct>>,
46 pub fare_media: HashMap<String, FareMedia>,
48 pub rider_categories: HashMap<String, RiderCategory>,
50 pub feed_info: Vec<FeedInfo>,
52}
53
54impl TryFrom<RawGtfs> for Gtfs {
55 type Error = Error;
56 fn try_from(raw: RawGtfs) -> Result<Gtfs, Error> {
60 let start = Instant::now();
61
62 let stops = to_stop_map(
63 raw.stops?,
64 raw.transfers.unwrap_or_else(|| Ok(Vec::new()))?,
65 raw.pathways.unwrap_or(Ok(Vec::new()))?,
66 )?;
67 let frequencies = raw.frequencies.unwrap_or_else(|| Ok(Vec::new()))?;
68 let trips = create_trips(raw.trips?, raw.stop_times?, frequencies, &stops)?;
69
70 let mut fare_rules = HashMap::<String, Vec<FareRule>>::new();
71 for f in raw.fare_rules.unwrap_or_else(|| Ok(Vec::new()))? {
72 fare_rules.entry(f.fare_id.clone()).or_default().push(f);
73 }
74
75 let mut fare_products = HashMap::<String, Vec<FareProduct>>::new();
76 for f in raw.fare_products.unwrap_or_else(|| Ok(Vec::new()))? {
77 fare_products.entry(f.id.clone()).or_default().push(f);
78 }
79
80 Ok(Gtfs {
81 stops,
82 routes: to_map(raw.routes?),
83 trips,
84 agencies: raw.agencies?,
85 shapes: to_shape_map(raw.shapes.unwrap_or_else(|| Ok(Vec::new()))?),
86 fare_attributes: to_map(raw.fare_attributes.unwrap_or_else(|| Ok(Vec::new()))?),
87 fare_rules,
88 fare_products,
89 fare_media: to_map(raw.fare_media.unwrap_or_else(|| Ok(Vec::new()))?),
90 rider_categories: to_map(raw.rider_categories.unwrap_or_else(|| Ok(Vec::new()))?),
91 feed_info: raw.feed_info.unwrap_or_else(|| Ok(Vec::new()))?,
92 calendar: to_map(raw.calendar.unwrap_or_else(|| Ok(Vec::new()))?),
93 calendar_dates: to_calendar_dates(
94 raw.calendar_dates.unwrap_or_else(|| Ok(Vec::new()))?,
95 ),
96 read_duration: raw.read_duration + start.elapsed(),
97 })
98 }
99}
100
101impl Gtfs {
102 pub fn print_stats(&self) {
104 println!("GTFS data:");
105 println!(" Read in {:?}", self.read_duration);
106 println!(" Stops: {}", self.stops.len());
107 println!(" Routes: {}", self.routes.len());
108 println!(" Trips: {}", self.trips.len());
109 println!(" Agencies: {}", self.agencies.len());
110 println!(" Shapes: {}", self.shapes.len());
111 println!(" Fare attributes: {}", self.fare_attributes.len());
112 println!(" Feed info: {}", self.feed_info.len());
113 }
114
115 #[cfg(not(target_arch = "wasm32"))]
120 pub fn new(gtfs: &str) -> Result<Gtfs, Error> {
121 RawGtfs::new(gtfs).and_then(Gtfs::try_from)
122 }
123
124 pub fn from_path<P>(path: P) -> Result<Gtfs, Error>
126 where
127 P: AsRef<std::path::Path>,
128 {
129 RawGtfs::from_path(path).and_then(Gtfs::try_from)
130 }
131
132 #[cfg(all(feature = "read-url", not(target_arch = "wasm32")))]
136 pub fn from_url<U: reqwest::IntoUrl>(url: U) -> Result<Gtfs, Error> {
137 RawGtfs::from_url(url).and_then(Gtfs::try_from)
138 }
139
140 #[cfg(feature = "read-url")]
144 pub async fn from_url_async<U: reqwest::IntoUrl>(url: U) -> Result<Gtfs, Error> {
145 RawGtfs::from_url_async(url).await.and_then(Gtfs::try_from)
146 }
147
148 pub fn from_reader<T: std::io::Read + std::io::Seek>(reader: T) -> Result<Gtfs, Error> {
152 RawGtfs::from_reader(reader).and_then(Gtfs::try_from)
153 }
154
155 pub fn trip_days(&self, service_id: &str, start_date: Date) -> Vec<u16> {
161 let mut result = Vec::new();
162
163 let mut removed_days = HashSet::new();
165 for extra_day in self
166 .calendar_dates
167 .get(service_id)
168 .iter()
169 .flat_map(|e| e.iter())
170 {
171 if extra_day.date >= start_date {
172 if extra_day.exception_type == Exception::Added
173 && let Ok(offset) = start_date.until(extra_day.date)
174 {
175 result.push(offset.get_days() as u16);
176 } else if extra_day.exception_type == Exception::Deleted {
177 removed_days.insert(extra_day.date);
178 }
179 }
180 }
181
182 if let Some(calendar) = self.calendar.get(service_id) {
183 for current_date in start_date.series(jiff::Span::new().days(1)) {
184 if current_date > calendar.end_date {
185 break;
186 }
187
188 if let Ok(days_offset) = start_date.until(current_date)
189 && calendar.start_date <= current_date
190 && calendar.end_date >= current_date
191 && calendar.valid_weekday(current_date)
192 && !removed_days.contains(¤t_date)
193 {
194 result.push(days_offset.get_days() as u16);
195 }
196 }
197 }
198
199 result
200 }
201
202 pub fn get_stop<'a>(&'a self, id: &str) -> Result<&'a Stop, Error> {
204 match self.stops.get(id) {
205 Some(stop) => Ok(stop),
206 None => Err(Error::ReferenceError(id.to_owned())),
207 }
208 }
209
210 pub fn get_trip<'a>(&'a self, id: &str) -> Result<&'a Trip, Error> {
212 self.trips
213 .get(id)
214 .ok_or_else(|| Error::ReferenceError(id.to_owned()))
215 }
216
217 pub fn get_route<'a>(&'a self, id: &str) -> Result<&'a Route, Error> {
219 self.routes
220 .get(id)
221 .ok_or_else(|| Error::ReferenceError(id.to_owned()))
222 }
223
224 pub fn get_calendar<'a>(&'a self, id: &str) -> Result<&'a Calendar, Error> {
226 self.calendar
227 .get(id)
228 .ok_or_else(|| Error::ReferenceError(id.to_owned()))
229 }
230
231 pub fn get_calendar_date<'a>(&'a self, id: &str) -> Result<&'a Vec<CalendarDate>, Error> {
233 self.calendar_dates
234 .get(id)
235 .ok_or_else(|| Error::ReferenceError(id.to_owned()))
236 }
237
238 pub fn get_shape<'a>(&'a self, id: &str) -> Result<&'a Vec<Shape>, Error> {
240 self.shapes
241 .get(id)
242 .ok_or_else(|| Error::ReferenceError(id.to_owned()))
243 }
244
245 pub fn get_fare_attributes<'a>(&'a self, id: &str) -> Result<&'a FareAttribute, Error> {
247 self.fare_attributes
248 .get(id)
249 .ok_or_else(|| Error::ReferenceError(id.to_owned()))
250 }
251}
252
253fn to_map<O: Id>(elements: impl IntoIterator<Item = O>) -> HashMap<String, O> {
254 elements
255 .into_iter()
256 .map(|e| (e.id().to_owned(), e))
257 .collect()
258}
259
260fn to_stop_map(
261 stops: Vec<Stop>,
262 raw_transfers: Vec<RawTransfer>,
263 raw_pathways: Vec<RawPathway>,
264) -> Result<HashMap<String, Arc<Stop>>, Error> {
265 let mut stop_map: HashMap<String, Stop> =
266 stops.into_iter().map(|s| (s.id.clone(), s)).collect();
267
268 for transfer in raw_transfers {
269 stop_map.get(&transfer.to_stop_id).ok_or_else(|| {
270 let stop_id = &transfer.to_stop_id;
271 Error::ReferenceError(format!("'{stop_id}' in transfers.txt"))
272 })?;
273 stop_map
274 .entry(transfer.from_stop_id.clone())
275 .and_modify(|stop| stop.transfers.push(StopTransfer::from(transfer)));
276 }
277
278 for pathway in raw_pathways {
279 stop_map.get(&pathway.to_stop_id).ok_or_else(|| {
280 let stop_id = &pathway.to_stop_id;
281 Error::ReferenceError(format!("'{stop_id}' in pathways.txt"))
282 })?;
283 stop_map
284 .entry(pathway.from_stop_id.clone())
285 .and_modify(|stop| stop.pathways.push(Pathway::from(pathway)));
286 }
287
288 let res = stop_map
289 .into_iter()
290 .map(|(i, s)| (i, Arc::new(s)))
291 .collect();
292 Ok(res)
293}
294
295fn to_shape_map(shapes: Vec<Shape>) -> HashMap<String, Vec<Shape>> {
296 let mut res = HashMap::default();
297 for s in shapes {
298 let shape = res.entry(s.id.to_owned()).or_insert_with(Vec::new);
299 shape.push(s);
300 }
301 for shapes in res.values_mut() {
303 shapes.sort_by_key(|s| s.sequence);
304 }
305
306 res
307}
308
309fn to_calendar_dates(cd: Vec<CalendarDate>) -> HashMap<String, Vec<CalendarDate>> {
310 let mut res = HashMap::default();
311 for c in cd {
312 let cal = res.entry(c.service_id.to_owned()).or_insert_with(Vec::new);
313 cal.push(c);
314 }
315 res
316}
317
318const NB_STOP_TIMES_BEFORE_SHRINK: usize = 1_000_000;
321
322fn create_trips(
323 raw_trips: Vec<RawTrip>,
324 mut raw_stop_times: Vec<RawStopTime>,
325 raw_frequencies: Vec<RawFrequency>,
326 stops: &HashMap<String, Arc<Stop>>,
327) -> Result<HashMap<String, Trip>, Error> {
328 let mut trips = to_map(raw_trips.into_iter().map(|rt| Trip {
329 id: rt.id,
330 service_id: rt.service_id,
331 route_id: rt.route_id,
332 stop_times: vec![],
333 shape_id: rt.shape_id,
334 trip_headsign: rt.trip_headsign,
335 trip_short_name: rt.trip_short_name,
336 direction_id: rt.direction_id,
337 block_id: rt.block_id,
338 wheelchair_accessible: rt.wheelchair_accessible,
339 bikes_allowed: rt.bikes_allowed,
340 frequencies: vec![],
341 }));
342
343 let mut st_idx = 0;
344 while let Some(s) = raw_stop_times.pop() {
345 st_idx += 1;
346 let trip = &mut trips
347 .get_mut(&s.trip_id)
348 .ok_or_else(|| Error::ReferenceError(s.trip_id.to_string()))?;
349 let stop = stops
350 .get(&s.stop_id)
351 .ok_or_else(|| Error::ReferenceError(s.stop_id.to_string()))?;
352 trip.stop_times.push(StopTime::from(s, Arc::clone(stop)));
353 if st_idx % NB_STOP_TIMES_BEFORE_SHRINK == 0 {
354 raw_stop_times.shrink_to_fit();
355 }
356 }
357
358 for trip in &mut trips.values_mut() {
359 trip.stop_times.sort_by_key(|st| st.stop_sequence);
360 }
361
362 for f in raw_frequencies {
363 let trip = &mut trips
364 .get_mut(&f.trip_id)
365 .ok_or_else(|| Error::ReferenceError(f.trip_id.to_string()))?;
366 trip.frequencies.push(Frequency::from(&f));
367 }
368
369 Ok(trips)
370}