Skip to content

Commit e8386ee

Browse files
authored
Merge pull request #487 from rmanoka/master
Fix Area logic for Polygon with interiors (holes)
2 parents f9537b9 + eb69315 commit e8386ee

File tree

1 file changed

+124
-5
lines changed

1 file changed

+124
-5
lines changed

geo/src/algorithm/area.rs

+124-5
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use num_traits::Float;
66

77
use crate::algorithm::winding_order::twice_signed_ring_area;
88

9-
/// Signed planar area of a geometry.
9+
/// Signed and unsigned planar area of a geometry.
1010
///
1111
/// # Examples
1212
///
@@ -88,16 +88,31 @@ where
8888
}
8989
}
9090

91+
/// **Note.** The implementation handles polygons whose
92+
/// holes do not all have the same orientation. The sign of
93+
/// the output is the same as that of the exterior shell.
9194
impl<T> Area<T> for Polygon<T>
9295
where
9396
T: Float,
9497
{
9598
fn signed_area(&self) -> T {
96-
self.interiors()
99+
let area = get_linestring_area(self.exterior());
100+
101+
// We could use winding order here, but that would
102+
// result in computing the shoelace formula twice.
103+
let is_negative = area < T::zero();
104+
105+
let area = self.interiors()
97106
.iter()
98-
.fold(get_linestring_area(self.exterior()), |total, next| {
99-
total - get_linestring_area(next)
100-
})
107+
.fold(area.abs(), |total, next| {
108+
total - get_linestring_area(next).abs()
109+
});
110+
111+
if is_negative {
112+
-area
113+
} else {
114+
area
115+
}
101116
}
102117

103118
fn unsigned_area(&self) -> T {
@@ -131,6 +146,12 @@ where
131146
}
132147
}
133148

