1use std::ops::Add;
2
3use geo_types::MultiPolygon;
4use serde::{Deserialize, Serialize};
5
6use crate::algorithm::covered_area::{cells_covered_area, CoveredArea};
7use h3ron::collections::hashbrown::hash_map::Entry;
8use h3ron::collections::{H3CellMap, H3EdgeMap, RandomState};
9use h3ron::{H3Cell, H3DirectedEdge, HasH3Resolution};
10
11use crate::error::Error;
12use crate::graph::node::NodeType;
13use crate::graph::{EdgeWeight, GetEdge, GetStats};
14
15use super::GraphStats;
16
17#[derive(Serialize, Deserialize, Clone)]
18pub struct H3EdgeGraph<W> {
19 pub edges: H3EdgeMap<W>,
20 pub h3_resolution: u8,
21}
22
23impl<W> H3EdgeGraph<W>
24where
25 W: PartialOrd + PartialEq + Add + Copy,
26{
27 pub fn new(h3_resolution: u8) -> Self {
28 Self {
29 h3_resolution,
30 edges: Default::default(),
31 }
32 }
33
34 pub fn num_nodes(&self) -> Result<usize, Error> {
38 Ok(self.nodes()?.len())
39 }
40
41 pub fn num_edges(&self) -> usize {
42 self.edges.len()
43 }
44
45 pub fn edge_weight(&self, edge: &H3DirectedEdge) -> Option<&W> {
46 self.edges.get(edge)
47 }
48
49 pub fn edges_from_cell(&self, cell: &H3Cell) -> Result<Vec<(&H3DirectedEdge, &W)>, Error> {
51 let edges = cell
52 .directed_edges()?
53 .iter()
54 .filter_map(|edge| self.edges.get_key_value(&edge))
55 .collect();
56 Ok(edges)
57 }
58
59 pub fn edges_to_cell(&self, cell: &H3Cell) -> Result<Vec<(&H3DirectedEdge, &W)>, Error> {
61 let mut edges = vec![];
62 for disk_cell in cell.grid_disk(1)?.iter() {
63 if &disk_cell == cell {
64 continue;
65 }
66 edges.extend(
67 disk_cell
68 .directed_edges()?
69 .iter()
70 .filter_map(|edge| self.edges.get_key_value(&edge)),
71 )
72 }
73 Ok(edges)
74 }
75
76 pub fn add_edge_using_cells(
77 &mut self,
78 cell_from: H3Cell,
79 cell_to: H3Cell,
80 weight: W,
81 ) -> Result<(), Error> {
82 let edge = cell_from.directed_edge_to(cell_to)?;
83 self.add_edge(edge, weight)
84 }
85
86 pub fn add_edge_using_cells_bidirectional(
87 &mut self,
88 cell_from: H3Cell,
89 cell_to: H3Cell,
90 weight: W,
91 ) -> Result<(), Error> {
92 self.add_edge_using_cells(cell_from, cell_to, weight)?;
93 self.add_edge_using_cells(cell_to, cell_from, weight)
94 }
95
96 pub fn add_edge(&mut self, edge: H3DirectedEdge, weight: W) -> Result<(), Error> {
97 match self.edges.entry(edge) {
98 Entry::Occupied(mut occ) => {
99 if &weight < occ.get() {
100 occ.insert(weight);
102 }
103 }
104 Entry::Vacant(vac) => {
105 vac.insert(weight);
106 }
107 }
108 Ok(())
109 }
110
111 pub fn try_add(&mut self, mut other: Self) -> Result<(), Error> {
112 if self.h3_resolution != other.h3_resolution {
113 return Err(Error::MixedH3Resolutions(
114 self.h3_resolution,
115 other.h3_resolution,
116 ));
117 }
118 for (edge, weight) in other.edges.drain() {
119 self.add_edge(edge, weight)?;
120 }
121 Ok(())
122 }
123
124 pub fn nodes(&self) -> Result<H3CellMap<NodeType>, Error> {
129 log::debug!(
130 "extracting nodes from the graph edges @ r={}",
131 self.h3_resolution
132 );
133 extract_nodes(&self.edges)
134 }
135
136 pub fn iter_edges(&self) -> impl Iterator<Item = (H3DirectedEdge, &W)> {
137 self.edges.iter().map(|(edge, weight)| (*edge, weight))
138 }
139}
140
141fn extract_nodes<W>(partition: &H3EdgeMap<W>) -> Result<H3CellMap<NodeType>, Error> {
142 let mut cells = H3CellMap::with_capacity_and_hasher(partition.len(), RandomState::default());
143 for edge in partition.keys() {
144 let cell_from = edge.origin_cell()?;
145 cells
146 .entry(cell_from)
147 .and_modify(|node_type| *node_type += NodeType::Origin)
148 .or_insert(NodeType::Origin);
149
150 let cell_to = edge.destination_cell()?;
151 cells
152 .entry(cell_to)
153 .and_modify(|node_type| *node_type += NodeType::Destination)
154 .or_insert(NodeType::Destination);
155 }
156 Ok(cells)
157}
158
159impl<W> HasH3Resolution for H3EdgeGraph<W> {
160 fn h3_resolution(&self) -> u8 {
161 self.h3_resolution
162 }
163}
164
165impl<W> GetStats for H3EdgeGraph<W>
166where
167 W: PartialEq + PartialOrd + Add + Copy,
168{
169 fn get_stats(&self) -> Result<GraphStats, Error> {
170 Ok(GraphStats {
171 h3_resolution: self.h3_resolution,
172 num_nodes: self.num_nodes()?,
173 num_edges: self.num_edges(),
174 })
175 }
176}
177
178impl<W> GetEdge for H3EdgeGraph<W>
179where
180 W: Copy,
181{
182 type EdgeWeightType = W;
183
184 fn get_edge(
185 &self,
186 edge: &H3DirectedEdge,
187 ) -> Result<Option<EdgeWeight<Self::EdgeWeightType>>, Error> {
188 Ok(self.edges.get(edge).map(|w| EdgeWeight::from(*w)))
189 }
190}
191
192impl<W> CoveredArea for H3EdgeGraph<W>
193where
194 W: PartialOrd + PartialEq + Add + Copy,
195{
196 type Error = Error;
197
198 fn covered_area(&self, reduce_resolution_by: u8) -> Result<MultiPolygon<f64>, Self::Error> {
199 cells_covered_area(
200 self.nodes()?.iter().map(|(cell, _)| cell),
201 self.h3_resolution(),
202 reduce_resolution_by,
203 )
204 }
205}
206
207pub fn downsample_graph<W, F>(
216 graph: &H3EdgeGraph<W>,
217 target_h3_resolution: u8,
218 weight_selector_fn: F,
219) -> Result<H3EdgeGraph<W>, Error>
220where
221 W: Sync + Send + Copy,
222 F: Fn(W, W) -> W + Sync + Send,
223{
224 if target_h3_resolution >= graph.h3_resolution {
225 return Err(Error::TooHighH3Resolution(target_h3_resolution));
226 }
227 log::debug!(
228 "downsampling graph from r={} to r={}",
229 graph.h3_resolution,
230 target_h3_resolution
231 );
232
233 let mut downsampled_edges = H3EdgeMap::with_capacity_and_hasher(
234 graph.edges.len()
235 / (graph.h3_resolution.saturating_sub(target_h3_resolution) as usize).pow(7),
236 RandomState::default(),
237 );
238
239 for (edge, weight) in graph.edges.iter() {
240 let edge_cells = edge.cells()?;
241 let cell_from = edge_cells.origin.get_parent(target_h3_resolution)?;
242 let cell_to = edge_cells.destination.get_parent(target_h3_resolution)?;
243 if cell_from != cell_to {
244 let downsampled_edge = cell_from.directed_edge_to(cell_to)?;
245
246 match downsampled_edges.entry(downsampled_edge) {
247 Entry::Occupied(mut occ) => {
248 occ.insert(weight_selector_fn(*occ.get(), *weight));
249 }
250 Entry::Vacant(vac) => {
251 vac.insert(*weight);
252 }
253 }
254 }
255 }
256 Ok(H3EdgeGraph {
257 edges: downsampled_edges,
258 h3_resolution: target_h3_resolution,
259 })
260}
261
262pub trait H3EdgeGraphBuilder<W>
263where
264 W: PartialOrd + PartialEq + Add + Copy,
265{
266 fn build_graph(self) -> Result<H3EdgeGraph<W>, Error>;
267}
268
269#[cfg(test)]
270mod tests {
271 use std::cmp::min;
272
273 use geo_types::{Coord, LineString};
274
275 use h3ron::H3Cell;
276
277 use super::{downsample_graph, H3EdgeGraph, NodeType};
278
279 #[test]
280 fn test_downsample() {
281 let full_h3_res = 8;
282 let cells: Vec<_> = h3ron::line(
283 &LineString::from(vec![Coord::from((23.3, 12.3)), Coord::from((24.2, 12.2))]),
284 full_h3_res,
285 )
286 .unwrap()
287 .into();
288 assert!(cells.len() > 100);
289
290 let mut graph = H3EdgeGraph::new(full_h3_res);
291 for w in cells.windows(2) {
292 graph.add_edge_using_cells(w[0], w[1], 20).unwrap();
293 }
294 assert!(graph.num_edges() > 50);
295 let downsampled_graph =
296 downsample_graph(&graph, full_h3_res.saturating_sub(3), min).unwrap();
297 assert!(downsampled_graph.num_edges() > 0);
298 assert!(downsampled_graph.num_edges() < 20);
299 }
300
301 #[test]
302 fn test_graph_nodes() {
303 let res = 8;
304 let origin = H3Cell::from_coordinate(Coord::from((23.3, 12.3)), res).unwrap();
305 let edges: Vec<_> = origin
306 .directed_edges()
307 .unwrap()
308 .drain()
309 .map(|edge| (edge, edge.destination_cell().unwrap()))
310 .collect();
311
312 let mut graph = H3EdgeGraph::new(res);
313 graph.add_edge(edges[0].0, 1).unwrap();
314 graph.add_edge(edges[1].0, 1).unwrap();
315
316 let edges2: Vec<_> = edges[1]
317 .1
318 .directed_edges()
319 .unwrap()
320 .drain()
321 .map(|edge| (edge, edge.destination_cell().unwrap()))
322 .collect();
323 graph.add_edge(edges2[0].0, 1).unwrap();
324
325 let nodes = graph.nodes().unwrap();
326 assert_eq!(nodes.len(), 4);
327 assert_eq!(nodes.get(&origin), Some(&NodeType::Origin));
328 assert_eq!(nodes.get(&edges[0].1), Some(&NodeType::Destination));
329 assert_eq!(
330 nodes.get(&edges[1].1),
331 Some(&NodeType::OriginAndDestination)
332 );
333 assert_eq!(nodes.get(&edges2[0].1), Some(&NodeType::Destination));
334 }
335}