h3ron_polars/algorithm/
bounding_rect.rs1use crate::{Error, IndexChunked, IndexValue};
2use geo::BoundingRect as GeoBoundingRect;
3use geo_types::{coord, CoordNum, Rect};
4use h3ron::to_geo::ToLine;
5use h3ron::{H3Cell, H3DirectedEdge, ToPolygon};
6
7pub trait BoundingRect {
8 fn bounding_rect(&self) -> Result<Option<Rect>, Error>;
9}
10
11impl BoundingRect for H3Cell {
12 fn bounding_rect(&self) -> Result<Option<Rect>, Error> {
13 Ok(self.to_polygon()?.bounding_rect())
14 }
15}
16
17impl BoundingRect for H3DirectedEdge {
18 fn bounding_rect(&self) -> Result<Option<Rect>, Error> {
19 Ok(Some(self.to_line()?.bounding_rect()))
20 }
21}
22
23impl<'a, IX: IndexValue> BoundingRect for IndexChunked<'a, IX>
24where
25 IX: BoundingRect,
26{
27 fn bounding_rect(&self) -> Result<Option<Rect>, Error> {
28 let mut rect = None;
29 for maybe_index in self.iter_indexes_validated().flatten() {
30 let new_rect = maybe_index?.bounding_rect()?;
31
32 match (rect.as_mut(), new_rect) {
33 (None, Some(r)) => rect = Some(r),
34 (Some(agg), Some(this)) => *agg = bounding_rect_merge(agg, &this),
35 _ => (),
36 }
37 }
38 Ok(rect)
39 }
40}
41
42fn bounding_rect_merge<T: CoordNum>(a: &Rect<T>, b: &Rect<T>) -> Rect<T> {
46 Rect::new(
47 coord! {
48 x: partial_min(a.min().x, b.min().x),
49 y: partial_min(a.min().y, b.min().y),
50 },
51 coord! {
52 x: partial_max(a.max().x, b.max().x),
53 y: partial_max(a.max().y, b.max().y),
54 },
55 )
56}
57
58pub fn partial_max<T: PartialOrd>(a: T, b: T) -> T {
60 if a > b {
61 a
62 } else {
63 b
64 }
65}
66
67pub fn partial_min<T: PartialOrd>(a: T, b: T) -> T {
69 if a < b {
70 a
71 } else {
72 b
73 }
74}