Skip to content

Commit

Permalink
feat: reversewindingorder
Browse files Browse the repository at this point in the history
PDOK-16853
  • Loading branch information
roelarents authored Aug 15, 2024
1 parent 8b231db commit 0eb3179
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 8 deletions.
14 changes: 12 additions & 2 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ const TILEMATRICES string = `tilematrices`
const PAGESIZE string = `pagesize`
const KEEPPOINTSANDLINES string = `keeppointsandlines`
const IGNOREOUTSIDEGRID string = `ignoreoutsidegrid`
const REVERSEWINDINGORDER string = `reversewindingorder`

//nolint:funlen
func main() {
Expand Down Expand Up @@ -102,6 +103,14 @@ func main() {
Required: false,
EnvVars: []string{strcase.ToScreamingSnake(IGNOREOUTSIDEGRID)},
},
&cli.BoolFlag{
Name: REVERSEWINDINGORDER,
Aliases: []string{"rwo"},
Usage: "Reverse the winding order of rings in polygons, contrary to the OGC simple features spec",
Value: false,
Required: false,
EnvVars: []string{strcase.ToScreamingSnake(REVERSEWINDINGORDER)},
},
}

app.Action = func(c *cli.Context) error {
Expand Down Expand Up @@ -133,8 +142,9 @@ func main() {
overwrite := c.Bool(OVERWRITE)
pagesize := c.Int(PAGESIZE) // TODO divide by tile matrices count
snapConfig := snap.Config{
KeepPointsAndLines: c.Bool(KEEPPOINTSANDLINES),
IgnoreOutsideGrid: c.Bool(IGNOREOUTSIDEGRID),
KeepPointsAndLines: c.Bool(KEEPPOINTSANDLINES),
IgnoreOutsideGrid: c.Bool(IGNOREOUTSIDEGRID),
ReverseWindingOrder: c.Bool(REVERSEWINDINGORDER),
}
for _, tmID := range tileMatrixIDs {
gpkgTargets[tmID] = initGPKGTarget(targetPathFmt, tmID, overwrite, pagesize)
Expand Down
25 changes: 19 additions & 6 deletions snap/snap.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,9 @@ const (
type IsOuter = bool

type Config struct {
KeepPointsAndLines bool
IgnoreOutsideGrid bool
KeepPointsAndLines bool
IgnoreOutsideGrid bool
ReverseWindingOrder bool
}

// SnapPolygon snaps polygons' points to a tile's internal pixel grid
Expand Down Expand Up @@ -62,7 +63,7 @@ func SnapPolygon(polygon geom.Polygon, tileMatrixSet tms20.TileMatrixSet, tmIDs
}
}

newPolygonsPerLevel := addPointsAndSnap(ix, polygon, levels, config.KeepPointsAndLines)
newPolygonsPerLevel := addPointsAndSnap(ix, polygon, levels, config)

newPolygonsPerTileMatrixID := make(map[tms20.TMID][]geom.Polygon, len(newPolygonsPerLevel))
for level, newPolygons := range newPolygonsPerLevel {
Expand All @@ -85,7 +86,7 @@ func tileMatrixIDsByLevels(tms tms20.TileMatrixSet, tmIDs []tms20.TMID) map[poin
}

//nolint:cyclop
func addPointsAndSnap(ix *pointindex.PointIndex, polygon geom.Polygon, levels []pointindex.Level, keepPointsAndLines bool) map[pointindex.Level][]geom.Polygon {
func addPointsAndSnap(ix *pointindex.PointIndex, polygon geom.Polygon, levels []pointindex.Level, config Config) map[pointindex.Level][]geom.Polygon {
levelMap := mapslicehelp.AsKeys(levels)
newOuters := make(map[pointindex.Level][][][2]float64, len(levels))
newInners := make(map[pointindex.Level][][][2]float64, len(levels))
Expand Down Expand Up @@ -122,13 +123,13 @@ func addPointsAndSnap(ix *pointindex.PointIndex, polygon geom.Polygon, levels []
for level := range levelMap {
outerRings, innerRings, pointsAndLines := cleanupNewRing(newRing[level], isOuter, ix.GetHitMultiple(level), ringIdx)
// Check if outer ring has become too small
if isOuter && len(outerRings) == 0 && (!keepPointsAndLines || len(pointsAndLines) == 0) {
if isOuter && len(outerRings) == 0 && (!config.KeepPointsAndLines || len(pointsAndLines) == 0) {
delete(levelMap, level) // If too small, delete it
continue
}
newOuters[level] = append(newOuters[level], outerRings...)
newInners[level] = append(newInners[level], innerRings...)
if keepPointsAndLines {
if config.KeepPointsAndLines {
newPointsAndLines[level] = append(newPointsAndLines[level], pointsAndLines...)
}
}
Expand All @@ -138,6 +139,7 @@ func addPointsAndSnap(ix *pointindex.PointIndex, polygon geom.Polygon, levels []
for l := range levelMap {
newOuters[l], newInners[l] = dedupeInnersOuters(newOuters[l], newInners[l])
newPolygonsForLevel := matchInnersToPolygons(outersToPolygons(newOuters[l]), newInners[l], len(polygon) > 1)
reverseWindingOrderIfConfigured(newPolygonsForLevel, config)
if len(newPolygonsForLevel) > 0 {
newPolygons[l] = newPolygonsForLevel
}
Expand All @@ -152,6 +154,17 @@ func addPointsAndSnap(ix *pointindex.PointIndex, polygon geom.Polygon, levels []
return geomhelp.FloatPolygonsToGeomPolygonsForAllKeys(newPolygons)
}

func reverseWindingOrderIfConfigured(polygons [][][][2]float64, config Config) {
if !config.ReverseWindingOrder {
return
}
for i := range polygons {
for j := range polygons[i] {
slices.Reverse(polygons[i][j])
}
}
}

func outersToPolygons(outers [][][2]float64) [][][][2]float64 {
polygons := make([][][][2]float64, len(outers))
for i := 0; i < len(outers); i++ {
Expand Down
64 changes: 64 additions & 0 deletions snap/snap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -715,6 +715,70 @@ func TestSnap_snapPolygon(t *testing.T) {
polygon: geom.Polygon{{{0.1, 0.1}, {0.2, 0.1}, {0.2, -0.1}}},
want: map[tms20.TMID][]geom.Polygon{}, // empty, ignored
},
{
name: "correct winding order",
tms: newSimpleTileMatrixSet(2, 64),
config: Config{ReverseWindingOrder: false},
tmIDs: []tms20.TMID{1},
polygon: geom.Polygon{
{{60.0, 124.0}, {60.0, 4.0}, {4.0, 4.0}, {4.0, 124.0}}, // cw
{{12.0, 76.0}, {28.0, 76.0}, {52.0, 76.0}, {52.0, 116.0}, {12.0, 116.0}}, // ccw
},
want: map[tms20.TMID][]geom.Polygon{
1: {{
{{4.0, 124.0}, {4.0, 4.0}, {60.0, 4.0}, {60.0, 124.0}}, // ccw
{{12.0, 116.0}, {52.0, 116.0}, {52.0, 76.0}, {28.0, 76.0}, {12.0, 76.0}}, // cw
}},
},
},
{
name: "reverse winding order",
tms: newSimpleTileMatrixSet(2, 64),
config: Config{ReverseWindingOrder: true},
tmIDs: []tms20.TMID{1},
polygon: geom.Polygon{
{{4.0, 124.0}, {4.0, 4.0}, {60.0, 4.0}, {60.0, 124.0}}, // ccw
{{12.0, 116.0}, {52.0, 116.0}, {52.0, 76.0}, {28.0, 76.0}, {12.0, 76.0}}, // cw
},
want: map[tms20.TMID][]geom.Polygon{
1: {{
{{60.0, 124.0}, {60.0, 4.0}, {4.0, 4.0}, {4.0, 124.0}}, // cw
{{12.0, 76.0}, {28.0, 76.0}, {52.0, 76.0}, {52.0, 116.0}, {12.0, 116.0}}, // ccw
}},
},
},
{
name: "do not reverse winding order",
tms: newSimpleTileMatrixSet(2, 64),
config: Config{ReverseWindingOrder: false},
tmIDs: []tms20.TMID{1},
polygon: geom.Polygon{
{{4.0, 124.0}, {4.0, 4.0}, {60.0, 4.0}, {60.0, 124.0}}, // ccw
{{12.0, 116.0}, {52.0, 116.0}, {52.0, 76.0}, {28.0, 76.0}, {12.0, 76.0}}, // cw
},
want: map[tms20.TMID][]geom.Polygon{
1: {{
{{4.0, 124.0}, {4.0, 4.0}, {60.0, 4.0}, {60.0, 124.0}}, // ccw
{{12.0, 116.0}, {52.0, 116.0}, {52.0, 76.0}, {28.0, 76.0}, {12.0, 76.0}}, // cw
}},
},
},
{
name: "keep reversed winding order",
tms: newSimpleTileMatrixSet(2, 64),
config: Config{ReverseWindingOrder: true},
tmIDs: []tms20.TMID{1},
polygon: geom.Polygon{
{{60.0, 124.0}, {60.0, 4.0}, {4.0, 4.0}, {4.0, 124.0}}, // cw
{{12.0, 76.0}, {28.0, 76.0}, {52.0, 76.0}, {52.0, 116.0}, {12.0, 116.0}}, // ccw
},
want: map[tms20.TMID][]geom.Polygon{
1: {{
{{60.0, 124.0}, {60.0, 4.0}, {4.0, 4.0}, {4.0, 124.0}}, // cw
{{12.0, 76.0}, {28.0, 76.0}, {52.0, 76.0}, {52.0, 116.0}, {12.0, 116.0}}, // ccw
}},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down

0 comments on commit 0eb3179

Please sign in to comment.