1use std::cmp::Ordering;
14
15use geo::CoordNum;
16use petgraph::{algo::astar, visit::EdgeRef};
17
18use crate::{
19 core::{Accessability, NodeId, TransitEdge},
20 graphs::TransitNetwork,
21};
22
23pub mod edge_length;
24
25pub trait ShortestPath<R, T> {
33 fn find_shortest_path(&self, from: NodeId, to: NodeId) -> Option<Vec<NodeId>>;
46}
47
48pub trait ShortestPathWithAccessability<R, T: CoordNum> {
56 fn calc_edge_cost<F>(
70 &self,
71 from: NodeId,
72 to: NodeId,
73 accessability: &Accessability,
74 edge_cost: &mut F,
75 ) -> f64
76 where
77 F: FnMut(TransitEdge<T>) -> f64;
78
79 fn find_shortest_path_with_accessability<F>(
93 &self,
94 from: NodeId,
95 to: NodeId,
96 accessability: Accessability,
97 edge_cost: F,
98 ) -> Option<(f64, Vec<NodeId>)>
99 where
100 F: FnMut(TransitEdge<T>) -> f64;
101}
102
103impl<R: Copy, T: CoordNum> ShortestPath<R, T> for TransitNetwork<R, T> {
104 fn find_shortest_path(&self, from: NodeId, to: NodeId) -> Option<Vec<NodeId>> {
105 self.find_shortest_path_with_accessability(
106 from,
107 to,
108 Accessability::UnreachableNodes(vec![]),
109 |_edge| 1.0,
110 )
111 .map(|(_, path)| path)
112 }
113}
114
115impl<R: Copy, T: CoordNum> ShortestPathWithAccessability<R, T> for TransitNetwork<R, T> {
116 fn calc_edge_cost<F>(
118 &self,
119 from: NodeId,
120 to: NodeId,
121 accessability: &Accessability,
122 edge_cost: &mut F,
123 ) -> f64
124 where
125 F: FnMut(TransitEdge<T>) -> f64,
126 {
127 if let Accessability::UnreachableNodes(reachable_nodes) = accessability
128 && reachable_nodes.contains(&to) {
129 return f64::INFINITY;
130 }
131 let from = self.physical_graph.id_to_index(from);
132 let to = self.physical_graph.id_to_index(to);
133 if let (Some(from), Some(to)) = (from, to) {
134 let edge = self.physical_graph.graph.find_edge(*from, *to).unwrap();
135 edge_cost(self.physical_graph.graph[edge].clone())
136 } else {
137 f64::INFINITY
138 }
139 }
140
141 fn find_shortest_path_with_accessability<F>(
142 &self,
143 from: NodeId,
144 to: NodeId,
145 accessability: Accessability,
146 mut edge_cost: F,
147 ) -> Option<(f64, Vec<NodeId>)>
148 where
149 F: FnMut(TransitEdge<T>) -> f64,
150 {
151 let start = self.topology_graph.id_to_index(from);
152 let goal = self.topology_graph.id_to_index(to);
153 if let (Some(start), Some(goal)) = (start, goal) {
154 let path1 = astar(
155 &self.topology_graph.graph,
156 start.0,
157 |finish| finish == goal.0 || finish == goal.1,
158 |edge| {
159 self.calc_edge_cost(
160 *self.topology_graph.index_to_id(edge.source()).unwrap(),
161 *self.topology_graph.index_to_id(edge.target()).unwrap(),
162 &accessability,
163 &mut edge_cost,
164 )
165 },
166 |_| 0.,
167 );
168 let path2 = astar(
169 &self.topology_graph.graph,
170 start.1,
171 |finish| finish == goal.0 || finish == goal.1,
172 |edge| {
173 self.calc_edge_cost(
174 *self.topology_graph.index_to_id(edge.source()).unwrap(),
175 *self.topology_graph.index_to_id(edge.target()).unwrap(),
176 &accessability,
177 &mut edge_cost,
178 )
179 },
180 |_| 1.,
181 );
182
183 let best = path1
184 .into_iter()
185 .chain(path2)
186 .min_by(|(cost1, _), (cost2, _)| {
187 cost1.partial_cmp(cost2).unwrap_or(Ordering::Equal)
188 });
189
190 best.map(|(cost, path)| {
191 let path = path
192 .into_iter()
193 .map(|index| *self.topology_graph.index_to_id(index).unwrap())
194 .collect();
195
196 (cost, path)
197 })
198 } else {
199 None
200 }
201 }
202}
203
204#[cfg(test)]
205mod tests {
206 use crate::{core::TransitNode, operations::TransitNetworkModifier};
207
208 use super::*;
209 use geo::{coord, point, LineString};
210
211 #[test]
212 fn test_shortest_path() {
213 let mut network = TransitNetwork::new();
215
216 let node0 = TransitNode {
218 id: 0,
219 location: point!(x: 0.0, y: 0.0),
220 };
221
222 let node1 = TransitNode {
223 id: 1,
224 location: point!(x: 1.0, y: 1.0),
225 };
226
227 let node2 = TransitNode {
228 id: 2,
229 location: point!(x: 2.0, y: 2.0),
230 };
231
232 let node3 = TransitNode {
233 id: 3,
234 location: point!(x: 3.0, y: 3.0),
235 };
236
237 let node4 = TransitNode {
238 id: 4,
239 location: point!(x: 4.0, y: 4.0),
240 };
241
242 network.add_node(node0);
244 network.add_node(node1);
245 network.add_node(node2);
246 network.add_node(node3);
247 network.add_node(node4);
248
249 let edge01 = TransitEdge {
251 id: 1,
252 source: 0,
253 target: 1,
254 length: 1.0,
255 path: LineString(vec![coord! {x: 0.0, y: 0.0}, coord! {x: 1.0, y: 1.0}]),
256 };
257 let edge12 = TransitEdge {
258 id: 12,
259 source: 1,
260 target: 2,
261 length: 1.0,
262 path: LineString(vec![coord! {x: 1.0, y: 1.0}, coord! {x: 2.0, y: 2.0}]),
263 };
264 let edge23 = TransitEdge {
265 id: 23,
266 source: 2,
267 target: 3,
268 length: 1.0,
269 path: LineString(vec![coord! {x: 2.0, y: 2.0}, coord! {x: 3.0, y: 3.0}]),
270 };
271 let edge34 = TransitEdge {
272 id: 34,
273 source: 3,
274 target: 4,
275 length: 1.0,
276 path: LineString(vec![coord! {x: 3.0, y: 3.0}, coord! {x: 4.0, y: 4.0}]),
277 };
278
279 network.add_edge(edge01);
281 network.add_edge_with_accessibility(edge12, Accessability::ReachableNodes(vec![0]));
282 network.add_edge_with_accessibility(edge23, Accessability::ReachableNodes(vec![1]));
283 network.add_edge_with_accessibility(edge34, Accessability::ReachableNodes(vec![2]));
284
285 let result = network.find_shortest_path(0, 4);
286 assert_eq!(result, Some(vec![0, 1, 2, 3, 4])); }
288
289 #[test]
290 fn test_shortest_path_with_accessability() {
291 let mut network = TransitNetwork::new();
294
295 let node0 = TransitNode {
297 id: 0,
298 location: point!(x: 0.0, y: 0.0),
299 };
300
301 let node1 = TransitNode {
302 id: 1,
303 location: point!(x: 1.0, y: 1.0),
304 };
305
306 let node2 = TransitNode {
307 id: 2,
308 location: point!(x: 2.0, y: 2.0),
309 };
310
311 let node3 = TransitNode {
312 id: 3,
313 location: point!(x: 3.0, y: 3.0),
314 };
315
316 let node4 = TransitNode {
317 id: 4,
318 location: point!(x: 4.0, y: 4.0),
319 };
320
321 let node5 = TransitNode {
322 id: 5,
323 location: point!(x: 5.0, y: 5.0),
324 };
325
326 network.add_node(node0);
328 network.add_node(node1);
329 network.add_node(node2);
330 network.add_node(node3);
331 network.add_node(node4);
332 network.add_node(node5);
333
334 let edge01 = TransitEdge {
336 id: 1,
337 source: 0,
338 target: 1,
339 length: 1.0,
340 path: LineString(vec![coord! {x: 0.0, y: 0.0}, coord! {x: 1.0, y: 1.0}]),
341 };
342 let edge02 = TransitEdge {
343 id: 2,
344 source: 0,
345 target: 2,
346 length: 1.0,
347 path: LineString(vec![coord! {x: 1.0, y: 1.0}, coord! {x: 2.0, y: 2.0}]),
348 };
349 let edge13 = TransitEdge {
350 id: 13,
351 source: 1,
352 target: 3,
353 length: 1.0,
354 path: LineString(vec![coord! {x: 1.0, y: 1.0}, coord! {x: 3.0, y: 3.0}]),
355 };
356 let edge14 = TransitEdge {
357 id: 14,
358 source: 1,
359 target: 4,
360 length: 1.0,
361 path: LineString(vec![coord! {x: 1.0, y: 1.0}, coord! {x: 4.0, y: 4.0}]),
362 };
363 let edge25 = TransitEdge {
364 id: 25,
365 source: 2,
366 target: 5,
367 length: 1.0,
368 path: LineString(vec![coord! {x: 1.0, y: 1.0}, coord! {x: 4.0, y: 4.0}]),
369 };
370 let edge45 = TransitEdge {
371 id: 45,
372 source: 5,
373 target: 4,
374 length: 1.0,
375 path: LineString(vec![coord! {x: 1.0, y: 1.0}, coord! {x: 4.0, y: 4.0}]),
376 };
377
378 network.add_edge(edge01);
380 network.add_edge_with_accessibility(edge14, Accessability::ReachableNodes(vec![0]));
381 network.add_edge_with_accessibility(edge02, Accessability::ReachableNodes(vec![0]));
382 network.add_edge_with_accessibility(edge13, Accessability::ReachableNodes(vec![0]));
383 network.add_edge_with_accessibility(edge25, Accessability::ReachableNodes(vec![1]));
384 network.add_edge_with_accessibility(edge45, Accessability::ReachableNodes(vec![2, 1]));
385
386 let edge_cost = |_edge: TransitEdge<f32>| 1.0;
388
389 let result = network.find_shortest_path_with_accessability(
391 0, 4, Accessability::UnreachableNodes(vec![]), edge_cost,
395 );
396 assert_eq!(result, Some((2.0, vec![0, 1, 4]))); let result = network.find_shortest_path_with_accessability(
400 3, 4, Accessability::UnreachableNodes(vec![]),
403 edge_cost,
404 );
405 assert_eq!(result, None); let result = network.find_shortest_path_with_accessability(
408 0, 4, Accessability::UnreachableNodes(vec![1]), edge_cost,
412 );
413 assert_eq!(result, Some((3.0, vec![0, 2, 5, 4]))); let not_exist = 99;
415 assert!(network
416 .find_shortest_path_with_accessability(
417 0,
418 not_exist,
419 Accessability::UnreachableNodes(vec![]),
420 edge_cost
421 )
422 .is_none());
423 }
424}