149+
/// **Note.** The implementation is a straight-forward
150+
/// summation of the signed areas of the individual
151+
/// polygons. In particular, `unsigned_area` is not
152+
/// necessarily the sum of the `unsigned_area` of the
153+
/// constituent polygons unless they are all oriented the
154+
/// same.
134155
impl<T> Area<T> for MultiPolygon<T>
135156
where
136157
T: Float,
@@ -406,4 +427,102 @@ mod test {
406427

407428
assert_eq!(polygon_area * 2., multi_polygon.unsigned_area());
408429
}
430+
431+
#[test]
432+
fn area_north_america_cutout() {
433+
let poly = polygon![
434+
exterior: [
435+
(x: -102.902861858977, y: 31.6943450891131),
436+
(x: -102.917375513247, y: 31.6990175356827),
437+
(x: -102.917887344527, y: 31.7044889522597),
438+
(x: -102.938892711173, y: 31.7032871894594),
439+
(x: -102.939919687305, y: 31.7142296141915),
440+
(x: -102.946922353444, y: 31.713828170995),
441+
(x: -102.954642979004, y: 31.7210594956594),
442+
(x: -102.960927457803, y: 31.7130240707676),
443+
(x: -102.967929895872, y: 31.7126214137469),
444+
(x: -102.966383373178, y: 31.6962079209847),
445+
(x: -102.973384192133, y: 31.6958049292994),
446+
(x: -102.97390013779, y: 31.701276160078),
447+
(x: -102.980901394769, y: 31.7008727405409),
448+
(x: -102.987902575456, y: 31.7004689164622),
449+
(x: -102.986878877087, y: 31.7127206248263),
450+
(x: -102.976474089689, y: 31.7054378797983),
451+
(x: -102.975448432121, y: 31.7176893134691),
452+
(x: -102.96619351228, y: 31.7237224912303),
453+
(x: -102.976481009643, y: 31.7286309669534),
454+
(x: -102.976997412845, y: 31.7341016591658),
455+
(x: -102.978030448215, y: 31.7450427747035),
456+
(x: -102.985035821671, y: 31.7446391683265),
457+
(x: -102.985552968771, y: 31.7501095683386),
458+
(x: -102.992558780682, y: 31.7497055338313),
459+
(x: -102.993594334215, y: 31.7606460184322),
460+
(x: -102.973746840657, y: 31.7546100958509),
461+
(x: -102.966082339116, y: 31.767730116605),
462+
(x: -102.959074676589, y: 31.768132602064),
463+
(x: -102.95206693787, y: 31.7685346826851),
464+
(x: -102.953096767614, y: 31.7794749110023),
465+
(x: -102.953611796704, y: 31.7849448911322),
466+
(x: -102.952629078076, y: 31.7996518517642),
467+
(x: -102.948661251495, y: 31.8072257578725),
468+
(x: -102.934638176282, y: 31.8080282207231),
469+
(x: -102.927626524626, y: 31.8084288446215),
470+
(x: -102.927113253813, y: 31.8029591283411),
471+
(x: -102.920102042027, y: 31.8033593239799),
472+
(x: -102.919076759513, y: 31.792419577395),
473+
(x: -102.912066503301, y: 31.7928193216213),
474+
(x: -102.911554491357, y: 31.7873492912889),
475+
(x: -102.904544675025, y: 31.7877486073783),
476+
(x: -102.904033254331, y: 31.7822784646103),
477+
(x: -102.903521909259, y: 31.7768082325431),
478+
(x: -102.895800463718, y: 31.7695748336589),
479+
(x: -102.889504111843, y: 31.7776055573633),
480+
(x: -102.882495099915, y: 31.7780036124077),
481+
(x: -102.868476849997, y: 31.7787985077398),
482+
(x: -102.866950998738, y: 31.7623869292283),
483+
(x: -102.873958615171, y: 31.7619897531194),
484+
(x: -102.87888647278, y: 31.7688910039026),
485+
(x: -102.879947237315, y: 31.750650764952),
486+
(x: -102.886953672823, y: 31.750252825268),
487+
(x: -102.89396003296, y: 31.7498544807869),
488+
(x: -102.892939355062, y: 31.7389128078806),
489+
(x: -102.913954892669, y: 31.7377154844276),
490+
(x: -102.913443122277, y: 31.7322445829725),
491+
(x: -102.912931427507, y: 31.7267735918962),
492+
(x: -102.911908264767, y: 31.7158313407426),
493+
(x: -102.904905220014, y: 31.7162307607961),
494+
(x: -102.904394266551, y: 31.7107594775392),
495+
(x: -102.903372586049, y: 31.6998166417321),
496+
(x: -102.902861858977, y: 31.6943450891131),
497+
],
498+
interiors: [
499+
[
500+
(x: -102.916514879554, y: 31.7650686485918),
501+
(x: -102.921022256876, y: 31.7770831833398),
502+
(x: -102.93367363719, y: 31.771184865332),
503+
(x: -102.916514879554, y: 31.7650686485918),
504+
],
505+
[
506+
(x: -102.935483140202, y: 31.7419852607081),
507+
(x: -102.932452314332, y: 31.7328567234689),
508+
(x: -102.918345099146, y: 31.7326099897391),
509+
(x: -102.925566322952, y: 31.7552505533503),
510+
(x: -102.928990700436, y: 31.747856686604),
511+
(x: -102.935996606762, y: 31.7474559134477),
512+
(x: -102.939021176592, y: 31.7539885279379),
513+
(x: -102.944714388971, y: 31.7488395547293),
514+
(x: -102.935996606762, y: 31.7474559134477),
515+
(x: -102.935483140202, y: 31.7419852607081),
516+
],
517+
[
518+
(x: -102.956498858767, y: 31.7407805824758),
519+
(x: -102.960959476367, y: 31.7475080456347),
520+
(x: -102.972817445204, y: 31.742072061889),
521+
(x: -102.956498858767, y: 31.7407805824758),
522+
]
523+
],
524+
];
525+
// Value from shapely
526+
assert_relative_eq!(poly.unsigned_area(), 0.006547948219252177, max_relative = 0.0001);
527+
}
409528
}

0 commit comments

Comments
 (0)