Add FixWinding and RequireWinding parse options (RFC 7946 §3.1.6)#29
Add FixWinding and RequireWinding parse options (RFC 7946 §3.1.6)#29anativ wants to merge 2 commits into
Conversation
Enforce RFC 7946 §3.1.6 polygon ring winding during parse: exterior rings counterclockwise, holes clockwise. FixWinding rewrites non-compliant rings in place (reversing both points and any z/m extras); RequireWinding rejects non-compliant polygons. Degenerate rings (zero signed area) are left untouched. Applies to both Polygon and MultiPolygon.
Risk AssessmentRisk Score: LowJustificationThis PR introduces two new opt-in Database Migration RisksNone detected. This is a Go library with no persistence layer or schema. Breaking API ChangesNone detected.
Backward CompatibilityFully backward compatible. Existing clients continue to work without any code changes because:
One minor note for callers: the doc comments state the two flags cannot be combined, but the implementation does not explicitly reject that combination — if both are set, RISK_SCORE: Low |
- Expand FixWinding/RequireWinding godoc: note AllowRects interaction, the antimeridian limitation of planar shoelace classification, RFC 7946's "SHOULD NOT reject" compatibility clause, and the FixWinding + RequireWinding precedence (strict wins). - ringOrientation: note the antimeridian caveat. - reverseExtraRange: bounds-check the computed slice range before swapping. - Add tests: RequireWinding accepts degenerate rings, FixWinding on a polygon with a hole and z-coords, MultiPolygon FixWinding with per-child z-values.
Summary
Adds two opt-in
ParseOptionsflags that enforce RFC 7946 §3.1.6 polygon ring winding (exterior CCW, holes CW) during parsing ofPolygonandMultiPolygon:FixWinding— silently rewrites non-compliant rings by reversing their points in place. Any z/m values inextra.valuesare reordered to stay aligned with their points, so 3D/4D polygons survive the fix.RequireWinding— returnserrCoordinatesInvalidwhen any ring has the wrong winding. Useful for strict validation pipelines.Rings with zero signed area (degenerate/collinear) are left alone — they carry no orientation to correct.
Both flags default to
false, so existing callers are unaffected.Implementation notes
> 0→ CCW;< 0→ CW;== 0→ skip.[P0, P1, ..., Pn, P0]preserves the first-last equality required by GeoJSON linear rings.geometry.Polyis constructed, so the resulting polygon and its bbox/index are computed from the corrected coordinates.Test plan
go test ./...passesgofmt -l ./go vet ./...cleanwinding_test.gocover:ringOrientationunit tests for CCW / CW / degenerate rings