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  }