hrdf_routing_engine/routing/
models.rs1use 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 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 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 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 pub fn last_section(&self) -> &RouteSection {
103 self.sections.last().unwrap()
105 }
106
107 pub fn last_section_mut(&mut self) -> &mut RouteSection {
108 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 pub fn mode(&self) -> RoutingAlgorithmMode {
180 self.mode
181 }
182
183 pub fn arrival_stop_id(&self) -> i32 {
185 self.arrival_stop_id.unwrap()
186 }
187
188 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
201impl 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 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 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_at(&self) -> Option<NaiveDateTime> {
380 self.arrival_at
381 }
382
383 pub fn duration(&self) -> Option<i16> {
384 self.duration
385 }
386
387 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}