1use crate::geometry::*;
2use crate::{CoordFloat, CoordNum};
3
4pub(crate) fn twice_signed_ring_area<T>(linestring: &LineString<T>) -> T
5where
6 T: CoordNum,
7{
8 if linestring.0.len() < 3 {
11 return T::zero();
12 }
13
14 if linestring.0.first().unwrap() != linestring.0.last().unwrap() {
17 return T::zero();
18 }
19
20 let shift = linestring.0[0];
31
32 let mut tmp = T::zero();
33 for line in linestring.lines() {
34 use crate::MapCoords;
35 let line = line.map_coords(|c| c - shift);
36 tmp = tmp + line.determinant();
37 }
38
39 tmp
40}
41
42pub trait Area<T>
69where
70 T: CoordNum,
71{
72 fn signed_area(&self) -> T;
73
74 fn unsigned_area(&self) -> T;
75}
76
77pub(crate) fn get_linestring_area<T>(linestring: &LineString<T>) -> T
79where
80 T: CoordFloat,
81{
82 twice_signed_ring_area(linestring) / (T::one() + T::one())
83}
84
85impl<T> Area<T> for Point<T>
86where
87 T: CoordNum,
88{
89 fn signed_area(&self) -> T {
90 T::zero()
91 }
92
93 fn unsigned_area(&self) -> T {
94 T::zero()
95 }
96}
97
98impl<T> Area<T> for LineString<T>
99where
100 T: CoordNum,
101{
102 fn signed_area(&self) -> T {
103 T::zero()
104 }
105
106 fn unsigned_area(&self) -> T {
107 T::zero()
108 }
109}
110
111impl<T> Area<T> for Line<T>
112where
113 T: CoordNum,
114{
115 fn signed_area(&self) -> T {
116 T::zero()
117 }
118
119 fn unsigned_area(&self) -> T {
120 T::zero()
121 }
122}
123
124impl<T> Area<T> for Polygon<T>
128where
129 T: CoordFloat,
130{
131 fn signed_area(&self) -> T {
132 let area = get_linestring_area(self.exterior());
133
134 let is_negative = area < T::zero();
137
138 let area = self.interiors().iter().fold(area.abs(), |total, next| {
139 total - get_linestring_area(next).abs()
140 });
141
142 if is_negative {
143 -area
144 } else {
145 area
146 }
147 }
148
149 fn unsigned_area(&self) -> T {
150 self.signed_area().abs()
151 }
152}
153
154impl<T> Area<T> for MultiPoint<T>
155where
156 T: CoordNum,
157{
158 fn signed_area(&self) -> T {
159 T::zero()
160 }
161
162 fn unsigned_area(&self) -> T {
163 T::zero()
164 }
165}
166
167impl<T> Area<T> for MultiLineString<T>
168where
169 T: CoordNum,
170{
171 fn signed_area(&self) -> T {
172 T::zero()
173 }
174
175 fn unsigned_area(&self) -> T {
176 T::zero()
177 }
178}
179
180impl<T> Area<T> for MultiPolygon<T>
187where
188 T: CoordFloat,
189{
190 fn signed_area(&self) -> T {
191 self.0
192 .iter()
193 .fold(T::zero(), |total, next| total + next.signed_area())
194 }
195
196 fn unsigned_area(&self) -> T {
197 self.0
198 .iter()
199 .fold(T::zero(), |total, next| total + next.signed_area().abs())
200 }
201}
202
203impl<T> Area<T> for Rect<T>
205where
206 T: CoordNum,
207{
208 fn signed_area(&self) -> T {
209 self.width() * self.height()
210 }
211
212 fn unsigned_area(&self) -> T {
213 self.width() * self.height()
214 }
215}
216
217impl<T> Area<T> for Triangle<T>
218where
219 T: CoordFloat,
220{
221 fn signed_area(&self) -> T {
222 self.to_lines()
223 .iter()
224 .fold(T::zero(), |total, line| total + line.determinant())
225 / (T::one() + T::one())
226 }
227
228 fn unsigned_area(&self) -> T {
229 self.signed_area().abs()
230 }
231}
232
233impl<T> Area<T> for Geometry<T>
234where
235 T: CoordFloat,
236{
237 crate::geometry_delegate_impl! {
238 fn signed_area(&self) -> T;
239 fn unsigned_area(&self) -> T;
240 }
241}
242
243impl<T> Area<T> for GeometryCollection<T>
244where
245 T: CoordFloat,
246{
247 fn signed_area(&self) -> T {
248 self.0
249 .iter()
250 .map(|g| g.signed_area())
251 .fold(T::zero(), |acc, next| acc + next)
252 }
253
254 fn unsigned_area(&self) -> T {
255 self.0
256 .iter()
257 .map(|g| g.unsigned_area())
258 .fold(T::zero(), |acc, next| acc + next)
259 }
260}
261
262#[cfg(test)]
263mod test {
264 use crate::Area;
265 use crate::{coord, polygon, Line, MultiPolygon, Polygon, Rect, Triangle};
266
267 #[test]
269 fn area_empty_polygon_test() {
270 let poly: Polygon<f32> = polygon![];
271 assert_relative_eq!(poly.signed_area(), 0.);
272 }
273
274 #[test]
275 fn area_one_point_polygon_test() {
276 let poly = polygon![(x: 1., y: 0.)];
277 assert_relative_eq!(poly.signed_area(), 0.);
278 }
279 #[test]
280 fn area_polygon_test() {
281 let polygon = polygon![
282 (x: 0., y: 0.),
283 (x: 5., y: 0.),
284 (x: 5., y: 6.),
285 (x: 0., y: 6.),
286 (x: 0., y: 0.)
287 ];
288 assert_relative_eq!(polygon.signed_area(), 30.);
289 }
290 #[test]
291 fn area_polygon_numerical_stability() {
292 let polygon = {
293 use std::f64::consts::PI;
294 const NUM_VERTICES: usize = 10;
295 const ANGLE_INC: f64 = 2. * PI / NUM_VERTICES as f64;
296
297 Polygon::new(
298 (0..NUM_VERTICES)
299 .map(|i| {
300 let angle = i as f64 * ANGLE_INC;
301 coord! {
302 x: angle.cos(),
303 y: angle.sin(),
304 }
305 })
306 .collect::<Vec<_>>()
307 .into(),
308 vec![],
309 )
310 };
311
312 let area = polygon.signed_area();
313
314 let shift = coord! { x: 1.5e8, y: 1.5e8 };
315
316 use crate::map_coords::MapCoords;
317 let polygon = polygon.map_coords(|c| c + shift);
318
319 let new_area = polygon.signed_area();
320 let err = (area - new_area).abs() / area;
321
322 assert!(err < 1e-2);
323 }
324 #[test]
325 fn rectangle_test() {
326 let rect1: Rect<f32> = Rect::new(coord! { x: 10., y: 30. }, coord! { x: 20., y: 40. });
327 assert_relative_eq!(rect1.signed_area(), 100.);
328
329 let rect2: Rect<i32> = Rect::new(coord! { x: 10, y: 30 }, coord! { x: 20, y: 40 });
330 assert_eq!(rect2.signed_area(), 100);
331 }
332 #[test]
333 fn area_polygon_inner_test() {
334 let poly = polygon![
335 exterior: [
336 (x: 0., y: 0.),
337 (x: 10., y: 0.),
338 (x: 10., y: 10.),
339 (x: 0., y: 10.),
340 (x: 0., y: 0.)
341 ],
342 interiors: [
343 [
344 (x: 1., y: 1.),
345 (x: 2., y: 1.),
346 (x: 2., y: 2.),
347 (x: 1., y: 2.),
348 (x: 1., y: 1.),
349 ],
350 [
351 (x: 5., y: 5.),
352 (x: 6., y: 5.),
353 (x: 6., y: 6.),
354 (x: 5., y: 6.),
355 (x: 5., y: 5.)
356 ],
357 ],
358 ];
359 assert_relative_eq!(poly.signed_area(), 98.);
360 }
361 #[test]
362 fn area_multipolygon_test() {
363 let poly0 = polygon![
364 (x: 0., y: 0.),
365 (x: 10., y: 0.),
366 (x: 10., y: 10.),
367 (x: 0., y: 10.),
368 (x: 0., y: 0.)
369 ];
370 let poly1 = polygon![
371 (x: 1., y: 1.),
372 (x: 2., y: 1.),
373 (x: 2., y: 2.),
374 (x: 1., y: 2.),
375 (x: 1., y: 1.)
376 ];
377 let poly2 = polygon![
378 (x: 5., y: 5.),
379 (x: 6., y: 5.),
380 (x: 6., y: 6.),
381 (x: 5., y: 6.),
382 (x: 5., y: 5.)
383 ];
384 let mpoly = MultiPolygon::new(vec![poly0, poly1, poly2]);
385 assert_relative_eq!(mpoly.signed_area(), 102.);
386 assert_relative_eq!(mpoly.signed_area(), 102.);
387 }
388 #[test]
389 fn area_line_test() {
390 let line1 = Line::new(coord! { x: 0.0, y: 0.0 }, coord! { x: 1.0, y: 1.0 });
391 assert_relative_eq!(line1.signed_area(), 0.);
392 }
393
394 #[test]
395 fn area_triangle_test() {
396 let triangle = Triangle::new(
397 coord! { x: 0.0, y: 0.0 },
398 coord! { x: 1.0, y: 0.0 },
399 coord! { x: 0.0, y: 1.0 },
400 );
401 assert_relative_eq!(triangle.signed_area(), 0.5);
402
403 let triangle = Triangle::new(
404 coord! { x: 0.0, y: 0.0 },
405 coord! { x: 0.0, y: 1.0 },
406 coord! { x: 1.0, y: 0.0 },
407 );
408 assert_relative_eq!(triangle.signed_area(), -0.5);
409 }
410
411 #[test]
412 fn area_multi_polygon_area_reversed() {
413 let polygon_cw: Polygon<f32> = polygon![
414 coord! { x: 0.0, y: 0.0 },
415 coord! { x: 0.0, y: 1.0 },
416 coord! { x: 1.0, y: 1.0 },
417 coord! { x: 1.0, y: 0.0 },
418 coord! { x: 0.0, y: 0.0 },
419 ];
420 let polygon_ccw: Polygon<f32> = polygon![
421 coord! { x: 0.0, y: 0.0 },
422 coord! { x: 1.0, y: 0.0 },
423 coord! { x: 1.0, y: 1.0 },
424 coord! { x: 0.0, y: 1.0 },
425 coord! { x: 0.0, y: 0.0 },
426 ];
427 let polygon_area = polygon_cw.unsigned_area();
428
429 let multi_polygon = MultiPolygon::new(vec![polygon_cw, polygon_ccw]);
430
431 assert_eq!(polygon_area * 2., multi_polygon.unsigned_area());
432 }
433
434 #[test]
435 fn area_north_america_cutout() {
436 let poly = polygon![
437 exterior: [
438 (x: -102.902861858977, y: 31.6943450891131),
439 (x: -102.917375513247, y: 31.6990175356827),
440 (x: -102.917887344527, y: 31.7044889522597),
441 (x: -102.938892711173, y: 31.7032871894594),
442 (x: -102.939919687305, y: 31.7142296141915),
443 (x: -102.946922353444, y: 31.713828170995),
444 (x: -102.954642979004, y: 31.7210594956594),
445 (x: -102.960927457803, y: 31.7130240707676),
446 (x: -102.967929895872, y: 31.7126214137469),
447 (x: -102.966383373178, y: 31.6962079209847),
448 (x: -102.973384192133, y: 31.6958049292994),
449 (x: -102.97390013779, y: 31.701276160078),
450 (x: -102.980901394769, y: 31.7008727405409),
451 (x: -102.987902575456, y: 31.7004689164622),
452 (x: -102.986878877087, y: 31.7127206248263),
453 (x: -102.976474089689, y: 31.7054378797983),
454 (x: -102.975448432121, y: 31.7176893134691),
455 (x: -102.96619351228, y: 31.7237224912303),
456 (x: -102.976481009643, y: 31.7286309669534),
457 (x: -102.976997412845, y: 31.7341016591658),
458 (x: -102.978030448215, y: 31.7450427747035),
459 (x: -102.985035821671, y: 31.7446391683265),
460 (x: -102.985552968771, y: 31.7501095683386),
461 (x: -102.992558780682, y: 31.7497055338313),
462 (x: -102.993594334215, y: 31.7606460184322),
463 (x: -102.973746840657, y: 31.7546100958509),
464 (x: -102.966082339116, y: 31.767730116605),
465 (x: -102.959074676589, y: 31.768132602064),
466 (x: -102.95206693787, y: 31.7685346826851),
467 (x: -102.953096767614, y: 31.7794749110023),
468 (x: -102.953611796704, y: 31.7849448911322),
469 (x: -102.952629078076, y: 31.7996518517642),
470 (x: -102.948661251495, y: 31.8072257578725),
471 (x: -102.934638176282, y: 31.8080282207231),
472 (x: -102.927626524626, y: 31.8084288446215),
473 (x: -102.927113253813, y: 31.8029591283411),
474 (x: -102.920102042027, y: 31.8033593239799),
475 (x: -102.919076759513, y: 31.792419577395),
476 (x: -102.912066503301, y: 31.7928193216213),
477 (x: -102.911554491357, y: 31.7873492912889),
478 (x: -102.904544675025, y: 31.7877486073783),
479 (x: -102.904033254331, y: 31.7822784646103),
480 (x: -102.903521909259, y: 31.7768082325431),
481 (x: -102.895800463718, y: 31.7695748336589),
482 (x: -102.889504111843, y: 31.7776055573633),
483 (x: -102.882495099915, y: 31.7780036124077),
484 (x: -102.868476849997, y: 31.7787985077398),
485 (x: -102.866950998738, y: 31.7623869292283),
486 (x: -102.873958615171, y: 31.7619897531194),
487 (x: -102.87888647278, y: 31.7688910039026),
488 (x: -102.879947237315, y: 31.750650764952),
489 (x: -102.886953672823, y: 31.750252825268),
490 (x: -102.89396003296, y: 31.7498544807869),
491 (x: -102.892939355062, y: 31.7389128078806),
492 (x: -102.913954892669, y: 31.7377154844276),
493 (x: -102.913443122277, y: 31.7322445829725),
494 (x: -102.912931427507, y: 31.7267735918962),
495 (x: -102.911908264767, y: 31.7158313407426),
496 (x: -102.904905220014, y: 31.7162307607961),
497 (x: -102.904394266551, y: 31.7107594775392),
498 (x: -102.903372586049, y: 31.6998166417321),
499 (x: -102.902861858977, y: 31.6943450891131),
500 ],
501 interiors: [
502 [
503 (x: -102.916514879554, y: 31.7650686485918),
504 (x: -102.921022256876, y: 31.7770831833398),
505 (x: -102.93367363719, y: 31.771184865332),
506 (x: -102.916514879554, y: 31.7650686485918),
507 ],
508 [
509 (x: -102.935483140202, y: 31.7419852607081),
510 (x: -102.932452314332, y: 31.7328567234689),
511 (x: -102.918345099146, y: 31.7326099897391),
512 (x: -102.925566322952, y: 31.7552505533503),
513 (x: -102.928990700436, y: 31.747856686604),
514 (x: -102.935996606762, y: 31.7474559134477),
515 (x: -102.939021176592, y: 31.7539885279379),
516 (x: -102.944714388971, y: 31.7488395547293),
517 (x: -102.935996606762, y: 31.7474559134477),
518 (x: -102.935483140202, y: 31.7419852607081),
519 ],
520 [
521 (x: -102.956498858767, y: 31.7407805824758),
522 (x: -102.960959476367, y: 31.7475080456347),
523 (x: -102.972817445204, y: 31.742072061889),
524 (x: -102.956498858767, y: 31.7407805824758),
525 ]
526 ],
527 ];
528 assert_relative_eq!(poly.unsigned_area(), 0.006547948219252177, max_relative = 0.0001);
530 }
531}