1use crate::utils::{partial_max, partial_min};
2use crate::{coord, geometry::*, CoordNum, GeometryCow};
3use geo_types::private_utils::{get_bounding_rect, line_string_bounding_rect};
4
5pub trait BoundingRect<T: CoordNum> {
7 type Output: Into<Option<Rect<T>>>;
8
9 fn bounding_rect(&self) -> Self::Output;
31}
32
33impl<T> BoundingRect<T> for Coord<T>
34where
35 T: CoordNum,
36{
37 type Output = Rect<T>;
38
39 fn bounding_rect(&self) -> Self::Output {
42 Rect::new(*self, *self)
43 }
44}
45
46impl<T> BoundingRect<T> for Point<T>
47where
48 T: CoordNum,
49{
50 type Output = Rect<T>;
51
52 fn bounding_rect(&self) -> Self::Output {
55 Rect::new(self.0, self.0)
56 }
57}
58
59impl<T> BoundingRect<T> for MultiPoint<T>
60where
61 T: CoordNum,
62{
63 type Output = Option<Rect<T>>;
64
65 fn bounding_rect(&self) -> Self::Output {
68 get_bounding_rect(self.0.iter().map(|p| p.0))
69 }
70}
71
72impl<T> BoundingRect<T> for Line<T>
73where
74 T: CoordNum,
75{
76 type Output = Rect<T>;
77
78 fn bounding_rect(&self) -> Self::Output {
79 Rect::new(self.start, self.end)
80 }
81}
82
83impl<T> BoundingRect<T> for LineString<T>
84where
85 T: CoordNum,
86{
87 type Output = Option<Rect<T>>;
88
89 fn bounding_rect(&self) -> Self::Output {
92 line_string_bounding_rect(self)
93 }
94}
95
96impl<T> BoundingRect<T> for MultiLineString<T>
97where
98 T: CoordNum,
99{
100 type Output = Option<Rect<T>>;
101
102 fn bounding_rect(&self) -> Self::Output {
105 get_bounding_rect(self.iter().flat_map(|line| line.0.iter().cloned()))
106 }
107}
108
109impl<T> BoundingRect<T> for Polygon<T>
110where
111 T: CoordNum,
112{
113 type Output = Option<Rect<T>>;
114
115 fn bounding_rect(&self) -> Self::Output {
118 let line = self.exterior();
119 get_bounding_rect(line.0.iter().cloned())
120 }
121}
122
123impl<T> BoundingRect<T> for MultiPolygon<T>
124where
125 T: CoordNum,
126{
127 type Output = Option<Rect<T>>;
128
129 fn bounding_rect(&self) -> Self::Output {
132 get_bounding_rect(
133 self.iter()
134 .flat_map(|poly| poly.exterior().0.iter().cloned()),
135 )
136 }
137}
138
139impl<T> BoundingRect<T> for Triangle<T>
140where
141 T: CoordNum,
142{
143 type Output = Rect<T>;
144
145 fn bounding_rect(&self) -> Self::Output {
146 get_bounding_rect(self.to_array().iter().cloned()).unwrap()
147 }
148}
149
150impl<T> BoundingRect<T> for Rect<T>
151where
152 T: CoordNum,
153{
154 type Output = Rect<T>;
155
156 fn bounding_rect(&self) -> Self::Output {
157 *self
158 }
159}
160
161impl<T> BoundingRect<T> for Geometry<T>
162where
163 T: CoordNum,
164{
165 type Output = Option<Rect<T>>;
166
167 crate::geometry_delegate_impl! {
168 fn bounding_rect(&self) -> Self::Output;
169 }
170}
171
172impl<T> BoundingRect<T> for GeometryCow<'_, T>
173where
174 T: CoordNum,
175{
176 type Output = Option<Rect<T>>;
177
178 crate::geometry_cow_delegate_impl! {
179 fn bounding_rect(&self) -> Self::Output;
180 }
181}
182
183impl<T> BoundingRect<T> for GeometryCollection<T>
184where
185 T: CoordNum,
186{
187 type Output = Option<Rect<T>>;
188
189 fn bounding_rect(&self) -> Self::Output {
190 self.iter().fold(None, |acc, next| {
191 let next_bounding_rect = next.bounding_rect();
192
193 match (acc, next_bounding_rect) {
194 (None, None) => None,
195 (Some(r), None) | (None, Some(r)) => Some(r),
196 (Some(r1), Some(r2)) => Some(bounding_rect_merge(r1, r2)),
197 }
198 })
199 }
200}
201
202fn bounding_rect_merge<T: CoordNum>(a: Rect<T>, b: Rect<T>) -> Rect<T> {
204 Rect::new(
205 coord! {
206 x: partial_min(a.min().x, b.min().x),
207 y: partial_min(a.min().y, b.min().y),
208 },
209 coord! {
210 x: partial_max(a.max().x, b.max().x),
211 y: partial_max(a.max().y, b.max().y),
212 },
213 )
214}
215
216#[cfg(test)]
217mod test {
218 use super::bounding_rect_merge;
219 use crate::line_string;
220 use crate::BoundingRect;
221 use crate::{
222 coord, point, polygon, Geometry, GeometryCollection, Line, LineString, MultiLineString,
223 MultiPoint, MultiPolygon, Polygon, Rect,
224 };
225
226 #[test]
227 fn empty_linestring_test() {
228 let linestring: LineString<f32> = line_string![];
229 let bounding_rect = linestring.bounding_rect();
230 assert!(bounding_rect.is_none());
231 }
232 #[test]
233 fn linestring_one_point_test() {
234 let linestring = line_string![(x: 40.02f64, y: 116.34)];
235 let bounding_rect = Rect::new(
236 coord! {
237 x: 40.02f64,
238 y: 116.34,
239 },
240 coord! {
241 x: 40.02,
242 y: 116.34,
243 },
244 );
245 assert_eq!(bounding_rect, linestring.bounding_rect().unwrap());
246 }
247 #[test]
248 fn linestring_test() {
249 let linestring = line_string![
250 (x: 1., y: 1.),
251 (x: 2., y: -2.),
252 (x: -3., y: -3.),
253 (x: -4., y: 4.)
254 ];
255 let bounding_rect = Rect::new(coord! { x: -4., y: -3. }, coord! { x: 2., y: 4. });
256 assert_eq!(bounding_rect, linestring.bounding_rect().unwrap());
257 }
258 #[test]
259 fn multilinestring_test() {
260 let multiline = MultiLineString::new(vec![
261 line_string![(x: 1., y: 1.), (x: -40., y: 1.)],
262 line_string![(x: 1., y: 1.), (x: 50., y: 1.)],
263 line_string![(x: 1., y: 1.), (x: 1., y: -60.)],
264 line_string![(x: 1., y: 1.), (x: 1., y: 70.)],
265 ]);
266 let bounding_rect = Rect::new(coord! { x: -40., y: -60. }, coord! { x: 50., y: 70. });
267 assert_eq!(bounding_rect, multiline.bounding_rect().unwrap());
268 }
269 #[test]
270 fn multipoint_test() {
271 let multipoint = MultiPoint::from(vec![(1., 1.), (2., -2.), (-3., -3.), (-4., 4.)]);
272 let bounding_rect = Rect::new(coord! { x: -4., y: -3. }, coord! { x: 2., y: 4. });
273 assert_eq!(bounding_rect, multipoint.bounding_rect().unwrap());
274 }
275 #[test]
276 fn polygon_test() {
277 let linestring = line_string![
278 (x: 0., y: 0.),
279 (x: 5., y: 0.),
280 (x: 5., y: 6.),
281 (x: 0., y: 6.),
282 (x: 0., y: 0.),
283 ];
284 let line_bounding_rect = linestring.bounding_rect().unwrap();
285 let poly = Polygon::new(linestring, Vec::new());
286 assert_eq!(line_bounding_rect, poly.bounding_rect().unwrap());
287 }
288 #[test]
289 fn multipolygon_test() {
290 let mpoly = MultiPolygon::new(vec![
291 polygon![(x: 0., y: 0.), (x: 50., y: 0.), (x: 0., y: -70.), (x: 0., y: 0.)],
292 polygon![(x: 0., y: 0.), (x: 5., y: 0.), (x: 0., y: 80.), (x: 0., y: 0.)],
293 polygon![(x: 0., y: 0.), (x: -60., y: 0.), (x: 0., y: 6.), (x: 0., y: 0.)],
294 ]);
295 let bounding_rect = Rect::new(coord! { x: -60., y: -70. }, coord! { x: 50., y: 80. });
296 assert_eq!(bounding_rect, mpoly.bounding_rect().unwrap());
297 }
298 #[test]
299 fn line_test() {
300 let line1 = Line::new(coord! { x: 0., y: 1. }, coord! { x: 2., y: 3. });
301 let line2 = Line::new(coord! { x: 2., y: 3. }, coord! { x: 0., y: 1. });
302 assert_eq!(
303 line1.bounding_rect(),
304 Rect::new(coord! { x: 0., y: 1. }, coord! { x: 2., y: 3. },)
305 );
306 assert_eq!(
307 line2.bounding_rect(),
308 Rect::new(coord! { x: 0., y: 1. }, coord! { x: 2., y: 3. },)
309 );
310 }
311
312 #[test]
313 fn bounding_rect_merge_test() {
314 assert_eq!(
315 bounding_rect_merge(
316 Rect::new(coord! { x: 0., y: 0. }, coord! { x: 1., y: 1. }),
317 Rect::new(coord! { x: 1., y: 1. }, coord! { x: 2., y: 2. }),
318 ),
319 Rect::new(coord! { x: 0., y: 0. }, coord! { x: 2., y: 2. }),
320 );
321 }
322
323 #[test]
324 fn point_bounding_rect_test() {
325 assert_eq!(
326 Rect::new(coord! { x: 1., y: 2. }, coord! { x: 1., y: 2. }),
327 point! { x: 1., y: 2. }.bounding_rect(),
328 );
329 }
330
331 #[test]
332 fn geometry_collection_bounding_rect_test() {
333 assert_eq!(
334 Some(Rect::new(coord! { x: 0., y: 0. }, coord! { x: 1., y: 2. })),
335 GeometryCollection::new_from(vec![
336 Geometry::Point(point! { x: 0., y: 0. }),
337 Geometry::Point(point! { x: 1., y: 2. }),
338 ])
339 .bounding_rect(),
340 );
341 }
342}