github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/geo/geo.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 geo contains the base types for spatial data type operations.
    12  package geo
    13  
    14  import (
    15  	"encoding/binary"
    16  
    17  	"github.com/cockroachdb/cockroach/pkg/geo/geopb"
    18  	"github.com/cockroachdb/errors"
    19  	"github.com/golang/geo/s2"
    20  	"github.com/twpayne/go-geom"
    21  	"github.com/twpayne/go-geom/encoding/ewkb"
    22  )
    23  
    24  // DefaultEWKBEncodingFormat is the default encoding format for EWKB.
    25  var DefaultEWKBEncodingFormat = binary.LittleEndian
    26  
    27  // EmptyBehavior is the behavior to adopt when an empty Geometry is encountered.
    28  type EmptyBehavior uint8
    29  
    30  const (
    31  	// EmptyBehaviorError will error with EmptyGeometryError when an empty geometry
    32  	// is encountered.
    33  	EmptyBehaviorError EmptyBehavior = 0
    34  	// EmptyBehaviorOmit will omit an entry when an empty geometry is encountered.
    35  	EmptyBehaviorOmit EmptyBehavior = 1
    36  )
    37  
    38  //
    39  // Geospatial Type
    40  //
    41  
    42  // GeospatialType are functions that are common between all Geospatial types.
    43  type GeospatialType interface {
    44  	// SRID returns the SRID of the given type.
    45  	SRID() geopb.SRID
    46  	// Shape returns the Shape of the given type.
    47  	Shape() geopb.Shape
    48  }
    49  
    50  var _ GeospatialType = (*Geometry)(nil)
    51  var _ GeospatialType = (*Geography)(nil)
    52  
    53  // GeospatialTypeFitsColumnMetadata determines whether a GeospatialType is compatible with the
    54  // given SRID and Shape.
    55  // Returns an error if the types does not fit.
    56  func GeospatialTypeFitsColumnMetadata(t GeospatialType, srid geopb.SRID, shape geopb.Shape) error {
    57  	// SRID 0 can take in any SRID. Otherwise SRIDs must match.
    58  	if srid != 0 && t.SRID() != srid {
    59  		return errors.Newf("object SRID %d does not match column SRID %d", t.SRID(), srid)
    60  	}
    61  	// Shape_Geometry/Shape_Unset can take in any kind of shape.
    62  	// Otherwise, shapes must match.
    63  	if shape != geopb.Shape_Unset && shape != geopb.Shape_Geometry && shape != t.Shape() {
    64  		return errors.Newf("object type %s does not match column type %s", t.Shape(), shape)
    65  	}
    66  	return nil
    67  }
    68  
    69  //
    70  // Geometry
    71  //
    72  
    73  // Geometry is planar spatial object.
    74  type Geometry struct {
    75  	geopb.SpatialObject
    76  }
    77  
    78  // NewGeometry returns a new Geometry. Assumes the input EWKB is validated and in little endian.
    79  func NewGeometry(spatialObject geopb.SpatialObject) *Geometry {
    80  	return &Geometry{SpatialObject: spatialObject}
    81  }
    82  
    83  // NewGeometryFromPointCoords makes a point from x, y coordinates.
    84  func NewGeometryFromPointCoords(x, y float64) (*Geometry, error) {
    85  	s, err := spatialObjectFromGeom(geom.NewPointFlat(geom.XY, []float64{x, y}))
    86  	if err != nil {
    87  		return nil, err
    88  	}
    89  	return &Geometry{SpatialObject: s}, nil
    90  }
    91  
    92  // NewGeometryFromGeom creates a new Geometry object from a geom.T object.
    93  func NewGeometryFromGeom(g geom.T) (*Geometry, error) {
    94  	spatialObject, err := spatialObjectFromGeom(g)
    95  	if err != nil {
    96  		return nil, err
    97  	}
    98  	return NewGeometry(spatialObject), nil
    99  }
   100  
   101  // ParseGeometry parses a Geometry from a given text.
   102  func ParseGeometry(str string) (*Geometry, error) {
   103  	spatialObject, err := parseAmbiguousText(str, geopb.DefaultGeometrySRID)
   104  	if err != nil {
   105  		return nil, err
   106  	}
   107  	return NewGeometry(spatialObject), nil
   108  }
   109  
   110  // MustParseGeometry behaves as ParseGeometry, but panics if there is an error.
   111  func MustParseGeometry(str string) *Geometry {
   112  	g, err := ParseGeometry(str)
   113  	if err != nil {
   114  		panic(err)
   115  	}
   116  	return g
   117  }
   118  
   119  // ParseGeometryFromEWKT parses the EWKT into a Geometry.
   120  func ParseGeometryFromEWKT(
   121  	ewkt geopb.EWKT, srid geopb.SRID, defaultSRIDOverwriteSetting defaultSRIDOverwriteSetting,
   122  ) (*Geometry, error) {
   123  	g, err := parseEWKT(ewkt, srid, defaultSRIDOverwriteSetting)
   124  	if err != nil {
   125  		return nil, err
   126  	}
   127  	return NewGeometry(g), nil
   128  }
   129  
   130  // ParseGeometryFromEWKB parses the EWKB into a Geometry.
   131  func ParseGeometryFromEWKB(ewkb geopb.EWKB) (*Geometry, error) {
   132  	g, err := parseEWKB(ewkb, geopb.DefaultGeometrySRID, DefaultSRIDIsHint)
   133  	if err != nil {
   134  		return nil, err
   135  	}
   136  	return NewGeometry(g), nil
   137  }
   138  
   139  // ParseGeometryFromWKB parses the WKB into a given Geometry.
   140  func ParseGeometryFromWKB(wkb geopb.WKB, srid geopb.SRID) (*Geometry, error) {
   141  	g, err := parseWKB(wkb, srid)
   142  	if err != nil {
   143  		return nil, err
   144  	}
   145  	return NewGeometry(g), nil
   146  }
   147  
   148  // ParseGeometryFromGeoJSON parses the GeoJSON into a given Geometry.
   149  func ParseGeometryFromGeoJSON(json []byte) (*Geometry, error) {
   150  	g, err := parseGeoJSON(json, geopb.DefaultGeometrySRID)
   151  	if err != nil {
   152  		return nil, err
   153  	}
   154  	return NewGeometry(g), nil
   155  }
   156  
   157  // ParseGeometryFromEWKBRaw returns a new Geometry from an EWKB, without any SRID checks.
   158  // You should only do this if you trust the EWKB is setup correctly.
   159  // You must likely want geo.ParseGeometryFromEWKB instead.
   160  func ParseGeometryFromEWKBRaw(ewkb geopb.EWKB) (*Geometry, error) {
   161  	base, err := parseEWKBRaw(ewkb)
   162  	if err != nil {
   163  		return nil, err
   164  	}
   165  	return &Geometry{SpatialObject: base}, nil
   166  }
   167  
   168  // MustParseGeometryFromEWKBRaw behaves as ParseGeometryFromEWKBRaw, but panics if an error occurs.
   169  func MustParseGeometryFromEWKBRaw(ewkb geopb.EWKB) *Geometry {
   170  	ret, err := ParseGeometryFromEWKBRaw(ewkb)
   171  	if err != nil {
   172  		panic(err)
   173  	}
   174  	return ret
   175  }
   176  
   177  // AsGeography converts a given Geometry to its Geography form.
   178  func (g *Geometry) AsGeography() (*Geography, error) {
   179  	if g.SRID() != 0 {
   180  		// TODO(otan): check SRID is latlng
   181  		return NewGeography(g.SpatialObject), nil
   182  	}
   183  
   184  	spatialObject, err := adjustEWKBSRID(g.EWKB(), geopb.DefaultGeographySRID)
   185  	if err != nil {
   186  		return nil, err
   187  	}
   188  	return NewGeography(spatialObject), nil
   189  }
   190  
   191  // CloneWithSRID sets a given Geometry's SRID to another, without any transformations.
   192  // Returns a new Geometry object.
   193  func (g *Geometry) CloneWithSRID(srid geopb.SRID) (*Geometry, error) {
   194  	spatialObject, err := adjustEWKBSRID(g.EWKB(), srid)
   195  	if err != nil {
   196  		return nil, err
   197  	}
   198  	return NewGeometry(spatialObject), nil
   199  }
   200  
   201  // adjustEWKBSRID returns the SpatialObject of an EWKB that has been overwritten
   202  // with the new given SRID.
   203  func adjustEWKBSRID(b geopb.EWKB, srid geopb.SRID) (geopb.SpatialObject, error) {
   204  	// Set a default SRID if one is not already set.
   205  	t, err := ewkb.Unmarshal(b)
   206  	if err != nil {
   207  		return geopb.SpatialObject{}, err
   208  	}
   209  	adjustGeomSRID(t, srid)
   210  	return spatialObjectFromGeom(t)
   211  }
   212  
   213  // AsGeomT returns the geometry as a geom.T object.
   214  func (g *Geometry) AsGeomT() (geom.T, error) {
   215  	return ewkb.Unmarshal(g.SpatialObject.EWKB)
   216  }
   217  
   218  // Empty returns whether the given Geometry is empty.
   219  func (g *Geometry) Empty() bool {
   220  	return g.SpatialObject.BoundingBox == nil
   221  }
   222  
   223  // EWKB returns the EWKB representation of the Geometry.
   224  func (g *Geometry) EWKB() geopb.EWKB {
   225  	return g.SpatialObject.EWKB
   226  }
   227  
   228  // SRID returns the SRID representation of the Geometry.
   229  func (g *Geometry) SRID() geopb.SRID {
   230  	return g.SpatialObject.SRID
   231  }
   232  
   233  // Shape returns the shape of the Geometry.
   234  func (g *Geometry) Shape() geopb.Shape {
   235  	return g.SpatialObject.Shape
   236  }
   237  
   238  // BoundingBoxIntersects returns whether the bounding box of the given geometry
   239  // intersects with the other.
   240  func (g *Geometry) BoundingBoxIntersects(o *Geometry) bool {
   241  	return g.SpatialObject.BoundingBox.Intersects(o.SpatialObject.BoundingBox)
   242  }
   243  
   244  //
   245  // Geography
   246  //
   247  
   248  // Geography is a spherical spatial object.
   249  type Geography struct {
   250  	geopb.SpatialObject
   251  }
   252  
   253  // NewGeography returns a new Geography. Assumes the input EWKB is validated and in little endian.
   254  func NewGeography(spatialObject geopb.SpatialObject) *Geography {
   255  	return &Geography{SpatialObject: spatialObject}
   256  }
   257  
   258  // NewGeographyFromGeom creates a new Geography from a geom.T object.
   259  func NewGeographyFromGeom(g geom.T) (*Geography, error) {
   260  	spatialObject, err := spatialObjectFromGeom(g)
   261  	if err != nil {
   262  		return nil, err
   263  	}
   264  	return NewGeography(spatialObject), nil
   265  }
   266  
   267  // ParseGeography parses a Geography from a given text.
   268  func ParseGeography(str string) (*Geography, error) {
   269  	spatialObject, err := parseAmbiguousText(str, geopb.DefaultGeographySRID)
   270  	if err != nil {
   271  		return nil, err
   272  	}
   273  	return NewGeography(spatialObject), nil
   274  }
   275  
   276  // MustParseGeography behaves as ParseGeography, but panics if there is an error.
   277  func MustParseGeography(str string) *Geography {
   278  	g, err := ParseGeography(str)
   279  	if err != nil {
   280  		panic(err)
   281  	}
   282  	return g
   283  }
   284  
   285  // ParseGeographyFromEWKT parses the EWKT into a Geography.
   286  func ParseGeographyFromEWKT(
   287  	ewkt geopb.EWKT, srid geopb.SRID, defaultSRIDOverwriteSetting defaultSRIDOverwriteSetting,
   288  ) (*Geography, error) {
   289  	g, err := parseEWKT(ewkt, srid, defaultSRIDOverwriteSetting)
   290  	if err != nil {
   291  		return nil, err
   292  	}
   293  	return NewGeography(g), nil
   294  }
   295  
   296  // ParseGeographyFromEWKB parses the EWKB into a Geography.
   297  func ParseGeographyFromEWKB(ewkb geopb.EWKB) (*Geography, error) {
   298  	g, err := parseEWKB(ewkb, geopb.DefaultGeographySRID, DefaultSRIDIsHint)
   299  	if err != nil {
   300  		return nil, err
   301  	}
   302  	return NewGeography(g), nil
   303  }
   304  
   305  // ParseGeographyFromWKB parses the WKB into a given Geography.
   306  func ParseGeographyFromWKB(wkb geopb.WKB, srid geopb.SRID) (*Geography, error) {
   307  	g, err := parseWKB(wkb, srid)
   308  	if err != nil {
   309  		return nil, err
   310  	}
   311  	return NewGeography(g), nil
   312  }
   313  
   314  // ParseGeographyFromGeoJSON parses the GeoJSON into a given Geography.
   315  func ParseGeographyFromGeoJSON(json []byte) (*Geography, error) {
   316  	g, err := parseGeoJSON(json, geopb.DefaultGeographySRID)
   317  	if err != nil {
   318  		return nil, err
   319  	}
   320  	return NewGeography(g), nil
   321  }
   322  
   323  // ParseGeographyFromEWKBRaw returns a new Geography from an EWKB, without any SRID checks.
   324  // You should only do this if you trust the EWKB is setup correctly.
   325  // You must likely want ParseGeographyFromEWKB instead.
   326  func ParseGeographyFromEWKBRaw(ewkb geopb.EWKB) (*Geography, error) {
   327  	base, err := parseEWKBRaw(ewkb)
   328  	if err != nil {
   329  		return nil, err
   330  	}
   331  	return &Geography{SpatialObject: base}, nil
   332  }
   333  
   334  // MustParseGeographyFromEWKBRaw behaves as ParseGeographyFromEWKBRaw, but panics if an error occurs.
   335  func MustParseGeographyFromEWKBRaw(ewkb geopb.EWKB) *Geography {
   336  	ret, err := ParseGeographyFromEWKBRaw(ewkb)
   337  	if err != nil {
   338  		panic(err)
   339  	}
   340  	return ret
   341  }
   342  
   343  // CloneWithSRID sets a given Geography's SRID to another, without any transformations.
   344  // Returns a new Geography object.
   345  func (g *Geography) CloneWithSRID(srid geopb.SRID) (*Geography, error) {
   346  	spatialObject, err := adjustEWKBSRID(g.EWKB(), srid)
   347  	if err != nil {
   348  		return nil, err
   349  	}
   350  	return NewGeography(spatialObject), nil
   351  }
   352  
   353  // AsGeometry converts a given Geography to its Geometry form.
   354  func (g *Geography) AsGeometry() *Geometry {
   355  	return NewGeometry(g.SpatialObject)
   356  }
   357  
   358  // AsGeomT returns the Geography as a geom.T object.
   359  func (g *Geography) AsGeomT() (geom.T, error) {
   360  	return ewkb.Unmarshal(g.SpatialObject.EWKB)
   361  }
   362  
   363  // EWKB returns the EWKB representation of the Geography.
   364  func (g *Geography) EWKB() geopb.EWKB {
   365  	return g.SpatialObject.EWKB
   366  }
   367  
   368  // SRID returns the SRID representation of the Geography.
   369  func (g *Geography) SRID() geopb.SRID {
   370  	return g.SpatialObject.SRID
   371  }
   372  
   373  // Shape returns the shape of the Geography.
   374  func (g *Geography) Shape() geopb.Shape {
   375  	return g.SpatialObject.Shape
   376  }
   377  
   378  // AsS2 converts a given Geography into it's S2 form.
   379  func (g *Geography) AsS2(emptyBehavior EmptyBehavior) ([]s2.Region, error) {
   380  	geomRepr, err := g.AsGeomT()
   381  	if err != nil {
   382  		return nil, err
   383  	}
   384  	// TODO(otan): convert by reading from EWKB to S2 directly.
   385  	return S2RegionsFromGeom(geomRepr, emptyBehavior)
   386  }
   387  
   388  // isLinearRingCCW returns whether a given linear ring is counter clock wise.
   389  // See 2.07 of http://www.faqs.org/faqs/graphics/algorithms-faq/.
   390  // "Find the lowest vertex (or, if  there is more than one vertex with the same lowest coordinate,
   391  //  the rightmost of those vertices) and then take the cross product of the edges fore and aft of it."
   392  func isLinearRingCCW(linearRing *geom.LinearRing) bool {
   393  	smallestIdx := 0
   394  	smallest := linearRing.Coord(0)
   395  
   396  	for pointIdx := 1; pointIdx < linearRing.NumCoords()-1; pointIdx++ {
   397  		curr := linearRing.Coord(pointIdx)
   398  		if curr.Y() < smallest.Y() || (curr.Y() == smallest.Y() && curr.X() > smallest.X()) {
   399  			smallestIdx = pointIdx
   400  			smallest = curr
   401  		}
   402  	}
   403  
   404  	// prevIdx is the previous point. If we are at the 0th point, the last coordinate
   405  	// is also the 0th point, so take the second last point.
   406  	// Note we don't have to apply this for "nextIdx" as we cap the search above at the
   407  	// second last vertex.
   408  	prevIdx := smallestIdx - 1
   409  	if smallestIdx == 0 {
   410  		prevIdx = linearRing.NumCoords() - 2
   411  	}
   412  	a := linearRing.Coord(prevIdx)
   413  	b := smallest
   414  	c := linearRing.Coord(smallestIdx + 1)
   415  
   416  	// We could do the cross product, but we are only interested in the sign.
   417  	// To find the sign, reorganize into the orientation matrix:
   418  	//  1 x_a y_a
   419  	//  1 x_b y_b
   420  	//  1 x_c y_c
   421  	// and find the determinant.
   422  	// https://en.wikipedia.org/wiki/Curve_orientation#Orientation_of_a_simple_polygon
   423  	areaSign := a.X()*b.Y() - a.Y()*b.X() +
   424  		a.Y()*c.X() - a.X()*c.Y() +
   425  		b.X()*c.Y() - c.X()*b.Y()
   426  	// Note having an area sign of 0 means it is a flat polygon, which is invalid.
   427  	return areaSign > 0
   428  }
   429  
   430  // S2RegionsFromGeom converts an geom representation of an object
   431  // to s2 regions.
   432  // As S2 does not really handle empty geometries well, we need to ingest emptyBehavior and
   433  // react appropriately.
   434  func S2RegionsFromGeom(geomRepr geom.T, emptyBehavior EmptyBehavior) ([]s2.Region, error) {
   435  	var regions []s2.Region
   436  	if geomRepr.Empty() {
   437  		switch emptyBehavior {
   438  		case EmptyBehaviorOmit:
   439  			return nil, nil
   440  		case EmptyBehaviorError:
   441  			return nil, NewEmptyGeometryError()
   442  		default:
   443  			return nil, errors.Newf("programmer error: unknown behavior")
   444  		}
   445  	}
   446  	switch repr := geomRepr.(type) {
   447  	case *geom.Point:
   448  		regions = []s2.Region{
   449  			s2.PointFromLatLng(s2.LatLngFromDegrees(repr.Y(), repr.X())),
   450  		}
   451  	case *geom.LineString:
   452  		latLngs := make([]s2.LatLng, repr.NumCoords())
   453  		for i := 0; i < repr.NumCoords(); i++ {
   454  			p := repr.Coord(i)
   455  			latLngs[i] = s2.LatLngFromDegrees(p.Y(), p.X())
   456  		}
   457  		regions = []s2.Region{
   458  			s2.PolylineFromLatLngs(latLngs),
   459  		}
   460  	case *geom.Polygon:
   461  		loops := make([]*s2.Loop, repr.NumLinearRings())
   462  		// All loops must be oriented CCW for S2.
   463  		for ringIdx := 0; ringIdx < repr.NumLinearRings(); ringIdx++ {
   464  			linearRing := repr.LinearRing(ringIdx)
   465  			points := make([]s2.Point, linearRing.NumCoords())
   466  			isCCW := isLinearRingCCW(linearRing)
   467  			for pointIdx := 0; pointIdx < linearRing.NumCoords(); pointIdx++ {
   468  				p := linearRing.Coord(pointIdx)
   469  				pt := s2.PointFromLatLng(s2.LatLngFromDegrees(p.Y(), p.X()))
   470  				if isCCW {
   471  					points[pointIdx] = pt
   472  				} else {
   473  					points[len(points)-pointIdx-1] = pt
   474  				}
   475  			}
   476  			loops[ringIdx] = s2.LoopFromPoints(points)
   477  		}
   478  		regions = []s2.Region{
   479  			s2.PolygonFromLoops(loops),
   480  		}
   481  	case *geom.GeometryCollection:
   482  		for _, geom := range repr.Geoms() {
   483  			subRegions, err := S2RegionsFromGeom(geom, emptyBehavior)
   484  			if err != nil {
   485  				return nil, err
   486  			}
   487  			regions = append(regions, subRegions...)
   488  		}
   489  	case *geom.MultiPoint:
   490  		for i := 0; i < repr.NumPoints(); i++ {
   491  			subRegions, err := S2RegionsFromGeom(repr.Point(i), emptyBehavior)
   492  			if err != nil {
   493  				return nil, err
   494  			}
   495  			regions = append(regions, subRegions...)
   496  		}
   497  	case *geom.MultiLineString:
   498  		for i := 0; i < repr.NumLineStrings(); i++ {
   499  			subRegions, err := S2RegionsFromGeom(repr.LineString(i), emptyBehavior)
   500  			if err != nil {
   501  				return nil, err
   502  			}
   503  			regions = append(regions, subRegions...)
   504  		}
   505  	case *geom.MultiPolygon:
   506  		for i := 0; i < repr.NumPolygons(); i++ {
   507  			subRegions, err := S2RegionsFromGeom(repr.Polygon(i), emptyBehavior)
   508  			if err != nil {
   509  				return nil, err
   510  			}
   511  			regions = append(regions, subRegions...)
   512  		}
   513  	}
   514  	return regions, nil
   515  }
   516  
   517  //
   518  // Common
   519  //
   520  
   521  // spatialObjectFromGeom creates a geopb.SpatialObject from a geom.T.
   522  func spatialObjectFromGeom(t geom.T) (geopb.SpatialObject, error) {
   523  	ret, err := ewkb.Marshal(t, DefaultEWKBEncodingFormat)
   524  	if err != nil {
   525  		return geopb.SpatialObject{}, err
   526  	}
   527  	shape, err := geopbShape(t)
   528  	if err != nil {
   529  		return geopb.SpatialObject{}, err
   530  	}
   531  	switch t.Layout() {
   532  	case geom.XY:
   533  	case geom.NoLayout:
   534  		if gc, ok := t.(*geom.GeometryCollection); !ok || !gc.Empty() {
   535  			return geopb.SpatialObject{}, errors.Newf("no layout found on object")
   536  		}
   537  	default:
   538  		return geopb.SpatialObject{}, errors.Newf("only 2D objects are currently supported")
   539  	}
   540  	bbox, err := BoundingBoxFromGeom(t)
   541  	if err != nil {
   542  		return geopb.SpatialObject{}, err
   543  	}
   544  	return geopb.SpatialObject{
   545  		EWKB:        geopb.EWKB(ret),
   546  		SRID:        geopb.SRID(t.SRID()),
   547  		Shape:       shape,
   548  		BoundingBox: bbox,
   549  	}, nil
   550  }
   551  
   552  func geopbShape(t geom.T) (geopb.Shape, error) {
   553  	switch t := t.(type) {
   554  	case *geom.Point:
   555  		return geopb.Shape_Point, nil
   556  	case *geom.LineString:
   557  		return geopb.Shape_LineString, nil
   558  	case *geom.Polygon:
   559  		return geopb.Shape_Polygon, nil
   560  	case *geom.MultiPoint:
   561  		return geopb.Shape_MultiPoint, nil
   562  	case *geom.MultiLineString:
   563  		return geopb.Shape_MultiLineString, nil
   564  	case *geom.MultiPolygon:
   565  		return geopb.Shape_MultiPolygon, nil
   566  	case *geom.GeometryCollection:
   567  		return geopb.Shape_GeometryCollection, nil
   568  	default:
   569  		return geopb.Shape_Unset, errors.Newf("unknown shape: %T", t)
   570  	}
   571  }