github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/geo/geomfn/unary_operators.go (about) 1 // Copyright 2020 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package geomfn 12 13 import ( 14 "github.com/cockroachdb/cockroach/pkg/geo" 15 "github.com/cockroachdb/cockroach/pkg/geo/geopb" 16 "github.com/cockroachdb/cockroach/pkg/geo/geos" 17 "github.com/cockroachdb/errors" 18 "github.com/twpayne/go-geom" 19 "github.com/twpayne/go-geom/encoding/ewkb" 20 ) 21 22 // Centroid returns the Centroid of a given Geometry. 23 func Centroid(g *geo.Geometry) (*geo.Geometry, error) { 24 // Empty geometries do not react well in GEOS, so we have to 25 // convert and check beforehand. 26 // Remove after #49209 is resolved. 27 t, err := g.AsGeomT() 28 if err != nil { 29 return nil, err 30 } 31 if t.Empty() { 32 return geo.NewGeometryFromGeom(geom.NewPointEmpty(geom.XY)) 33 } 34 centroidEWKB, err := geos.Centroid(g.EWKB()) 35 if err != nil { 36 return nil, err 37 } 38 return geo.ParseGeometryFromEWKB(centroidEWKB) 39 } 40 41 // Length returns the length of a given Geometry. 42 // Note only (MULTI)LINESTRING objects have a length. 43 // (MULTI)POLYGON objects should use Perimeter. 44 func Length(g *geo.Geometry) (float64, error) { 45 geomRepr, err := g.AsGeomT() 46 if err != nil { 47 return 0, err 48 } 49 // Length in GEOS will also include polygon "perimeters". 50 // As such, gate based on on shape underneath. 51 switch geomRepr := geomRepr.(type) { 52 case *geom.Point, *geom.MultiPoint, *geom.Polygon, *geom.MultiPolygon: 53 return 0, nil 54 case *geom.LineString, *geom.MultiLineString: 55 return geos.Length(g.EWKB()) 56 case *geom.GeometryCollection: 57 total := float64(0) 58 for _, subG := range geomRepr.Geoms() { 59 switch subG := subG.(type) { 60 case *geom.Point, *geom.MultiPoint, *geom.Polygon, *geom.MultiPolygon: 61 continue 62 case *geom.LineString, *geom.MultiLineString: 63 subGEWKB, err := ewkb.Marshal(subG, geo.DefaultEWKBEncodingFormat) 64 if err != nil { 65 return 0, err 66 } 67 length, err := geos.Length(geopb.EWKB(subGEWKB)) 68 if err != nil { 69 return 0, err 70 } 71 total += length 72 default: 73 return 0, errors.AssertionFailedf("unknown geometry type in GeometryCollection: %T", subG) 74 } 75 } 76 return total, nil 77 default: 78 return 0, errors.AssertionFailedf("unknown geometry type: %T", geomRepr) 79 } 80 } 81 82 // Perimeter returns the perimeter of a given Geometry. 83 // Note only (MULTI)POLYGON objects have a perimeter. 84 // (MULTI)LineString objects should use Length. 85 func Perimeter(g *geo.Geometry) (float64, error) { 86 geomRepr, err := g.AsGeomT() 87 if err != nil { 88 return 0, err 89 } 90 switch geomRepr := geomRepr.(type) { 91 case *geom.Point, *geom.MultiPoint, *geom.LineString, *geom.MultiLineString: 92 return 0, nil 93 case *geom.Polygon, *geom.MultiPolygon: 94 return geos.Length(g.EWKB()) 95 case *geom.GeometryCollection: 96 total := float64(0) 97 for _, subG := range geomRepr.Geoms() { 98 switch subG := subG.(type) { 99 case *geom.Point, *geom.MultiPoint, *geom.LineString, *geom.MultiLineString: 100 continue 101 case *geom.Polygon, *geom.MultiPolygon: 102 subGEWKB, err := ewkb.Marshal(subG, geo.DefaultEWKBEncodingFormat) 103 if err != nil { 104 return 0, err 105 } 106 perimeter, err := geos.Length(geopb.EWKB(subGEWKB)) 107 if err != nil { 108 return 0, err 109 } 110 total += perimeter 111 default: 112 return 0, errors.AssertionFailedf("unknown geometry type in GeometryCollection: %T", subG) 113 } 114 } 115 return total, nil 116 default: 117 return 0, errors.AssertionFailedf("unknown geometry type: %T", geomRepr) 118 } 119 } 120 121 // Area returns the area of a given Geometry. 122 func Area(g *geo.Geometry) (float64, error) { 123 return geos.Area(g.EWKB()) 124 }