Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace Geometry interface with sum-type #87

Merged
merged 32 commits into from
Jan 6, 2020
Merged
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
cb7729d
Rename Geometry to GeometryX to free up Geometry name
peterstace Jan 1, 2020
19abb1f
Add selection of subtype to Geometry
peterstace Jan 1, 2020
1cc995a
Add conversion from each geometry type to a Geometry
peterstace Jan 1, 2020
c4ff8cb
Fix compile error in tests
peterstace Jan 1, 2020
75b31be
Remove AsText from GeometryX interface
peterstace Jan 2, 2020
86a2a0c
Remove AppendWKT from GeometryX
peterstace Jan 2, 2020
ba62caf
Remove AsBinary from GeometryX
peterstace Jan 2, 2020
7cea0b7
Remove Dimension from GeometryX
peterstace Jan 2, 2020
e0e6586
Get fuzz tests working again
peterstace Jan 2, 2020
3e123c8
Remove Intersects and json.Marshaler from GeometryX
peterstace Jan 2, 2020
6500cc4
Implement UnmarshalJSON for Geometry
peterstace Jan 2, 2020
302ae86
Remove AnyGeometry type
peterstace Jan 2, 2020
d7141cf
Get tests working again (?)
peterstace Jan 3, 2020
4a4e479
Remove Intersects from GeometryX interface
peterstace Jan 3, 2020
b8e02e7
Remove IsEmpty from GeometryX
peterstace Jan 3, 2020
bc706c2
Remove ConvexHull from GeometryX
peterstace Jan 3, 2020
fb23456
Remove Envelope from GeometryX interface
peterstace Jan 3, 2020
838f278
Remove Boundary from GeometryX
peterstace Jan 3, 2020
3fe1069
Remove EqualsExact from GeometryX interface
peterstace Jan 3, 2020
6d25e5f
Remove IsValid from GeometryX interface
peterstace Jan 3, 2020
d4400db
Remove TransformXY from GeometryX
peterstace Jan 3, 2020
7598086
WIP hacking to remove GeometryX interface
peterstace Jan 3, 2020
e8d7904
Fix broken test
peterstace Jan 4, 2020
c1b047b
Fix IsSimple for Geometry
peterstace Jan 4, 2020
47f5b54
Fix Centroid function
peterstace Jan 4, 2020
7cbc637
Fix fuzz test for centroid
peterstace Jan 4, 2020
02ea888
Fix area tests
peterstace Jan 4, 2020
aa3e935
Rename gFromWKT back to geomFromWKT
peterstace Jan 4, 2020
ce8ec89
Simplifications
peterstace Jan 4, 2020
0aeb969
Remove GeometryBuilder type
peterstace Jan 5, 2020
fc40f43
Minor fixes from self-review
peterstace Jan 5, 2020
0868707
Merge branch 'master' into sum_type
peterstace Jan 6, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 41 additions & 46 deletions fuzztests/checks.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"errors"
"fmt"
"math"
"reflect"
"strconv"
"strings"
"testing"
Expand Down Expand Up @@ -138,13 +137,13 @@ func CheckWKT(t *testing.T, pg PostGIS, g geom.Geometry) {

func CheckWKB(t *testing.T, pg PostGIS, g geom.Geometry) {
t.Run("CheckWKB", func(t *testing.T) {
if _, ok := g.(geom.EmptySet); ok && g.AsText() == "POINT EMPTY" {
if g.IsEmptySet() && g.AsText() == "POINT EMPTY" {
// Empty point WKB use NaN as part of their representation.
// Go's math.NaN() and Postgis use slightly different (but
// compatible) representations of NaN.
return
}
if _, ok := g.(geom.GeometryCollection); ok && g.IsEmpty() {
if g.IsGeometryCollection() && g.IsEmpty() {
// The behaviour for GeometryCollections in Postgis is to just
// give 'GEOMETRYCOLLECTION EMPTY' whenever the contents of a
// geometry collection are all empty geometries. This doesn't
Expand Down Expand Up @@ -177,9 +176,10 @@ func CheckGeoJSON(t *testing.T, pg PostGIS, g geom.Geometry) {
// SHOULD be avoided when that single part or a single object of
// multipart type (MultiPoint, MultiLineString, or MultiPolygon)
// could be used instead.
if gc, ok := g.(geom.GeometryCollection); ok {
if g.IsGeometryCollection() {
gc := g.AsGeometryCollection()
for i := 0; i < gc.NumGeometries(); i++ {
if _, ok := gc.GeometryN(i).(geom.GeometryCollection); ok {
if gc.GeometryN(i).IsGeometryCollection() {
return
}
}
Expand Down Expand Up @@ -238,7 +238,7 @@ func CheckEnvelope(t *testing.T, pg PostGIS, g geom.Geometry) {
got := env.AsGeometry()
want := pg.Envelope(t, g)

if !reflect.DeepEqual(got, want) {
if !got.EqualsExact(want) {
t.Logf("got: %v", got.AsText())
t.Logf("want: %v", want.AsText())
t.Error("mismatch")
Expand All @@ -248,12 +248,11 @@ func CheckEnvelope(t *testing.T, pg PostGIS, g geom.Geometry) {

func CheckIsSimple(t *testing.T, pg PostGIS, g geom.Geometry) {
t.Run("CheckIsSimple", func(t *testing.T) {
s, ok := g.(interface{ IsSimple() bool })
got, ok := g.IsSimple()
if ok == g.IsGeometryCollection() {
t.Error("Expected not to have IsSimple defined for GC")
}
if !ok {
_, ok := g.(geom.GeometryCollection)
if !ok {
t.Fatalf("GeometryCollection is the only type that doesn't implement IsSimple")
}
return
}

Expand All @@ -263,18 +262,18 @@ func CheckIsSimple(t *testing.T, pg PostGIS, g geom.Geometry) {
// deduplicating the LineStrings before checking simplicity. This
// library doesn't do that, so skip any LineStrings that contain
// duplicates.
if mls, ok := g.(geom.MultiLineString); ok {
if g.IsMultiLineString() {
mls := g.AsMultiLineString()
n := mls.NumLineStrings()
for i := 0; i < n; i++ {
for j := 0; j < n; j++ {
if reflect.DeepEqual(mls.LineStringN(i), mls.LineStringN(j)) {
if mls.LineStringN(i).EqualsExact(mls.LineStringN(j).AsGeometry()) {
return
}
}
}
}

got := s.IsSimple()
want := pg.IsSimple(t, g)
if got != want {
t.Logf("got: %v", got)
Expand All @@ -286,7 +285,7 @@ func CheckIsSimple(t *testing.T, pg PostGIS, g geom.Geometry) {

func CheckBoundary(t *testing.T, pg PostGIS, g geom.Geometry) {
t.Run("CheckBoundary", func(t *testing.T) {
if _, ok := g.(geom.GeometryCollection); ok {
if g.IsGeometryCollection() {
// PostGIS cannot calculate the boundary of GeometryCollections.
// Some other libraries can, so simplefeatures does as well.
return
Expand Down Expand Up @@ -327,8 +326,10 @@ func CheckIsValid(t *testing.T, pg PostGIS, g geom.Geometry) {

func CheckIsRing(t *testing.T, pg PostGIS, g geom.Geometry) {
t.Run("CheckIsRing", func(t *testing.T) {
ringer, ok := g.(interface{ IsRing() bool })
got := ok && ringer.IsRing()
var got bool
if g.IsLineString() {
got = g.AsLineString().IsRing()
}
want := pg.IsRing(t, g)
if got != want {
t.Logf("got: %t", got)
Expand All @@ -339,13 +340,17 @@ func CheckIsRing(t *testing.T, pg PostGIS, g geom.Geometry) {
}

func CheckLength(t *testing.T, pg PostGIS, g geom.Geometry) {
switch g.(type) {
case geom.Line, geom.LineString, geom.MultiLineString:
default:
if !g.IsLine() && !g.IsLineString() && !g.IsMultiLineString() {
if _, ok := g.Length(); ok {
t.Error("didn't expect to be able to get length but could")
}
return
}
t.Run("CheckLength", func(t *testing.T) {
got := g.(interface{ Length() float64 }).Length()
got, ok := g.Length()
if !ok {
t.Error("could not get length")
}
want := pg.Length(t, g)
if math.Abs(got-want) > 1e-6 {
t.Logf("got: %v", got)
Expand All @@ -369,9 +374,7 @@ func CheckEqualsExact(t *testing.T, pg PostGIS, g1, g2 geom.Geometry) {

func CheckEquals(t *testing.T, pg PostGIS, g1, g2 geom.Geometry) {
t.Run("CheckEquals", func(t *testing.T) {
_, g1GC := g1.(geom.GeometryCollection)
_, g2GC := g2.(geom.GeometryCollection)
if g1GC || g2GC {
if g1.IsGeometryCollection() || g2.IsGeometryCollection() {
// PostGIS cannot calculate Equals for geometry collections.
return
}
Expand Down Expand Up @@ -412,9 +415,7 @@ func CheckIntersection(t *testing.T, pg PostGIS, g1, g2 geom.Geometry) {
return // Both empty, so they match.
}

_, gotIsGC := got.(geom.GeometryCollection)
_, wantIsGC := want.(geom.GeometryCollection)
if gotIsGC || wantIsGC {
if got.IsGeometryCollection() || want.IsGeometryCollection() {
// GeometryCollections are not supported by ST_Equals. So there's
// not much that we can do here.
return
Expand All @@ -435,13 +436,14 @@ func CheckIntersection(t *testing.T, pg PostGIS, g1, g2 geom.Geometry) {
}

func CheckArea(t *testing.T, pg PostGIS, g geom.Geometry) {
switch g.(type) {
case geom.Polygon, geom.MultiPolygon:
default:
if !g.IsPolygon() && !g.IsMultiPolygon() {
return
}
t.Run("CheckArea", func(t *testing.T) {
got := g.(interface{ Area() float64 }).Area()
got, ok := g.Area()
if !ok {
t.Fatal("could not get area")
}
want := pg.Area(t, g)
const eps = 0.000000001
if math.Abs(got-want) > eps {
Expand All @@ -454,30 +456,23 @@ func CheckArea(t *testing.T, pg PostGIS, g geom.Geometry) {

func CheckCentroid(t *testing.T, pg PostGIS, g geom.Geometry) {
t.Run("CheckCentroid", func(t *testing.T) {
var got geom.Point
var empty bool
switch g := g.(type) {
case geom.Polygon:
got = g.Centroid()
case geom.MultiPolygon:
var ok bool
got, ok = g.Centroid()
empty = !ok
default:
if !g.IsPolygon() && !g.IsMultiPolygon() {
return
}

got, ok := g.Centroid()
want := pg.Centroid(t, g)

if empty {
if !ok {
if !want.IsEmpty() {
t.Log("got: empty", got)
t.Logf("want: %v", want)
t.Log("got: undefined")
t.Logf("want: %v", want.AsText())
t.Error("mismatch")
}
} else {
if !got.EqualsExact(want, geom.Tolerance(0.000000001)) {
t.Logf("got: %v", got)
t.Logf("want: %v", want)
t.Logf("got: %v", got.AsText())
t.Logf("want: %v", want.AsText())
t.Error("mismatch")
}
}
Expand Down
12 changes: 6 additions & 6 deletions fuzztests/postgis.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,13 @@ func (p PostGIS) GeoJSONIsValidWithReason(t *testing.T, geojson string) (bool, s
}

func (p PostGIS) geomFunc(t *testing.T, g geom.Geometry, stFunc string) geom.Geometry {
var ag geom.AnyGeometry
var result geom.Geometry
if err := p.db.QueryRow(
"SELECT ST_AsBinary("+stFunc+"(ST_GeomFromWKB($1)))", g,
).Scan(&ag); err != nil {
).Scan(&result); err != nil {
t.Fatalf("pg error: %v", err)
}
return ag.Geom
return result
}

func (p PostGIS) boolFunc(t *testing.T, g geom.Geometry, stFunc string) bool {
Expand Down Expand Up @@ -165,14 +165,14 @@ func (p PostGIS) boolBinary(t *testing.T, g1, g2 geom.Geometry, stFunc string) b
}

func (p PostGIS) geomBinary(t *testing.T, g1, g2 geom.Geometry, stFunc string) geom.Geometry {
var ag geom.AnyGeometry
var result geom.Geometry
if err := p.db.QueryRow(
"SELECT ST_AsBinary("+stFunc+"(ST_GeomFromWKB($1), ST_GeomFromWKB($2)))",
g1, g2,
).Scan(&ag); err != nil {
).Scan(&result); err != nil {
t.Fatalf("pg error: %v", err)
}
return ag.Geom
return result
}

func (p PostGIS) AsText(t *testing.T, g geom.Geometry) string {
Expand Down
Loading