hrdf_routing_engine/routing/
models.rs

1use chrono::{Duration, NaiveDateTime, TimeDelta};
2use hrdf_parser::{Coordinates, DataStorage, Journey, TransportType};
3use rustc_hash::FxHashSet;
4use serde::Serialize;
5
6#[derive(Debug, Clone, PartialEq)]
7pub struct RouteSection {
8    journey_id: Option<i32>,
9    departure_stop_id: i32,
10    arrival_stop_id: i32,
11    arrival_at: NaiveDateTime,
12    duration: Option<i16>,
13}
14
15impl RouteSection {
16    pub fn new(
17        journey_id: Option<i32>,
18        departure_stop_id: i32,
19        arrival_stop_id: i32,
20        arrival_at: NaiveDateTime,
21        duration: Option<i16>,
22    ) -> Self {
23        Self {
24            journey_id,
25            departure_stop_id,
26            arrival_stop_id,
27            arrival_at,
28            duration,
29        }
30    }
31
32    // Getters/Setters
33
34    pub fn journey_id(&self) -> Option<i32> {
35        self.journey_id
36    }
37
38    pub fn departure_stop_id(&self) -> i32 {
39        self.departure_stop_id
40    }
41
42    pub fn arrival_stop_id(&self) -> i32 {
43        self.arrival_stop_id
44    }
45
46    pub fn set_arrival_stop_id(&mut self, value: i32) {
47        self.arrival_stop_id = value;
48    }
49
50    pub fn arrival_at(&self) -> NaiveDateTime {
51        self.arrival_at
52    }
53
54    pub fn set_arrival_at(&mut self, value: NaiveDateTime) {
55        self.arrival_at = value;
56    }
57
58    pub fn duration(&self) -> Option<i16> {
59        self.duration
60    }
61
62    // Functions
63
64    // pub fn journey<'a>(&'a self, data_storage: &'a DataStorage) -> Option<&Journey> {
65    //     self.journey_id.map(|id| data_storage.journeys().find(id))?
66    pub fn journey<'a>(&'a self, data_storage: &'a DataStorage) -> Option<&'a Journey> {
67        self.journey_id.map(|id| {
68            data_storage
69                .journeys()
70                .find(id)
71                .unwrap_or_else(|| panic!("Journey {:?} not found.", id))
72        })
73    }
74}
75
76#[derive(Debug, Clone, PartialEq)]
77pub struct Route {
78    sections: Vec<RouteSection>,
79    visited_stops: FxHashSet<i32>,
80}
81
82impl Route {
83    pub fn new(sections: Vec<RouteSection>, visited_stops: FxHashSet<i32>) -> Self {
84        Self {
85            sections,
86            visited_stops,
87        }
88    }
89
90    // Getters/Setters
91
92    pub fn sections(&self) -> &Vec<RouteSection> {
93        &self.sections
94    }
95
96    pub fn visited_stops(&self) -> &FxHashSet<i32> {
97        &self.visited_stops
98    }
99
100    // Functions
101
102    pub fn last_section(&self) -> &RouteSection {
103        // A route always contains at least one section.
104        self.sections.last().unwrap()
105    }
106
107    pub fn last_section_mut(&mut self) -> &mut RouteSection {
108        // A route always contains at least one section.
109        self.sections.last_mut().unwrap()
110    }
111
112    pub fn arrival_stop_id(&self) -> i32 {
113        self.last_section().arrival_stop_id()
114    }
115
116    pub fn arrival_at(&self) -> NaiveDateTime {
117        self.last_section().arrival_at()
118    }
119
120    pub fn has_visited_any_stops(&self, stops: &FxHashSet<i32>) -> bool {
121        !self.visited_stops.is_disjoint(stops)
122    }
123
124    pub fn sections_having_journey(&self) -> Vec<&RouteSection> {
125        self.sections
126            .iter()
127            .filter(|section| section.journey_id().is_some())
128            .collect()
129    }
130
131    pub fn count_connections(&self) -> usize {
132        self.sections_having_journey().len()
133    }
134}
135
136#[derive(Copy, Clone, Eq, PartialEq)]
137pub enum RoutingAlgorithmMode {
138    SolveFromDepartureStopToArrivalStop,
139    SolveFromDepartureStopToReachableArrivalStops,
140}
141
142pub struct RoutingAlgorithmArgs {
143    mode: RoutingAlgorithmMode,
144    arrival_stop_id: Option<i32>,
145    time_limit: Option<NaiveDateTime>,
146}
147
148impl RoutingAlgorithmArgs {
149    pub fn new(
150        mode: RoutingAlgorithmMode,
151        arrival_stop_id: Option<i32>,
152        time_limit: Option<NaiveDateTime>,
153    ) -> Self {
154        Self {
155            mode,
156            arrival_stop_id,
157            time_limit,
158        }
159    }
160
161    pub fn solve_from_departure_stop_to_arrival_stop(arrival_stop_id: i32) -> Self {
162        Self::new(
163            RoutingAlgorithmMode::SolveFromDepartureStopToArrivalStop,
164            Some(arrival_stop_id),
165            None,
166        )
167    }
168
169    pub fn solve_from_departure_stop_to_reachable_arrival_stops(time_limit: NaiveDateTime) -> Self {
170        Self::new(
171            RoutingAlgorithmMode::SolveFromDepartureStopToReachableArrivalStops,
172            None,
173            Some(time_limit),
174        )
175    }
176
177    // Getters/Setters
178
179    pub fn mode(&self) -> RoutingAlgorithmMode {
180        self.mode
181    }
182
183    /// Do not call this function if you are not sure that arrival_stop_id is not None.
184    pub fn arrival_stop_id(&self) -> i32 {
185        self.arrival_stop_id.unwrap()
186    }
187
188    /// Do not call this function if you are not sure that time_limit is not None.
189    pub fn time_limit(&self) -> NaiveDateTime {
190        self.time_limit.unwrap()
191    }
192}
193
194#[derive(Debug, Clone, Serialize)]
195pub struct RouteResult {
196    departure_at: NaiveDateTime,
197    arrival_at: NaiveDateTime,
198    sections: Vec<RouteSectionResult>,
199}
200
201// impl Clone for RouteResult {
202//     fn clone(&self) -> Self {
203//         RouteResult {
204//             departure_at: self.departure_at,
205//             arrival_at: self.arrival_at,
206//             sections: self.sections.clone(),
207//         }
208//     }
209// }
210
211impl RouteResult {
212    pub fn new(
213        departure_at: NaiveDateTime,
214        arrival_at: NaiveDateTime,
215        sections: Vec<RouteSectionResult>,
216    ) -> Self {
217        Self {
218            departure_at,
219            arrival_at,
220            sections,
221        }
222    }
223
224    // Getters/Setters
225
226    pub fn departure_at(&self) -> NaiveDateTime {
227        if let Some(rs) = self.sections.first() {
228            if rs.is_walking_trip() {
229                self.departure_at - TimeDelta::minutes(rs.duration().unwrap() as i64)
230            } else {
231                self.departure_at
232            }
233        } else {
234            self.departure_at
235        }
236    }
237
238    pub fn arrival_at(&self) -> NaiveDateTime {
239        if let Some(rs) = self.sections.last() {
240            if rs.is_walking_trip() {
241                self.arrival_at + TimeDelta::minutes(rs.duration().unwrap() as i64)
242            } else {
243                self.arrival_at
244            }
245        } else {
246            self.arrival_at
247        }
248    }
249
250    pub fn sections(&self) -> &Vec<RouteSectionResult> {
251        &self.sections
252    }
253
254    pub fn number_changes(&self) -> usize {
255        if !self.sections().is_empty() {
256            self.sections()
257                .iter()
258                .filter(|s| !s.is_walking_trip())
259                .count()
260                - 1
261        } else {
262            0
263        }
264    }
265
266    pub fn total_walking_time(&self) -> Duration {
267        self.sections()
268            .iter()
269            .filter(|s| s.is_walking_trip())
270            .fold(Duration::minutes(0), |total, d| {
271                total + Duration::minutes(d.duration().unwrap_or(0i16) as i64)
272            })
273    }
274
275    pub fn total_time(&self) -> Duration {
276        self.arrival_at() - self.departure_at()
277    }
278
279    pub fn departure_stop_id(&self) -> Option<i32> {
280        self.sections().first().map(|s| s.departure_stop_id)
281    }
282
283    pub fn arrival_stop_id(&self) -> Option<i32> {
284        self.sections().last().map(|s| s.arrival_stop_id())
285    }
286
287    pub fn departure_stop_name(&self, data_storage: &DataStorage) -> Option<String> {
288        self.departure_stop_id().map(|id| {
289            String::from(
290                data_storage
291                    .stops()
292                    .find(id)
293                    .unwrap_or_else(|| panic!("stop {id} not found"))
294                    .name(),
295            )
296        })
297    }
298
299    pub fn arrival_stop_name(&self, data_storage: &DataStorage) -> Option<String> {
300        self.arrival_stop_id().map(|id| {
301            String::from(
302                data_storage
303                    .stops()
304                    .find(id)
305                    .unwrap_or_else(|| panic!("stop {id} not found"))
306                    .name(),
307            )
308        })
309    }
310}
311
312#[derive(Debug, Serialize, Copy, Clone)]
313pub struct RouteSectionResult {
314    journey_id: Option<i32>,
315    departure_stop_id: i32,
316    departure_stop_lv95_coordinates: Option<Coordinates>,
317    departure_stop_wgs84_coordinates: Option<Coordinates>,
318    arrival_stop_id: i32,
319    arrival_stop_lv95_coordinates: Option<Coordinates>,
320    arrival_stop_wgs84_coordinates: Option<Coordinates>,
321    departure_at: Option<NaiveDateTime>,
322    arrival_at: Option<NaiveDateTime>,
323    duration: Option<i16>,
324    transport: Transport,
325}
326
327impl RouteSectionResult {
328    #[allow(clippy::too_many_arguments)]
329    pub fn new(
330        journey_id: Option<i32>,
331        departure_stop_id: i32,
332        departure_stop_lv95_coordinates: Option<Coordinates>,
333        departure_stop_wgs84_coordinates: Option<Coordinates>,
334        arrival_stop_id: i32,
335        arrival_stop_lv95_coordinates: Option<Coordinates>,
336        arrival_stop_wgs84_coordinates: Option<Coordinates>,
337        departure_at: Option<NaiveDateTime>,
338        arrival_at: Option<NaiveDateTime>,
339        duration: Option<i16>,
340        transport: Transport,
341    ) -> Self {
342        Self {
343            journey_id,
344            departure_stop_id,
345            departure_stop_lv95_coordinates,
346            departure_stop_wgs84_coordinates,
347            arrival_stop_id,
348            arrival_stop_lv95_coordinates,
349            arrival_stop_wgs84_coordinates,
350            departure_at,
351            arrival_at,
352            duration,
353            transport,
354        }
355    }
356
357    // Getters/Setters
358
359    pub fn departure_stop_id(&self) -> i32 {
360        self.departure_stop_id
361    }
362
363    pub fn departure_at(&self) -> Option<NaiveDateTime> {
364        self.departure_at
365    }
366
367    pub fn arrival_stop_id(&self) -> i32 {
368        self.arrival_stop_id
369    }
370
371    pub fn arrival_stop_lv95_coordinates(&self) -> Option<Coordinates> {
372        self.arrival_stop_lv95_coordinates
373    }
374
375    // pub fn arrival_stop_wgs84_coordinates(&self) -> Option<Coordinates> {
376    //     self.arrival_stop_wgs84_coordinates
377    // }
378
379    pub fn arrival_at(&self) -> Option<NaiveDateTime> {
380        self.arrival_at
381    }
382
383    pub fn duration(&self) -> Option<i16> {
384        self.duration
385    }
386
387    // Functions
388    pub fn journey<'a>(&'a self, data_storage: &'a DataStorage) -> Option<&'a Journey> {
389        self.journey_id.map(|id| {
390            data_storage
391                .journeys()
392                .find(id)
393                .unwrap_or_else(|| panic!("Journey {:?} not found.", id))
394        })
395    }
396
397    pub fn departure_stop_name<'a>(&'a self, data_storage: &'a DataStorage) -> &'a str {
398        let id = self.departure_stop_id();
399        data_storage
400            .stops()
401            .find(id)
402            .unwrap_or_else(|| panic!("stop {id} not found"))
403            .name()
404    }
405
406    pub fn arrival_stop_name<'a>(&'a self, data_storage: &'a DataStorage) -> &'a str {
407        let id = self.arrival_stop_id();
408        data_storage
409            .stops()
410            .find(id)
411            .unwrap_or_else(|| panic!("stop {id} not found"))
412            .name()
413    }
414
415    pub fn is_walking_trip(&self) -> bool {
416        self.journey_id.is_none()
417    }
418
419    pub fn transport(&self) -> &Transport {
420        &self.transport
421    }
422}
423
424#[derive(Debug, Clone, Copy, Serialize)]
425pub enum Transport {
426    Boat,
427    Bus,
428    Chairlift,
429    Elevator,
430    Funicular,
431    GondolaLift,
432    RackRailroad,
433    Train,
434    Tramway,
435    Unknown,
436    Underground,
437    Walk,
438}
439
440impl From<&TransportType> for Transport {
441    fn from(value: &TransportType) -> Self {
442        match value.designation() {
443            "SL" => Transport::Chairlift,
444            "ASC" => Transport::Elevator,
445            "CC" => Transport::RackRailroad,
446            "BAT" | "FAE" => Transport::Boat,
447            "B" | "BN" | "BP" | "CAR" | "EV" | "EXB" | "RUB" | "TX" => Transport::Bus,
448            "FUN" => Transport::Funicular,
449            "M" => Transport::Underground,
450            "GB" | "PB" => Transport::GondolaLift,
451            "EC" | "EXT" | "IC" | "ICE" | "IR" | "NJ" | "PE" | "R" | "RB" | "RE" | "RJX" | "S"
452            | "TER" | "TGV" | "SN" => Transport::Train,
453            "T" => Transport::Tramway,
454            "UUU" => Transport::Unknown,
455            _ => panic!("Uknown transport designation: {}", value.designation()),
456        }
457    }
458}