1use crate::utils::{partial_max, partial_min};
2use crate::{
3 CoordNum, GeoNum, GeometryCow, MonotoneChains, coord, geometry::*, monotone_chain::geometry::*,
4};
5use geo_types::private_utils::{get_bounding_rect, line_string_bounding_rect};
6
7pub trait BoundingRect<T: CoordNum> {
9 type Output: Into<Option<Rect<T>>>;
10
11 fn bounding_rect(&self) -> Self::Output;
33}
34
35impl<T> BoundingRect<T> for Coord<T>
36where
37 T: CoordNum,
38{
39 type Output = Rect<T>;
40
41 fn bounding_rect(&self) -> Self::Output {
44 Rect::new(*self, *self)
45 }
46}
47
48impl<T> BoundingRect<T> for Point<T>
49where
50 T: CoordNum,
51{
52 type Output = Rect<T>;
53
54 fn bounding_rect(&self) -> Self::Output {
57 Rect::new(self.0, self.0)
58 }
59}
60
61impl<T> BoundingRect<T> for MultiPoint<T>
62where
63 T: CoordNum,
64{
65 type Output = Option<Rect<T>>;
66
67 fn bounding_rect(&self) -> Self::Output {
70 get_bounding_rect(&self.0)
71 }
72}
73
74impl<T> BoundingRect<T> for Line<T>
75where
76 T: CoordNum,
77{
78 type Output = Rect<T>;
79
80 fn bounding_rect(&self) -> Self::Output {
81 Rect::new(self.start, self.end)
82 }
83}
84
85impl<T> BoundingRect<T> for LineString<T>
86where
87 T: CoordNum,
88{
89 type Output = Option<Rect<T>>;
90
91 fn bounding_rect(&self) -> Self::Output {
94 line_string_bounding_rect(self)
95 }
96}
97
98impl<T> BoundingRect<T> for MultiLineString<T>
99where
100 T: CoordNum,
101{
102 type Output = Option<Rect<T>>;
103
104 fn bounding_rect(&self) -> Self::Output {
107 get_bounding_rect(self.iter().flat_map(|line| &line.0))
108 }
109}
110
111impl<T> BoundingRect<T> for Polygon<T>
112where
113 T: CoordNum,
114{
115 type Output = Option<Rect<T>>;
116
117 fn bounding_rect(&self) -> Self::Output {
120 let line = self.exterior();
121 get_bounding_rect(&line.0)
122 }
123}
124
125impl<T> BoundingRect<T> for MultiPolygon<T>
126where
127 T: CoordNum,
128{
129 type Output = Option<Rect<T>>;
130
131 fn bounding_rect(&self) -> Self::Output {
134 get_bounding_rect(self.iter().flat_map(|poly| &poly.exterior().0))
135 }
136}
137
138impl<T> BoundingRect<T> for Triangle<T>
139where
140 T: CoordNum,
141{
142 type Output = Rect<T>;
143
144 fn bounding_rect(&self) -> Self::Output {
145 get_bounding_rect(self.to_array()).unwrap()
146 }
147}
148
149impl<T> BoundingRect<T> for Rect<T>
150where
151 T: CoordNum,
152{
153 type Output = Rect<T>;
154
155 fn bounding_rect(&self) -> Self::Output {
156 *self
157 }
158}
159
160impl<T> BoundingRect<T> for Geometry<T>
161where
162 T: CoordNum,
163{
164 type Output = Option<Rect<T>>;
165
166 crate::geometry_delegate_impl! {
167 fn bounding_rect(&self) -> Self::Output;
168 }
169}
170
171impl<T> BoundingRect<T> for GeometryCow<'_, T>
172where
173 T: CoordNum,
174{
175 type Output = Option<Rect<T>>;
176
177 crate::geometry_cow_delegate_impl! {
178 fn bounding_rect(&self) -> Self::Output;
179 }
180}
181
182impl<T> BoundingRect<T> for GeometryCollection<T>
183where
184 T: CoordNum,
185{
186 type Output = Option<Rect<T>>;
187
188 fn bounding_rect(&self) -> Self::Output {
189 self.iter().fold(None, |acc, next| {
190 let next_bounding_rect = next.bounding_rect();
191
192 match (acc, next_bounding_rect) {
193 (None, None) => None,
194 (Some(r), None) | (None, Some(r)) => Some(r),
195 (Some(r1), Some(r2)) => Some(bounding_rect_merge(r1, r2)),
196 }
197 })
198 }
199}
200
201impl<'a, T> BoundingRect<T> for MonotoneChainLineString<'a, T>
202where
203 T: GeoNum,
204{
205 type Output = Option<Rect<T>>;
206 fn bounding_rect(&self) -> Self::Output {
207 self.chain().bounding_rect()
208 }
209}
210
211impl<'a, T> BoundingRect<T> for MonotoneChainMultiLineString<'a, T>
212where
213 T: GeoNum,
214{
215 type Output = Option<Rect<T>>;
216 fn bounding_rect(&self) -> Self::Output {
217 self.components().iter().fold(None, |acc, next| {
218 let next_bounding_rect = next.bounding_rect();
219
220 match (acc, next_bounding_rect) {
221 (None, None) => None,
222 (Some(r), None) | (None, Some(r)) => Some(r),
223 (Some(r1), Some(r2)) => Some(bounding_rect_merge(r1, r2)),
224 }
225 })
226 }
227}
228
229impl<'a, T> BoundingRect<T> for MonotoneChainPolygon<'a, T>
230where
231 T: GeoNum,
232{
233 type Output = Option<Rect<T>>;
234 fn bounding_rect(&self) -> Self::Output {
235 self.chains().fold(None, |acc, next| {
236 let next_bounding_rect = next.bounding_rect();
237
238 match (acc, next_bounding_rect) {
239 (None, None) => None,
240 (Some(r), None) | (None, Some(r)) => Some(r),
241 (Some(r1), Some(r2)) => Some(bounding_rect_merge(r1, r2)),
242 }
243 })
244 }
245}
246
247impl<'a, T> BoundingRect<T> for MonotoneChainMultiPolygon<'a, T>
248where
249 T: GeoNum,
250{
251 type Output = Option<Rect<T>>;
252 fn bounding_rect(&self) -> Self::Output {
253 self.components().iter().fold(None, |acc, next| {
254 let next_bounding_rect = next.bounding_rect();
255
256 match (acc, next_bounding_rect) {
257 (None, None) => None,
258 (Some(r), None) | (None, Some(r)) => Some(r),
259 (Some(r1), Some(r2)) => Some(bounding_rect_merge(r1, r2)),
260 }
261 })
262 }
263}
264
265impl<'a, T> BoundingRect<T> for MonotoneChainGeometry<'a, T>
266where
267 T: GeoNum,
268{
269 type Output = Option<Rect<T>>;
270 fn bounding_rect(&self) -> Self::Output {
271 match self {
272 MonotoneChainGeometry::LineString(a) => a.bounding_rect(),
273 MonotoneChainGeometry::MultiLineString(a) => a.bounding_rect(),
274 MonotoneChainGeometry::Polygon(a) => a.bounding_rect(),
275 MonotoneChainGeometry::MultiPolygon(a) => a.bounding_rect(),
276 }
277 }
278}
279
280fn bounding_rect_merge<T: CoordNum>(a: Rect<T>, b: Rect<T>) -> Rect<T> {
282 Rect::new(
283 coord! {
284 x: partial_min(a.min().x, b.min().x),
285 y: partial_min(a.min().y, b.min().y),
286 },
287 coord! {
288 x: partial_max(a.max().x, b.max().x),
289 y: partial_max(a.max().y, b.max().y),
290 },
291 )
292}
293
294#[cfg(test)]
295mod test {
296 use super::bounding_rect_merge;
297 use crate::BoundingRect;
298 use crate::line_string;
299 use crate::{
300 Geometry, GeometryCollection, Line, LineString, MultiLineString, MultiPoint, MultiPolygon,
301 Polygon, Rect, coord, point, polygon,
302 };
303
304 #[test]
305 fn empty_linestring_test() {
306 let linestring: LineString<f32> = line_string![];
307 let bounding_rect = linestring.bounding_rect();
308 assert!(bounding_rect.is_none());
309 }
310 #[test]
311 fn linestring_one_point_test() {
312 let linestring = line_string![(x: 40.02f64, y: 116.34)];
313 let bounding_rect = Rect::new(
314 coord! {
315 x: 40.02f64,
316 y: 116.34,
317 },
318 coord! {
319 x: 40.02,
320 y: 116.34,
321 },
322 );
323 assert_eq!(bounding_rect, linestring.bounding_rect().unwrap());
324 }
325 #[test]
326 fn linestring_test() {
327 let linestring = line_string![
328 (x: 1., y: 1.),
329 (x: 2., y: -2.),
330 (x: -3., y: -3.),
331 (x: -4., y: 4.)
332 ];
333 let bounding_rect = Rect::new(coord! { x: -4., y: -3. }, coord! { x: 2., y: 4. });
334 assert_eq!(bounding_rect, linestring.bounding_rect().unwrap());
335 }
336 #[test]
337 fn multilinestring_test() {
338 let multiline = MultiLineString::new(vec![
339 line_string![(x: 1., y: 1.), (x: -40., y: 1.)],
340 line_string![(x: 1., y: 1.), (x: 50., y: 1.)],
341 line_string![(x: 1., y: 1.), (x: 1., y: -60.)],
342 line_string![(x: 1., y: 1.), (x: 1., y: 70.)],
343 ]);
344 let bounding_rect = Rect::new(coord! { x: -40., y: -60. }, coord! { x: 50., y: 70. });
345 assert_eq!(bounding_rect, multiline.bounding_rect().unwrap());
346 }
347 #[test]
348 fn multipoint_test() {
349 let multipoint = MultiPoint::from(vec![(1., 1.), (2., -2.), (-3., -3.), (-4., 4.)]);
350 let bounding_rect = Rect::new(coord! { x: -4., y: -3. }, coord! { x: 2., y: 4. });
351 assert_eq!(bounding_rect, multipoint.bounding_rect().unwrap());
352 }
353 #[test]
354 fn polygon_test() {
355 let linestring = line_string![
356 (x: 0., y: 0.),
357 (x: 5., y: 0.),
358 (x: 5., y: 6.),
359 (x: 0., y: 6.),
360 (x: 0., y: 0.),
361 ];
362 let line_bounding_rect = linestring.bounding_rect().unwrap();
363 let poly = Polygon::new(linestring, Vec::new());
364 assert_eq!(line_bounding_rect, poly.bounding_rect().unwrap());
365 }
366 #[test]
367 fn multipolygon_test() {
368 let mpoly = MultiPolygon::new(vec![
369 polygon![(x: 0., y: 0.), (x: 50., y: 0.), (x: 0., y: -70.), (x: 0., y: 0.)],
370 polygon![(x: 0., y: 0.), (x: 5., y: 0.), (x: 0., y: 80.), (x: 0., y: 0.)],
371 polygon![(x: 0., y: 0.), (x: -60., y: 0.), (x: 0., y: 6.), (x: 0., y: 0.)],
372 ]);
373 let bounding_rect = Rect::new(coord! { x: -60., y: -70. }, coord! { x: 50., y: 80. });
374 assert_eq!(bounding_rect, mpoly.bounding_rect().unwrap());
375 }
376 #[test]
377 fn line_test() {
378 let line1 = Line::new(coord! { x: 0., y: 1. }, coord! { x: 2., y: 3. });
379 let line2 = Line::new(coord! { x: 2., y: 3. }, coord! { x: 0., y: 1. });
380 assert_eq!(
381 line1.bounding_rect(),
382 Rect::new(coord! { x: 0., y: 1. }, coord! { x: 2., y: 3. },)
383 );
384 assert_eq!(
385 line2.bounding_rect(),
386 Rect::new(coord! { x: 0., y: 1. }, coord! { x: 2., y: 3. },)
387 );
388 }
389
390 #[test]
391 fn bounding_rect_merge_test() {
392 assert_eq!(
393 bounding_rect_merge(
394 Rect::new(coord! { x: 0., y: 0. }, coord! { x: 1., y: 1. }),
395 Rect::new(coord! { x: 1., y: 1. }, coord! { x: 2., y: 2. }),
396 ),
397 Rect::new(coord! { x: 0., y: 0. }, coord! { x: 2., y: 2. }),
398 );
399 }
400
401 #[test]
402 fn point_bounding_rect_test() {
403 assert_eq!(
404 Rect::new(coord! { x: 1., y: 2. }, coord! { x: 1., y: 2. }),
405 point! { x: 1., y: 2. }.bounding_rect(),
406 );
407 }
408
409 #[test]
410 fn geometry_collection_bounding_rect_test() {
411 assert_eq!(
412 Some(Rect::new(coord! { x: 0., y: 0. }, coord! { x: 1., y: 2. })),
413 GeometryCollection::new_from(vec![
414 Geometry::Point(point! { x: 0., y: 0. }),
415 Geometry::Point(point! { x: 1., y: 2. }),
416 ])
417 .bounding_rect(),
418 );
419 }
420}