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 }