h3ron_graph/graph/
longedge.rs

1use std::borrow::Borrow;
2
3use geo_types::LineString;
4use serde::{Deserialize, Serialize};
5
6use h3ron::collections::compressed::{IndexBlock, OwningDecompressedIter};
7use h3ron::collections::H3Treemap;
8use h3ron::to_geo::{ToLineString, ToMultiLineString};
9use h3ron::{H3Cell, H3DirectedEdge};
10
11use crate::error::Error;
12
13/// `h3dge_path` is a iterator of `H3DirectedEdge` where the edges form a continuous path
14fn h3edge_path_to_h3cell_path<I>(h3edge_path: I) -> Result<Vec<H3Cell>, Error>
15where
16    I: IntoIterator,
17    I::Item: Borrow<H3DirectedEdge>,
18{
19    let mut iter = h3edge_path.into_iter();
20    let mut out_vec = Vec::with_capacity(iter.size_hint().0 + 1);
21    if let Some(h3edge) = iter.next() {
22        out_vec.push(h3edge.borrow().origin_cell()?);
23        out_vec.push(h3edge.borrow().destination_cell()?);
24    }
25    for h3edge in iter {
26        out_vec.push(h3edge.borrow().destination_cell()?);
27    }
28    Ok(out_vec)
29}
30
31/// A `LongEdge` is an artificial construct to combine a continuous path
32/// of [`H3DirectedEdge`] values into a single edge.
33///
34/// This intended to be used to compress longer paths into a single edge to
35/// reduce the number of nodes to visit during routing.
36#[derive(Serialize, Deserialize, Clone)]
37pub struct LongEdge {
38    pub in_edge: H3DirectedEdge,
39    pub out_edge: H3DirectedEdge,
40
41    /// the path of the longedge described by multiple, successive
42    /// `H3DirectedEdge` values.
43    pub(crate) edge_path: IndexBlock<H3DirectedEdge>,
44
45    /// provides an efficient lookup to check for intersection of
46    /// the edge with `H3Cell` values.
47    cell_lookup: H3Treemap<H3Cell>,
48}
49
50impl LongEdge {
51    pub fn destination_cell(&self) -> Result<H3Cell, Error> {
52        Ok(self.out_edge.destination_cell()?)
53    }
54
55    pub fn origin_cell(&self) -> Result<H3Cell, Error> {
56        Ok(self.in_edge.origin_cell()?)
57    }
58
59    pub fn is_disjoint(&self, celltreemap: &H3Treemap<H3Cell>) -> bool {
60        self.cell_lookup.is_disjoint(celltreemap)
61    }
62
63    /// length of `self` as the number of contained h3edges
64    pub const fn h3edges_len(&self) -> usize {
65        self.edge_path.len().saturating_sub(1)
66    }
67
68    /// the path of the longedge described by multiple, successive `H3DirectedEdge` values
69    pub fn h3edge_path(&self) -> Result<OwningDecompressedIter<H3DirectedEdge>, Error> {
70        Ok(self.edge_path.iter_uncompressed()?)
71    }
72}
73
74/// construct an longedge from a vec of `H3DirectedEdge`.
75///
76/// The `H3DirectedEdge` must be sorted according to the path they describe
77impl TryFrom<Vec<H3DirectedEdge>> for LongEdge {
78    type Error = Error;
79
80    fn try_from(mut h3edges: Vec<H3DirectedEdge>) -> Result<Self, Self::Error> {
81        h3edges.dedup();
82        h3edges.shrink_to_fit();
83        if h3edges.len() >= 2 {
84            let cell_lookup: H3Treemap<_> = h3edge_path_to_h3cell_path(&h3edges)?.iter().collect();
85            Ok(Self {
86                in_edge: h3edges[0],
87                out_edge: *h3edges.last().unwrap(),
88                edge_path: h3edges.into(),
89                cell_lookup,
90            })
91        } else {
92            Err(Error::InsufficientNumberOfEdges)
93        }
94    }
95}
96
97impl ToLineString for LongEdge {
98    type Error = Error;
99
100    fn to_linestring(&self) -> Result<LineString<f64>, Self::Error> {
101        match self
102            .h3edge_path()?
103            .collect::<Vec<_>>()
104            .as_slice()
105            .to_multilinestring()
106        {
107            Ok(mut mls) => {
108                if mls.0.len() != 1 {
109                    Err(Error::SegmentedPath)
110                } else {
111                    Ok(mls.0.swap_remove(0))
112                }
113            }
114            Err(e) => Err(e.into()),
115        }
116    }
117}