diff --git a/README.md b/README.md index 0d3c64c..5d5559f 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ geometries and use a geometric operation that is only available in `go-geos`. | | To `geom.T` | To `*geos.Geom` | To `orb.Geometry` | | ------------------- | ----------- | --------------- | ----------------- | | From `geom.T` | n/a | no | yes | -| From `*geos.Geom` | no | n/a | yes | +| From `*geos.Geom` | yes | n/a | yes | | From `orb.Geometry` | yes | yes | n/a | ## License diff --git a/geobabel_test.go b/geobabel_test.go index 61a4595..4c07419 100644 --- a/geobabel_test.go +++ b/geobabel_test.go @@ -126,6 +126,7 @@ func TestAll(t *testing.T) { require.True(t, tc.geos.IsValid()) require.Equal(t, "Valid Geometry", tc.geos.IsValidReason()) + assert.Equal(t, tc.geom, geobabel.NewGeomTFromGEOSGeom(tc.geos)) assert.Equal(t, tc.geom, geobabel.NewGeomTFromOrbGeometry(tc.orb)) assert.True(t, tc.geos.Equals(geobabel.NewGEOSGeomFromOrbGeometry(geosContext, tc.orb))) diff --git a/geomfromgeos.go b/geomfromgeos.go new file mode 100644 index 0000000..46ddaec --- /dev/null +++ b/geomfromgeos.go @@ -0,0 +1,137 @@ +package geobabel + +// FIXME add a geos.Geom.Flat method and use that instead +// FIXME test non-XY layouts + +import ( + "fmt" + + "github.com/twpayne/go-geom" + "github.com/twpayne/go-geos" +) + +func NewGeomTFromGEOSGeom(geosGeom *geos.Geom) geom.T { + switch geosGeom.TypeID() { + case geos.TypeIDPoint: + return NewGeomPointFromGEOSGeom(geosGeom) + case geos.TypeIDLineString: + return NewGeomLineStringFromGEOSGeom(geosGeom) + case geos.TypeIDLinearRing: + return NewGeomLinearRingFromGEOSGeom(geosGeom) + case geos.TypeIDPolygon: + return NewGeomPolygonFromGEOSGeom(geosGeom) + case geos.TypeIDMultiPoint: + return NewGeomMultiPointFromGEOSGeom(geosGeom) + case geos.TypeIDMultiLineString: + return NewGeomMultiLineStringFromGEOSGeom(geosGeom) + case geos.TypeIDMultiPolygon: + return NewGeomMultiPolygonFromGEOSGeom(geosGeom) + case geos.TypeIDGeometryCollection: + return NewGeomGeometryCollectionFromGEOSGeom(geosGeom) + default: + panic(fmt.Sprintf("%s: unsupported GEOS type", geosGeom.Type())) + } +} + +func NewGeomPointFromGEOSGeom(geosGeom *geos.Geom) *geom.Point { + geosCoordSeq := geosGeom.CoordSeq() + geomLayout := GeomLayoutFromGEOSDimensions(geosCoordSeq.Dimensions()) + return geom.NewPointFlat(geomLayout, geosCoordSeq.FlatCoords()) +} + +func NewGeomLineStringFromGEOSGeom(geosGeom *geos.Geom) *geom.LineString { + geosCoordSeq := geosGeom.CoordSeq() + geomLayout := GeomLayoutFromGEOSDimensions(geosCoordSeq.Dimensions()) + return geom.NewLineStringFlat(geomLayout, geosCoordSeq.FlatCoords()) +} + +func NewGeomLinearRingFromGEOSGeom(geosGeom *geos.Geom) *geom.LinearRing { + geosCoordSeq := geosGeom.CoordSeq() + geomLayout := GeomLayoutFromGEOSDimensions(geosCoordSeq.Dimensions()) + return geom.NewLinearRingFlat(geomLayout, geosCoordSeq.FlatCoords()) +} + +func NewGeomPolygonFromGEOSGeom(geosGeom *geos.Geom) *geom.Polygon { + geosExteriorRingCoordSeq := geosGeom.ExteriorRing().CoordSeq() + geomLayout := GeomLayoutFromGEOSDimensions(geosExteriorRingCoordSeq.Dimensions()) + geomFlatCoords := geosExteriorRingCoordSeq.FlatCoords() + geomEnds := []int{len(geomFlatCoords)} + for i, n := 0, geosGeom.NumInteriorRings(); i < n; i++ { + geosInteriorRing := geosGeom.InteriorRing(i) + geomFlatCoords = append(geomFlatCoords, geosInteriorRing.CoordSeq().FlatCoords()...) + geomEnds = append(geomEnds, len(geomFlatCoords)) + } + return geom.NewPolygonFlat(geomLayout, geomFlatCoords, geomEnds) +} + +func NewGeomMultiPointFromGEOSGeom(geosGeom *geos.Geom) *geom.MultiPoint { + geosNumGeometries := geosGeom.NumGeometries() + geomLayout := geom.XY + var geomFlatCoords []float64 + for i := 0; i < geosNumGeometries; i++ { + geosCoordSeq := geosGeom.Geometry(i).CoordSeq() + geomLayout = GeomLayoutFromGEOSDimensions(geosCoordSeq.Dimensions()) + geomFlatCoords = append(geomFlatCoords, geosCoordSeq.FlatCoords()...) + } + return geom.NewMultiPointFlat(geomLayout, geomFlatCoords) +} + +func NewGeomMultiLineStringFromGEOSGeom(geosGeom *geos.Geom) *geom.MultiLineString { + geosNumGeometries := geosGeom.NumGeometries() + geomLayout := geom.XY + var geomFlatCoords []float64 + geomEnds := make([]int, 0, geosNumGeometries) + for i := 0; i < geosNumGeometries; i++ { + geosCoordSeq := geosGeom.Geometry(i).CoordSeq() + geomLayout = GeomLayoutFromGEOSDimensions(geosCoordSeq.Dimensions()) + geomFlatCoords = append(geomFlatCoords, geosCoordSeq.FlatCoords()...) + geomEnds = append(geomEnds, len(geomFlatCoords)) + } + return geom.NewMultiLineStringFlat(geomLayout, geomFlatCoords, geomEnds) +} + +func NewGeomMultiPolygonFromGEOSGeom(geosGeom *geos.Geom) *geom.MultiPolygon { + geosNumGeometries := geosGeom.NumGeometries() + geomLayout := geom.XY + var geomFlatCoords []float64 + geomEndss := make([][]int, 0, geosNumGeometries) + for i := 0; i < geosNumGeometries; i++ { + geosPolygon := geosGeom.Geometry(i) + geosExteriorRingCoordSeq := geosPolygon.ExteriorRing().CoordSeq() + geomLayout = GeomLayoutFromGEOSDimensions(geosExteriorRingCoordSeq.Dimensions()) + geomFlatCoords = append(geomFlatCoords, geosExteriorRingCoordSeq.FlatCoords()...) + geomEnds := []int{len(geomFlatCoords)} + for i, n := 0, geosGeom.NumInteriorRings(); i < n; i++ { + geosInteriorRing := geosGeom.InteriorRing(i) + geomFlatCoords = append(geomFlatCoords, geosInteriorRing.CoordSeq().FlatCoords()...) + geomEnds = append(geomEnds, len(geomFlatCoords)) + } + geomEndss = append(geomEndss, geomEnds) + } + return geom.NewMultiPolygonFlat(geomLayout, geomFlatCoords, geomEndss) +} + +func NewGeomGeometryCollectionFromGEOSGeom(geosGeom *geos.Geom) *geom.GeometryCollection { + geosNumGeometries := geosGeom.NumGeometries() + geomGeomTs := make([]geom.T, 0, geosNumGeometries) + for i := 0; i < geosNumGeometries; i++ { + geosGeomT := NewGeomTFromGEOSGeom(geosGeom.Geometry(i)) + geomGeomTs = append(geomGeomTs, geosGeomT) + } + return geom.NewGeometryCollection().MustPush(geomGeomTs...) +} + +func GeomLayoutFromGEOSDimensions(geosDimensions int) geom.Layout { + switch { + case geosDimensions == 2: + return geom.XY + case geosDimensions == 3: + return geom.XYZ + case geosDimensions == 4: + return geom.XYZM + case geosDimensions > 4: + return geom.Layout(geosDimensions) + default: + panic(fmt.Sprintf("%d: unsupported GEOS dimensions", geosDimensions)) + } +} diff --git a/go.mod b/go.mod index 903b4cb..e825909 100644 --- a/go.mod +++ b/go.mod @@ -14,3 +14,5 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) + +replace github.com/twpayne/go-geos => ../go-geos diff --git a/go.sum b/go.sum index 67ddf37..e3860e1 100644 --- a/go.sum +++ b/go.sum @@ -20,8 +20,6 @@ github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKs github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/twpayne/go-geom v1.5.0 h1:seB5SE58wtTDOljFXFnyz2UmKI2SU86tRb2l4yFWH6c= github.com/twpayne/go-geom v1.5.0/go.mod h1:Kz4sX4LtdesDQgkhsMERazLlH/NiCg90s6FPaNr0KNI= -github.com/twpayne/go-geos v0.9.0 h1:Dcgmnqyj6EeaGlq6WH3yZ2uKGQ3e/VxdnxBhWu5mXe0= -github.com/twpayne/go-geos v0.9.0/go.mod h1:ghmhzeDJuLrFJFvOXUbn+sjPukdKYh11fxaGVq3e7Hk= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=