h3ron_graph/graph/
prepared.rs

1use std::ops::Add;
2
3use geo::bounding_rect::BoundingRect;
4use geo::concave_hull::ConcaveHull;
5use geo_types::{Coord, MultiPoint, MultiPolygon, Point, Polygon, Rect};
6use num_traits::Zero;
7use rayon::prelude::*;
8use serde::{Deserialize, Serialize};
9use smallvec::{smallvec, SmallVec};
10
11use h3ron::collections::compressed::Decompressor;
12use h3ron::collections::hashbrown::hash_map::Entry;
13use h3ron::collections::{H3Treemap, HashMap};
14use h3ron::iter::H3DirectedEdgesBuilder;
15use h3ron::{H3Cell, H3DirectedEdge, HasH3Resolution, ToCoordinate};
16
17use crate::algorithm::covered_area::{cells_covered_area, CoveredArea};
18use crate::error::Error;
19use crate::graph::longedge::LongEdge;
20use crate::graph::node::NodeType;
21use crate::graph::{
22    EdgeWeight, GetCellEdges, GetCellNode, GetStats, GraphStats, H3EdgeGraph, IterateCellNodes,
23};
24
25#[derive(Serialize, Deserialize, Clone)]
26struct OwnedEdgeWeight<W> {
27    pub weight: W,
28
29    /// the longedge is a shortcut which includes many consequent edges while
30    /// allowing to visit each of then individually.
31    ///
32    /// The Box takes care of allocating the LongEdge on the heap. That reduces
33    /// the footprint of the OwnedEdgeValue - when W = f32 to nearly 10% compared to
34    /// allocating the LongEdge on the stack.
35    pub longedge: Option<Box<(LongEdge, W)>>,
36}
37
38impl<'a, W> From<&'a OwnedEdgeWeight<W>> for EdgeWeight<'a, W>
39where
40    W: Copy,
41{
42    fn from(owned_edge_value: &'a OwnedEdgeWeight<W>) -> Self {
43        EdgeWeight {
44            weight: owned_edge_value.weight,
45            longedge: owned_edge_value
46                .longedge
47                .as_ref()
48                .map(|boxed| (&boxed.0, boxed.1)),
49        }
50    }
51}
52
53type OwnedEdgeTuple<W> = (H3DirectedEdge, OwnedEdgeWeight<W>);
54
55/// A smallvec with an array length of 2 allows storing the - probably - most common
56/// number of edges on the heap
57type OwnedEdgeTupleList<W> = SmallVec<[OwnedEdgeTuple<W>; 2]>;
58
59/// A prepared graph which can be used with a few algorithms.
60///
61/// Consequent H3DirectedEdges without forks get extended by a `LongEdge` to allow
62/// skipping the individual `H3DirectedEdge` values for a more efficient graph
63/// traversal.
64///
65/// <p>
66#[doc=include_str!("../../doc/images/edges-and-longedges.svg")]
67/// </p>
68///
69/// <p>
70#[doc=include_str!("../../doc/images/prepared_h3_edge_graph.svg")]
71/// </p>
72///
73#[derive(Serialize, Deserialize, Clone)]
74pub struct PreparedH3EdgeGraph<W> {
75    outgoing_edges: HashMap<H3Cell, OwnedEdgeTupleList<W>>,
76    h3_resolution: u8,
77    graph_nodes: HashMap<H3Cell, NodeType>,
78}
79
80unsafe impl<W> Sync for PreparedH3EdgeGraph<W> where W: Sync {}
81
82impl<W> PreparedH3EdgeGraph<W> {
83    /// count the number of edges in the graph
84    ///
85    /// The returned tuple is (`num_edges`, `num_long_edges`)
86    pub fn count_edges(&self) -> (usize, usize) {
87        let mut num_edges = 0usize;
88        let mut num_long_edges = 0usize;
89
90        for (_cell, oevs) in self.outgoing_edges.iter() {
91            num_edges += oevs.len();
92            num_long_edges += oevs
93                .iter()
94                .filter(|(_, oev)| oev.longedge.is_some())
95                .count();
96        }
97        (num_edges, num_long_edges)
98    }
99}
100
101impl<W> PreparedH3EdgeGraph<W>
102where
103    W: Copy,
104{
105    /// iterate over all edges of the graph
106    pub fn iter_edges(&self) -> impl Iterator<Item = (H3DirectedEdge, EdgeWeight<W>)> {
107        self.outgoing_edges
108            .iter()
109            .flat_map(|(_, oevs)| oevs.iter().map(|(edge, oev)| (*edge, oev.into())))
110    }
111
112    /// iterate over all edges of the graph, while skipping simple `H3DirectedEdge`
113    /// which are already covered in other `LongEdge` instances of the graph.
114    ///
115    /// This function iterates the graph twice - the first time to collect
116    /// all edges which are part of long-edges.
117    pub fn iter_edges_non_overlapping(
118        &self,
119    ) -> Result<impl Iterator<Item = (H3DirectedEdge, EdgeWeight<W>)>, Error> {
120        let mut covered_edges = H3Treemap::<H3DirectedEdge>::default();
121        let mut decompressor = Decompressor::default();
122        for (_, owned_edge_values) in self.outgoing_edges.iter() {
123            for (_, owned_edge_value) in owned_edge_values.iter() {
124                if let Some(boxed_longedge) = owned_edge_value.longedge.as_ref() {
125                    for edge in decompressor
126                        .decompress_block(&boxed_longedge.0.edge_path)?
127                        .skip(1)
128                    {
129                        covered_edges.insert(edge);
130                    }
131                }
132            }
133        }
134        Ok(self.iter_edges().filter_map(move |(edge, weight)| {
135            if covered_edges.contains(&edge) {
136                None
137            } else {
138                Some((edge, weight))
139            }
140        }))
141    }
142}
143
144/// Iterator item type to build [`PreparedH3EdgeGraph`] from
145pub type FromIterItem<W> = (H3DirectedEdge, W, Option<(Vec<H3DirectedEdge>, W)>);
146
147impl<W> PreparedH3EdgeGraph<W>
148where
149    W: Copy + Send + Sync,
150{
151    pub fn try_from_iter<I>(iter: I) -> Result<Self, Error>
152    where
153        I: Iterator<Item = FromIterItem<W>>,
154    {
155        let mut h3_resolution = None;
156        let mut outgoing_edges: HashMap<H3Cell, OwnedEdgeTupleList<W>> = Default::default();
157        let mut graph_nodes: HashMap<H3Cell, NodeType> = Default::default();
158
159        for (edge, edge_weight, longedge_components) in iter {
160            let edge_cells = edge.cells()?;
161
162            // ensure no mixed h3 resolutions
163            if let Some(h3_resolution) = h3_resolution {
164                if h3_resolution != edge_cells.origin.h3_resolution() {
165                    return Err(Error::MixedH3Resolutions(
166                        h3_resolution,
167                        edge_cells.origin.h3_resolution(),
168                    ));
169                }
170            } else {
171                h3_resolution = Some(edge_cells.origin.h3_resolution());
172            }
173
174            graph_nodes
175                .entry(edge_cells.origin)
176                .and_modify(|nt| *nt += NodeType::Origin)
177                .or_insert(NodeType::Origin);
178            graph_nodes
179                .entry(edge_cells.destination)
180                .and_modify(|nt| *nt += NodeType::Destination)
181                .or_insert(NodeType::Destination);
182
183            let edge_with_weight = (
184                edge,
185                OwnedEdgeWeight {
186                    weight: edge_weight,
187                    longedge: match longedge_components {
188                        Some((le_edges, le_weight)) => {
189                            Some(Box::new((LongEdge::try_from(le_edges)?, le_weight)))
190                        }
191                        None => None,
192                    },
193                },
194            );
195            match outgoing_edges.entry(edge_cells.origin) {
196                Entry::Occupied(mut occ) => {
197                    occ.get_mut().push(edge_with_weight);
198                }
199                Entry::Vacant(vac) => {
200                    vac.insert(smallvec![edge_with_weight]);
201                }
202            }
203        }
204
205        remove_duplicated_edges(&mut outgoing_edges);
206
207        if let Some(h3_resolution) = h3_resolution {
208            Ok(Self {
209                outgoing_edges,
210                h3_resolution,
211                graph_nodes,
212            })
213        } else {
214            Err(Error::InsufficientNumberOfEdges)
215        }
216    }
217}
218
219impl<W> HasH3Resolution for PreparedH3EdgeGraph<W> {
220    fn h3_resolution(&self) -> u8 {
221        self.h3_resolution
222    }
223}
224
225impl<W> GetStats for PreparedH3EdgeGraph<W> {
226    fn get_stats(&self) -> Result<GraphStats, Error> {
227        Ok(GraphStats {
228            h3_resolution: self.h3_resolution,
229            num_nodes: self.graph_nodes.len(),
230            num_edges: self.count_edges().0,
231        })
232    }
233}
234
235impl<W> GetCellNode for PreparedH3EdgeGraph<W> {
236    fn get_cell_node(&self, cell: &H3Cell) -> Option<NodeType> {
237        self.graph_nodes.get(cell).copied()
238    }
239}
240
241impl<W: Copy> GetCellEdges for PreparedH3EdgeGraph<W> {
242    type EdgeWeightType = W;
243
244    fn get_edges_originating_from(
245        &self,
246        cell: &H3Cell,
247    ) -> Result<Vec<(H3DirectedEdge, EdgeWeight<Self::EdgeWeightType>)>, Error> {
248        let mut out_vec = Vec::with_capacity(7);
249        if let Some(edges_with_weights) = self.outgoing_edges.get(cell) {
250            out_vec.extend(
251                edges_with_weights
252                    .iter()
253                    .map(|(edge, owv)| (*edge, owv.into())),
254            );
255        }
256        Ok(out_vec)
257    }
258}
259
260const MIN_LONGEDGE_LENGTH: usize = 3;
261
262fn to_longedge_edges<W>(
263    input_graph: H3EdgeGraph<W>,
264    min_longedge_length: usize,
265) -> Result<HashMap<H3Cell, OwnedEdgeTupleList<W>>, Error>
266where
267    W: PartialOrd + PartialEq + Add<Output = W> + Copy + Send + Sync,
268{
269    if min_longedge_length < MIN_LONGEDGE_LENGTH {
270        return Err(Error::Other(format!(
271            "minimum longedge length must be >= {}",
272            MIN_LONGEDGE_LENGTH
273        )));
274    }
275
276    let outgoing_edge_vecs = input_graph
277        .edges
278        .par_iter()
279        .try_fold(
280            || (Vec::new(), H3DirectedEdgesBuilder::new()),
281            |(mut output_vec, mut edge_builder), (edge, weight)| {
282                assemble_edge_with_longedge(
283                    &input_graph.edges,
284                    min_longedge_length,
285                    edge,
286                    weight,
287                    &mut edge_builder,
288                )
289                .map(|cell_edge| {
290                    output_vec.push(cell_edge);
291                    (output_vec, edge_builder)
292                })
293            },
294        )
295        .collect::<Result<Vec<_>, _>>()?;
296
297    let mut outgoing_edges: HashMap<H3Cell, OwnedEdgeTupleList<W>> = Default::default();
298    for (outgoing_edge_vec, _) in outgoing_edge_vecs.into_iter() {
299        for (cell, edge_with_weight) in outgoing_edge_vec.into_iter() {
300            match outgoing_edges.entry(cell) {
301                Entry::Occupied(mut occ) => occ.get_mut().push(edge_with_weight),
302                Entry::Vacant(vac) => {
303                    vac.insert(smallvec![edge_with_weight]);
304                }
305            }
306        }
307    }
308
309    remove_duplicated_edges(&mut outgoing_edges);
310
311    Ok(outgoing_edges)
312}
313
314/// remove duplicates if there are any. Ignores any differences in weights
315fn remove_duplicated_edges<W>(outgoing_edges: &mut HashMap<H3Cell, OwnedEdgeTupleList<W>>)
316where
317    W: Send + Sync,
318{
319    outgoing_edges
320        .par_iter_mut()
321        .for_each(|(_cell, edges_with_weights)| {
322            edges_with_weights.sort_unstable_by_key(|eww| eww.0);
323            edges_with_weights.dedup_by(|a, b| a.0 == b.0);
324            edges_with_weights.shrink_to_fit();
325        });
326}
327
328fn assemble_edge_with_longedge<W>(
329    input_edges: &HashMap<H3DirectedEdge, W>,
330    min_longedge_length: usize,
331    edge: &H3DirectedEdge,
332    weight: &W,
333    edge_builder: &mut H3DirectedEdgesBuilder,
334) -> Result<(H3Cell, OwnedEdgeTuple<W>), Error>
335where
336    W: PartialOrd + PartialEq + Add<Output = W> + Copy,
337{
338    let mut graph_entry = OwnedEdgeWeight {
339        weight: *weight,
340        longedge: None,
341    };
342
343    let origin_cell = edge.origin_cell()?;
344
345    // number of upstream edges leading to this one
346    let num_edges_leading_to_this_one = edge_builder
347        .from_origin_cell(&origin_cell)?
348        .filter(|new_edge| new_edge != edge) // ignore the backwards edge
349        .filter(|new_edge| {
350            new_edge
351                .reversed()
352                .ok()
353                .map(|rev_edge| input_edges.get(&rev_edge).is_some())
354                .unwrap_or(false)
355        })
356        .count();
357
358    // attempt to build a longedge when this edge is either the end of a path, or a path
359    // starting after a conjunction of multiple edges
360    if num_edges_leading_to_this_one != 1 {
361        let mut edge_path = vec![*edge];
362        let mut longedge_weight = *weight;
363
364        let mut last_edge = *edge;
365        loop {
366            let last_edge_reverse = last_edge.reversed()?;
367            // follow the edges until the end or a conjunction is reached
368            let following_edges: Vec<_> = edge_builder
369                .from_origin_cell(&last_edge.destination_cell()?)?
370                .filter_map(|this_edge| {
371                    if this_edge != last_edge_reverse {
372                        input_edges.get_key_value(&this_edge)
373                    } else {
374                        None
375                    }
376                })
377                .collect();
378
379            // found no further continuing edge or conjunction
380            if following_edges.len() != 1 {
381                break;
382            }
383            let following_edge = *(following_edges[0].0);
384
385            // stop when encountering circles
386            if edge_path.contains(&following_edge) {
387                break;
388            }
389
390            edge_path.push(following_edge);
391            longedge_weight = *(following_edges[0].1) + longedge_weight;
392            // find the next following edge in the next iteration of the loop
393            last_edge = following_edge;
394        }
395
396        if edge_path.len() >= min_longedge_length {
397            graph_entry.longedge =
398                Some(Box::new((LongEdge::try_from(edge_path)?, longedge_weight)));
399        }
400    }
401    Ok((origin_cell, (*edge, graph_entry)))
402}
403
404impl<W> PreparedH3EdgeGraph<W>
405where
406    W: PartialOrd + PartialEq + Add + Copy + Ord + Zero + Send + Sync,
407{
408    pub fn from_h3edge_graph(
409        graph: H3EdgeGraph<W>,
410        min_longedge_length: usize,
411    ) -> Result<Self, Error> {
412        let h3_resolution = graph.h3_resolution();
413        let graph_nodes = graph.nodes()?;
414        let outgoing_edges = to_longedge_edges(graph, min_longedge_length)?;
415        Ok(Self {
416            graph_nodes,
417            h3_resolution,
418            outgoing_edges,
419        })
420    }
421}
422
423impl<W> TryFrom<H3EdgeGraph<W>> for PreparedH3EdgeGraph<W>
424where
425    W: PartialOrd + PartialEq + Add + Copy + Ord + Zero + Send + Sync,
426{
427    type Error = Error;
428
429    fn try_from(graph: H3EdgeGraph<W>) -> Result<Self, Self::Error> {
430        Self::from_h3edge_graph(graph, 4)
431    }
432}
433
434impl<W> From<PreparedH3EdgeGraph<W>> for H3EdgeGraph<W>
435where
436    W: PartialOrd + PartialEq + Add + Copy + Ord + Zero,
437{
438    fn from(prepared_graph: PreparedH3EdgeGraph<W>) -> Self {
439        Self {
440            edges: prepared_graph
441                .iter_edges()
442                .map(|(edge, edge_value)| (edge, edge_value.weight))
443                .collect(),
444            h3_resolution: prepared_graph.h3_resolution,
445        }
446    }
447}
448
449impl<W> CoveredArea for PreparedH3EdgeGraph<W> {
450    type Error = Error;
451
452    fn covered_area(&self, reduce_resolution_by: u8) -> Result<MultiPolygon<f64>, Self::Error> {
453        cells_covered_area(
454            self.graph_nodes.iter().map(|(cell, _)| cell),
455            self.h3_resolution(),
456            reduce_resolution_by,
457        )
458    }
459}
460
461impl<'a, W> IterateCellNodes<'a> for PreparedH3EdgeGraph<W> {
462    type CellNodeIterator = h3ron::collections::hashbrown::hash_map::Iter<'a, H3Cell, NodeType>;
463
464    fn iter_cell_nodes(&'a self) -> Self::CellNodeIterator {
465        self.graph_nodes.iter()
466    }
467}
468
469impl<W> ConcaveHull for PreparedH3EdgeGraph<W> {
470    type Scalar = f64;
471
472    /// concave hull - this implementation leaves out invalid cells
473    fn concave_hull(&self, concavity: Self::Scalar) -> Polygon<Self::Scalar> {
474        let mpoint = MultiPoint::from(
475            self.iter_cell_nodes()
476                .filter_map(|(cell, _)| cell.to_coordinate().ok().map(Point::from))
477                .collect::<Vec<_>>(),
478        );
479        mpoint.concave_hull(concavity)
480    }
481}
482
483impl<W> BoundingRect<f64> for PreparedH3EdgeGraph<W> {
484    type Output = Option<Rect<f64>>;
485
486    fn bounding_rect(&self) -> Self::Output {
487        let mut iter = self.iter_cell_nodes();
488        let mut rect = {
489            // consume until encountering the first valid cell
490            if let Some(coord) = iter.find_map(|(cell, _)| cell.to_coordinate().ok()) {
491                Point::from(coord).bounding_rect()
492            } else {
493                return None;
494            }
495        };
496
497        for (cell, _) in iter {
498            if let Ok(coord) = cell.to_coordinate() {
499                rect = Rect::new(
500                    Coord {
501                        x: if coord.x < rect.min().x {
502                            coord.x
503                        } else {
504                            rect.min().x
505                        },
506                        y: if coord.y < rect.min().y {
507                            coord.y
508                        } else {
509                            rect.min().y
510                        },
511                    },
512                    Coord {
513                        x: if coord.x > rect.max().x {
514                            coord.x
515                        } else {
516                            rect.max().x
517                        },
518                        y: if coord.y > rect.max().y {
519                            coord.y
520                        } else {
521                            rect.max().y
522                        },
523                    },
524                );
525            }
526        }
527        Some(rect)
528    }
529}
530
531#[cfg(test)]
532mod tests {
533    use std::convert::TryInto;
534
535    use geo_types::{Coord, LineString};
536
537    use crate::graph::{H3EdgeGraph, PreparedH3EdgeGraph};
538
539    fn build_line_prepared_graph() -> PreparedH3EdgeGraph<u32> {
540        let full_h3_res = 8;
541        let cells: Vec<_> = h3ron::line(
542            &LineString::from(vec![Coord::from((23.3, 12.3)), Coord::from((24.2, 12.2))]),
543            full_h3_res,
544        )
545        .unwrap()
546        .into();
547        assert!(cells.len() > 100);
548
549        let mut graph = H3EdgeGraph::new(full_h3_res);
550        for w in cells.windows(2) {
551            graph.add_edge_using_cells(w[0], w[1], 20u32).unwrap();
552        }
553        assert!(graph.num_edges() > 50);
554        let prep_graph: PreparedH3EdgeGraph<_> = graph.try_into().unwrap();
555        assert_eq!(prep_graph.count_edges().1, 1);
556        prep_graph
557    }
558
559    #[test]
560    fn test_iter_edges() {
561        let graph = build_line_prepared_graph();
562        assert!(graph.iter_edges().count() > 50);
563    }
564
565    #[test]
566    fn test_iter_non_overlapping_edges() {
567        let graph = build_line_prepared_graph();
568        assert_eq!(graph.iter_edges_non_overlapping().unwrap().count(), 1);
569    }
570}