From e742d9398ffafe32fcba60dcff1ec2f97585e22f Mon Sep 17 00:00:00 2001 From: Sascha Brawer Date: Wed, 12 Aug 2026 15:46:06 +0200 Subject: [PATCH] Add test for an inner ring touching the outer at two points Covers a PolygonAssembler case not exercised elsewhere: an inner ring that touches the outer ring at exactly two points (not along an overlapping edge, not crossing), so removing it splits the outer into two disjoint polygons rather than leaving one polygon with a hole. Found while checking whether the (2012, unlicensed, since-superseded) nimix/osm_conv_tests test suite had anything our vendored osm-testdata grid corpus doesn't. This was the one case it had that grid didn't (grid covers single-point and shared-line inner/outer touches, and a single self-touching way resolving into two components, but not this two-separate-rings, two-point-touch case) -- also proposed upstream as osmcode/osm-testdata#11. Co-Authored-By: Claude Sonnet 5 --- src/geometry/polygon_assembler.rs | 33 +++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/geometry/polygon_assembler.rs b/src/geometry/polygon_assembler.rs index 295a1698..040fb9e2 100644 --- a/src/geometry/polygon_assembler.rs +++ b/src/geometry/polygon_assembler.rs @@ -384,6 +384,39 @@ mod tests { } } + /// An inner ring that touches the outer ring at exactly two points + /// (not along an overlapping edge, not crossing) so removing it splits + /// the outer into two disjoint pieces rather than leaving one polygon + /// with a hole. Mirrors `geometry_multipolygon/8` from `nimix/osm_conv_tests` + /// (2012, unlicensed, not vendored here -- we only inspected it to see + /// whether it had any test cases `osm-testdata`'s vendored `grid` + /// fixtures lack). `grid` covers a single-point inner/outer touch + /// (`7/754`-`7/758`) and touching along a shared line (`7/756`, + /// `7/757`), but not this two-point-touch-splits-the-outer-in-two + /// case, so this test closes that one gap directly in our own suite. + #[test] + fn inner_touching_outer_at_two_points_splits_it_into_two_pieces() { + let mut a = PolygonAssembler::new(); + // Outer: a rectangle with redundant midpoints on its top and + // bottom edges -- the exact points the inner ring below touches. + a.add_ring(&ring(&[ + (0.0, 0.0), + (2.0, 0.0), // bottom midpoint, shared with the inner ring + (4.0, 0.0), + (4.0, 2.0), + (2.0, 2.0), // top midpoint, shared with the inner ring + (0.0, 2.0), + ])); + // Inner: a diamond whose top and bottom vertices land exactly on + // the outer's midpoints above, touching but never crossing. + a.add_ring(&ring(&[(2.0, 2.0), (1.0, 1.0), (2.0, 0.0), (3.0, 1.0)])); + + match a.finish() { + Some(Geometry::MultiPolygon(mp)) => assert_eq!(mp.0.len(), 2), + other => panic!("expected the outer split into two pieces, got {other:?}"), + } + } + /// https://github.com/alltheplaces/osm-diffs/issues/532, mirroring /// grid fixture `7/790`: a ring added twice (the same OSM way listed /// twice as a member) must not cancel itself out under the even-odd