toolbox_rs/
bounding_box.rs1use crate::geometry::primitives::FPCoordinate;
2
3#[derive(Debug, Eq, PartialEq)]
4pub struct BoundingBox {
5 min: FPCoordinate,
6 max: FPCoordinate,
7}
8
9impl BoundingBox {
10 pub fn from_coordinates(coordinates: &[FPCoordinate]) -> BoundingBox {
11 debug_assert!(!coordinates.is_empty());
12 let mut min_coordinate = FPCoordinate::max();
13 let mut max_coordinate = FPCoordinate::min();
14
15 coordinates.iter().for_each(|coordinate| {
16 min_coordinate.lat = min_coordinate.lat.min(coordinate.lat);
17 min_coordinate.lon = min_coordinate.lon.min(coordinate.lon);
18 max_coordinate.lat = max_coordinate.lat.max(coordinate.lat);
19 max_coordinate.lon = max_coordinate.lon.max(coordinate.lon);
20 });
21
22 BoundingBox {
23 min: min_coordinate,
24 max: max_coordinate,
25 }
26 }
27
28 pub fn invalid() -> BoundingBox {
29 BoundingBox {
30 min: FPCoordinate::max(),
31 max: FPCoordinate::min(),
32 }
33 }
34
35 pub fn extend_with(&mut self, other: &BoundingBox) {
36 self.min.lat = self.min.lat.min(other.min.lat);
37 self.min.lon = self.min.lon.min(other.min.lon);
38
39 self.max.lat = self.max.lat.max(other.max.lat);
40 self.max.lon = self.max.lat.max(other.max.lon);
41 }
42
43 pub fn center(&self) -> FPCoordinate {
44 debug_assert!(self.min.lat <= self.max.lat);
45 debug_assert!(self.min.lon <= self.max.lon);
46
47 let lat_diff = self.max.lat - self.min.lat;
48 let lon_diff = self.max.lon - self.min.lon;
49
50 FPCoordinate {
51 lat: self.min.lat + lat_diff / 2,
52 lon: self.min.lon + lon_diff / 2,
53 }
54 }
55
56 pub fn is_valid(&self) -> bool {
57 self.min.lat <= self.max.lat && self.min.lon <= self.max.lon
58 }
59}
60
61impl From<&BoundingBox> for geojson::Bbox {
62 fn from(bbox: &BoundingBox) -> geojson::Bbox {
63 let result = vec![
64 bbox.min.lon as f64 / 1000000.,
65 bbox.min.lat as f64 / 1000000.,
66 bbox.max.lon as f64 / 1000000.,
67 bbox.max.lat as f64 / 1000000.,
68 ];
69 result
70 }
71}
72
73#[cfg(test)]
74pub mod tests {
75 use crate::{bounding_box::BoundingBox, geometry::primitives::FPCoordinate};
76
77 #[test]
78 fn grid() {
79 let mut coordinates: Vec<FPCoordinate> = Vec::new();
80 for i in 0..100 {
81 coordinates.push(FPCoordinate::new(i / 10, i % 10));
82 }
83
84 let expected = BoundingBox {
85 min: FPCoordinate::new(0, 0),
86 max: FPCoordinate::new(9, 9),
87 };
88 assert!(expected.is_valid());
89 let result = BoundingBox::from_coordinates(&coordinates);
90 assert_eq!(expected, result);
91 }
92
93 #[test]
94 fn center() {
95 let bbox = BoundingBox {
96 min: FPCoordinate::new_from_lat_lon(33.406637, -115.000801),
97 max: FPCoordinate::new_from_lat_lon(33.424732, -114.905286),
98 };
99 assert!(bbox.is_valid());
100 let center = bbox.center();
101 assert_eq!(center, FPCoordinate::new(33415684, -114953044));
102 }
103
104 #[test]
105 fn center_with_rounding() {
106 let bbox = BoundingBox {
107 min: FPCoordinate::new(0, 0),
108 max: FPCoordinate::new(9, 9),
109 };
110 assert!(bbox.is_valid());
111 let center = bbox.center();
112 assert_eq!(center, FPCoordinate::new(4, 4));
113 }
114
115 #[test]
116 fn center_without_rounding() {
117 let bbox = BoundingBox {
118 min: FPCoordinate::new(0, 0),
119 max: FPCoordinate::new(100, 100),
120 };
121 assert!(bbox.is_valid());
122 let center = bbox.center();
123 assert_eq!(center, FPCoordinate::new(50, 50));
124 }
125
126 #[test]
127 fn invalid() {
128 let bbox = BoundingBox::invalid();
129 assert!(bbox.min.lat > bbox.max.lat);
130 assert!(bbox.min.lon > bbox.max.lon);
131 }
132
133 #[test]
134 fn extend_with_extend_invalid() {
135 let mut c1 = BoundingBox::invalid();
136 let c2 =
137 BoundingBox::from_coordinates(&[FPCoordinate::new(11, 50), FPCoordinate::new(50, 37)]);
138 c1.extend_with(&c2);
139 assert!(c1.is_valid());
140
141 assert_eq!(c2.min, FPCoordinate::new(11, 37));
142 assert_eq!(c2.max, FPCoordinate::new(50, 50));
143 }
144
145 #[test]
146 fn geojson_conversion() {
147 let b1 =
148 BoundingBox::from_coordinates(&[FPCoordinate::new(11, 50), FPCoordinate::new(50, 37)]);
149 let g1 = geojson::Bbox::from(&b1);
150 assert_eq!(4, g1.len());
151
152 assert_eq!(b1.min.lon as f64 / 1000000., g1[0]);
153 assert_eq!(b1.min.lat as f64 / 1000000., g1[1]);
154 assert_eq!(b1.max.lon as f64 / 1000000., g1[2]);
155 assert_eq!(b1.max.lat as f64 / 1000000., g1[3]);
156 }
157}