1use std::borrow::Borrow;
2use std::ops::Add;
3
4use num_traits::Zero;
5use serde::{Deserialize, Serialize};
6
7use h3ron::collections::{ContainsIndex, H3CellMap, H3Treemap, HashMap, RandomState};
8use h3ron::{H3Cell, HasH3Resolution, Index};
9
10use crate::algorithm::path::Path;
11use crate::algorithm::shortest_path::{ShortestPathManyToMany, ShortestPathOptions};
12use crate::algorithm::NearestGraphNodes;
13use crate::error::Error;
14use crate::graph::modifiers::ExcludeCells;
15use crate::graph::{GetCellEdges, GetCellNode};
16
17#[derive(Serialize, Deserialize)]
18pub struct ExclusionDiff<T> {
19 pub before_cell_exclusion: Vec<T>,
22
23 pub after_cell_exclusion: Vec<T>,
26}
27
28pub trait DifferentialShortestPath<W>
33where
34 W: Send + Sync + Ord + Copy,
35{
36 fn differential_shortest_path<I, OPT>(
37 &self,
38 origin_cells: I,
39 destination_cells: I,
40 exclude_cells: &H3Treemap<H3Cell>,
41 options: &OPT,
42 ) -> Result<HashMap<H3Cell, ExclusionDiff<Path<W>>>, Error>
43 where
44 I: IntoIterator,
45 I::Item: Borrow<H3Cell>,
46 OPT: ShortestPathOptions + Send + Sync,
47 {
48 self.differential_shortest_path_map(
49 origin_cells,
50 destination_cells,
51 exclude_cells,
52 options,
53 Ok,
54 )
55 }
56
57 fn differential_shortest_path_map<I, OPT, PM, O>(
58 &self,
59 origin_cells: I,
60 destination_cells: I,
61 exclude_cells: &H3Treemap<H3Cell>,
62 options: &OPT,
63 path_transform_fn: PM,
64 ) -> Result<HashMap<H3Cell, ExclusionDiff<O>>, Error>
65 where
66 I: IntoIterator,
67 I::Item: Borrow<H3Cell>,
68 OPT: ShortestPathOptions + Send + Sync,
69 O: Send + Ord + Clone,
70 PM: Fn(Path<W>) -> Result<O, Error> + Send + Sync;
71}
72
73impl<G, W> DifferentialShortestPath<W> for G
74where
75 W: PartialOrd + PartialEq + Add + Copy + Send + Ord + Zero + Sync,
76 G: GetCellEdges<EdgeWeightType = W>
77 + GetCellNode
78 + HasH3Resolution
79 + NearestGraphNodes
80 + Sync
81 + ShortestPathManyToMany<W>,
82{
83 fn differential_shortest_path_map<I, OPT, PM, O>(
84 &self,
85 origin_cells: I,
86 destination_cells: I,
87 exclude_cells: &H3Treemap<H3Cell>,
88 options: &OPT,
89 path_transform_fn: PM,
90 ) -> Result<HashMap<H3Cell, ExclusionDiff<O>>, Error>
91 where
92 I: IntoIterator,
93 I::Item: Borrow<H3Cell>,
94 OPT: ShortestPathOptions + Send + Sync,
95 O: Send + Ord + Clone,
96 PM: Fn(Path<W>) -> Result<O, Error> + Send + Sync,
97 {
98 if exclude_cells.is_empty() {
99 return Err(Error::Other("exclude_cells must not be empty".to_string()));
100 };
101 let origin_cells = check_resolution_and_collect(
102 origin_cells.into_iter().filter(|c| {
103 !exclude_cells.contains_index(c.borrow())
105 }),
106 self.h3_resolution(),
107 )?;
108 let destination_cells =
109 check_resolution_and_collect(destination_cells, self.h3_resolution())?;
110
111 let mut paths_before = self.shortest_path_many_to_many_map(
112 &origin_cells,
113 &destination_cells,
114 options,
115 &path_transform_fn,
116 )?;
117
118 let exclude_wrapper = ExcludeCells::new(self, exclude_cells);
119 let mut paths_after = exclude_wrapper.shortest_path_many_to_many_map(
120 &origin_cells,
121 &destination_cells,
122 options,
123 path_transform_fn,
124 )?;
125
126 let mut out_diffs =
127 H3CellMap::with_capacity_and_hasher(paths_before.len(), RandomState::default());
128 for (cell, paths) in paths_before.drain() {
129 out_diffs.insert(
130 cell,
131 ExclusionDiff {
132 before_cell_exclusion: paths,
133 after_cell_exclusion: paths_after.remove(&cell).unwrap_or_default(),
134 },
135 );
136 }
137 Ok(out_diffs)
138 }
139}
140
141fn check_resolution_and_collect<I>(in_cells: I, h3_resolution: u8) -> Result<Vec<H3Cell>, Error>
142where
143 I: IntoIterator,
144 I::Item: Borrow<H3Cell>,
145{
146 let mut out_cells = in_cells
147 .into_iter()
148 .map(|cell| {
149 if cell.borrow().resolution() != h3_resolution {
150 Err(Error::MixedH3Resolutions(
151 h3_resolution,
152 cell.borrow().resolution(),
153 ))
154 } else {
155 Ok(*cell.borrow())
156 }
157 })
158 .collect::<Result<Vec<_>, _>>()?;
159 out_cells.sort_unstable();
160 out_cells.dedup();
161 Ok(out_cells)
162}