-
Notifications
You must be signed in to change notification settings - Fork 16
Open
Description
Issue Description
Summary
The Union() method incorrectly converts non-overlapping polygons into a single Polygon with multiple rings (treating the second+ polygons as holes) instead of returning a proper MultiPolygon with separate polygons.
Expected Behavior
When unioning two non-overlapping multipolygons, the result should be a MultiPolygon containing two separate polygons:
// Input: Two non-overlapping squares
mp1 := geom.MultiPolygon{{{{X: 0, Y: 0}, {X: 1, Y: 0}, {X: 1, Y: 1}, {X: 0, Y: 1}, {X: 0, Y: 0}}}}
mp2 := geom.MultiPolygon{{{{X: 2, Y: 0}, {X: 3, Y: 0}, {X: 3, Y: 1}, {X: 2, Y: 1}, {X: 2, Y: 0}}}}
result := mp1.Union(mp2)
// Expected: geom.MultiPolygon with 2 separate polygons, each with 1 ring
// Expected GeoJSON structure:
// [
// [[[0,0], [1,0], [1,1], [0,1], [0,0]]], // Polygon 1
// [[[2,0], [3,0], [3,1], [2,1], [2,0]]] // Polygon 2
// ]Actual Behavior
The result is a MultiPolygon with a single Polygon containing 2 rings, where the second ring is incorrectly treated as a hole:
result := mp1.Union(mp2)
// Actual: geom.MultiPolygon with 1 polygon containing 2 rings (second ring treated as hole)
// Actual GeoJSON structure:
// [
// [
// [[0,0], [1,0], [1,1], [0,1], [0,0]], // Ring 0: exterior
// [[2,0], [3,0], [3,1], [2,1], [2,0]] // Ring 1: incorrectly treated as hole
// ]
// ]Minimal Reproducible Example
package main
import (
"fmt"
"github.com/ctessum/geom"
)
func main() {
// Two non-overlapping squares
mp1 := geom.MultiPolygon{
{
{{X: 0, Y: 0}, {X: 1, Y: 0}, {X: 1, Y: 1}, {X: 0, Y: 1}, {X: 0, Y: 0}},
},
}
mp2 := geom.MultiPolygon{
{
{{X: 2, Y: 0}, {X: 3, Y: 0}, {X: 3, Y: 1}, {X: 2, Y: 1}, {X: 2, Y: 0}},
},
}
result := mp1.Union(mp2)
// Check result
if mp, ok := result.(geom.MultiPolygon); ok {
if len(mp) == 2 {
fmt.Printf("✓ Correct: MultiPolygon with %d polygons\n", len(mp))
} else if len(mp) == 1 && len(mp[0]) > 1 {
fmt.Printf("✗ Bug: MultiPolygon with 1 polygon containing %d rings\n", len(mp[0]))
fmt.Printf(" Ring 0: %d points (exterior)\n", len(mp[0][0]))
fmt.Printf(" Ring 1: %d points (incorrectly treated as hole)\n", len(mp[0][1]))
fmt.Printf(" Area: %.2f (coincidentally correct, but structure is wrong)\n", mp[0].Area())
}
} else if p, ok := result.(geom.Polygon); ok {
fmt.Printf("✗ Bug: Single Polygon with %d rings\n", len(p))
fmt.Printf(" Ring 0: %d points\n", len(p[0]))
fmt.Printf(" Ring 1: %d points (incorrectly treated as hole)\n", len(p[1]))
fmt.Printf(" Area: %.2f (coincidentally correct, but structure is wrong)\n", p.Area())
}
}Output:
✗ Bug: MultiPolygon with 1 polygon containing 2 rings
Ring 0: 6 points (exterior)
Ring 1: 6 points (incorrectly treated as hole)
Area: 2.00 (coincidentally correct, but structure is wrong)
Metadata
Metadata
Assignees
Labels
No labels