h3ron_graph/algorithm/
shortest_path.rs

1//! Dijkstra shortest-path routing.
2//!
3use std::borrow::Borrow;
4use std::ops::Add;
5
6use num_traits::Zero;
7use rayon::prelude::*;
8
9use h3ron::collections::hashbrown::hash_map::Entry;
10use h3ron::collections::{H3CellMap, H3Treemap, HashMap};
11use h3ron::iter::change_resolution;
12use h3ron::{H3Cell, HasH3Resolution};
13
14use crate::algorithm::dijkstra::edge_dijkstra;
15use crate::algorithm::path::Path;
16use crate::algorithm::NearestGraphNodes;
17use crate::error::Error;
18use crate::graph::{GetCellEdges, GetCellNode};
19
20///
21/// Generic type parameters:
22/// * `W`: The weight used in the graph.
23pub trait ShortestPathOptions {
24    /// Number of cells to be allowed to be missing between
25    /// a cell and the graph while the cell is still counted as being connected
26    /// to the graph.
27    ///
28    /// Implemented using see [`NearestGraphNodes`].
29    fn max_distance_to_graph(&self) -> u32 {
30        0
31    }
32
33    /// number of destinations to reach.
34    /// Routing for the origin cell will stop when this number of destinations are reached. When not set,
35    /// routing will continue until all destinations are reached
36    fn num_destinations_to_reach(&self) -> Option<usize> {
37        None
38    }
39}
40
41/// Default implementation of a type implementing the `ShortestPathOptions`
42/// trait.
43#[derive(Default)]
44pub struct DefaultShortestPathOptions {}
45
46impl ShortestPathOptions for DefaultShortestPathOptions {}
47
48impl DefaultShortestPathOptions {
49    pub fn new() -> Self {
50        Default::default()
51    }
52}
53
54/// Implements a simple Dijkstra shortest path route finding.
55///
56/// While this is not the most efficient routing algorithm, it has the
57/// benefit of finding the nearest destinations first. So it can be used
58/// to answer questions like "which are the N nearest destinations" using a
59/// large amount of possible destinations.
60pub trait ShortestPath<W> {
61    fn shortest_path<I, OPT: ShortestPathOptions>(
62        &self,
63        origin_cell: H3Cell,
64        destination_cells: I,
65        options: &OPT,
66    ) -> Result<Vec<Path<W>>, Error>
67    where
68        I: IntoIterator,
69        I::Item: Borrow<H3Cell>;
70}
71
72/// Variant of the [`ShortestPath`] trait routing from multiple
73/// origins in parallel.
74pub trait ShortestPathManyToMany<W>
75where
76    W: Send + Sync + Ord + Copy,
77{
78    /// Returns found paths keyed by the origin cell.
79    ///
80    /// All cells must be in the h3 resolution of the graph.
81    #[inline]
82    fn shortest_path_many_to_many<I, OPT>(
83        &self,
84        origin_cells: I,
85        destination_cells: I,
86        options: &OPT,
87    ) -> Result<H3CellMap<Vec<Path<W>>>, Error>
88    where
89        I: IntoIterator,
90        I::Item: Borrow<H3Cell>,
91        OPT: ShortestPathOptions + Send + Sync,
92    {
93        self.shortest_path_many_to_many_map(origin_cells, destination_cells, options, Ok)
94    }
95
96    /// Returns found paths, transformed by the `path_map_fn` and keyed by the
97    /// origin cell.
98    ///
99    /// `path_transform_fn` can be used to directly convert the paths to a less memory intensive
100    /// type.
101    ///
102    /// All cells must be in the h3 resolution of the graph.
103    fn shortest_path_many_to_many_map<I, OPT, PM, O>(
104        &self,
105        origin_cells: I,
106        destination_cells: I,
107        options: &OPT,
108        path_transform_fn: PM,
109    ) -> Result<H3CellMap<Vec<O>>, Error>
110    where
111        I: IntoIterator,
112        I::Item: Borrow<H3Cell>,
113        OPT: ShortestPathOptions + Send + Sync,
114        PM: Fn(Path<W>) -> Result<O, Error> + Send + Sync,
115        O: Send + Ord + Clone;
116}
117
118impl<W, G> ShortestPathManyToMany<W> for G
119where
120    G: GetCellEdges<EdgeWeightType = W> + GetCellNode + HasH3Resolution + NearestGraphNodes + Sync,
121    W: PartialOrd + PartialEq + Add + Copy + Send + Ord + Zero + Sync,
122{
123    fn shortest_path_many_to_many_map<I, OPT, PM, O>(
124        &self,
125        origin_cells: I,
126        destination_cells: I,
127        options: &OPT,
128        path_transform_fn: PM,
129    ) -> Result<H3CellMap<Vec<O>>, Error>
130    where
131        I: IntoIterator,
132        I::Item: Borrow<H3Cell>,
133        OPT: ShortestPathOptions + Send + Sync,
134        PM: Fn(Path<W>) -> Result<O, Error> + Send + Sync,
135        O: Send + Ord + Clone,
136    {
137        let filtered_origin_cells = substitute_origin_cells(
138            self,
139            options.max_distance_to_graph(),
140            origin_cells,
141            true, // speeds up the creation of the treemap from the origins further below
142        )?;
143        if filtered_origin_cells.is_empty() {
144            return Ok(Default::default());
145        }
146
147        let destination_substmap = {
148            let origins_treemap: H3Treemap<H3Cell> =
149                filtered_origin_cells.iter().map(|(k, _)| *k).collect();
150
151            substitute_destination_cells(
152                self,
153                options.max_distance_to_graph(),
154                destination_cells,
155                &origins_treemap,
156            )?
157        };
158
159        if destination_substmap.0.is_empty() {
160            return Ok(Default::default());
161        }
162
163        let destination_treemap =
164            H3Treemap::from_iter_with_sort(destination_substmap.0.keys().copied());
165
166        log::debug!(
167            "shortest_path many-to-many: from {} cells to {} cells at resolution {} with max_distance_to_graph = {}",
168            filtered_origin_cells.len(),
169            destination_substmap.0.len(),
170            self.h3_resolution(),
171            options.max_distance_to_graph()
172        );
173
174        let mut cellmap: HashMap<H3Cell, Vec<O>> = Default::default();
175        for par_result in filtered_origin_cells
176            .par_iter()
177            .map(|(graph_connected_origin_cell, output_origin_cells)| {
178                shortest_path_many_worker(
179                    self,
180                    graph_connected_origin_cell,
181                    output_origin_cells.as_slice(),
182                    &destination_treemap,
183                    &destination_substmap,
184                    options,
185                    |path| {
186                        let origin_cell = path.origin_cell;
187                        path_transform_fn(path).map(|transformed| (origin_cell, transformed))
188                    },
189                )
190            })
191            .collect::<Result<Vec<_>, _>>()?
192        {
193            for (origin_cell, transformed) in par_result {
194                match cellmap.entry(origin_cell) {
195                    Entry::Occupied(mut entry) => entry.get_mut().push(transformed),
196                    Entry::Vacant(entry) => {
197                        entry.insert(vec![transformed]);
198                    }
199                }
200            }
201        }
202        Ok(cellmap)
203    }
204}
205
206impl<W, G> ShortestPath<W> for G
207where
208    G: GetCellEdges<EdgeWeightType = W> + GetCellNode + HasH3Resolution + NearestGraphNodes,
209    W: PartialOrd + PartialEq + Add + Copy + Ord + Zero,
210{
211    fn shortest_path<I, OPT>(
212        &self,
213        origin_cell: H3Cell,
214        destination_cells: I,
215        options: &OPT,
216    ) -> Result<Vec<Path<W>>, Error>
217    where
218        I: IntoIterator,
219        I::Item: Borrow<H3Cell>,
220        OPT: ShortestPathOptions,
221    {
222        let (graph_connected_origin_cell, requested_origin_cells) = {
223            let mut filtered_origin_cells = substitute_origin_cells(
224                self,
225                options.max_distance_to_graph(),
226                std::iter::once(origin_cell),
227                false, // not necessary
228            )?;
229            if filtered_origin_cells.is_empty() {
230                return Ok(Default::default());
231            } else {
232                filtered_origin_cells.remove(0)
233            }
234        };
235
236        let destination_substmap = {
237            let mut origins_treemap: H3Treemap<H3Cell> = Default::default();
238            origins_treemap.insert(graph_connected_origin_cell);
239            substitute_destination_cells(
240                self,
241                options.max_distance_to_graph(),
242                destination_cells,
243                &origins_treemap,
244            )?
245        };
246
247        if destination_substmap.0.is_empty() {
248            return Ok(Default::default());
249        }
250
251        let destination_treemap =
252            H3Treemap::from_iter_with_sort(destination_substmap.0.keys().copied());
253
254        shortest_path_many_worker(
255            self,
256            &graph_connected_origin_cell,
257            requested_origin_cells.as_slice(),
258            &destination_treemap,
259            &destination_substmap,
260            options,
261            Ok,
262        )
263    }
264}
265
266fn shortest_path_many_worker<G, W, OPT, PM, O>(
267    graph: &G,
268    origin_cell: &H3Cell,
269    requested_origin_cells: &[H3Cell],
270    destination_cells: &H3Treemap<H3Cell>,
271    destination_substmap: &SubstituteMap,
272    options: &OPT,
273    path_transform_fn: PM,
274) -> Result<Vec<O>, Error>
275where
276    G: GetCellEdges<EdgeWeightType = W>,
277    W: Add + Copy + Ord + Zero,
278    PM: Fn(Path<W>) -> Result<O, Error>,
279    O: Clone,
280    OPT: ShortestPathOptions,
281{
282    let found_paths = edge_dijkstra(
283        graph,
284        origin_cell,
285        destination_cells,
286        options.num_destinations_to_reach(),
287    )?;
288
289    let mut transformed_paths = Vec::with_capacity(found_paths.len());
290
291    for path in found_paths.into_iter() {
292        for destination_cell in destination_substmap.cells_substituted_by(&path.destination_cell) {
293            for origin_cell in requested_origin_cells {
294                let mut this_path = path.clone();
295                this_path.origin_cell = *origin_cell;
296                this_path.destination_cell = *destination_cell;
297
298                transformed_paths.push(path_transform_fn(this_path)?);
299            }
300        }
301    }
302    Ok(transformed_paths)
303}
304
305/// Maps Cells which are part of the graph - the keys - to requested
306/// cells values.
307#[derive(Default)]
308struct SubstituteMap(H3CellMap<Vec<H3Cell>>);
309
310impl SubstituteMap {
311    /// get a slices of cells the given `cell` substitutes
312    fn cells_substituted_by(&self, cell: &H3Cell) -> &[H3Cell] {
313        self.0
314            .get(cell)
315            .map_or_else(|| &[] as &[H3Cell], |sub| sub.as_slice())
316    }
317
318    /// `substituted_by` is the cell connected to the graph
319    fn add_substitute(&mut self, cell: H3Cell, substituted_by: H3Cell) {
320        match self.0.entry(substituted_by) {
321            Entry::Occupied(mut occupied) => occupied.get_mut().push(cell),
322            Entry::Vacant(vacant) => {
323                vacant.insert(vec![cell]);
324            }
325        }
326    }
327
328    fn is_empty(&self) -> bool {
329        self.0.is_empty()
330    }
331}
332
333/// finds the corresponding cells in the graph for the given
334/// destinations. When no corresponding cell is found, that destination
335/// is filtered out.
336///
337/// The cell resolution is changed to the resolution of the graph.
338///
339/// There must be at least one destination to get Result::Ok, otherwise
340/// the complete graph would be traversed.
341fn substitute_destination_cells<G, I>(
342    graph: &G,
343    max_distance_to_graph: u32,
344    destination_cells: I,
345    origins_treemap: &H3Treemap<H3Cell>,
346) -> Result<SubstituteMap, Error>
347where
348    G: GetCellNode + NearestGraphNodes + HasH3Resolution,
349    I: IntoIterator,
350    I::Item: Borrow<H3Cell>,
351{
352    // maps cells which are members of the graph to their closest found neighbors which are contained in the
353    // destinations_cells
354    let mut destination_substmap = SubstituteMap::default();
355
356    for destination in change_resolution(destination_cells, graph.h3_resolution()) {
357        let destination_cell = destination?;
358        for (graph_cell, node_type, _) in
359            graph.nearest_graph_nodes(&destination_cell, max_distance_to_graph)?
360        {
361            // destinations which are origins at the same time are always allowed as they can
362            // always be reached even when they are not a destination in the graph.
363            if node_type.is_destination() || origins_treemap.contains(&graph_cell) {
364                destination_substmap.add_substitute(destination_cell, graph_cell);
365                break;
366            }
367        }
368    }
369
370    if destination_substmap.is_empty() {
371        return Err(Error::DestinationsNotInGraph);
372    }
373    Ok(destination_substmap)
374}
375
376/// Locates the corresponding cells for the given ones in the graph.
377///
378/// The returned hashmap maps cells, which are members of the graph to all
379/// surrounding cells which are not directly part of the graph. This depends
380/// on the gap-bridging in the options. With no gap bridging, cells are only mapped
381/// to themselves.
382///
383/// The cell resolution is changed to the resolution of the graph.
384fn substitute_origin_cells<G, I>(
385    graph: &G,
386    max_distance_to_graph: u32,
387    origin_cells: I,
388    return_sorted: bool,
389) -> Result<Vec<(H3Cell, Vec<H3Cell>)>, Error>
390where
391    G: GetCellNode + NearestGraphNodes + HasH3Resolution,
392    I: IntoIterator,
393    I::Item: Borrow<H3Cell>,
394{
395    // maps cells which are members of the graph to their closest found neighbors which are contained in the
396    // origin_cells.
397    let mut origin_substmap = SubstituteMap::default();
398
399    for cell in change_resolution(origin_cells, graph.h3_resolution()) {
400        let cell = cell?;
401        for (graph_cell, node_type, _) in graph.nearest_graph_nodes(&cell, max_distance_to_graph)? {
402            if node_type.is_origin() {
403                origin_substmap.add_substitute(cell, graph_cell);
404                break;
405            }
406        }
407    }
408
409    let mut out_vec: Vec<_> = origin_substmap.0.drain().collect();
410    if return_sorted {
411        out_vec.sort_unstable_by_key(|v| v.0);
412    }
413    Ok(out_vec)
414}
415
416#[cfg(test)]
417mod tests {
418    use std::convert::TryInto;
419
420    use geo_types::Coord;
421
422    use h3ron::H3Cell;
423
424    use crate::algorithm::shortest_path::{DefaultShortestPathOptions, ShortestPathManyToMany};
425    use crate::graph::{H3EdgeGraph, PreparedH3EdgeGraph};
426
427    #[test]
428    fn test_shortest_path_same_origin_and_destination() {
429        let res = 8;
430        let origin = H3Cell::from_coordinate(Coord::from((23.3, 12.3)), res).unwrap();
431        let edge = origin.directed_edges().unwrap().first().unwrap();
432        let destination = edge.destination_cell().unwrap();
433
434        // build a micro-graph
435        let prepared_graph: PreparedH3EdgeGraph<_> = {
436            let mut graph = H3EdgeGraph::new(res);
437            graph.add_edge(edge, 5_u32).unwrap();
438            graph.try_into().unwrap()
439        };
440
441        let paths = prepared_graph
442            .shortest_path_many_to_many(
443                &vec![origin],
444                // find the path to the origin cell itself, and to the neighbor
445                &vec![origin, destination],
446                &DefaultShortestPathOptions::default(),
447            )
448            .unwrap();
449
450        assert_eq!(paths.len(), 1);
451        let path_vec = paths.get(&origin).unwrap();
452        assert_eq!(path_vec.len(), 2);
453        for path in path_vec.iter() {
454            if path.destination_cell == origin {
455                assert!(path.is_empty());
456                assert_eq!(path.cost, 0);
457            } else if path.destination_cell == destination {
458                assert!(!path.is_empty());
459                assert_eq!(path.cost, 5);
460            } else {
461                unreachable!()
462            }
463        }
464    }
465}