Skip to content

Commit

Permalink
Merge pull request #87 from paulmach/covering-error
Browse files Browse the repository at this point in the history
maptile/tilecover: polygon converings can return errors
  • Loading branch information
paulmach authored May 10, 2022
2 parents e141706 + 9c5b337 commit 082aab3
Show file tree
Hide file tree
Showing 6 changed files with 70 additions and 34 deletions.
2 changes: 1 addition & 1 deletion maptile/tilecover/benchmarks_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ func BenchmarkRussia_z0z9(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
tiles := Geometry(g, 9)
tiles, _ := Geometry(g, 9)
MergeUp(tiles, 0)
}
}
Expand Down
6 changes: 3 additions & 3 deletions maptile/tilecover/cover_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,11 +96,11 @@ func TestTestdata(t *testing.T) {

expected := loadFeatureCollection(t, "./testdata/"+tc.name+"_out.geojson")

tiles := Geometry(f.Geometry, tc.max)
tiles, _ := Geometry(f.Geometry, tc.max)
result := MergeUp(tiles, tc.min).ToFeatureCollection()
compareFeatureCollections(t, tc.name, result, expected)

tiles = Geometry(f.Geometry, tc.max)
tiles, _ = Geometry(f.Geometry, tc.max)
result = MergeUpPartial(tiles, tc.min, 4).ToFeatureCollection()
compareFeatureCollections(t, tc.name, result, expected)
})
Expand All @@ -123,7 +123,7 @@ func TestCountries(t *testing.T) {
for _, country := range countries {
t.Run(country, func(t *testing.T) {
f := loadFeature(t, "./testdata/world/"+country+".geo.json")
tiles := Geometry(f.Geometry, 6)
tiles, _ := Geometry(f.Geometry, 6)
tiles = MergeUp(tiles, 1)

expected := loadFeatureCollection(t, "./testdata/world/"+country+"_out.geojson")
Expand Down
24 changes: 14 additions & 10 deletions maptile/tilecover/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,20 @@ import (
)

// Geometry returns the covering set of tiles for the given geometry.
func Geometry(g orb.Geometry, z maptile.Zoom) maptile.Set {
func Geometry(g orb.Geometry, z maptile.Zoom) (maptile.Set, error) {
if g == nil {
return nil
return nil, nil
}

switch g := g.(type) {
case orb.Point:
return Point(g, z)
return Point(g, z), nil
case orb.MultiPoint:
return MultiPoint(g, z)
return MultiPoint(g, z), nil
case orb.LineString:
return LineString(g, z)
return LineString(g, z), nil
case orb.MultiLineString:
return MultiLineString(g, z)
return MultiLineString(g, z), nil
case orb.Ring:
return Ring(g, z)
case orb.Polygon:
Expand All @@ -32,7 +32,7 @@ func Geometry(g orb.Geometry, z maptile.Zoom) maptile.Set {
case orb.Collection:
return Collection(g, z)
case orb.Bound:
return Bound(g, z)
return Bound(g, z), nil
}

panic(fmt.Sprintf("geometry type not supported: %T", g))
Expand Down Expand Up @@ -75,11 +75,15 @@ func Bound(b orb.Bound, z maptile.Zoom) maptile.Set {

// Collection returns the covering set of tiles for the
// geoemtry collection.
func Collection(c orb.Collection, z maptile.Zoom) maptile.Set {
func Collection(c orb.Collection, z maptile.Zoom) (maptile.Set, error) {
set := make(maptile.Set)
for _, g := range c {
set.Merge(Geometry(g, z))
s, err := Geometry(g, z)
if err != nil {
return nil, err
}
set.Merge(s)
}

return set
return set, nil
}
18 changes: 9 additions & 9 deletions maptile/tilecover/merge_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,19 @@ import (
func TestMergeUp(t *testing.T) {
f := loadFeature(t, "./testdata/line.geojson")

tiles := Geometry(f.Geometry, 15)
tiles, _ := Geometry(f.Geometry, 15)
c1 := len(MergeUpPartial(tiles, 1, 1))

tiles = Geometry(f.Geometry, 15)
tiles, _ = Geometry(f.Geometry, 15)
c2 := len(MergeUpPartial(tiles, 1, 2))

tiles = Geometry(f.Geometry, 15)
tiles, _ = Geometry(f.Geometry, 15)
c3 := len(MergeUpPartial(tiles, 1, 3))

tiles = Geometry(f.Geometry, 15)
tiles, _ = Geometry(f.Geometry, 15)
c4 := len(MergeUpPartial(tiles, 1, 4))

tiles = Geometry(f.Geometry, 15)
tiles, _ = Geometry(f.Geometry, 15)
c := len(MergeUp(tiles, 1))

if c1 > c2 {
Expand All @@ -43,7 +43,7 @@ func TestMergeUp(t *testing.T) {

func BenchmarkMergeUp_z0z10(b *testing.B) {
g := loadFeature(b, "./testdata/russia.geojson").Geometry
tiles := Geometry(g, 10)
tiles, _ := Geometry(g, 10)

b.ReportAllocs()
b.ResetTimer()
Expand All @@ -54,7 +54,7 @@ func BenchmarkMergeUp_z0z10(b *testing.B) {

func BenchmarkMergeUp_z8z9(b *testing.B) {
g := loadFeature(b, "./testdata/russia.geojson").Geometry
tiles := Geometry(g, 9)
tiles, _ := Geometry(g, 9)

b.ReportAllocs()
b.ResetTimer()
Expand All @@ -65,7 +65,7 @@ func BenchmarkMergeUp_z8z9(b *testing.B) {

func BenchmarkMergeUpPartial4_z0z10(b *testing.B) {
g := loadFeature(b, "./testdata/russia.geojson").Geometry
tiles := Geometry(g, 10)
tiles, _ := Geometry(g, 10)

b.ReportAllocs()
b.ResetTimer()
Expand All @@ -76,7 +76,7 @@ func BenchmarkMergeUpPartial4_z0z10(b *testing.B) {

func BenchmarkMergeUpPartial4_z8z9(b *testing.B) {
g := loadFeature(b, "./testdata/russia.geojson").Geometry
tiles := Geometry(g, 9)
tiles, _ := Geometry(g, 9)

b.ReportAllocs()
b.ResetTimer()
Expand Down
36 changes: 25 additions & 11 deletions maptile/tilecover/polygon.go
Original file line number Diff line number Diff line change
@@ -1,40 +1,52 @@
package tilecover

import (
"errors"
"sort"

"github.com/paulmach/orb"
"github.com/paulmach/orb/maptile"
)

// ErrUnevenIntersections can be returned when clipping polygons
// and there are issues with the geometries, like the rings are not closed.
var ErrUnevenIntersections = errors.New("tilecover: uneven intersections, ring not closed?")

// Ring creates a tile cover for the ring.
func Ring(r orb.Ring, z maptile.Zoom) maptile.Set {
func Ring(r orb.Ring, z maptile.Zoom) (maptile.Set, error) {
if len(r) == 0 {
return make(maptile.Set)
return make(maptile.Set), nil
}

return Polygon(orb.Polygon{r}, z)
}

// Polygon creates a tile cover for the polygon.
func Polygon(p orb.Polygon, z maptile.Zoom) maptile.Set {
func Polygon(p orb.Polygon, z maptile.Zoom) (maptile.Set, error) {
set := make(maptile.Set)
polygon(set, p, z)

return set
err := polygon(set, p, z)
if err != nil {
return nil, err
}

return set, nil
}

// MultiPolygon creates a tile cover for the multi-polygon.
func MultiPolygon(mp orb.MultiPolygon, z maptile.Zoom) maptile.Set {
func MultiPolygon(mp orb.MultiPolygon, z maptile.Zoom) (maptile.Set, error) {
set := make(maptile.Set)
for _, p := range mp {
polygon(set, p, z)
err := polygon(set, p, z)
if err != nil {
return nil, err
}
}

return set
return set, nil
}

func polygon(set maptile.Set, p orb.Polygon, zoom maptile.Zoom) {
func polygon(set maptile.Set, p orb.Polygon, zoom maptile.Zoom) error {
intersections := make([][2]uint32, 0)

for _, r := range p {
Expand All @@ -47,7 +59,7 @@ func polygon(set maptile.Set, p orb.Polygon, zoom maptile.Zoom) {
y := ring[i][1]

// add interesction if it's not local extremum or duplicate
if (y > ring[pi][1] || y > ring[ni][1]) && // not local minimum
if (ring[pi][1] < y || ring[ni][1] < y) && // not local minimum
(y < ring[pi][1] || y < ring[ni][1]) && // not local maximum
y != ring[ni][1] {

Expand All @@ -57,7 +69,7 @@ func polygon(set maptile.Set, p orb.Polygon, zoom maptile.Zoom) {
}

if len(intersections)%2 != 0 {
panic("tilecover: uneven intersections, ring not closed?")
return ErrUnevenIntersections
}

// sort by y, then x
Expand All @@ -79,4 +91,6 @@ func polygon(set maptile.Set, p orb.Polygon, zoom maptile.Zoom) {
set[maptile.New(x, y, zoom)] = true
}
}

return nil
}
18 changes: 18 additions & 0 deletions maptile/tilecover/polygon_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package tilecover

import (
"testing"

"github.com/paulmach/orb"
)

func TestRing_error(t *testing.T) {
// not a closed ring
f := loadFeature(t, "./testdata/line.geojson")
l := f.Geometry.(orb.LineString)

_, err := Ring(orb.Ring(l), 25)
if err != ErrUnevenIntersections {
t.Errorf("incorrect error: %v", err)
}
}

0 comments on commit 082aab3

Please sign in to comment.