diff --git a/CHANGELOG.md b/CHANGELOG.md index a5b148fd..64cc21b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,24 @@ # Changelog +## Unreleased + +- **Breaking change**: Renames the `AsFoo` methods of the Geometry type to + `MustAsFoo` (where `Foo` is a concrete geometry type such as `Point`). This + follows the go convention that methods and functions prefixed with Must may + panic if preconditions are not met. Note that there's no change in behaviour + here, it's simply a rename (these methods previously panicked). Users may + resolve this breaking change by just updating the names of any `AsFoo` + methods they are calling to `MustAsFoo`. + +- **Breaking change**: Adds new methods named `AsFoo` to the Geometry type. + These methods have the signature `AsFoo() (Foo, bool)`. The boolean return + value indicates if the conversion was successful or not. These methods are + useful because they allow concrete geometries to be extracted from a Geometry + value, with the concrete type for the `Is` and `As` call only specified once. + Users now just have to call `AsFoo`, and can then check the flag. This helps + to eliminate the class of bugs there the type specified with `IsFoo` + erroneously differs from the type specified by `AsFoo`. + ## v0.33.1 __Special thanks to Albert Teoh for contributing to this release.__ diff --git a/geom/accessor_test.go b/geom/accessor_test.go index 5777efa0..53e0d812 100644 --- a/geom/accessor_test.go +++ b/geom/accessor_test.go @@ -7,18 +7,18 @@ import ( ) func TestPointAccessorNonEmpty(t *testing.T) { - xy, ok := geomFromWKT(t, "POINT(1 2)").AsPoint().XY() + xy, ok := geomFromWKT(t, "POINT(1 2)").MustAsPoint().XY() expectBoolEq(t, ok, true) expectXYEq(t, xy, XY{1, 2}) } func TestPointAccessorEmpty(t *testing.T) { - _, ok := geomFromWKT(t, "POINT EMPTY").AsPoint().XY() + _, ok := geomFromWKT(t, "POINT EMPTY").MustAsPoint().XY() expectBoolEq(t, ok, false) } func TestLineStringAccessor(t *testing.T) { - ls := geomFromWKT(t, "LINESTRING(1 2,3 4,5 6)").AsLineString() + ls := geomFromWKT(t, "LINESTRING(1 2,3 4,5 6)").MustAsLineString() seq := ls.Coordinates() pt12 := xyCoords(1, 2) pt34 := xyCoords(3, 4) @@ -47,7 +47,7 @@ func TestLineStringAccessor(t *testing.T) { } func TestLineStringEmptyAccessor(t *testing.T) { - ls := geomFromWKT(t, "LINESTRING EMPTY").AsLineString() + ls := geomFromWKT(t, "LINESTRING EMPTY").MustAsLineString() seq := ls.Coordinates() emptyPoint := geomFromWKT(t, "POINT EMPTY") @@ -68,7 +68,7 @@ func TestLineStringEmptyAccessor(t *testing.T) { } func TestLineStringAccessorWithDuplicates(t *testing.T) { - ls := geomFromWKT(t, "LINESTRING(1 2,3 4,3 4,5 6)").AsLineString() + ls := geomFromWKT(t, "LINESTRING(1 2,3 4,3 4,5 6)").MustAsLineString() seq := ls.Coordinates() pt12 := xyCoords(1, 2) pt34 := xyCoords(3, 4) @@ -88,7 +88,7 @@ func TestLineStringAccessorWithDuplicates(t *testing.T) { } func TestLineStringAccessorWithMoreDuplicates(t *testing.T) { - ls := geomFromWKT(t, "LINESTRING(1 2,1 2,3 4,3 4,3 4,5 6,5 6)").AsLineString() + ls := geomFromWKT(t, "LINESTRING(1 2,1 2,3 4,3 4,3 4,5 6,5 6)").MustAsLineString() seq := ls.Coordinates() pt12 := xyCoords(1, 2) pt34 := xyCoords(3, 4) @@ -111,7 +111,7 @@ func TestLineStringAccessorWithMoreDuplicates(t *testing.T) { } func TestPolygonAccessor(t *testing.T) { - poly := geomFromWKT(t, "POLYGON((0 0,5 0,5 3,0 3,0 0),(1 1,2 1,2 2,1 2,1 1),(3 1,4 1,4 2,3 2,3 1))").AsPolygon() + poly := geomFromWKT(t, "POLYGON((0 0,5 0,5 3,0 3,0 0),(1 1,2 1,2 2,1 2,1 1),(3 1,4 1,4 2,3 2,3 1))").MustAsPolygon() outer := geomFromWKT(t, "LINESTRING(0 0,5 0,5 3,0 3,0 0)") inner0 := geomFromWKT(t, "LINESTRING(1 1,2 1,2 2,1 2,1 1)") inner1 := geomFromWKT(t, "LINESTRING(3 1,4 1,4 2,3 2,3 1)") @@ -125,7 +125,7 @@ func TestPolygonAccessor(t *testing.T) { } func TestMultiPointAccessor(t *testing.T) { - mp := geomFromWKT(t, "MULTIPOINT((4 5),(2 3),(8 7))").AsMultiPoint() + mp := geomFromWKT(t, "MULTIPOINT((4 5),(2 3),(8 7))").MustAsMultiPoint() pt0 := geomFromWKT(t, "POINT(4 5)") pt1 := geomFromWKT(t, "POINT(2 3)") pt2 := geomFromWKT(t, "POINT(8 7)") @@ -139,7 +139,7 @@ func TestMultiPointAccessor(t *testing.T) { } func TestMultiLineStringAccessors(t *testing.T) { - mls := geomFromWKT(t, "MULTILINESTRING((1 2,3 4,5 6),(7 8,9 10,11 12))").AsMultiLineString() + mls := geomFromWKT(t, "MULTILINESTRING((1 2,3 4,5 6),(7 8,9 10,11 12))").MustAsMultiLineString() ls0 := geomFromWKT(t, "LINESTRING(1 2,3 4,5 6)") ls1 := geomFromWKT(t, "LINESTRING(7 8,9 10,11 12)") @@ -151,7 +151,7 @@ func TestMultiLineStringAccessors(t *testing.T) { } func TestMultiPolygonAccessors(t *testing.T) { - polys := geomFromWKT(t, "MULTIPOLYGON(((0 0,0 1,1 0,0 0)),((2 0,2 1,3 0,2 0)))").AsMultiPolygon() + polys := geomFromWKT(t, "MULTIPOLYGON(((0 0,0 1,1 0,0 0)),((2 0,2 1,3 0,2 0)))").MustAsMultiPolygon() poly0 := geomFromWKT(t, "POLYGON((0 0,0 1,1 0,0 0))") poly1 := geomFromWKT(t, "POLYGON((2 0,2 1,3 0,2 0))") @@ -163,7 +163,7 @@ func TestMultiPolygonAccessors(t *testing.T) { } func TestGeometryCollectionAccessors(t *testing.T) { - geoms := geomFromWKT(t, "GEOMETRYCOLLECTION(POLYGON((0 0,0 1,1 0,0 0)),POLYGON((2 0,2 1,3 0,2 0)))").AsGeometryCollection() + geoms := geomFromWKT(t, "GEOMETRYCOLLECTION(POLYGON((0 0,0 1,1 0,0 0)),POLYGON((2 0,2 1,3 0,2 0)))").MustAsGeometryCollection() geom0 := geomFromWKT(t, "POLYGON((0 0,0 1,1 0,0 0))") geom1 := geomFromWKT(t, "POLYGON((2 0,2 1,3 0,2 0))") diff --git a/geom/alg_convex_hull.go b/geom/alg_convex_hull.go index cf538404..579fd22a 100644 --- a/geom/alg_convex_hull.go +++ b/geom/alg_convex_hull.go @@ -80,7 +80,7 @@ func convexHullPointSet(g Geometry) []XY { switch { case g.IsGeometryCollection(): var points []XY - c := g.AsGeometryCollection() + c := g.MustAsGeometryCollection() n := c.NumGeometries() for i := 0; i < n; i++ { points = append( @@ -90,13 +90,13 @@ func convexHullPointSet(g Geometry) []XY { } return points case g.IsPoint(): - xy, ok := g.AsPoint().XY() + xy, ok := g.MustAsPoint().XY() if !ok { return nil } return []XY{xy} case g.IsLineString(): - cs := g.AsLineString().Coordinates() + cs := g.MustAsLineString().Coordinates() n := cs.Length() points := make([]XY, n) for i := 0; i < n; i++ { @@ -104,10 +104,10 @@ func convexHullPointSet(g Geometry) []XY { } return points case g.IsPolygon(): - p := g.AsPolygon() + p := g.MustAsPolygon() return convexHullPointSet(p.ExteriorRing().AsGeometry()) case g.IsMultiPoint(): - m := g.AsMultiPoint() + m := g.MustAsMultiPoint() n := m.NumPoints() points := make([]XY, 0, n) for i := 0; i < n; i++ { @@ -118,7 +118,7 @@ func convexHullPointSet(g Geometry) []XY { } return points case g.IsMultiLineString(): - m := g.AsMultiLineString() + m := g.MustAsMultiLineString() var points []XY n := m.NumLineStrings() for i := 0; i < n; i++ { @@ -130,7 +130,7 @@ func convexHullPointSet(g Geometry) []XY { } return points case g.IsMultiPolygon(): - m := g.AsMultiPolygon() + m := g.MustAsMultiPolygon() var points []XY numPolys := m.NumPolygons() for i := 0; i < numPolys; i++ { diff --git a/geom/alg_distance.go b/geom/alg_distance.go index e014e30b..95148352 100644 --- a/geom/alg_distance.go +++ b/geom/alg_distance.go @@ -103,21 +103,21 @@ func Distance(g1, g2 Geometry) (float64, bool) { func extractXYsAndLines(g Geometry) ([]XY, []line) { switch g.Type() { case TypePoint: - return g.AsPoint().asXYs(), nil + return g.MustAsPoint().asXYs(), nil case TypeLineString: - return nil, g.AsLineString().asLines() + return nil, g.MustAsLineString().asLines() case TypePolygon: - return nil, g.AsPolygon().Boundary().asLines() + return nil, g.MustAsPolygon().Boundary().asLines() case TypeMultiPoint: - return g.AsMultiPoint().asXYs(), nil + return g.MustAsMultiPoint().asXYs(), nil case TypeMultiLineString: - return nil, g.AsMultiLineString().asLines() + return nil, g.MustAsMultiLineString().asLines() case TypeMultiPolygon: - return nil, g.AsMultiPolygon().Boundary().asLines() + return nil, g.MustAsMultiPolygon().Boundary().asLines() case TypeGeometryCollection: var allXYs []XY var allLines []line - g.AsGeometryCollection().walk(func(child Geometry) { + g.MustAsGeometryCollection().walk(func(child Geometry) { xys, lns := extractXYsAndLines(child) allXYs = append(allXYs, xys...) allLines = append(allLines, lns...) diff --git a/geom/alg_dump_test.go b/geom/alg_dump_test.go index de22d78f..af422e0d 100644 --- a/geom/alg_dump_test.go +++ b/geom/alg_dump_test.go @@ -119,7 +119,7 @@ func TestDumpMultiPoint(t *testing.T) { } { t.Run(strconv.Itoa(i), func(t *testing.T) { want := geomsFromWKTs(t, tc.wantOutputWKT) - got := upcastPoints(geomFromWKT(t, tc.inputWKT).AsMultiPoint().Dump()) + got := upcastPoints(geomFromWKT(t, tc.inputWKT).MustAsMultiPoint().Dump()) expectGeomsEq(t, got, want) }) } @@ -153,7 +153,7 @@ func TestDumpMultiLineString(t *testing.T) { } { t.Run(strconv.Itoa(i), func(t *testing.T) { want := geomsFromWKTs(t, tc.wantOutputWKT) - got := upcastLineStrings(geomFromWKT(t, tc.inputWKT).AsMultiLineString().Dump()) + got := upcastLineStrings(geomFromWKT(t, tc.inputWKT).MustAsMultiLineString().Dump()) expectGeomsEq(t, got, want) }) } @@ -179,7 +179,7 @@ func TestDumpMultiPolygon(t *testing.T) { } { t.Run(strconv.Itoa(i), func(t *testing.T) { want := geomsFromWKTs(t, tc.wantOutputWKT) - got := upcastPolygons(geomFromWKT(t, tc.inputWKT).AsMultiPolygon().Dump()) + got := upcastPolygons(geomFromWKT(t, tc.inputWKT).MustAsMultiPolygon().Dump()) expectGeomsEq(t, got, want) }) } @@ -213,7 +213,7 @@ func TestDumpGeometryCollection(t *testing.T) { } { t.Run(strconv.Itoa(i), func(t *testing.T) { want := geomsFromWKTs(t, tc.wantOutputWKT) - got := geomFromWKT(t, tc.inputWKT).AsGeometryCollection().Dump() + got := geomFromWKT(t, tc.inputWKT).MustAsGeometryCollection().Dump() expectGeomsEq(t, got, want) }) } diff --git a/geom/alg_exact_equals.go b/geom/alg_exact_equals.go index 72281f16..09166d24 100644 --- a/geom/alg_exact_equals.go +++ b/geom/alg_exact_equals.go @@ -83,19 +83,19 @@ func (c exactEqualsComparator) geometriesEq(g1, g2 Geometry) bool { } switch typ := g1.Type(); typ { case TypePoint: - return c.pointsEq(g1.AsPoint(), g2.AsPoint()) + return c.pointsEq(g1.MustAsPoint(), g2.MustAsPoint()) case TypeMultiPoint: - return c.multiPointsEq(g1.AsMultiPoint(), g2.AsMultiPoint()) + return c.multiPointsEq(g1.MustAsMultiPoint(), g2.MustAsMultiPoint()) case TypeLineString: - return c.lineStringsEq(g1.AsLineString(), g2.AsLineString()) + return c.lineStringsEq(g1.MustAsLineString(), g2.MustAsLineString()) case TypeMultiLineString: - return c.multiLineStringsEq(g1.AsMultiLineString(), g2.AsMultiLineString()) + return c.multiLineStringsEq(g1.MustAsMultiLineString(), g2.MustAsMultiLineString()) case TypePolygon: - return c.polygonsEq(g1.AsPolygon(), g2.AsPolygon()) + return c.polygonsEq(g1.MustAsPolygon(), g2.MustAsPolygon()) case TypeMultiPolygon: - return c.multiPolygonsEq(g1.AsMultiPolygon(), g2.AsMultiPolygon()) + return c.multiPolygonsEq(g1.MustAsMultiPolygon(), g2.MustAsMultiPolygon()) case TypeGeometryCollection: - return c.geometryCollectionsEq(g1.AsGeometryCollection(), g2.AsGeometryCollection()) + return c.geometryCollectionsEq(g1.MustAsGeometryCollection(), g2.MustAsGeometryCollection()) default: panic("unknown geometry type: " + typ.String()) } diff --git a/geom/alg_intersects.go b/geom/alg_intersects.go index 5d89ee11..979802fd 100644 --- a/geom/alg_intersects.go +++ b/geom/alg_intersects.go @@ -15,7 +15,7 @@ func Intersects(g1, g2 Geometry) bool { } if g2.IsGeometryCollection() { - gc := g2.AsGeometryCollection() + gc := g2.MustAsGeometryCollection() n := gc.NumGeometries() for i := 0; i < n; i++ { g := gc.GeometryN(i) @@ -30,112 +30,112 @@ func Intersects(g1, g2 Geometry) bool { case g1.IsPoint(): switch { case g2.IsPoint(): - return hasIntersectionPointWithPoint(g1.AsPoint(), g2.AsPoint()) + return hasIntersectionPointWithPoint(g1.MustAsPoint(), g2.MustAsPoint()) case g2.IsLineString(): - return hasIntersectionPointWithLineString(g1.AsPoint(), g2.AsLineString()) + return hasIntersectionPointWithLineString(g1.MustAsPoint(), g2.MustAsLineString()) case g2.IsPolygon(): - return hasIntersectionPointWithPolygon(g1.AsPoint(), g2.AsPolygon()) + return hasIntersectionPointWithPolygon(g1.MustAsPoint(), g2.MustAsPolygon()) case g2.IsMultiPoint(): - return hasIntersectionPointWithMultiPoint(g1.AsPoint(), g2.AsMultiPoint()) + return hasIntersectionPointWithMultiPoint(g1.MustAsPoint(), g2.MustAsMultiPoint()) case g2.IsMultiLineString(): - return hasIntersectionPointWithMultiLineString(g1.AsPoint(), g2.AsMultiLineString()) + return hasIntersectionPointWithMultiLineString(g1.MustAsPoint(), g2.MustAsMultiLineString()) case g2.IsMultiPolygon(): - return hasIntersectionPointWithMultiPolygon(g1.AsPoint(), g2.AsMultiPolygon()) + return hasIntersectionPointWithMultiPolygon(g1.MustAsPoint(), g2.MustAsMultiPolygon()) } case g1.IsLineString(): switch { case g2.IsLineString(): has, _ := hasIntersectionLineStringWithLineString( - g1.AsLineString(), - g2.AsLineString(), + g1.MustAsLineString(), + g2.MustAsLineString(), false, ) return has case g2.IsPolygon(): return hasIntersectionMultiLineStringWithMultiPolygon( - g1.AsLineString().AsMultiLineString(), - g2.AsPolygon().AsMultiPolygon(), + g1.MustAsLineString().AsMultiLineString(), + g2.MustAsPolygon().AsMultiPolygon(), ) case g2.IsMultiPoint(): return hasIntersectionMultiPointWithMultiLineString( - g2.AsMultiPoint(), - g1.AsLineString().AsMultiLineString(), + g2.MustAsMultiPoint(), + g1.MustAsLineString().AsMultiLineString(), ) case g2.IsMultiLineString(): has, _ := hasIntersectionMultiLineStringWithMultiLineString( - g1.AsLineString().AsMultiLineString(), - g2.AsMultiLineString(), + g1.MustAsLineString().AsMultiLineString(), + g2.MustAsMultiLineString(), false, ) return has case g2.IsMultiPolygon(): return hasIntersectionMultiLineStringWithMultiPolygon( - g1.AsLineString().AsMultiLineString(), - g2.AsMultiPolygon(), + g1.MustAsLineString().AsMultiLineString(), + g2.MustAsMultiPolygon(), ) } case g1.IsPolygon(): switch { case g2.IsPolygon(): return hasIntersectionPolygonWithPolygon( - g1.AsPolygon(), - g2.AsPolygon(), + g1.MustAsPolygon(), + g2.MustAsPolygon(), ) case g2.IsMultiPoint(): return hasIntersectionMultiPointWithPolygon( - g2.AsMultiPoint(), - g1.AsPolygon(), + g2.MustAsMultiPoint(), + g1.MustAsPolygon(), ) case g2.IsMultiLineString(): return hasIntersectionMultiLineStringWithMultiPolygon( - g2.AsMultiLineString(), - g1.AsPolygon().AsMultiPolygon(), + g2.MustAsMultiLineString(), + g1.MustAsPolygon().AsMultiPolygon(), ) case g2.IsMultiPolygon(): return hasIntersectionMultiPolygonWithMultiPolygon( - g1.AsPolygon().AsMultiPolygon(), - g2.AsMultiPolygon(), + g1.MustAsPolygon().AsMultiPolygon(), + g2.MustAsMultiPolygon(), ) } case g1.IsMultiPoint(): switch { case g2.IsMultiPoint(): return hasIntersectionMultiPointWithMultiPoint( - g1.AsMultiPoint(), - g2.AsMultiPoint(), + g1.MustAsMultiPoint(), + g2.MustAsMultiPoint(), ) case g2.IsMultiLineString(): return hasIntersectionMultiPointWithMultiLineString( - g1.AsMultiPoint(), - g2.AsMultiLineString(), + g1.MustAsMultiPoint(), + g2.MustAsMultiLineString(), ) case g2.IsMultiPolygon(): return hasIntersectionMultiPointWithMultiPolygon( - g1.AsMultiPoint(), - g2.AsMultiPolygon(), + g1.MustAsMultiPoint(), + g2.MustAsMultiPolygon(), ) } case g1.IsMultiLineString(): switch { case g2.IsMultiLineString(): has, _ := hasIntersectionMultiLineStringWithMultiLineString( - g1.AsMultiLineString(), - g2.AsMultiLineString(), + g1.MustAsMultiLineString(), + g2.MustAsMultiLineString(), false, ) return has case g2.IsMultiPolygon(): return hasIntersectionMultiLineStringWithMultiPolygon( - g1.AsMultiLineString(), - g2.AsMultiPolygon(), + g1.MustAsMultiLineString(), + g2.MustAsMultiPolygon(), ) } case g1.IsMultiPolygon(): switch { case g2.IsMultiPolygon(): return hasIntersectionMultiPolygonWithMultiPolygon( - g1.AsMultiPolygon(), - g2.AsMultiPolygon(), + g1.MustAsMultiPolygon(), + g2.MustAsMultiPolygon(), ) } } diff --git a/geom/alg_point_in_ring_test.go b/geom/alg_point_in_ring_test.go index d29e82ed..7f560d85 100644 --- a/geom/alg_point_in_ring_test.go +++ b/geom/alg_point_in_ring_test.go @@ -146,7 +146,7 @@ func TestPointInRing(t *testing.T) { if !g.IsPolygon() { t.Fatal("expected a polygon") } - poly := g.AsPolygon() + poly := g.MustAsPolygon() ring := poly.ExteriorRing() for j, st := range tc.subTests { @@ -154,7 +154,7 @@ func TestPointInRing(t *testing.T) { if err != nil { t.Fatal(err) } - xy, ok := pt.AsPoint().XY() + xy, ok := pt.MustAsPoint().XY() if !ok { panic("point empty not expected in this test") } @@ -185,7 +185,7 @@ func TestPointInPolygon(t *testing.T) { if err != nil { t.Fatal(err) } - poly := g.AsPolygon() + poly := g.MustAsPolygon() il := newIndexedLines(poly.Boundary().asLines()) for i, tt := range []struct { diff --git a/geom/alg_simplify.go b/geom/alg_simplify.go index 4f38787c..a1ec02ad 100644 --- a/geom/alg_simplify.go +++ b/geom/alg_simplify.go @@ -9,23 +9,23 @@ func Simplify(g Geometry, threshold float64, opts ...ConstructorOption) (Geometr s := simplifier{threshold, opts} switch g.gtype { case TypeGeometryCollection: - gc, err := s.simplifyGeometryCollection(g.AsGeometryCollection()) + gc, err := s.simplifyGeometryCollection(g.MustAsGeometryCollection()) return gc.AsGeometry(), wrapSimplified(err) case TypePoint: return g, nil case TypeLineString: - ls, err := s.simplifyLineString(g.AsLineString()) + ls, err := s.simplifyLineString(g.MustAsLineString()) return ls.AsGeometry(), wrapSimplified(err) case TypePolygon: - poly, err := s.simplifyPolygon(g.AsPolygon()) + poly, err := s.simplifyPolygon(g.MustAsPolygon()) return poly.AsGeometry(), wrapSimplified(err) case TypeMultiPoint: return g, nil case TypeMultiLineString: - mls, err := s.simplifyMultiLineString(g.AsMultiLineString()) + mls, err := s.simplifyMultiLineString(g.MustAsMultiLineString()) return mls.AsGeometry(), wrapSimplified(err) case TypeMultiPolygon: - mp, err := s.simplifyMultiPolygon(g.AsMultiPolygon()) + mp, err := s.simplifyMultiPolygon(g.MustAsMultiPolygon()) return mp.AsGeometry(), wrapSimplified(err) default: panic("unknown geometry: " + g.gtype.String()) diff --git a/geom/attr_test.go b/geom/attr_test.go index c9589995..0d03b63d 100644 --- a/geom/attr_test.go +++ b/geom/attr_test.go @@ -316,24 +316,24 @@ func TestBoundary(t *testing.T) { func TestCoordinatesSequence(t *testing.T) { t.Run("point", func(t *testing.T) { t.Run("populated", func(t *testing.T) { - c, ok := geomFromWKT(t, "POINT(1 2)").AsPoint().Coordinates() + c, ok := geomFromWKT(t, "POINT(1 2)").MustAsPoint().Coordinates() expectBoolEq(t, ok, true) expectXYEq(t, c.XY, XY{1, 2}) }) t.Run("empty", func(t *testing.T) { - _, ok := geomFromWKT(t, "POINT EMPTY").AsPoint().Coordinates() + _, ok := geomFromWKT(t, "POINT EMPTY").MustAsPoint().Coordinates() expectBoolEq(t, ok, false) }) }) t.Run("linestring", func(t *testing.T) { - seq := geomFromWKT(t, "LINESTRING(0 1,2 3,4 5)").AsLineString().Coordinates() + seq := geomFromWKT(t, "LINESTRING(0 1,2 3,4 5)").MustAsLineString().Coordinates() expectIntEq(t, seq.Length(), 3) expectXYEq(t, seq.GetXY(0), XY{0, 1}) expectXYEq(t, seq.GetXY(1), XY{2, 3}) expectXYEq(t, seq.GetXY(2), XY{4, 5}) }) t.Run("linestring with dupe", func(t *testing.T) { - seq := geomFromWKT(t, "LINESTRING(1 5,5 2,5 2,4 9)").AsLineString().Coordinates() + seq := geomFromWKT(t, "LINESTRING(1 5,5 2,5 2,4 9)").MustAsLineString().Coordinates() expectIntEq(t, seq.Length(), 4) expectXYEq(t, seq.GetXY(0), XY{1, 5}) expectXYEq(t, seq.GetXY(1), XY{5, 2}) @@ -341,7 +341,7 @@ func TestCoordinatesSequence(t *testing.T) { expectXYEq(t, seq.GetXY(3), XY{4, 9}) }) t.Run("polygon", func(t *testing.T) { - seq := geomFromWKT(t, "POLYGON((0 0,0 10,10 0,0 0),(2 2,2 7,7 2,2 2))").AsPolygon().Coordinates() + seq := geomFromWKT(t, "POLYGON((0 0,0 10,10 0,0 0),(2 2,2 7,7 2,2 2))").MustAsPolygon().Coordinates() expectIntEq(t, len(seq), 2) expectIntEq(t, seq[0].Length(), 4) expectXYEq(t, seq[0].GetXY(0), XY{0, 0}) @@ -356,14 +356,14 @@ func TestCoordinatesSequence(t *testing.T) { }) t.Run("multipoint", func(t *testing.T) { - seq := geomFromWKT(t, "MULTIPOINT(0 1,2 3,EMPTY,4 5)").AsMultiPoint().Coordinates() + seq := geomFromWKT(t, "MULTIPOINT(0 1,2 3,EMPTY,4 5)").MustAsMultiPoint().Coordinates() expectIntEq(t, seq.Length(), 3) expectXYEq(t, seq.GetXY(0), XY{0, 1}) expectXYEq(t, seq.GetXY(1), XY{2, 3}) expectXYEq(t, seq.GetXY(2), XY{4, 5}) }) t.Run("multilinestring", func(t *testing.T) { - seq := geomFromWKT(t, "MULTILINESTRING((0 0,0 10,10 0,0 0),(2 2,2 8,8 2,2 2))").AsMultiLineString().Coordinates() + seq := geomFromWKT(t, "MULTILINESTRING((0 0,0 10,10 0,0 0),(2 2,2 8,8 2,2 2))").MustAsMultiLineString().Coordinates() expectIntEq(t, len(seq), 2) expectIntEq(t, seq[0].Length(), 4) expectXYEq(t, seq[0].GetXY(0), XY{0, 0}) @@ -388,7 +388,7 @@ func TestCoordinatesSequence(t *testing.T) { (102 102,102 107,107 102,102 102) ) )`, - ).AsMultiPolygon().Coordinates() + ).MustAsMultiPolygon().Coordinates() expectIntEq(t, len(seq), 2) expectIntEq(t, len(seq[0]), 2) @@ -465,7 +465,7 @@ func TestIsRing(t *testing.T) { {"LINESTRING(0 0,1 0,1 1,0 1)", false}, // not closed } { t.Run(strconv.Itoa(i), func(t *testing.T) { - got := geomFromWKT(t, tt.wkt).AsLineString().IsRing() + got := geomFromWKT(t, tt.wkt).MustAsLineString().IsRing() if got != tt.want { t.Logf("WKT: %v", tt.wkt) t.Errorf("got=%v want=%v", got, tt.want) @@ -484,7 +484,7 @@ func TestIsClosed(t *testing.T) { {"LINESTRING(0 0,1 0,1 1,0 1)", false}, } { t.Run(strconv.Itoa(i), func(t *testing.T) { - got := geomFromWKT(t, tt.wkt).AsLineString().IsClosed() + got := geomFromWKT(t, tt.wkt).MustAsLineString().IsClosed() if got != tt.want { t.Logf("WKT: %v", tt.wkt) t.Errorf("got=%v want=%v", got, tt.want) @@ -710,7 +710,7 @@ func TestCentroid(t *testing.T) { } func TestLineStringToMultiLineString(t *testing.T) { - ls := geomFromWKT(t, "LINESTRING(1 2,3 4,5 6)").AsLineString() + ls := geomFromWKT(t, "LINESTRING(1 2,3 4,5 6)").MustAsLineString() got := ls.AsMultiLineString() want := geomFromWKT(t, "MULTILINESTRING((1 2,3 4,5 6))") if !ExactEquals(got.AsGeometry(), want) { @@ -719,7 +719,7 @@ func TestLineStringToMultiLineString(t *testing.T) { } func TestPolygonToMultiPolygon(t *testing.T) { - p := geomFromWKT(t, "POLYGON((0 0,0 1,1 0,0 0))").AsPolygon() + p := geomFromWKT(t, "POLYGON((0 0,0 1,1 0,0 0))").MustAsPolygon() mp := p.AsMultiPolygon() if mp.AsText() != "MULTIPOLYGON(((0 0,0 1,1 0,0 0)))" { t.Errorf("got %v", mp.AsText()) diff --git a/geom/dcel.go b/geom/dcel.go index 26c6b980..2425eca8 100644 --- a/geom/dcel.go +++ b/geom/dcel.go @@ -76,22 +76,22 @@ func newDCELFromGeometry(g Geometry, ghosts MultiLineString, operand operand, in var dcel *doublyConnectedEdgeList switch g.Type() { case TypePolygon: - poly := g.AsPolygon() + poly := g.MustAsPolygon() dcel = newDCELFromMultiPolygon(poly.AsMultiPolygon(), operand, interactions) case TypeMultiPolygon: - mp := g.AsMultiPolygon() + mp := g.MustAsMultiPolygon() dcel = newDCELFromMultiPolygon(mp, operand, interactions) case TypeLineString: - mls := g.AsLineString().AsMultiLineString() + mls := g.MustAsLineString().AsMultiLineString() dcel = newDCELFromMultiLineString(mls, operand, interactions) case TypeMultiLineString: - mls := g.AsMultiLineString() + mls := g.MustAsMultiLineString() dcel = newDCELFromMultiLineString(mls, operand, interactions) case TypePoint: - mp := g.AsPoint().AsMultiPoint() + mp := g.MustAsPoint().AsMultiPoint() dcel = newDCELFromMultiPoint(mp, operand) case TypeMultiPoint: - mp := g.AsMultiPoint() + mp := g.MustAsMultiPoint() dcel = newDCELFromMultiPoint(mp, operand) case TypeGeometryCollection: panic("geometry collection not supported") diff --git a/geom/dcel_ghosts.go b/geom/dcel_ghosts.go index 430ca2ad..851e5543 100644 --- a/geom/dcel_ghosts.go +++ b/geom/dcel_ghosts.go @@ -79,19 +79,19 @@ func appendXYsForPolygon(xys []XY, poly Polygon) []XY { func appendComponentPoints(xys []XY, g Geometry) []XY { switch g.Type() { case TypePoint: - return appendXYForPoint(xys, g.AsPoint()) + return appendXYForPoint(xys, g.MustAsPoint()) case TypeMultiPoint: - mp := g.AsMultiPoint() + mp := g.MustAsMultiPoint() n := mp.NumPoints() for i := 0; i < n; i++ { xys = appendXYForPoint(xys, mp.PointN(i)) } return xys case TypeLineString: - ls := g.AsLineString() + ls := g.MustAsLineString() return appendXYForLineString(xys, ls) case TypeMultiLineString: - mls := g.AsMultiLineString() + mls := g.MustAsMultiLineString() n := mls.NumLineStrings() for i := 0; i < n; i++ { ls := mls.LineStringN(i) @@ -99,10 +99,10 @@ func appendComponentPoints(xys []XY, g Geometry) []XY { } return xys case TypePolygon: - poly := g.AsPolygon() + poly := g.MustAsPolygon() return appendXYsForPolygon(xys, poly) case TypeMultiPolygon: - mp := g.AsMultiPolygon() + mp := g.MustAsMultiPolygon() n := mp.NumPolygons() for i := 0; i < n; i++ { poly := mp.PolygonN(i) @@ -110,7 +110,7 @@ func appendComponentPoints(xys []XY, g Geometry) []XY { } return xys case TypeGeometryCollection: - gc := g.AsGeometryCollection() + gc := g.MustAsGeometryCollection() n := gc.NumGeometries() for i := 0; i < n; i++ { xys = appendComponentPoints(xys, gc.GeometryN(i)) diff --git a/geom/dcel_interaction_points.go b/geom/dcel_interaction_points.go index db237f4f..1c3eb902 100644 --- a/geom/dcel_interaction_points.go +++ b/geom/dcel_interaction_points.go @@ -38,17 +38,17 @@ type xyPair struct { func addGeometryInteractions(g Geometry, adjacents map[XY]xyPair, interactions map[XY]struct{}) { switch g.Type() { case TypePoint: - addPointInteractions(g.AsPoint(), interactions) + addPointInteractions(g.MustAsPoint(), interactions) case TypeMultiPoint: - addMultiPointInteractions(g.AsMultiPoint(), interactions) + addMultiPointInteractions(g.MustAsMultiPoint(), interactions) case TypeLineString: - addLineStringInteractions(g.AsLineString(), adjacents, interactions) + addLineStringInteractions(g.MustAsLineString(), adjacents, interactions) case TypeMultiLineString: - addMultiLineStringInteractions(g.AsMultiLineString(), adjacents, interactions) + addMultiLineStringInteractions(g.MustAsMultiLineString(), adjacents, interactions) case TypePolygon: - addMultiLineStringInteractions(g.AsPolygon().Boundary(), adjacents, interactions) + addMultiLineStringInteractions(g.MustAsPolygon().Boundary(), adjacents, interactions) case TypeMultiPolygon: - addMultiLineStringInteractions(g.AsMultiPolygon().Boundary(), adjacents, interactions) + addMultiLineStringInteractions(g.MustAsMultiPolygon().Boundary(), adjacents, interactions) case TypeGeometryCollection: panic("geometry collection not supported") default: diff --git a/geom/dcel_re_noding.go b/geom/dcel_re_noding.go index 004ed5d3..afce4c76 100644 --- a/geom/dcel_re_noding.go +++ b/geom/dcel_re_noding.go @@ -114,19 +114,19 @@ func reNodeGeometries(g1, g2 Geometry, mls MultiLineString) (Geometry, Geometry, func reNodeGeometry(g Geometry, cut cutSet, nodes nodeSet) (Geometry, error) { switch g.Type() { case TypeGeometryCollection: - gc, err := reNodeGeometryCollection(g.AsGeometryCollection(), cut, nodes) + gc, err := reNodeGeometryCollection(g.MustAsGeometryCollection(), cut, nodes) return gc.AsGeometry(), err case TypeLineString: - ls, err := reNodeLineString(g.AsLineString(), cut, nodes) + ls, err := reNodeLineString(g.MustAsLineString(), cut, nodes) return ls.AsGeometry(), err case TypePolygon: - poly, err := reNodePolygon(g.AsPolygon(), cut, nodes) + poly, err := reNodePolygon(g.MustAsPolygon(), cut, nodes) return poly.AsGeometry(), err case TypeMultiLineString: - mls, err := reNodeMultiLineString(g.AsMultiLineString(), cut, nodes) + mls, err := reNodeMultiLineString(g.MustAsMultiLineString(), cut, nodes) return mls.AsGeometry(), err case TypeMultiPolygon: - mp, err := reNodeMultiPolygonString(g.AsMultiPolygon(), cut, nodes) + mp, err := reNodeMultiPolygonString(g.MustAsMultiPolygon(), cut, nodes) return mp.AsGeometry(), err case TypePoint, TypeMultiPoint: return g, nil @@ -155,7 +155,7 @@ func newCutSet(g Geometry) cutSet { func appendLines(lines []line, g Geometry) []line { switch g.Type() { case TypeLineString: - seq := g.AsLineString().Coordinates() + seq := g.MustAsLineString().Coordinates() n := seq.Length() for i := 0; i < n; i++ { ln, ok := getLine(seq, i) @@ -164,17 +164,17 @@ func appendLines(lines []line, g Geometry) []line { } } case TypeMultiLineString: - mls := g.AsMultiLineString() + mls := g.MustAsMultiLineString() for i := 0; i < mls.NumLineStrings(); i++ { ls := mls.LineStringN(i) lines = appendLines(lines, ls.AsGeometry()) } case TypePolygon: - lines = appendLines(lines, g.AsPolygon().Boundary().AsGeometry()) + lines = appendLines(lines, g.MustAsPolygon().Boundary().AsGeometry()) case TypeMultiPolygon: - lines = appendLines(lines, g.AsMultiPolygon().Boundary().AsGeometry()) + lines = appendLines(lines, g.MustAsMultiPolygon().Boundary().AsGeometry()) case TypeGeometryCollection: - gc := g.AsGeometryCollection() + gc := g.MustAsGeometryCollection() n := gc.NumGeometries() for i := 0; i < n; i++ { lines = appendLines(lines, gc.GeometryN(i)) @@ -186,18 +186,18 @@ func appendLines(lines []line, g Geometry) []line { func appendPoints(points []XY, g Geometry) []XY { switch g.Type() { case TypePoint: - coords, ok := g.AsPoint().Coordinates() + coords, ok := g.MustAsPoint().Coordinates() if ok { points = append(points, coords.XY) } case TypeMultiPoint: - mp := g.AsMultiPoint() + mp := g.MustAsMultiPoint() n := mp.NumPoints() for i := 0; i < n; i++ { points = appendPoints(points, mp.PointN(i).AsGeometry()) } case TypeGeometryCollection: - gc := g.AsGeometryCollection() + gc := g.MustAsGeometryCollection() n := gc.NumGeometries() for i := 0; i < n; i++ { points = appendPoints(points, gc.GeometryN(i)) diff --git a/geom/dcel_test.go b/geom/dcel_test.go index 4fb14b50..f88f7148 100644 --- a/geom/dcel_test.go +++ b/geom/dcel_test.go @@ -339,7 +339,7 @@ func TestGraphTriangle(t *testing.T) { if err != nil { t.Fatal(err) } - dcel := newDCELFromMultiPolygon(poly.AsPolygon().AsMultiPolygon(), operandA, findInteractionPoints([]Geometry{poly})) + dcel := newDCELFromMultiPolygon(poly.MustAsPolygon().AsMultiPolygon(), operandA, findInteractionPoints([]Geometry{poly})) /* @@ -407,7 +407,7 @@ func TestGraphWithHoles(t *testing.T) { */ - dcel := newDCELFromMultiPolygon(poly.AsPolygon().AsMultiPolygon(), operandB, findInteractionPoints([]Geometry{poly})) + dcel := newDCELFromMultiPolygon(poly.MustAsPolygon().AsMultiPolygon(), operandB, findInteractionPoints([]Geometry{poly})) v0 := XY{0, 0} v1 := XY{5, 0} @@ -480,7 +480,7 @@ func TestGraphWithMultiPolygon(t *testing.T) { v0-----v1 v4-----v5 */ - dcel := newDCELFromMultiPolygon(mp.AsMultiPolygon(), operandB, findInteractionPoints([]Geometry{mp})) + dcel := newDCELFromMultiPolygon(mp.MustAsMultiPolygon(), operandB, findInteractionPoints([]Geometry{mp})) v0 := XY{0, 0} v1 := XY{1, 0} @@ -675,7 +675,7 @@ func TestGraphGhostDeduplication(t *testing.T) { t.Fatal(err) } - dcel := newDCELFromGeometry(ls, ghost.AsMultiLineString(), operandA, findInteractionPoints([]Geometry{ls, ghost})) + dcel := newDCELFromGeometry(ls, ghost.MustAsMultiLineString(), operandA, findInteractionPoints([]Geometry{ls, ghost})) v0 := XY{0, 0} v1 := XY{1, 0} diff --git a/geom/dump_coordinates_test.go b/geom/dump_coordinates_test.go index 13404f94..432c379b 100644 --- a/geom/dump_coordinates_test.go +++ b/geom/dump_coordinates_test.go @@ -54,7 +54,7 @@ func TestDumpCoordinatesPoint(t *testing.T) { }, } { t.Run(tc.description, func(t *testing.T) { - got := geomFromWKT(t, tc.inputWKT).AsPoint().DumpCoordinates() + got := geomFromWKT(t, tc.inputWKT).MustAsPoint().DumpCoordinates() expectSequenceEq(t, got, tc.want) }) } @@ -108,7 +108,7 @@ func TestDumpCoordinatesMultiLineString(t *testing.T) { }, } { t.Run(tc.description, func(t *testing.T) { - got := geomFromWKT(t, tc.inputWKT).AsMultiLineString().DumpCoordinates() + got := geomFromWKT(t, tc.inputWKT).MustAsMultiLineString().DumpCoordinates() expectSequenceEq(t, got, tc.want) }) } @@ -162,7 +162,7 @@ func TestDumpCoordinatesPolygon(t *testing.T) { }, } { t.Run(tc.description, func(t *testing.T) { - got := geomFromWKT(t, tc.inputWKT).AsPolygon().DumpCoordinates() + got := geomFromWKT(t, tc.inputWKT).MustAsPolygon().DumpCoordinates() expectSequenceEq(t, got, tc.want) }) } @@ -216,7 +216,7 @@ func TestDumpCoordinatesMultiPolygon(t *testing.T) { }, } { t.Run(tc.description, func(t *testing.T) { - got := geomFromWKT(t, tc.inputWKT).AsMultiPolygon().DumpCoordinates() + got := geomFromWKT(t, tc.inputWKT).MustAsMultiPolygon().DumpCoordinates() expectSequenceEq(t, got, tc.want) }) } @@ -255,7 +255,7 @@ func TestDumpCoordinatesGeometryCollection(t *testing.T) { }, } { t.Run(tc.description, func(t *testing.T) { - got := geomFromWKT(t, tc.inputWKT).AsGeometryCollection().DumpCoordinates() + got := geomFromWKT(t, tc.inputWKT).MustAsGeometryCollection().DumpCoordinates() expectSequenceEq(t, got, tc.want) }) } diff --git a/geom/marshal_unmarshal_test.go b/geom/marshal_unmarshal_test.go index ef415ac4..72f1d26b 100644 --- a/geom/marshal_unmarshal_test.go +++ b/geom/marshal_unmarshal_test.go @@ -221,7 +221,7 @@ func TestMarshalUnmarshal(t *testing.T) { // in GeoJSON as per the spec. if original.IsMultiPoint() { var hasEmptyPointInMultiPoint bool - mp := original.AsMultiPoint() + mp := original.MustAsMultiPoint() for j := 0; j < mp.NumPoints(); j++ { if mp.PointN(j).IsEmpty() { hasEmptyPointInMultiPoint = true diff --git a/geom/perf_test.go b/geom/perf_test.go index ed6902a5..a8648b0b 100644 --- a/geom/perf_test.go +++ b/geom/perf_test.go @@ -194,7 +194,7 @@ func BenchmarkPolygonZigZagRingsValidation(b *testing.B) { b.Run(fmt.Sprintf("n=%d", sz), func(b *testing.B) { outerRingEnv, err := NewEnvelope([]XY{{}, {7, float64(sz + 1)}}) expectNoErr(b, err) - outerRing := outerRingEnv.AsGeometry().AsPolygon().ExteriorRing() + outerRing := outerRingEnv.AsGeometry().MustAsPolygon().ExteriorRing() var leftFloats, rightFloats []float64 for i := 0; i < sz; i++ { leftFloats = append(leftFloats, float64(2+(i%2)*2), float64(1+i)) diff --git a/geom/type_envelope_test.go b/geom/type_envelope_test.go index 64ca934f..57a0b6e3 100644 --- a/geom/type_envelope_test.go +++ b/geom/type_envelope_test.go @@ -190,11 +190,11 @@ func TestEnvelopeAttributes(t *testing.T) { gotMin, gotMax, gotOK := tc.env.MinMaxXYs() expectBoolEq(t, gotOK, !tc.isEmpty) if gotOK { - wantMin, minOK := geomFromWKT(t, tc.min).AsPoint().XY() + wantMin, minOK := geomFromWKT(t, tc.min).MustAsPoint().XY() expectTrue(t, minOK) expectXYEq(t, gotMin, wantMin) - wantMax, maxOK := geomFromWKT(t, tc.max).AsPoint().XY() + wantMax, maxOK := geomFromWKT(t, tc.max).MustAsPoint().XY() expectTrue(t, maxOK) expectXYEq(t, gotMax, wantMax) } diff --git a/geom/type_geometry.go b/geom/type_geometry.go index 3e0c1579..585f9298 100644 --- a/geom/type_geometry.go +++ b/geom/type_geometry.go @@ -58,19 +58,19 @@ func (t GeometryType) String() string { func (g Geometry) Type() GeometryType { switch g.gtype { case TypeGeometryCollection: - return g.AsGeometryCollection().Type() + return g.MustAsGeometryCollection().Type() case TypePoint: - return g.AsPoint().Type() + return g.MustAsPoint().Type() case TypeLineString: - return g.AsLineString().Type() + return g.MustAsLineString().Type() case TypePolygon: - return g.AsPolygon().Type() + return g.MustAsPolygon().Type() case TypeMultiPoint: - return g.AsMultiPoint().Type() + return g.MustAsMultiPoint().Type() case TypeMultiLineString: - return g.AsMultiLineString().Type() + return g.MustAsMultiLineString().Type() case TypeMultiPolygon: - return g.AsMultiPolygon().Type() + return g.MustAsMultiPolygon().Type() default: panic("unknown geometry: " + g.gtype.String()) } @@ -103,9 +103,9 @@ func (g Geometry) check(gtype GeometryType) { } } -// AsGeometryCollection returns the geometry as a GeometryCollection. It panics -// if the geometry is not a GeometryCollection. -func (g Geometry) AsGeometryCollection() GeometryCollection { +// MustAsGeometryCollection returns the geometry as a GeometryCollection. It +// panics if the geometry is not a GeometryCollection. +func (g Geometry) MustAsGeometryCollection() GeometryCollection { g.check(TypeGeometryCollection) if g.ptr == nil { // Special case so that the zero Geometry value is interpreted as an @@ -115,65 +115,133 @@ func (g Geometry) AsGeometryCollection() GeometryCollection { return *(*GeometryCollection)(g.ptr) } -// AsPoint returns the geometry as a Point. It panics if the geometry is not a -// Point. -func (g Geometry) AsPoint() Point { +// MustAsPoint returns the geometry as a Point. It panics if the geometry is +// not a Point. +func (g Geometry) MustAsPoint() Point { g.check(TypePoint) return *(*Point)(g.ptr) } -// AsLineString returns the geometry as a LineString. It panics if the geometry -// is not a LineString. -func (g Geometry) AsLineString() LineString { +// MustAsLineString returns the geometry as a LineString. It panics if the +// geometry is not a LineString. +func (g Geometry) MustAsLineString() LineString { g.check(TypeLineString) return *(*LineString)(g.ptr) } -// AsPolygon returns the geometry as a Polygon. It panics if the geometry is -// not a Polygon. -func (g Geometry) AsPolygon() Polygon { +// MustAsPolygon returns the geometry as a Polygon. It panics if the geometry +// is not a Polygon. +func (g Geometry) MustAsPolygon() Polygon { g.check(TypePolygon) return *(*Polygon)(g.ptr) } -// AsMultiPoint returns the geometry as a MultiPoint. It panics if the geometry -// is not a MultiPoint. -func (g Geometry) AsMultiPoint() MultiPoint { +// MustAsMultiPoint returns the geometry as a MultiPoint. It panics if the +// geometry is not a MultiPoint. +func (g Geometry) MustAsMultiPoint() MultiPoint { g.check(TypeMultiPoint) return *(*MultiPoint)(g.ptr) } -// AsMultiLineString returns the geometry as a MultiLineString. It panics if -// the geometry is not a MultiLineString. -func (g Geometry) AsMultiLineString() MultiLineString { +// MustAsMultiLineString returns the geometry as a MultiLineString. It panics +// if the geometry is not a MultiLineString. +func (g Geometry) MustAsMultiLineString() MultiLineString { g.check(TypeMultiLineString) return *(*MultiLineString)(g.ptr) } -// AsMultiPolygon returns the geometry as a MultiPolygon. It panics if the +// MustAsMultiPolygon returns the geometry as a MultiPolygon. It panics if the // Geometry is not a MultiPolygon. -func (g Geometry) AsMultiPolygon() MultiPolygon { +func (g Geometry) MustAsMultiPolygon() MultiPolygon { g.check(TypeMultiPolygon) return *(*MultiPolygon)(g.ptr) } +// AsGeometryCollection checks if the geometry is a GeometryCollection, and +// returns it as a GeometryCollection if it is. The returned flag indicates if +// the conversion was successful. +func (g Geometry) AsGeometryCollection() (GeometryCollection, bool) { + if !g.IsGeometryCollection() { + return GeometryCollection{}, false + } + return g.MustAsGeometryCollection(), true +} + +// AsPoint checks if the geometry is a Point, and returns it as a Point if it +// is. The returned flag indicates if the conversion was successful. +func (g Geometry) AsPoint() (Point, bool) { + if !g.IsPoint() { + return Point{}, false + } + return g.MustAsPoint(), true +} + +// AsLineString checks if the geometry is a LineString, and returns it as a +// LineString if it is. The returned flag indicates if the conversion was +// successful. +func (g Geometry) AsLineString() (LineString, bool) { + if !g.IsLineString() { + return LineString{}, false + } + return g.MustAsLineString(), true +} + +// AsPolygon checks if the geometry is a Polygon, and returns it as a Polygon +// if it is. The returned flag indicates if the conversion was successful. +func (g Geometry) AsPolygon() (Polygon, bool) { + if !g.IsPolygon() { + return Polygon{}, false + } + return g.MustAsPolygon(), true +} + +// AsMultiPoint checks if the geometry is a MultiPoint, and returns it as a +// MultiPoint if it is. The returned flag indicates if the conversion was +// successful. +func (g Geometry) AsMultiPoint() (MultiPoint, bool) { + if !g.IsMultiPoint() { + return MultiPoint{}, false + } + return g.MustAsMultiPoint(), true +} + +// AsMultiLineString checks if the geometry is a MultiLineString, and returns +// it as a MultiLineString if it is. The returned flag indicates if the +// conversion was successful. +func (g Geometry) AsMultiLineString() (MultiLineString, bool) { + if !g.IsMultiLineString() { + return MultiLineString{}, false + } + return g.MustAsMultiLineString(), true +} + +// AsMultiPolygon checks if the geometry is a MultiPolygon, and returns it as a +// MultiPolygon if it is. The returned flag indicates if the conversion was +// successful. +func (g Geometry) AsMultiPolygon() (MultiPolygon, bool) { + if !g.IsMultiPolygon() { + return MultiPolygon{}, false + } + return g.MustAsMultiPolygon(), true +} + // AsText returns the WKT (Well Known Text) representation of this geometry. func (g Geometry) AsText() string { switch g.gtype { case TypeGeometryCollection: - return g.AsGeometryCollection().AsText() + return g.MustAsGeometryCollection().AsText() case TypePoint: - return g.AsPoint().AsText() + return g.MustAsPoint().AsText() case TypeLineString: - return g.AsLineString().AsText() + return g.MustAsLineString().AsText() case TypePolygon: - return g.AsPolygon().AsText() + return g.MustAsPolygon().AsText() case TypeMultiPoint: - return g.AsMultiPoint().AsText() + return g.MustAsMultiPoint().AsText() case TypeMultiLineString: - return g.AsMultiLineString().AsText() + return g.MustAsMultiLineString().AsText() case TypeMultiPolygon: - return g.AsMultiPolygon().AsText() + return g.MustAsMultiPolygon().AsText() default: panic("unknown geometry: " + g.gtype.String()) } @@ -184,19 +252,19 @@ func (g Geometry) AsText() string { func (g Geometry) MarshalJSON() ([]byte, error) { switch g.gtype { case TypeGeometryCollection: - return g.AsGeometryCollection().MarshalJSON() + return g.MustAsGeometryCollection().MarshalJSON() case TypePoint: - return g.AsPoint().MarshalJSON() + return g.MustAsPoint().MarshalJSON() case TypeLineString: - return g.AsLineString().MarshalJSON() + return g.MustAsLineString().MarshalJSON() case TypePolygon: - return g.AsPolygon().MarshalJSON() + return g.MustAsPolygon().MarshalJSON() case TypeMultiPoint: - return g.AsMultiPoint().MarshalJSON() + return g.MustAsMultiPoint().MarshalJSON() case TypeMultiLineString: - return g.AsMultiLineString().MarshalJSON() + return g.MustAsMultiLineString().MarshalJSON() case TypeMultiPolygon: - return g.AsMultiPolygon().MarshalJSON() + return g.MustAsMultiPolygon().MarshalJSON() default: panic("unknown geometry: " + g.gtype.String()) } @@ -251,19 +319,19 @@ func (g Geometry) AsBinary() []byte { func (g Geometry) AppendWKB(dst []byte) []byte { switch g.gtype { case TypeGeometryCollection: - return g.AsGeometryCollection().AppendWKB(dst) + return g.MustAsGeometryCollection().AppendWKB(dst) case TypePoint: - return g.AsPoint().AppendWKB(dst) + return g.MustAsPoint().AppendWKB(dst) case TypeLineString: - return g.AsLineString().AppendWKB(dst) + return g.MustAsLineString().AppendWKB(dst) case TypePolygon: - return g.AsPolygon().AppendWKB(dst) + return g.MustAsPolygon().AppendWKB(dst) case TypeMultiPoint: - return g.AsMultiPoint().AppendWKB(dst) + return g.MustAsMultiPoint().AppendWKB(dst) case TypeMultiLineString: - return g.AsMultiLineString().AppendWKB(dst) + return g.MustAsMultiLineString().AppendWKB(dst) case TypeMultiPolygon: - return g.AsMultiPolygon().AppendWKB(dst) + return g.MustAsMultiPolygon().AppendWKB(dst) default: panic("unknown geometry: " + g.gtype.String()) } @@ -319,19 +387,19 @@ func scanAsType(src interface{}, dst interface{}, typ GeometryType) error { } switch typ { case TypeGeometryCollection: - *dst.(*GeometryCollection) = g.AsGeometryCollection() + *dst.(*GeometryCollection) = g.MustAsGeometryCollection() case TypePoint: - *dst.(*Point) = g.AsPoint() + *dst.(*Point) = g.MustAsPoint() case TypeLineString: - *dst.(*LineString) = g.AsLineString() + *dst.(*LineString) = g.MustAsLineString() case TypePolygon: - *dst.(*Polygon) = g.AsPolygon() + *dst.(*Polygon) = g.MustAsPolygon() case TypeMultiPoint: - *dst.(*MultiPoint) = g.AsMultiPoint() + *dst.(*MultiPoint) = g.MustAsMultiPoint() case TypeMultiLineString: - *dst.(*MultiLineString) = g.AsMultiLineString() + *dst.(*MultiLineString) = g.MustAsMultiLineString() case TypeMultiPolygon: - *dst.(*MultiPolygon) = g.AsMultiPolygon() + *dst.(*MultiPolygon) = g.MustAsMultiPolygon() default: panic("unknown geometry type: " + typ.String()) } @@ -346,7 +414,7 @@ func scanAsType(src interface{}, dst interface{}, typ GeometryType) error { func (g Geometry) Dimension() int { switch g.gtype { case TypeGeometryCollection: - return g.AsGeometryCollection().Dimension() + return g.MustAsGeometryCollection().Dimension() case TypePoint, TypeMultiPoint: return 0 case TypeLineString, TypeMultiLineString: @@ -363,19 +431,19 @@ func (g Geometry) Dimension() int { func (g Geometry) IsEmpty() bool { switch g.gtype { case TypeGeometryCollection: - return g.AsGeometryCollection().IsEmpty() + return g.MustAsGeometryCollection().IsEmpty() case TypePoint: - return g.AsPoint().IsEmpty() + return g.MustAsPoint().IsEmpty() case TypeLineString: - return g.AsLineString().IsEmpty() + return g.MustAsLineString().IsEmpty() case TypePolygon: - return g.AsPolygon().IsEmpty() + return g.MustAsPolygon().IsEmpty() case TypeMultiPoint: - return g.AsMultiPoint().IsEmpty() + return g.MustAsMultiPoint().IsEmpty() case TypeMultiLineString: - return g.AsMultiLineString().IsEmpty() + return g.MustAsMultiLineString().IsEmpty() case TypeMultiPolygon: - return g.AsMultiPolygon().IsEmpty() + return g.MustAsMultiPolygon().IsEmpty() default: panic("unknown geometry: " + g.gtype.String()) } @@ -386,19 +454,19 @@ func (g Geometry) IsEmpty() bool { func (g Geometry) Envelope() Envelope { switch g.gtype { case TypeGeometryCollection: - return g.AsGeometryCollection().Envelope() + return g.MustAsGeometryCollection().Envelope() case TypePoint: - return g.AsPoint().Envelope() + return g.MustAsPoint().Envelope() case TypeLineString: - return g.AsLineString().Envelope() + return g.MustAsLineString().Envelope() case TypePolygon: - return g.AsPolygon().Envelope() + return g.MustAsPolygon().Envelope() case TypeMultiPoint: - return g.AsMultiPoint().Envelope() + return g.MustAsMultiPoint().Envelope() case TypeMultiLineString: - return g.AsMultiLineString().Envelope() + return g.MustAsMultiLineString().Envelope() case TypeMultiPolygon: - return g.AsMultiPolygon().Envelope() + return g.MustAsMultiPolygon().Envelope() default: panic("unknown geometry: " + g.gtype.String()) } @@ -411,24 +479,24 @@ func (g Geometry) Envelope() Envelope { func (g Geometry) Boundary() Geometry { switch g.gtype { case TypeGeometryCollection: - return g.AsGeometryCollection().Boundary().AsGeometry() + return g.MustAsGeometryCollection().Boundary().AsGeometry() case TypePoint: - return g.AsPoint().Boundary().AsGeometry() + return g.MustAsPoint().Boundary().AsGeometry() case TypeLineString: - return g.AsLineString().Boundary().AsGeometry() + return g.MustAsLineString().Boundary().AsGeometry() case TypePolygon: - mls := g.AsPolygon().Boundary() + mls := g.MustAsPolygon().Boundary() // Ensure holeless polygons return a LineString boundary. if mls.NumLineStrings() == 1 { return mls.LineStringN(0).AsGeometry() } return mls.AsGeometry() case TypeMultiPoint: - return g.AsMultiPoint().Boundary().AsGeometry() + return g.MustAsMultiPoint().Boundary().AsGeometry() case TypeMultiLineString: - return g.AsMultiLineString().Boundary().AsGeometry() + return g.MustAsMultiLineString().Boundary().AsGeometry() case TypeMultiPolygon: - return g.AsMultiPolygon().Boundary().AsGeometry() + return g.MustAsMultiPolygon().Boundary().AsGeometry() default: panic("unknown geometry: " + g.gtype.String()) } @@ -449,25 +517,25 @@ func (g Geometry) ConvexHull() Geometry { func (g Geometry) TransformXY(fn func(XY) XY, opts ...ConstructorOption) (Geometry, error) { switch g.gtype { case TypeGeometryCollection: - gt, err := g.AsGeometryCollection().TransformXY(fn, opts...) + gt, err := g.MustAsGeometryCollection().TransformXY(fn, opts...) return gt.AsGeometry(), err case TypePoint: - gt, err := g.AsPoint().TransformXY(fn, opts...) + gt, err := g.MustAsPoint().TransformXY(fn, opts...) return gt.AsGeometry(), err case TypeLineString: - gt, err := g.AsLineString().TransformXY(fn, opts...) + gt, err := g.MustAsLineString().TransformXY(fn, opts...) return gt.AsGeometry(), err case TypePolygon: - gt, err := g.AsPolygon().TransformXY(fn, opts...) + gt, err := g.MustAsPolygon().TransformXY(fn, opts...) return gt.AsGeometry(), err case TypeMultiPoint: - gt, err := g.AsMultiPoint().TransformXY(fn, opts...) + gt, err := g.MustAsMultiPoint().TransformXY(fn, opts...) return gt.AsGeometry(), err case TypeMultiLineString: - gt, err := g.AsMultiLineString().TransformXY(fn, opts...) + gt, err := g.MustAsMultiLineString().TransformXY(fn, opts...) return gt.AsGeometry(), err case TypeMultiPolygon: - gt, err := g.AsMultiPolygon().TransformXY(fn, opts...) + gt, err := g.MustAsMultiPolygon().TransformXY(fn, opts...) return gt.AsGeometry(), err default: panic("unknown geometry: " + g.gtype.String()) @@ -482,11 +550,11 @@ func (g Geometry) Length() float64 { case g.IsEmpty(): return 0 case g.IsGeometryCollection(): - return g.AsGeometryCollection().Length() + return g.MustAsGeometryCollection().Length() case g.IsLineString(): - return g.AsLineString().Length() + return g.MustAsLineString().Length() case g.IsMultiLineString(): - return g.AsMultiLineString().Length() + return g.MustAsMultiLineString().Length() case g.IsPoint(): return 0 case g.IsMultiPoint(): @@ -505,19 +573,19 @@ func (g Geometry) Length() float64 { func (g Geometry) Centroid() Point { switch g.gtype { case TypeGeometryCollection: - return g.AsGeometryCollection().Centroid() + return g.MustAsGeometryCollection().Centroid() case TypePoint: - return g.AsPoint().Centroid() + return g.MustAsPoint().Centroid() case TypeLineString: - return g.AsLineString().Centroid() + return g.MustAsLineString().Centroid() case TypePolygon: - return g.AsPolygon().Centroid() + return g.MustAsPolygon().Centroid() case TypeMultiPoint: - return g.AsMultiPoint().Centroid() + return g.MustAsMultiPoint().Centroid() case TypeMultiLineString: - return g.AsMultiLineString().Centroid() + return g.MustAsMultiLineString().Centroid() case TypeMultiPolygon: - return g.AsMultiPolygon().Centroid() + return g.MustAsMultiPolygon().Centroid() default: panic("unknown geometry: " + g.gtype.String()) } @@ -528,19 +596,19 @@ func (g Geometry) Centroid() Point { func (g Geometry) Area(opts ...AreaOption) float64 { switch g.gtype { case TypeGeometryCollection: - return g.AsGeometryCollection().Area(opts...) + return g.MustAsGeometryCollection().Area(opts...) case TypePoint: return 0 case TypeLineString: return 0 case TypePolygon: - return g.AsPolygon().Area(opts...) + return g.MustAsPolygon().Area(opts...) case TypeMultiPoint: return 0 case TypeMultiLineString: return 0 case TypeMultiPolygon: - return g.AsMultiPolygon().Area(opts...) + return g.MustAsMultiPolygon().Area(opts...) default: panic("unknown geometry: " + g.gtype.String()) } @@ -556,17 +624,17 @@ func (g Geometry) IsSimple() (isSimple bool, wellDefined bool) { case TypeGeometryCollection: return false, false case TypePoint: - return g.AsPoint().IsSimple(), true + return g.MustAsPoint().IsSimple(), true case TypeLineString: - return g.AsLineString().IsSimple(), true + return g.MustAsLineString().IsSimple(), true case TypePolygon: - return g.AsPolygon().IsSimple(), true + return g.MustAsPolygon().IsSimple(), true case TypeMultiPoint: - return g.AsMultiPoint().IsSimple(), true + return g.MustAsMultiPoint().IsSimple(), true case TypeMultiLineString: - return g.AsMultiLineString().IsSimple(), true + return g.MustAsMultiLineString().IsSimple(), true case TypeMultiPolygon: - return g.AsMultiPolygon().IsSimple(), true + return g.MustAsMultiPolygon().IsSimple(), true default: panic("unknown geometry: " + g.gtype.String()) } @@ -578,19 +646,19 @@ func (g Geometry) IsSimple() (isSimple bool, wellDefined bool) { func (g Geometry) Reverse() Geometry { switch g.gtype { case TypeGeometryCollection: - return g.AsGeometryCollection().Reverse().AsGeometry() + return g.MustAsGeometryCollection().Reverse().AsGeometry() case TypePoint: - return g.AsPoint().Reverse().AsGeometry() + return g.MustAsPoint().Reverse().AsGeometry() case TypeLineString: - return g.AsLineString().Reverse().AsGeometry() + return g.MustAsLineString().Reverse().AsGeometry() case TypePolygon: - return g.AsPolygon().Reverse().AsGeometry() + return g.MustAsPolygon().Reverse().AsGeometry() case TypeMultiPoint: - return g.AsMultiPoint().Reverse().AsGeometry() + return g.MustAsMultiPoint().Reverse().AsGeometry() case TypeMultiLineString: - return g.AsMultiLineString().Reverse().AsGeometry() + return g.MustAsMultiLineString().Reverse().AsGeometry() case TypeMultiPolygon: - return g.AsMultiPolygon().Reverse().AsGeometry() + return g.MustAsMultiPolygon().Reverse().AsGeometry() default: panic("unknown geometry: " + g.gtype.String()) } @@ -601,19 +669,19 @@ func (g Geometry) Reverse() Geometry { func (g Geometry) CoordinatesType() CoordinatesType { switch g.gtype { case TypeGeometryCollection: - return g.AsGeometryCollection().CoordinatesType() + return g.MustAsGeometryCollection().CoordinatesType() case TypePoint: - return g.AsPoint().CoordinatesType() + return g.MustAsPoint().CoordinatesType() case TypeLineString: - return g.AsLineString().CoordinatesType() + return g.MustAsLineString().CoordinatesType() case TypePolygon: - return g.AsPolygon().CoordinatesType() + return g.MustAsPolygon().CoordinatesType() case TypeMultiPoint: - return g.AsMultiPoint().CoordinatesType() + return g.MustAsMultiPoint().CoordinatesType() case TypeMultiLineString: - return g.AsMultiLineString().CoordinatesType() + return g.MustAsMultiLineString().CoordinatesType() case TypeMultiPolygon: - return g.AsMultiPolygon().CoordinatesType() + return g.MustAsMultiPolygon().CoordinatesType() default: panic("unknown geometry: " + g.gtype.String()) } @@ -624,19 +692,19 @@ func (g Geometry) CoordinatesType() CoordinatesType { func (g Geometry) ForceCoordinatesType(newCType CoordinatesType) Geometry { switch g.gtype { case TypeGeometryCollection: - return g.AsGeometryCollection().ForceCoordinatesType(newCType).AsGeometry() + return g.MustAsGeometryCollection().ForceCoordinatesType(newCType).AsGeometry() case TypePoint: - return g.AsPoint().ForceCoordinatesType(newCType).AsGeometry() + return g.MustAsPoint().ForceCoordinatesType(newCType).AsGeometry() case TypeLineString: - return g.AsLineString().ForceCoordinatesType(newCType).AsGeometry() + return g.MustAsLineString().ForceCoordinatesType(newCType).AsGeometry() case TypePolygon: - return g.AsPolygon().ForceCoordinatesType(newCType).AsGeometry() + return g.MustAsPolygon().ForceCoordinatesType(newCType).AsGeometry() case TypeMultiPoint: - return g.AsMultiPoint().ForceCoordinatesType(newCType).AsGeometry() + return g.MustAsMultiPoint().ForceCoordinatesType(newCType).AsGeometry() case TypeMultiLineString: - return g.AsMultiLineString().ForceCoordinatesType(newCType).AsGeometry() + return g.MustAsMultiLineString().ForceCoordinatesType(newCType).AsGeometry() case TypeMultiPolygon: - return g.AsMultiPolygon().ForceCoordinatesType(newCType).AsGeometry() + return g.MustAsMultiPolygon().ForceCoordinatesType(newCType).AsGeometry() default: panic("unknown geometry: " + g.gtype.String()) } @@ -651,19 +719,19 @@ func (g Geometry) Force2D() Geometry { func (g Geometry) PointOnSurface() Point { switch g.gtype { case TypeGeometryCollection: - return g.AsGeometryCollection().PointOnSurface() + return g.MustAsGeometryCollection().PointOnSurface() case TypePoint: - return g.AsPoint().PointOnSurface() + return g.MustAsPoint().PointOnSurface() case TypeLineString: - return g.AsLineString().PointOnSurface() + return g.MustAsLineString().PointOnSurface() case TypePolygon: - return g.AsPolygon().PointOnSurface() + return g.MustAsPolygon().PointOnSurface() case TypeMultiPoint: - return g.AsMultiPoint().PointOnSurface() + return g.MustAsMultiPoint().PointOnSurface() case TypeMultiLineString: - return g.AsMultiLineString().PointOnSurface() + return g.MustAsMultiLineString().PointOnSurface() case TypeMultiPolygon: - return g.AsMultiPolygon().PointOnSurface() + return g.MustAsMultiPolygon().PointOnSurface() default: panic("unknown geometry: " + g.gtype.String()) } @@ -686,11 +754,11 @@ func (g Geometry) ForceCCW() Geometry { func (g Geometry) forceOrientation(forceCW bool) Geometry { switch g.gtype { case TypePolygon: - return g.AsPolygon().forceOrientation(forceCW).AsGeometry() + return g.MustAsPolygon().forceOrientation(forceCW).AsGeometry() case TypeMultiPolygon: - return g.AsMultiPolygon().forceOrientation(forceCW).AsGeometry() + return g.MustAsMultiPolygon().forceOrientation(forceCW).AsGeometry() case TypeGeometryCollection: - return g.AsGeometryCollection().forceOrientation(forceCW).AsGeometry() + return g.MustAsGeometryCollection().forceOrientation(forceCW).AsGeometry() default: return g } @@ -700,22 +768,22 @@ func (g Geometry) controlPoints() int { switch g.gtype { case TypeGeometryCollection: var sum int - for _, g := range g.AsGeometryCollection().geoms { + for _, g := range g.MustAsGeometryCollection().geoms { sum += g.controlPoints() } return sum case TypePoint: return 1 case TypeLineString: - return g.AsLineString().Coordinates().Length() + return g.MustAsLineString().Coordinates().Length() case TypePolygon: - return g.AsPolygon().controlPoints() + return g.MustAsPolygon().controlPoints() case TypeMultiPoint: - return g.AsMultiPoint().NumPoints() + return g.MustAsMultiPoint().NumPoints() case TypeMultiLineString: - return g.AsMultiLineString().controlPoints() + return g.MustAsMultiLineString().controlPoints() case TypeMultiPolygon: - return g.AsMultiPolygon().controlPoints() + return g.MustAsMultiPolygon().controlPoints() default: panic("unknown geometry: " + g.gtype.String()) } @@ -738,25 +806,25 @@ func (g Geometry) appendDump(gs []Geometry) []Geometry { case TypePoint, TypeLineString, TypePolygon: gs = append(gs, g) case TypeMultiPoint: - mp := g.AsMultiPoint() + mp := g.MustAsMultiPoint() n := mp.NumPoints() for i := 0; i < n; i++ { gs = append(gs, mp.PointN(i).AsGeometry()) } case TypeMultiLineString: - mls := g.AsMultiLineString() + mls := g.MustAsMultiLineString() n := mls.NumLineStrings() for i := 0; i < n; i++ { gs = append(gs, mls.LineStringN(i).AsGeometry()) } case TypeMultiPolygon: - mp := g.AsMultiPolygon() + mp := g.MustAsMultiPolygon() n := mp.NumPolygons() for i := 0; i < n; i++ { gs = append(gs, mp.PolygonN(i).AsGeometry()) } case TypeGeometryCollection: - gc := g.AsGeometryCollection() + gc := g.MustAsGeometryCollection() n := gc.NumGeometries() for i := 0; i < n; i++ { gs = gc.GeometryN(i).appendDump(gs) @@ -772,19 +840,19 @@ func (g Geometry) appendDump(gs []Geometry) []Geometry { func (g Geometry) DumpCoordinates() Sequence { switch g.gtype { case TypeGeometryCollection: - return g.AsGeometryCollection().DumpCoordinates() + return g.MustAsGeometryCollection().DumpCoordinates() case TypePoint: - return g.AsPoint().DumpCoordinates() + return g.MustAsPoint().DumpCoordinates() case TypeLineString: - return g.AsLineString().Coordinates() + return g.MustAsLineString().Coordinates() case TypePolygon: - return g.AsPolygon().DumpCoordinates() + return g.MustAsPolygon().DumpCoordinates() case TypeMultiPoint: - return g.AsMultiPoint().Coordinates() + return g.MustAsMultiPoint().Coordinates() case TypeMultiLineString: - return g.AsMultiLineString().DumpCoordinates() + return g.MustAsMultiLineString().DumpCoordinates() case TypeMultiPolygon: - return g.AsMultiPolygon().DumpCoordinates() + return g.MustAsMultiPolygon().DumpCoordinates() default: panic("unknown type: " + g.Type().String()) } @@ -794,19 +862,19 @@ func (g Geometry) DumpCoordinates() Sequence { func (g Geometry) Summary() string { switch g.gtype { case TypeGeometryCollection: - return g.AsGeometryCollection().Summary() + return g.MustAsGeometryCollection().Summary() case TypePoint: - return g.AsPoint().Summary() + return g.MustAsPoint().Summary() case TypeLineString: - return g.AsLineString().Summary() + return g.MustAsLineString().Summary() case TypePolygon: - return g.AsPolygon().Summary() + return g.MustAsPolygon().Summary() case TypeMultiPoint: - return g.AsMultiPoint().Summary() + return g.MustAsMultiPoint().Summary() case TypeMultiLineString: - return g.AsMultiLineString().Summary() + return g.MustAsMultiLineString().Summary() case TypeMultiPolygon: - return g.AsMultiPolygon().Summary() + return g.MustAsMultiPolygon().Summary() default: panic("unknown type: " + g.Type().String()) } diff --git a/geom/type_geometry_collection.go b/geom/type_geometry_collection.go index 6f148863..e0f26321 100644 --- a/geom/type_geometry_collection.go +++ b/geom/type_geometry_collection.go @@ -56,7 +56,7 @@ func (c GeometryCollection) NumTotalGeometries() int { var n int for _, geom := range c.geoms { if geom.IsGeometryCollection() { - n += geom.AsGeometryCollection().NumTotalGeometries() + n += geom.MustAsGeometryCollection().NumTotalGeometries() } } return n + c.NumGeometries() @@ -117,7 +117,7 @@ func (c GeometryCollection) Dimension() int { func (c GeometryCollection) walk(fn func(Geometry)) { for _, g := range c.geoms { if g.IsGeometryCollection() { - g.AsGeometryCollection().walk(fn) + g.MustAsGeometryCollection().walk(fn) } else { fn(g) } @@ -271,7 +271,7 @@ func highestDimensionIgnoreEmpties(g Geometry) int { if !g.IsGeometryCollection() { return g.Dimension() } - c := g.AsGeometryCollection() + c := g.MustAsGeometryCollection() highestDim := 0 for _, g2 := range c.geoms { highestDim = max(highestDim, highestDimensionIgnoreEmpties(g2)) @@ -304,13 +304,13 @@ func (c GeometryCollection) pointCentroid() Point { c.walk(func(g Geometry) { switch { case g.IsPoint(): - xy, ok := g.AsPoint().XY() + xy, ok := g.MustAsPoint().XY() if ok { numPoints++ sumPoints = sumPoints.Add(xy) } case g.IsMultiPoint(): - mp := g.AsMultiPoint() + mp := g.MustAsMultiPoint() for i := 0; i < mp.NumPoints(); i++ { xy, ok := mp.PointN(i).XY() if ok { @@ -331,7 +331,7 @@ func (c GeometryCollection) linearCentroid() Point { c.walk(func(g Geometry) { switch { case g.IsLineString(): - ls := g.AsLineString() + ls := g.MustAsLineString() centroid, ok := ls.Centroid().XY() if ok { length := ls.Length() @@ -339,7 +339,7 @@ func (c GeometryCollection) linearCentroid() Point { weightedCentroid = weightedCentroid.Add(centroid.Scale(length)) } case g.IsMultiLineString(): - mls := g.AsMultiLineString() + mls := g.MustAsMultiLineString() for i := 0; i < mls.NumLineStrings(); i++ { ls := mls.LineStringN(i) centroid, ok := ls.Centroid().XY() diff --git a/geom/type_geometry_test.go b/geom/type_geometry_test.go index 31612a9c..07071b31 100644 --- a/geom/type_geometry_test.go +++ b/geom/type_geometry_test.go @@ -13,8 +13,11 @@ import ( func TestZeroGeometry(t *testing.T) { var z Geometry expectBoolEq(t, z.IsGeometryCollection(), true) - z.AsGeometryCollection() // Doesn't crash. + z.MustAsGeometryCollection() // Doesn't crash. expectStringEq(t, z.AsText(), "GEOMETRYCOLLECTION EMPTY") + gc, ok := z.AsGeometryCollection() + expectTrue(t, ok) + expectIntEq(t, gc.NumGeometries(), 0) var buf bytes.Buffer err := json.NewEncoder(&buf).Encode(z) @@ -92,3 +95,82 @@ func TestGeometryTypeString(t *testing.T) { }) } } + +func TestAsConcreteType(t *testing.T) { + for _, wkt := range []string{ + "GEOMETRYCOLLECTION(POINT(1 2))", + "POINT(1 2)", + "LINESTRING(1 2,3 4)", + "POLYGON((0 0,0 1,1 0,0 0))", + "MULTIPOINT((1 2))", + "MULTILINESTRING((1 2,3 4))", + "MULTIPOLYGON(((0 0,0 1,1 0,0 0)))", + } { + t.Run(wkt, func(t *testing.T) { + g := geomFromWKT(t, wkt) + + if g.IsGeometryCollection() { + concrete, ok := g.AsGeometryCollection() + expectTrue(t, ok) + expectFalse(t, concrete.IsEmpty()) + } else { + _, ok := g.AsGeometryCollection() + expectFalse(t, ok) + } + + if g.IsPoint() { + concrete, ok := g.AsPoint() + expectTrue(t, ok) + expectFalse(t, concrete.IsEmpty()) + } else { + _, ok := g.AsPoint() + expectFalse(t, ok) + } + + if g.IsLineString() { + concrete, ok := g.AsLineString() + expectTrue(t, ok) + expectFalse(t, concrete.IsEmpty()) + } else { + _, ok := g.AsLineString() + expectFalse(t, ok) + } + + if g.IsPolygon() { + concrete, ok := g.AsPolygon() + expectTrue(t, ok) + expectFalse(t, concrete.IsEmpty()) + } else { + _, ok := g.AsPolygon() + expectFalse(t, ok) + } + + if g.IsMultiPoint() { + concrete, ok := g.AsMultiPoint() + expectTrue(t, ok) + expectFalse(t, concrete.IsEmpty()) + } else { + _, ok := g.AsMultiPoint() + expectFalse(t, ok) + } + + if g.IsMultiLineString() { + concrete, ok := g.AsMultiLineString() + expectTrue(t, ok) + expectFalse(t, concrete.IsEmpty()) + } else { + _, ok := g.AsMultiLineString() + expectFalse(t, ok) + } + + if g.IsMultiPolygon() { + concrete, ok := g.AsMultiPolygon() + expectTrue(t, ok) + expectFalse(t, concrete.IsEmpty()) + } else { + _, ok := g.AsMultiPolygon() + expectFalse(t, ok) + } + }) + } +} diff --git a/geom/walk.go b/geom/walk.go index 93be7f55..3eea5d96 100644 --- a/geom/walk.go +++ b/geom/walk.go @@ -6,11 +6,11 @@ import "fmt" func walk(g Geometry, fn func(XY)) { switch g.Type() { case TypePoint: - if xy, ok := g.AsPoint().XY(); ok { + if xy, ok := g.MustAsPoint().XY(); ok { fn(xy) } case TypeLineString: - seq := g.AsLineString().Coordinates() + seq := g.MustAsLineString().Coordinates() n := seq.Length() for i := 0; i < n; i++ { fn(seq.GetXY(i)) @@ -18,7 +18,7 @@ func walk(g Geometry, fn func(XY)) { case TypePolygon: walk(g.Boundary(), fn) case TypeMultiPoint: - mp := g.AsMultiPoint() + mp := g.MustAsMultiPoint() n := mp.NumPoints() for i := 0; i < n; i++ { if xy, ok := mp.PointN(i).XY(); ok { @@ -26,7 +26,7 @@ func walk(g Geometry, fn func(XY)) { } } case TypeMultiLineString: - mls := g.AsMultiLineString() + mls := g.MustAsMultiLineString() n := mls.NumLineStrings() for i := 0; i < n; i++ { walk(mls.LineStringN(i).AsGeometry(), fn) @@ -34,7 +34,7 @@ func walk(g Geometry, fn func(XY)) { case TypeMultiPolygon: walk(g.Boundary(), fn) case TypeGeometryCollection: - gc := g.AsGeometryCollection() + gc := g.MustAsGeometryCollection() n := gc.NumGeometries() for i := 0; i < n; i++ { walk(gc.GeometryN(i), fn) diff --git a/geom/wkb_parser.go b/geom/wkb_parser.go index 641aec53..dfed4a89 100644 --- a/geom/wkb_parser.go +++ b/geom/wkb_parser.go @@ -278,7 +278,7 @@ func (p *wkbParser) parseMultiPoint(ctype CoordinatesType) (MultiPoint, error) { if !geom.IsPoint() { return MultiPoint{}, wkbSyntaxError{"MultiPoint contains non-Point element"} } - pts[i] = geom.AsPoint() + pts[i] = geom.MustAsPoint() } return NewMultiPoint(pts, p.opts...), nil } @@ -300,7 +300,7 @@ func (p *wkbParser) parseMultiLineString(ctype CoordinatesType) (MultiLineString if !geom.IsLineString() { return MultiLineString{}, wkbSyntaxError{"MultiLineString contains non-LineString element"} } - lss[i] = geom.AsLineString() + lss[i] = geom.MustAsLineString() } return NewMultiLineString(lss, p.opts...), nil } @@ -322,7 +322,7 @@ func (p *wkbParser) parseMultiPolygon(ctype CoordinatesType) (MultiPolygon, erro if !geom.IsPolygon() { return MultiPolygon{}, wkbSyntaxError{"MultiPolygon contains non-Polygon element"} } - polys[i] = geom.AsPolygon() + polys[i] = geom.MustAsPolygon() } return NewMultiPolygon(polys, p.opts...) } diff --git a/geom/wkt_test.go b/geom/wkt_test.go index 2dea85d6..d9423541 100644 --- a/geom/wkt_test.go +++ b/geom/wkt_test.go @@ -174,7 +174,7 @@ func TestUnmarshalWKTSyntaxErrors(t *testing.T) { func TestUnmarshalWKT(t *testing.T) { t.Run("multi line string containing an empty line string", func(t *testing.T) { g := geomFromWKT(t, "MULTILINESTRING((1 2,3 4),EMPTY,(5 6,7 8))") - mls := g.AsMultiLineString() + mls := g.MustAsMultiLineString() expectIntEq(t, mls.NumLineStrings(), 3) expectGeomEq(t, mls.LineStringN(0).AsGeometry(), diff --git a/internal/cmprefimpl/cmpgeos/checks.go b/internal/cmprefimpl/cmpgeos/checks.go index 0031b5df..ffda57da 100644 --- a/internal/cmprefimpl/cmpgeos/checks.go +++ b/internal/cmprefimpl/cmpgeos/checks.go @@ -195,8 +195,8 @@ func checkFromText(h *Handle, g geom.Geometry, log *log.Logger) error { // gives the following error: ParseException: Unexpected token: WORD EMPTY. // Skip the check in that case. if g.IsMultiPoint() && - g.AsMultiPoint().NumPoints() > 0 && - g.AsMultiPoint().PointN(0).IsEmpty() { + g.MustAsMultiPoint().NumPoints() > 0 && + g.MustAsMultiPoint().PointN(0).IsEmpty() { return nil } @@ -259,7 +259,7 @@ func checkFromBinary(h *Handle, g geom.Geometry, log *log.Logger) error { // Skip any MultiPoints that contain empty Points. Libgeos seems has // trouble handling these. if g.IsMultiPoint() { - mp := g.AsMultiPoint() + mp := g.MustAsMultiPoint() n := mp.NumPoints() for i := 0; i < n; i++ { if mp.PointN(i).IsEmpty() { @@ -431,7 +431,7 @@ func checkIsRing(h *Handle, g geom.Geometry, log *log.Logger) error { if err != nil { return err } - got := g.IsLineString() && g.AsLineString().IsRing() + got := g.IsLineString() && g.MustAsLineString().IsRing() if want != got { log.Printf("want: %v", want) @@ -468,7 +468,7 @@ func isArealGeometry(g geom.Geometry) bool { case g.IsPolygon() || g.IsMultiPolygon(): return true case g.IsGeometryCollection(): - gc := g.AsGeometryCollection() + gc := g.MustAsGeometryCollection() for i := 0; i < gc.NumGeometries(); i++ { if isArealGeometry(gc.GeometryN(i)) { return true diff --git a/internal/cmprefimpl/cmpgeos/handle.go b/internal/cmprefimpl/cmpgeos/handle.go index 27feda5e..fbfdcfa9 100644 --- a/internal/cmprefimpl/cmpgeos/handle.go +++ b/internal/cmprefimpl/cmpgeos/handle.go @@ -123,13 +123,13 @@ func (h *Handle) intToErr(i C.int) error { func (h *Handle) createGeomHandle(g geom.Geometry) (*C.GEOSGeometry, error) { switch { case g.IsPoint(): - return h.createGeomHandleForPoint(g.AsPoint()) + return h.createGeomHandleForPoint(g.MustAsPoint()) case g.IsMultiPoint(): - return h.createGeomHandleForMultiPoint(g.AsMultiPoint()) + return h.createGeomHandleForMultiPoint(g.MustAsMultiPoint()) case g.IsMultiPolygon(): - return h.createGeomHandleForMultiPolygon(g.AsMultiPolygon()) + return h.createGeomHandleForMultiPolygon(g.MustAsMultiPolygon()) case g.IsGeometryCollection(): - return h.createGeomHandleForGeometryCollection(g.AsGeometryCollection()) + return h.createGeomHandleForGeometryCollection(g.MustAsGeometryCollection()) default: return h.createGeomHandleUsingWKB(g) } @@ -290,7 +290,7 @@ func (h *Handle) decodeGeomHandle(gh *C.GEOSGeometry) (geom.Geometry, error) { return geom.Geometry{}, errors.New( "internal error: expected point") } - subPoints[i] = subPointAsGeom.AsPoint() + subPoints[i] = subPointAsGeom.MustAsPoint() } } return geom.NewMultiPoint(subPoints).AsGeometry(), nil @@ -313,7 +313,7 @@ func (h *Handle) decodeGeomHandle(gh *C.GEOSGeometry) (geom.Geometry, error) { return geom.Geometry{}, errors.New( "internal error: expected polygon") } - subPolys[i] = subPolyAsGeom.AsPolygon() + subPolys[i] = subPolyAsGeom.MustAsPolygon() } mp, err := geom.NewMultiPolygon(subPolys) return mp.AsGeometry(), err diff --git a/internal/cmprefimpl/cmpgeos/util.go b/internal/cmprefimpl/cmpgeos/util.go index 3c97273e..245e8175 100644 --- a/internal/cmprefimpl/cmpgeos/util.go +++ b/internal/cmprefimpl/cmpgeos/util.go @@ -12,14 +12,14 @@ import ( func containsNonEmptyPointInMultiPoint(g geom.Geometry) bool { switch { case g.IsGeometryCollection(): - gc := g.AsGeometryCollection() + gc := g.MustAsGeometryCollection() for i := 0; i < gc.NumGeometries(); i++ { if containsNonEmptyPointInMultiPoint(gc.GeometryN(i)) { return true } } case g.IsMultiPoint(): - mp := g.AsMultiPoint() + mp := g.MustAsMultiPoint() for i := 0; i < mp.NumPoints(); i++ { if !mp.PointN(i).IsEmpty() { return true @@ -32,7 +32,7 @@ func containsNonEmptyPointInMultiPoint(g geom.Geometry) bool { func containsCollectionWithOnlyEmptyElements(g geom.Geometry) bool { switch { case g.IsGeometryCollection(): - gc := g.AsGeometryCollection() + gc := g.MustAsGeometryCollection() if gc.IsEmpty() && gc.NumGeometries() > 0 { return true } @@ -43,13 +43,13 @@ func containsCollectionWithOnlyEmptyElements(g geom.Geometry) bool { } return false case g.IsMultiPoint(): - mp := g.AsMultiPoint() + mp := g.MustAsMultiPoint() return mp.IsEmpty() && mp.NumPoints() > 0 case g.IsMultiLineString(): - mls := g.AsMultiLineString() + mls := g.MustAsMultiLineString() return mls.IsEmpty() && mls.NumLineStrings() > 0 case g.IsMultiPolygon(): - mp := g.AsMultiPolygon() + mp := g.MustAsMultiPolygon() return mp.IsEmpty() && mp.NumPolygons() > 0 default: return false @@ -60,7 +60,7 @@ func containsOnlyGeometryCollections(g geom.Geometry) bool { if !g.IsGeometryCollection() { return false } - gc := g.AsGeometryCollection() + gc := g.MustAsGeometryCollection() for i := 0; i < gc.NumGeometries(); i++ { if !containsOnlyGeometryCollections(gc.GeometryN(i)) { return false @@ -72,7 +72,7 @@ func containsOnlyGeometryCollections(g geom.Geometry) bool { func containsMultiPolygonWithEmptyPolygon(g geom.Geometry) bool { switch { case g.IsMultiPolygon(): - mp := g.AsMultiPolygon() + mp := g.MustAsMultiPolygon() for i := 0; i < mp.NumPolygons(); i++ { if mp.PolygonN(i).IsEmpty() { return true @@ -80,7 +80,7 @@ func containsMultiPolygonWithEmptyPolygon(g geom.Geometry) bool { } return false case g.IsGeometryCollection(): - gc := g.AsGeometryCollection() + gc := g.MustAsGeometryCollection() for i := 0; i < gc.NumGeometries(); i++ { if containsMultiPolygonWithEmptyPolygon(gc.GeometryN(i)) { return true @@ -95,14 +95,14 @@ func containsMultiPolygonWithEmptyPolygon(g geom.Geometry) bool { func containsMultiPointWithEmptyPoint(g geom.Geometry) bool { switch { case g.IsMultiPoint(): - mp := g.AsMultiPoint() + mp := g.MustAsMultiPoint() for i := 0; i < mp.NumPoints(); i++ { if mp.PointN(i).IsEmpty() { return true } } case g.IsGeometryCollection(): - gc := g.AsGeometryCollection() + gc := g.MustAsGeometryCollection() for i := 0; i < gc.NumGeometries(); i++ { if containsMultiPointWithEmptyPoint(gc.GeometryN(i)) { return true @@ -115,14 +115,14 @@ func containsMultiPointWithEmptyPoint(g geom.Geometry) bool { func containsMultiLineStringWithEmptyLineString(g geom.Geometry) bool { switch { case g.IsMultiLineString(): - mls := g.AsMultiLineString() + mls := g.MustAsMultiLineString() for i := 0; i < mls.NumLineStrings(); i++ { if mls.LineStringN(i).IsEmpty() { return true } } case g.IsGeometryCollection(): - gc := g.AsGeometryCollection() + gc := g.MustAsGeometryCollection() for i := 0; i < gc.NumGeometries(); i++ { if containsMultiLineStringWithEmptyLineString(gc.GeometryN(i)) { return true @@ -137,7 +137,7 @@ func hasEmptyRing(g geom.Geometry) bool { // called with invalid geometries. switch { case g.IsPolygon(): - p := g.AsPolygon() + p := g.MustAsPolygon() if p.ExteriorRing().IsEmpty() { return true } @@ -147,14 +147,14 @@ func hasEmptyRing(g geom.Geometry) bool { } } case g.IsMultiPolygon(): - mp := g.AsMultiPolygon() + mp := g.MustAsMultiPolygon() for i := 0; i < mp.NumPolygons(); i++ { if hasEmptyRing(mp.PolygonN(i).AsGeometry()) { return true } } case g.IsGeometryCollection(): - gc := g.AsGeometryCollection() + gc := g.MustAsGeometryCollection() for i := 0; i < gc.NumGeometries(); i++ { if hasEmptyRing(gc.GeometryN(i)) { return true @@ -169,7 +169,7 @@ func hasEmptyPoint(g geom.Geometry) bool { case g.IsPoint(): return g.IsEmpty() case g.IsMultiPoint(): - mp := g.AsMultiPoint() + mp := g.MustAsMultiPoint() n := mp.NumPoints() for i := 0; i < n; i++ { if mp.PointN(i).IsEmpty() { @@ -177,7 +177,7 @@ func hasEmptyPoint(g geom.Geometry) bool { } } case g.IsGeometryCollection(): - gc := g.AsGeometryCollection() + gc := g.MustAsGeometryCollection() n := gc.NumGeometries() for i := 0; i < n; i++ { if hasEmptyPoint(gc.GeometryN(i)) { @@ -220,10 +220,10 @@ func mantissaTerminatesQuickly(g geom.Geometry) bool { switch g.Type() { case geom.TypePoint: - xy, ok := g.AsPoint().XY() + xy, ok := g.MustAsPoint().XY() return !ok || termXY(xy) case geom.TypeLineString: - seq := g.AsLineString().Coordinates() + seq := g.MustAsLineString().Coordinates() for i := 0; i < seq.Length(); i++ { if !termXY(seq.GetXY(i)) { return false @@ -233,7 +233,7 @@ func mantissaTerminatesQuickly(g geom.Geometry) bool { case geom.TypePolygon: return g.IsEmpty() || mantissaTerminatesQuickly(g.Boundary()) case geom.TypeMultiPoint: - mp := g.AsMultiPoint() + mp := g.MustAsMultiPoint() for i := 0; i < mp.NumPoints(); i++ { pt := mp.PointN(i) if !mantissaTerminatesQuickly(pt.AsGeometry()) { @@ -242,7 +242,7 @@ func mantissaTerminatesQuickly(g geom.Geometry) bool { } return true case geom.TypeMultiLineString: - mls := g.AsMultiLineString() + mls := g.MustAsMultiLineString() for i := 0; i < mls.NumLineStrings(); i++ { ls := mls.LineStringN(i) if !mantissaTerminatesQuickly(ls.AsGeometry()) { @@ -253,7 +253,7 @@ func mantissaTerminatesQuickly(g geom.Geometry) bool { case geom.TypeMultiPolygon: return g.IsEmpty() || mantissaTerminatesQuickly(g.Boundary()) case geom.TypeGeometryCollection: - gc := g.AsGeometryCollection() + gc := g.MustAsGeometryCollection() for i := 0; i < gc.NumGeometries(); i++ { g := gc.GeometryN(i) if !mantissaTerminatesQuickly(g) { diff --git a/internal/cmprefimpl/cmppg/checks.go b/internal/cmprefimpl/cmppg/checks.go index 74687b3d..c72a0b52 100644 --- a/internal/cmprefimpl/cmppg/checks.go +++ b/internal/cmprefimpl/cmppg/checks.go @@ -135,10 +135,10 @@ func checkGeoJSONParse(t *testing.T, pg PostGIS, candidates []string) { func checkWKB(t *testing.T, want UnaryResult, g geom.Geometry) { t.Run("CheckWKB", func(t *testing.T) { - if g.IsEmpty() && ((g.IsGeometryCollection() && g.AsGeometryCollection().NumGeometries() > 0) || - (g.IsMultiPoint() && g.AsMultiPoint().NumPoints() > 0) || - (g.IsMultiLineString() && g.AsMultiLineString().NumLineStrings() > 0) || - (g.IsMultiPolygon() && g.AsMultiPolygon().NumPolygons() > 0)) { + if g.IsEmpty() && ((g.IsGeometryCollection() && g.MustAsGeometryCollection().NumGeometries() > 0) || + (g.IsMultiPoint() && g.MustAsMultiPoint().NumPoints() > 0) || + (g.IsMultiLineString() && g.MustAsMultiLineString().NumLineStrings() > 0) || + (g.IsMultiPolygon() && g.MustAsMultiPolygon().NumPolygons() > 0)) { // The behaviour for collections in PostGIS is to just give the // collection with zero elements (even if there are some empty // elements). This doesn't seem like correct behaviour, so these @@ -309,7 +309,7 @@ func checkIsRing(t *testing.T, want UnaryResult, g geom.Geometry) { } var got bool // Defaults to false for Line case. if g.IsLineString() { - got = g.AsLineString().IsRing() + got = g.MustAsLineString().IsRing() } want := want.IsRing.Bool if got != want { @@ -334,14 +334,14 @@ func checkLength(t *testing.T, want UnaryResult, g geom.Geometry) { func containsMultiPointContainingEmptyPoint(g geom.Geometry) bool { switch { case g.IsMultiPoint(): - mp := g.AsMultiPoint() + mp := g.MustAsMultiPoint() for i := 0; i < mp.NumPoints(); i++ { if mp.PointN(i).IsEmpty() { return true } } case g.IsGeometryCollection(): - gc := g.AsGeometryCollection() + gc := g.MustAsGeometryCollection() for i := 0; i < gc.NumGeometries(); i++ { if containsMultiPointContainingEmptyPoint(gc.GeometryN(i)) { return true @@ -487,7 +487,7 @@ func containsOnlyPolygonsOrMultiPolygons(g geom.Geometry) bool { case geom.TypePolygon, geom.TypeMultiPolygon: return true case geom.TypeGeometryCollection: - gc := g.AsGeometryCollection() + gc := g.MustAsGeometryCollection() for i := 0; i < gc.NumGeometries(); i++ { if !containsOnlyPolygonsOrMultiPolygons(gc.GeometryN(i)) { return false diff --git a/internal/cmprefimpl/cmppg/pg.go b/internal/cmprefimpl/cmppg/pg.go index ef153e99..0f266967 100644 --- a/internal/cmprefimpl/cmppg/pg.go +++ b/internal/cmprefimpl/cmppg/pg.go @@ -185,7 +185,7 @@ func isNestedGeometryCollection(g geom.Geometry) bool { if !g.IsGeometryCollection() { return false } - gc := g.AsGeometryCollection() + gc := g.MustAsGeometryCollection() for i := 0; i < gc.NumGeometries(); i++ { if gc.GeometryN(i).IsGeometryCollection() { return true