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)
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))
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)
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(self.iter().flat_map(|poly| &poly.exterior().0))
133 }
134}
135
136impl<T> BoundingRect<T> for Triangle<T>
137where
138 T: CoordNum,
139{
140 type Output = Rect<T>;
141
142 fn bounding_rect(&self) -> Self::Output {
143 get_bounding_rect(self.to_array()).unwrap()
144 }
145}
146
147impl<T> BoundingRect<T> for Rect<T>
148where
149 T: CoordNum,
150{
151 type Output = Rect<T>;
152
153 fn bounding_rect(&self) -> Self::Output {
154 *self
155 }
156}
157
158impl<T> BoundingRect<T> for Geometry<T>
159where
160 T: CoordNum,
161{
162 type Output = Option<Rect<T>>;
163
164 crate::geometry_delegate_impl! {
165 fn bounding_rect(&self) -> Self::Output;
166 }
167}
168
169impl<T> BoundingRect<T> for GeometryCow<'_, T>
170where
171 T: CoordNum,
172{
173 type Output = Option<Rect<T>>;
174
175 crate::geometry_cow_delegate_impl! {
176 fn bounding_rect(&self) -> Self::Output;
177 }
178}
179
180impl<T> BoundingRect<T> for GeometryCollection<T>
181where
182 T: CoordNum,
183{
184 type Output = Option<Rect<T>>;
185
186 fn bounding_rect(&self) -> Self::Output {
187 self.iter().fold(None, |acc, next| {
188 let next_bounding_rect = next.bounding_rect();
189
190 match (acc, next_bounding_rect) {
191 (None, None) => None,
192 (Some(r), None) | (None, Some(r)) => Some(r),
193 (Some(r1), Some(r2)) => Some(bounding_rect_merge(r1, r2)),
194 }
195 })
196 }
197}
198
199fn bounding_rect_merge<T: CoordNum>(a: Rect<T>, b: Rect<T>) -> Rect<T> {
201 Rect::new(
202 coord! {
203 x: partial_min(a.min().x, b.min().x),
204 y: partial_min(a.min().y, b.min().y),
205 },
206 coord! {
207 x: partial_max(a.max().x, b.max().x),
208 y: partial_max(a.max().y, b.max().y),
209 },
210 )
211}
212
213#[cfg(test)]
214mod test {
215 use super::bounding_rect_merge;
216 use crate::line_string;
217 use crate::BoundingRect;
218 use crate::{
219 coord, point, polygon, Geometry, GeometryCollection, Line, LineString, MultiLineString,
220 MultiPoint, MultiPolygon, Polygon, Rect,
221 };
222
223 #[test]
224 fn empty_linestring_test() {
225 let linestring: LineString<f32> = line_string![];
226 let bounding_rect = linestring.bounding_rect();
227 assert!(bounding_rect.is_none());
228 }
229 #[test]
230 fn linestring_one_point_test() {
231 let linestring = line_string![(x: 40.02f64, y: 116.34)];
232 let bounding_rect = Rect::new(
233 coord! {
234 x: 40.02f64,
235 y: 116.34,
236 },
237 coord! {
238 x: 40.02,
239 y: 116.34,
240 },
241 );
242 assert_eq!(bounding_rect, linestring.bounding_rect().unwrap());
243 }
244 #[test]
245 fn linestring_test() {
246 let linestring = line_string![
247 (x: 1., y: 1.),
248 (x: 2., y: -2.),
249 (x: -3., y: -3.),
250 (x: -4., y: 4.)
251 ];
252 let bounding_rect = Rect::new(coord! { x: -4., y: -3. }, coord! { x: 2., y: 4. });
253 assert_eq!(bounding_rect, linestring.bounding_rect().unwrap());
254 }
255 #[test]
256 fn multilinestring_test() {
257 let multiline = MultiLineString::new(vec![
258 line_string![(x: 1., y: 1.), (x: -40., y: 1.)],
259 line_string![(x: 1., y: 1.), (x: 50., y: 1.)],
260 line_string![(x: 1., y: 1.), (x: 1., y: -60.)],
261 line_string![(x: 1., y: 1.), (x: 1., y: 70.)],
262 ]);
263 let bounding_rect = Rect::new(coord! { x: -40., y: -60. }, coord! { x: 50., y: 70. });
264 assert_eq!(bounding_rect, multiline.bounding_rect().unwrap());
265 }
266 #[test]
267 fn multipoint_test() {
268 let multipoint = MultiPoint::from(vec![(1., 1.), (2., -2.), (-3., -3.), (-4., 4.)]);
269 let bounding_rect = Rect::new(coord! { x: -4., y: -3. }, coord! { x: 2., y: 4. });
270 assert_eq!(bounding_rect, multipoint.bounding_rect().unwrap());
271 }
272 #[test]
273 fn polygon_test() {
274 let linestring = line_string![
275 (x: 0., y: 0.),
276 (x: 5., y: 0.),
277 (x: 5., y: 6.),
278 (x: 0., y: 6.),
279 (x: 0., y: 0.),
280 ];
281 let line_bounding_rect = linestring.bounding_rect().unwrap();
282 let poly = Polygon::new(linestring, Vec::new());
283 assert_eq!(line_bounding_rect, poly.bounding_rect().unwrap());
284 }
285 #[test]
286 fn multipolygon_test() {
287 let mpoly = MultiPolygon::new(vec![
288 polygon![(x: 0., y: 0.), (x: 50., y: 0.), (x: 0., y: -70.), (x: 0., y: 0.)],
289 polygon![(x: 0., y: 0.), (x: 5., y: 0.), (x: 0., y: 80.), (x: 0., y: 0.)],
290 polygon![(x: 0., y: 0.), (x: -60., y: 0.), (x: 0., y: 6.), (x: 0., y: 0.)],
291 ]);
292 let bounding_rect = Rect::new(coord! { x: -60., y: -70. }, coord! { x: 50., y: 80. });
293 assert_eq!(bounding_rect, mpoly.bounding_rect().unwrap());
294 }
295 #[test]
296 fn line_test() {
297 let line1 = Line::new(coord! { x: 0., y: 1. }, coord! { x: 2., y: 3. });
298 let line2 = Line::new(coord! { x: 2., y: 3. }, coord! { x: 0., y: 1. });
299 assert_eq!(
300 line1.bounding_rect(),
301 Rect::new(coord! { x: 0., y: 1. }, coord! { x: 2., y: 3. },)
302 );
303 assert_eq!(
304 line2.bounding_rect(),
305 Rect::new(coord! { x: 0., y: 1. }, coord! { x: 2., y: 3. },)
306 );
307 }
308
309 #[test]
310 fn bounding_rect_merge_test() {
311 assert_eq!(
312 bounding_rect_merge(
313 Rect::new(coord! { x: 0., y: 0. }, coord! { x: 1., y: 1. }),
314 Rect::new(coord! { x: 1., y: 1. }, coord! { x: 2., y: 2. }),
315 ),
316 Rect::new(coord! { x: 0., y: 0. }, coord! { x: 2., y: 2. }),
317 );
318 }
319
320 #[test]
321 fn point_bounding_rect_test() {
322 assert_eq!(
323 Rect::new(coord! { x: 1., y: 2. }, coord! { x: 1., y: 2. }),
324 point! { x: 1., y: 2. }.bounding_rect(),
325 );
326 }
327
328 #[test]
329 fn geometry_collection_bounding_rect_test() {
330 assert_eq!(
331 Some(Rect::new(coord! { x: 0., y: 0. }, coord! { x: 1., y: 2. })),
332 GeometryCollection::new_from(vec![
333 Geometry::Point(point! { x: 0., y: 0. }),
334 Geometry::Point(point! { x: 1., y: 2. }),
335 ])
336 .bounding_rect(),
337 );
338 }
339}