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