1use crate::collections::H3CellMap;
2use std::os::raw::c_int;
3
4use geo::algorithm::euclidean_distance::EuclideanDistance;
5use geo_types::{Coord, Line, LineString, MultiLineString, Point, Polygon};
6
7use h3ron_h3_sys::H3Index;
8
9use crate::algorithm::smoothen_h3_linked_polygon;
10use crate::collections::indexvec::IndexVec;
11use crate::collections::CompactedCellVec;
12use crate::{Error, H3Cell};
13
14pub trait ToPolygon {
15 type Error;
16
17 fn to_polygon(&self) -> Result<Polygon<f64>, Self::Error>;
18}
19
20pub trait ToCoordinate {
21 type Error;
22
23 fn to_coordinate(&self) -> Result<Coord<f64>, Self::Error>;
24}
25
26pub trait ToLine {
27 type Error;
28
29 fn to_line(&self) -> Result<Line<f64>, Self::Error>;
30}
31
32pub trait ToLineString {
33 type Error;
34
35 fn to_linestring(&self) -> Result<LineString<f64>, Self::Error>;
36}
37
38pub trait ToMultiLineString {
39 type Error;
40
41 fn to_multilinestring(&self) -> Result<MultiLineString<f64>, Self::Error>;
42}
43
44pub trait ToLinkedPolygons {
46 type Error;
47
48 fn to_linked_polygons(&self, smoothen: bool) -> Result<Vec<Polygon<f64>>, Self::Error>;
49}
50
51impl ToLinkedPolygons for Vec<H3Cell> {
52 type Error = Error;
53
54 fn to_linked_polygons(&self, smoothen: bool) -> Result<Vec<Polygon<f64>>, Self::Error> {
55 let mut cells = self.clone();
56 cells.sort_unstable();
57 cells.dedup();
58 to_linked_polygons(&cells, smoothen)
59 }
60}
61
62impl ToLinkedPolygons for IndexVec<H3Cell> {
63 type Error = Error;
64
65 fn to_linked_polygons(&self, smoothen: bool) -> Result<Vec<Polygon<f64>>, Self::Error> {
66 let mut cells = self.iter().collect::<Vec<_>>();
67 cells.sort_unstable();
68 cells.dedup();
69 to_linked_polygons(&cells, smoothen)
70 }
71}
72
73impl ToLinkedPolygons for CompactedCellVec {
74 type Error = Error;
75
76 fn to_linked_polygons(&self, smoothen: bool) -> Result<Vec<Polygon<f64>>, Self::Error> {
77 match self.finest_resolution_contained() {
78 Some(resolution) => {
79 let mut cells: Vec<_> = self
80 .iter_uncompacted_cells(resolution)
81 .collect::<Result<Vec<_>, _>>()?;
82 cells.sort_unstable();
83 cells.dedup();
84 to_linked_polygons(&cells, smoothen)
85 }
86 None => Ok(Vec::new()),
87 }
88 }
89}
90
91pub trait ToAlignedLinkedPolygons {
101 type Error;
102
103 fn to_aligned_linked_polygons(
104 &self,
105 align_to_h3_resolution: u8,
106 smoothen: bool,
107 ) -> Result<Vec<Polygon<f64>>, Self::Error>;
108}
109
110impl ToAlignedLinkedPolygons for Vec<H3Cell> {
111 type Error = Error;
112
113 fn to_aligned_linked_polygons(
114 &self,
115 align_to_h3_resolution: u8,
116 smoothen: bool,
117 ) -> Result<Vec<Polygon<f64>>, Self::Error> {
118 let mut cells_grouped = H3CellMap::default();
119 for cell in self.iter() {
120 let parent_cell = cell.get_parent(align_to_h3_resolution)?;
121 cells_grouped
122 .entry(parent_cell)
123 .or_insert_with(Self::new)
124 .push(*cell);
125 }
126
127 let mut polygons = Vec::new();
128 for (parent_cell, cells) in cells_grouped.drain() {
129 if smoothen {
130 let parent_poly_vertices: Vec<_> = parent_cell
135 .to_polygon()?
136 .exterior()
137 .0
138 .iter()
139 .map(|c| Point::from(*c))
140 .collect();
141
142 let edge_length = {
144 let ring = cells[0].to_polygon()?;
145 let p1 = Point::from(ring.exterior().0[0]);
146 let p2 = Point::from(ring.exterior().0[1]);
147 p1.euclidean_distance(&p2)
148 };
149
150 for poly in to_linked_polygons(&cells, true)?.drain(..) {
151 let points_new: Vec<_> = poly
152 .exterior()
153 .0
154 .iter()
155 .map(|c| {
156 let p = Point::from(*c);
157 parent_poly_vertices
158 .iter()
159 .find(|pv| p.euclidean_distance(*pv) < edge_length)
160 .map_or_else(|| *c, |pv| pv.0)
161 })
162 .collect();
163 polygons.push(Polygon::new(
164 LineString::from(points_new),
165 poly.interiors().to_vec(),
166 ));
167 }
168 } else {
169 polygons.append(&mut to_linked_polygons(&cells, false)?);
170 }
171 }
172 Ok(polygons)
173 }
174}
175
176pub fn to_linked_polygons(cells: &[H3Cell], smoothen: bool) -> Result<Vec<Polygon<f64>>, Error> {
183 if cells.is_empty() {
184 return Ok(vec![]);
185 }
186 unsafe {
187 let mut lgp = h3ron_h3_sys::LinkedGeoPolygon {
188 first: std::ptr::null_mut(),
189 last: std::ptr::null_mut(),
190 next: std::ptr::null_mut(),
191 };
192 let h3index_slice =
194 std::slice::from_raw_parts(cells.as_ptr().cast::<H3Index>(), cells.len());
195 Error::check_returncode(h3ron_h3_sys::cellsToLinkedMultiPolygon(
196 h3index_slice.as_ptr(),
197 h3index_slice.len() as c_int,
198 &mut lgp,
199 ))?;
200
201 let mut polygons = vec![];
202 let mut cur_linked_geo_polygon = Some(&lgp);
203 while let Some(poly) = cur_linked_geo_polygon.as_ref() {
204 let mut exterior = None;
205 let mut interiors = vec![];
206 let mut linked_loop_i = 0;
207 let mut cur_linked_geo_loop = poly.first.as_ref();
208 while let Some(linked_loop) = cur_linked_geo_loop {
209 let mut coordinates = vec![];
210 let mut cur_linked_geo_coord = linked_loop.first.as_ref();
211 while let Some(linked_coord) = cur_linked_geo_coord {
212 coordinates.push((
213 linked_coord.vertex.lng.to_degrees(),
214 linked_coord.vertex.lat.to_degrees(),
215 ));
216 cur_linked_geo_coord = linked_coord.next.as_ref();
217 }
218
219 if coordinates.len() >= 3 {
220 let linestring = LineString::from(coordinates);
221 if linked_loop_i == 0 {
222 exterior = Some(linestring);
223 } else {
224 interiors.push(linestring);
225 }
226 }
227
228 linked_loop_i += 1;
229 cur_linked_geo_loop = linked_loop.next.as_ref();
230 }
231 if let Some(ext) = exterior {
232 let poly = Polygon::new(ext, interiors);
233 if smoothen {
234 polygons.push(smoothen_h3_linked_polygon(&poly));
235 } else {
236 polygons.push(poly);
237 }
238 }
239 cur_linked_geo_polygon = poly.next.as_ref();
240 }
241 h3ron_h3_sys::destroyLinkedMultiPolygon(&mut lgp);
242 Ok(polygons)
243 }
244}
245
246#[cfg(test)]
247mod tests {
248 use geo_types::Coord;
249
250 use crate::{H3Cell, ToLinkedPolygons};
251
252 #[test]
253 fn donut_linked_polygon() {
254 let ring = H3Cell::from_coordinate(Coord::from((23.3, 12.3)), 6)
255 .unwrap()
256 .grid_ring_unsafe(1)
257 .unwrap();
258 let polygons = ring.to_linked_polygons(false).unwrap();
259 assert_eq!(polygons.len(), 1);
260 assert_eq!(polygons[0].exterior().0.len(), 19);
261 assert_eq!(polygons[0].interiors().len(), 1);
262 assert_eq!(polygons[0].interiors()[0].0.len(), 7);
263 }
264}