github.com/cockroachdb/cockroachdb-parser@v0.23.3-0.20240213214944-911057d40c9a/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 "bytes" 16 "encoding/binary" 17 "math" 18 "unsafe" 19 20 "github.com/cockroachdb/cockroachdb-parser/pkg/geo/geopb" 21 "github.com/cockroachdb/cockroachdb-parser/pkg/geo/geoprojbase" 22 "github.com/cockroachdb/cockroachdb-parser/pkg/sql/pgwire/pgcode" 23 "github.com/cockroachdb/cockroachdb-parser/pkg/sql/pgwire/pgerror" 24 "github.com/cockroachdb/cockroachdb-parser/pkg/util/protoutil" 25 "github.com/cockroachdb/errors" 26 "github.com/golang/geo/r1" 27 "github.com/golang/geo/s1" 28 "github.com/golang/geo/s2" 29 "github.com/twpayne/go-geom" 30 "github.com/twpayne/go-geom/encoding/ewkb" 31 ) 32 33 // DefaultEWKBEncodingFormat is the default encoding format for EWKB. 34 var DefaultEWKBEncodingFormat = binary.LittleEndian 35 36 // EmptyBehavior is the behavior to adopt when an empty Geometry is encountered. 37 type EmptyBehavior uint8 38 39 const ( 40 // EmptyBehaviorError will error with EmptyGeometryError when an empty geometry 41 // is encountered. 42 EmptyBehaviorError EmptyBehavior = 0 43 // EmptyBehaviorOmit will omit an entry when an empty geometry is encountered. 44 EmptyBehaviorOmit EmptyBehavior = 1 45 ) 46 47 // FnExclusivity is used to indicate whether a geo function should have 48 // inclusive or exclusive semantics. For example, DWithin == (Distance <= x), 49 // while DWithinExclusive == (Distance < x). 50 type FnExclusivity bool 51 52 // MaxAllowedSplitPoints is the maximum number of points any spatial function may split to. 53 const MaxAllowedSplitPoints = 65336 54 55 const ( 56 // FnExclusive indicates that the corresponding geo function should have 57 // exclusive semantics. 58 FnExclusive FnExclusivity = true 59 // FnInclusive indicates that the corresponding geo function should have 60 // inclusive semantics. 61 FnInclusive FnExclusivity = false 62 ) 63 64 // SpatialObjectFitsColumnMetadata determines whether a GeospatialType is compatible with the 65 // given SRID and Shape. 66 // Returns an error if the types does not fit. 67 func SpatialObjectFitsColumnMetadata( 68 so geopb.SpatialObject, srid geopb.SRID, shapeType geopb.ShapeType, 69 ) error { 70 // SRID 0 can take in any SRID. Otherwise SRIDs must match. 71 if srid != 0 && so.SRID != srid { 72 return pgerror.Newf( 73 pgcode.InvalidParameterValue, 74 "object SRID %d does not match column SRID %d", 75 so.SRID, 76 srid, 77 ) 78 } 79 // Shape_Unset can take in any kind of shape. 80 // Shape_Geometry[ZM] must match dimensions. 81 // Otherwise, shapes must match. 82 switch shapeType { 83 case geopb.ShapeType_Unset: 84 break 85 case geopb.ShapeType_Geometry, geopb.ShapeType_GeometryM, geopb.ShapeType_GeometryZ, geopb.ShapeType_GeometryZM: 86 if ShapeTypeToLayout(shapeType) != ShapeTypeToLayout(so.ShapeType) { 87 return pgerror.Newf( 88 pgcode.InvalidParameterValue, 89 "object type %s does not match column dimensionality %s", 90 so.ShapeType, 91 shapeType, 92 ) 93 } 94 default: 95 if shapeType != so.ShapeType { 96 return pgerror.Newf( 97 pgcode.InvalidParameterValue, 98 "object type %s does not match column type %s", 99 so.ShapeType, 100 shapeType, 101 ) 102 } 103 } 104 return nil 105 } 106 107 // ShapeTypeToLayout returns the geom.Layout of the given ShapeType. 108 // Note this is not a definition on ShapeType to prevent geopb from importing twpayne/go-geom. 109 func ShapeTypeToLayout(s geopb.ShapeType) geom.Layout { 110 switch { 111 case (s&geopb.MShapeTypeFlag > 0) && (s&geopb.ZShapeTypeFlag > 0): 112 return geom.XYZM 113 case s&geopb.ZShapeTypeFlag > 0: 114 return geom.XYZ 115 case s&geopb.MShapeTypeFlag > 0: 116 return geom.XYM 117 default: 118 return geom.XY 119 } 120 } 121 122 // 123 // Geometry 124 // 125 126 // Geometry is planar spatial object. 127 type Geometry struct { 128 spatialObject geopb.SpatialObject 129 } 130 131 // MakeGeometry returns a new Geometry. Assumes the input EWKB is validated and in little endian. 132 func MakeGeometry(spatialObject geopb.SpatialObject) (Geometry, error) { 133 if spatialObject.SRID != 0 { 134 if _, err := geoprojbase.Projection(spatialObject.SRID); err != nil { 135 return Geometry{}, err 136 } 137 } 138 if spatialObject.Type != geopb.SpatialObjectType_GeometryType { 139 return Geometry{}, pgerror.Newf( 140 pgcode.InvalidObjectDefinition, 141 "expected geometry type, found %s", 142 spatialObject.Type, 143 ) 144 } 145 return Geometry{spatialObject: spatialObject}, nil 146 } 147 148 // MakeGeometryUnsafe creates a geometry object that assumes spatialObject is from the DB. 149 // It assumes the spatialObject underneath is safe. 150 func MakeGeometryUnsafe(spatialObject geopb.SpatialObject) Geometry { 151 return Geometry{spatialObject: spatialObject} 152 } 153 154 // MakeGeometryFromPointCoords makes a point from x, y coordinates. 155 func MakeGeometryFromPointCoords(x, y float64) (Geometry, error) { 156 return MakeGeometryFromLayoutAndPointCoords(geom.XY, []float64{x, y}) 157 } 158 159 // MakeGeometryFromLayoutAndPointCoords makes a point with a given layout and ordered slice of coordinates. 160 func MakeGeometryFromLayoutAndPointCoords( 161 layout geom.Layout, flatCoords []float64, 162 ) (Geometry, error) { 163 // Validate that the stride matches what is expected for the layout. 164 switch { 165 case layout == geom.XY && len(flatCoords) == 2: 166 case layout == geom.XYM && len(flatCoords) == 3: 167 case layout == geom.XYZ && len(flatCoords) == 3: 168 case layout == geom.XYZM && len(flatCoords) == 4: 169 default: 170 return Geometry{}, pgerror.Newf( 171 pgcode.InvalidParameterValue, 172 "mismatch between layout %d and stride %d", 173 layout, 174 len(flatCoords), 175 ) 176 } 177 s, err := spatialObjectFromGeomT(geom.NewPointFlat(layout, flatCoords), geopb.SpatialObjectType_GeometryType) 178 if err != nil { 179 return Geometry{}, err 180 } 181 return MakeGeometry(s) 182 } 183 184 // MakeGeometryFromGeomT creates a new Geometry object from a geom.T object. 185 func MakeGeometryFromGeomT(g geom.T) (Geometry, error) { 186 spatialObject, err := spatialObjectFromGeomT(g, geopb.SpatialObjectType_GeometryType) 187 if err != nil { 188 return Geometry{}, err 189 } 190 return MakeGeometry(spatialObject) 191 } 192 193 // ParseGeometry parses a Geometry from a given text. 194 func ParseGeometry(str string) (Geometry, error) { 195 spatialObject, err := parseAmbiguousText(geopb.SpatialObjectType_GeometryType, str, geopb.DefaultGeometrySRID) 196 if err != nil { 197 return Geometry{}, err 198 } 199 return MakeGeometry(spatialObject) 200 } 201 202 // MustParseGeometry behaves as ParseGeometry, but panics if there is an error. 203 func MustParseGeometry(str string) Geometry { 204 g, err := ParseGeometry(str) 205 if err != nil { 206 panic(err) 207 } 208 return g 209 } 210 211 // ParseGeometryFromEWKT parses the EWKT into a Geometry. 212 func ParseGeometryFromEWKT( 213 ewkt geopb.EWKT, srid geopb.SRID, defaultSRIDOverwriteSetting defaultSRIDOverwriteSetting, 214 ) (Geometry, error) { 215 g, err := parseEWKT(geopb.SpatialObjectType_GeometryType, ewkt, srid, defaultSRIDOverwriteSetting) 216 if err != nil { 217 return Geometry{}, err 218 } 219 return MakeGeometry(g) 220 } 221 222 // ParseGeometryFromEWKB parses the EWKB into a Geometry. 223 func ParseGeometryFromEWKB(ewkb geopb.EWKB) (Geometry, error) { 224 g, err := parseEWKB(geopb.SpatialObjectType_GeometryType, ewkb, geopb.DefaultGeometrySRID, DefaultSRIDIsHint) 225 if err != nil { 226 return Geometry{}, err 227 } 228 return MakeGeometry(g) 229 } 230 231 // ParseGeometryFromEWKBAndSRID parses the EWKB into a given Geometry with the given 232 // SRID set. 233 func ParseGeometryFromEWKBAndSRID(ewkb geopb.EWKB, srid geopb.SRID) (Geometry, error) { 234 g, err := parseEWKB(geopb.SpatialObjectType_GeometryType, ewkb, srid, DefaultSRIDShouldOverwrite) 235 if err != nil { 236 return Geometry{}, err 237 } 238 return MakeGeometry(g) 239 } 240 241 // MustParseGeometryFromEWKB behaves as ParseGeometryFromEWKB, but panics if an error occurs. 242 func MustParseGeometryFromEWKB(ewkb geopb.EWKB) Geometry { 243 ret, err := ParseGeometryFromEWKB(ewkb) 244 if err != nil { 245 panic(err) 246 } 247 return ret 248 } 249 250 // ParseGeometryFromGeoJSON parses the GeoJSON into a given Geometry. 251 func ParseGeometryFromGeoJSON(json []byte) (Geometry, error) { 252 // Note we set SRID to 4326 from here, to match PostGIS's behavior as per 253 // RFC7946 (https://tools.ietf.org/html/rfc7946#section-4). 254 g, err := parseGeoJSON(geopb.SpatialObjectType_GeometryType, json, geopb.DefaultGeographySRID) 255 if err != nil { 256 return Geometry{}, err 257 } 258 return MakeGeometry(g) 259 } 260 261 // ParseGeometryFromEWKBUnsafe returns a new Geometry from an EWKB, without any SRID checks. 262 // You should only do this if you trust the EWKB is setup correctly. 263 // You most likely want geo.ParseGeometryFromEWKB instead. 264 func ParseGeometryFromEWKBUnsafe(ewkb geopb.EWKB) (Geometry, error) { 265 base, err := parseEWKBRaw(geopb.SpatialObjectType_GeometryType, ewkb) 266 if err != nil { 267 return Geometry{}, err 268 } 269 return MakeGeometryUnsafe(base), nil 270 } 271 272 // AsGeography converts a given Geometry to its Geography form. 273 func (g *Geometry) AsGeography() (Geography, error) { 274 srid := g.SRID() 275 if srid == 0 { 276 // Set a geography SRID if one is not already set. 277 srid = geopb.DefaultGeographySRID 278 } 279 spatialObject, err := adjustSpatialObject(g.spatialObject, srid, geopb.SpatialObjectType_GeographyType) 280 if err != nil { 281 return Geography{}, err 282 } 283 return MakeGeography(spatialObject) 284 } 285 286 // CloneWithSRID sets a given Geometry's SRID to another, without any transformations. 287 // Returns a new Geometry object. 288 func (g *Geometry) CloneWithSRID(srid geopb.SRID) (Geometry, error) { 289 spatialObject, err := adjustSpatialObject(g.spatialObject, srid, geopb.SpatialObjectType_GeometryType) 290 if err != nil { 291 return Geometry{}, err 292 } 293 return MakeGeometry(spatialObject) 294 } 295 296 // adjustSpatialObject returns the SpatialObject with new parameters. 297 func adjustSpatialObject( 298 so geopb.SpatialObject, srid geopb.SRID, soType geopb.SpatialObjectType, 299 ) (geopb.SpatialObject, error) { 300 t, err := ewkb.Unmarshal(so.EWKB) 301 if err != nil { 302 return geopb.SpatialObject{}, err 303 } 304 AdjustGeomTSRID(t, srid) 305 return spatialObjectFromGeomT(t, soType) 306 } 307 308 // AsGeomT returns the geometry as a geom.T object. 309 func (g *Geometry) AsGeomT() (geom.T, error) { 310 return ewkb.Unmarshal(g.spatialObject.EWKB) 311 } 312 313 // Empty returns whether the given Geometry is empty. 314 func (g *Geometry) Empty() bool { 315 return g.spatialObject.BoundingBox == nil 316 } 317 318 // EWKB returns the EWKB representation of the Geometry. 319 func (g *Geometry) EWKB() geopb.EWKB { 320 return g.spatialObject.EWKB 321 } 322 323 // SpatialObject returns the SpatialObject representation of the Geometry. 324 func (g *Geometry) SpatialObject() geopb.SpatialObject { 325 return g.spatialObject 326 } 327 328 // SpatialObjectRef return a pointer to the SpatialObject representation of the 329 // Geometry. 330 func (g *Geometry) SpatialObjectRef() *geopb.SpatialObject { 331 return &g.spatialObject 332 } 333 334 // EWKBHex returns the EWKBHex representation of the Geometry. 335 func (g *Geometry) EWKBHex() string { 336 return g.spatialObject.EWKBHex() 337 } 338 339 // SRID returns the SRID representation of the Geometry. 340 func (g *Geometry) SRID() geopb.SRID { 341 return g.spatialObject.SRID 342 } 343 344 // ShapeType returns the shape type of the Geometry. 345 func (g *Geometry) ShapeType() geopb.ShapeType { 346 return g.spatialObject.ShapeType 347 } 348 349 // ShapeType2D returns the 2D shape type of the Geometry. 350 func (g *Geometry) ShapeType2D() geopb.ShapeType { 351 return g.ShapeType().To2D() 352 } 353 354 // CartesianBoundingBox returns a Cartesian bounding box. 355 func (g *Geometry) CartesianBoundingBox() *CartesianBoundingBox { 356 if g.spatialObject.BoundingBox == nil { 357 return nil 358 } 359 return &CartesianBoundingBox{BoundingBox: *g.spatialObject.BoundingBox} 360 } 361 362 // BoundingBoxRef returns a pointer to the BoundingBox, if any. 363 func (g *Geometry) BoundingBoxRef() *geopb.BoundingBox { 364 return g.spatialObject.BoundingBox 365 } 366 367 // SpaceCurveIndex returns an uint64 index to use representing an index into a space-filling curve. 368 // This will return 0 for empty spatial objects, and math.MaxUint64 for any object outside 369 // the defined bounds of the given SRID projection. 370 func (g *Geometry) SpaceCurveIndex() (uint64, error) { 371 bbox := g.CartesianBoundingBox() 372 if bbox == nil { 373 return 0, nil 374 } 375 centerX := (bbox.BoundingBox.LoX + bbox.BoundingBox.HiX) / 2 376 centerY := (bbox.BoundingBox.LoY + bbox.BoundingBox.HiY) / 2 377 // By default, bound by MaxInt32 (we have not typically seen bounds greater than 1B). 378 bounds := geoprojbase.Bounds{ 379 MinX: math.MinInt32, 380 MaxX: math.MaxInt32, 381 MinY: math.MinInt32, 382 MaxY: math.MaxInt32, 383 } 384 if g.SRID() != 0 { 385 proj, err := geoprojbase.Projection(g.SRID()) 386 if err != nil { 387 return 0, err 388 } 389 bounds = proj.Bounds 390 } 391 // If we're out of bounds, give up and return a large number. 392 if centerX > bounds.MaxX || centerY > bounds.MaxY || centerX < bounds.MinX || centerY < bounds.MinY { 393 return math.MaxUint64, nil 394 } 395 396 const boxLength = 1 << 32 397 // Add 1 to each bound so that we normalize the coordinates to [0, 1) before 398 // multiplying by boxLength to give coordinates that are integers in the interval [0, boxLength-1]. 399 xBounds := (bounds.MaxX - bounds.MinX) + 1 400 yBounds := (bounds.MaxY - bounds.MinY) + 1 401 // hilbertInverse returns values in the interval [0, boxLength^2-1], so return [0, 2^64-1]. 402 xPos := uint64(((centerX - bounds.MinX) / xBounds) * boxLength) 403 yPos := uint64(((centerY - bounds.MinY) / yBounds) * boxLength) 404 return hilbertInverse(boxLength, xPos, yPos), nil 405 } 406 407 // Compare compares a Geometry against another. 408 // It compares using SpaceCurveIndex, followed by the byte representation of the Geometry. 409 // This must produce the same ordering as the index mechanism. 410 func (g *Geometry) Compare(o Geometry) int { 411 lhs, err := g.SpaceCurveIndex() 412 if err != nil { 413 // We should always be able to compare a valid geometry. 414 panic(err) 415 } 416 rhs, err := o.SpaceCurveIndex() 417 if err != nil { 418 // We should always be able to compare a valid geometry. 419 panic(err) 420 } 421 if lhs > rhs { 422 return 1 423 } 424 if lhs < rhs { 425 return -1 426 } 427 return compareSpatialObjectBytes(g.SpatialObjectRef(), o.SpatialObjectRef()) 428 } 429 430 // 431 // Geography 432 // 433 434 // Geography is a spherical spatial object. 435 type Geography struct { 436 spatialObject geopb.SpatialObject 437 } 438 439 // MakeGeography returns a new Geography. Assumes the input EWKB is validated and in little endian. 440 func MakeGeography(spatialObject geopb.SpatialObject) (Geography, error) { 441 projection, err := geoprojbase.Projection(spatialObject.SRID) 442 if err != nil { 443 return Geography{}, err 444 } 445 if !projection.IsLatLng { 446 return Geography{}, pgerror.Newf( 447 pgcode.InvalidParameterValue, 448 "SRID %d cannot be used for geography as it is not in a lon/lat coordinate system", 449 spatialObject.SRID, 450 ) 451 } 452 if spatialObject.Type != geopb.SpatialObjectType_GeographyType { 453 return Geography{}, pgerror.Newf( 454 pgcode.InvalidObjectDefinition, 455 "expected geography type, found %s", 456 spatialObject.Type, 457 ) 458 } 459 return Geography{spatialObject: spatialObject}, nil 460 } 461 462 // MakeGeographyUnsafe creates a geometry object that assumes spatialObject is from the DB. 463 // It assumes the spatialObject underneath is safe. 464 func MakeGeographyUnsafe(spatialObject geopb.SpatialObject) Geography { 465 return Geography{spatialObject: spatialObject} 466 } 467 468 // MakeGeographyFromGeomT creates a new Geography from a geom.T object. 469 func MakeGeographyFromGeomT(g geom.T) (Geography, error) { 470 spatialObject, err := spatialObjectFromGeomT(g, geopb.SpatialObjectType_GeographyType) 471 if err != nil { 472 return Geography{}, err 473 } 474 return MakeGeography(spatialObject) 475 } 476 477 // MustMakeGeographyFromGeomT enforces no error from MakeGeographyFromGeomT. 478 func MustMakeGeographyFromGeomT(g geom.T) Geography { 479 ret, err := MakeGeographyFromGeomT(g) 480 if err != nil { 481 panic(err) 482 } 483 return ret 484 } 485 486 // ParseGeography parses a Geography from a given text. 487 func ParseGeography(str string) (Geography, error) { 488 spatialObject, err := parseAmbiguousText(geopb.SpatialObjectType_GeographyType, str, geopb.DefaultGeographySRID) 489 if err != nil { 490 return Geography{}, err 491 } 492 return MakeGeography(spatialObject) 493 } 494 495 // MustParseGeography behaves as ParseGeography, but panics if there is an error. 496 func MustParseGeography(str string) Geography { 497 g, err := ParseGeography(str) 498 if err != nil { 499 panic(err) 500 } 501 return g 502 } 503 504 // ParseGeographyFromEWKT parses the EWKT into a Geography. 505 func ParseGeographyFromEWKT( 506 ewkt geopb.EWKT, srid geopb.SRID, defaultSRIDOverwriteSetting defaultSRIDOverwriteSetting, 507 ) (Geography, error) { 508 g, err := parseEWKT(geopb.SpatialObjectType_GeographyType, ewkt, srid, defaultSRIDOverwriteSetting) 509 if err != nil { 510 return Geography{}, err 511 } 512 return MakeGeography(g) 513 } 514 515 // ParseGeographyFromEWKB parses the EWKB into a Geography. 516 func ParseGeographyFromEWKB(ewkb geopb.EWKB) (Geography, error) { 517 g, err := parseEWKB(geopb.SpatialObjectType_GeographyType, ewkb, geopb.DefaultGeographySRID, DefaultSRIDIsHint) 518 if err != nil { 519 return Geography{}, err 520 } 521 return MakeGeography(g) 522 } 523 524 // ParseGeographyFromEWKBAndSRID parses the EWKB into a given Geography with the 525 // given SRID set. 526 func ParseGeographyFromEWKBAndSRID(ewkb geopb.EWKB, srid geopb.SRID) (Geography, error) { 527 g, err := parseEWKB(geopb.SpatialObjectType_GeographyType, ewkb, srid, DefaultSRIDShouldOverwrite) 528 if err != nil { 529 return Geography{}, err 530 } 531 return MakeGeography(g) 532 } 533 534 // MustParseGeographyFromEWKB behaves as ParseGeographyFromEWKB, but panics if an error occurs. 535 func MustParseGeographyFromEWKB(ewkb geopb.EWKB) Geography { 536 ret, err := ParseGeographyFromEWKB(ewkb) 537 if err != nil { 538 panic(err) 539 } 540 return ret 541 } 542 543 // ParseGeographyFromGeoJSON parses the GeoJSON into a given Geography. 544 func ParseGeographyFromGeoJSON(json []byte) (Geography, error) { 545 g, err := parseGeoJSON(geopb.SpatialObjectType_GeographyType, json, geopb.DefaultGeographySRID) 546 if err != nil { 547 return Geography{}, err 548 } 549 return MakeGeography(g) 550 } 551 552 // ParseGeographyFromEWKBUnsafe returns a new Geography from an EWKB, without any SRID checks. 553 // You should only do this if you trust the EWKB is setup correctly. 554 // You most likely want ParseGeographyFromEWKB instead. 555 func ParseGeographyFromEWKBUnsafe(ewkb geopb.EWKB) (Geography, error) { 556 base, err := parseEWKBRaw(geopb.SpatialObjectType_GeographyType, ewkb) 557 if err != nil { 558 return Geography{}, err 559 } 560 return MakeGeographyUnsafe(base), nil 561 } 562 563 // CloneWithSRID sets a given Geography's SRID to another, without any transformations. 564 // Returns a new Geography object. 565 func (g *Geography) CloneWithSRID(srid geopb.SRID) (Geography, error) { 566 spatialObject, err := adjustSpatialObject(g.spatialObject, srid, geopb.SpatialObjectType_GeographyType) 567 if err != nil { 568 return Geography{}, err 569 } 570 return MakeGeography(spatialObject) 571 } 572 573 // AsGeometry converts a given Geography to its Geometry form. 574 func (g *Geography) AsGeometry() (Geometry, error) { 575 spatialObject, err := adjustSpatialObject(g.spatialObject, g.SRID(), geopb.SpatialObjectType_GeometryType) 576 if err != nil { 577 return Geometry{}, err 578 } 579 return MakeGeometry(spatialObject) 580 } 581 582 // AsGeomT returns the Geography as a geom.T object. 583 func (g *Geography) AsGeomT() (geom.T, error) { 584 return ewkb.Unmarshal(g.spatialObject.EWKB) 585 } 586 587 // EWKB returns the EWKB representation of the Geography. 588 func (g *Geography) EWKB() geopb.EWKB { 589 return g.spatialObject.EWKB 590 } 591 592 // SpatialObject returns the SpatialObject representation of the Geography. 593 func (g *Geography) SpatialObject() geopb.SpatialObject { 594 return g.spatialObject 595 } 596 597 // SpatialObjectRef returns a pointer to the SpatialObject representation of the 598 // Geography. 599 func (g *Geography) SpatialObjectRef() *geopb.SpatialObject { 600 return &g.spatialObject 601 } 602 603 // EWKBHex returns the EWKBHex representation of the Geography. 604 func (g *Geography) EWKBHex() string { 605 return g.spatialObject.EWKBHex() 606 } 607 608 // SRID returns the SRID representation of the Geography. 609 func (g *Geography) SRID() geopb.SRID { 610 return g.spatialObject.SRID 611 } 612 613 // ShapeType returns the shape type of the Geography. 614 func (g *Geography) ShapeType() geopb.ShapeType { 615 return g.spatialObject.ShapeType 616 } 617 618 // ShapeType2D returns the 2D shape type of the Geography. 619 func (g *Geography) ShapeType2D() geopb.ShapeType { 620 return g.ShapeType().To2D() 621 } 622 623 // AsS2 converts a given Geography into it's S2 form. 624 func (g *Geography) AsS2(emptyBehavior EmptyBehavior) ([]s2.Region, error) { 625 geomRepr, err := g.AsGeomT() 626 if err != nil { 627 return nil, err 628 } 629 // TODO(otan): convert by reading from EWKB to S2 directly. 630 return S2RegionsFromGeomT(geomRepr, emptyBehavior) 631 } 632 633 // BoundingRect returns the bounding s2.Rect of the given Geography. 634 func (g *Geography) BoundingRect() s2.Rect { 635 bbox := g.spatialObject.BoundingBox 636 if bbox == nil { 637 return s2.EmptyRect() 638 } 639 return s2.Rect{ 640 Lat: r1.Interval{Lo: bbox.LoY, Hi: bbox.HiY}, 641 Lng: s1.Interval{Lo: bbox.LoX, Hi: bbox.HiX}, 642 } 643 } 644 645 // BoundingBoxRef returns a pointer to the BoundingBox, if any. 646 func (g *Geography) BoundingBoxRef() *geopb.BoundingBox { 647 return g.spatialObject.BoundingBox 648 } 649 650 // BoundingCap returns the bounding s2.Cap of the given Geography. 651 func (g *Geography) BoundingCap() s2.Cap { 652 return g.BoundingRect().CapBound() 653 } 654 655 // SpaceCurveIndex returns an uint64 index to use representing an index into a space-filling curve. 656 // This will return 0 for empty spatial objects. 657 func (g *Geography) SpaceCurveIndex() uint64 { 658 rect := g.BoundingRect() 659 if rect.IsEmpty() { 660 return 0 661 } 662 return uint64(s2.CellIDFromLatLng(rect.Center())) 663 } 664 665 // Compare compares a Geography against another. 666 // It compares using SpaceCurveIndex, followed by the byte representation of the Geography. 667 // This must produce the same ordering as the index mechanism. 668 func (g *Geography) Compare(o Geography) int { 669 lhs := g.SpaceCurveIndex() 670 rhs := o.SpaceCurveIndex() 671 if lhs > rhs { 672 return 1 673 } 674 if lhs < rhs { 675 return -1 676 } 677 return compareSpatialObjectBytes(g.SpatialObjectRef(), o.SpatialObjectRef()) 678 } 679 680 // 681 // Common 682 // 683 684 // AdjustGeomTSRID adjusts the SRID of a given geom.T. 685 // Ideally SetSRID is an interface of geom.T, but that is not the case. 686 func AdjustGeomTSRID(t geom.T, srid geopb.SRID) { 687 switch t := t.(type) { 688 case *geom.Point: 689 t.SetSRID(int(srid)) 690 case *geom.LineString: 691 t.SetSRID(int(srid)) 692 case *geom.Polygon: 693 t.SetSRID(int(srid)) 694 case *geom.GeometryCollection: 695 t.SetSRID(int(srid)) 696 case *geom.MultiPoint: 697 t.SetSRID(int(srid)) 698 case *geom.MultiLineString: 699 t.SetSRID(int(srid)) 700 case *geom.MultiPolygon: 701 t.SetSRID(int(srid)) 702 default: 703 panic(errors.AssertionFailedf("geo: unknown geom type: %v", t)) 704 } 705 } 706 707 // IsLinearRingCCW returns whether a given linear ring is counter clock wise. 708 // See 2.07 of http://www.faqs.org/faqs/graphics/algorithms-faq/. 709 // "Find the lowest vertex (or, if there is more than one vertex with the same lowest coordinate, 710 // 711 // the rightmost of those vertices) and then take the cross product of the edges fore and aft of it." 712 func IsLinearRingCCW(linearRing *geom.LinearRing) bool { 713 smallestIdx := 0 714 smallest := linearRing.Coord(0) 715 716 for pointIdx := 1; pointIdx < linearRing.NumCoords()-1; pointIdx++ { 717 curr := linearRing.Coord(pointIdx) 718 if curr.Y() < smallest.Y() || (curr.Y() == smallest.Y() && curr.X() > smallest.X()) { 719 smallestIdx = pointIdx 720 smallest = curr 721 } 722 } 723 724 // Find the previous point in the ring that is not the same as smallest. 725 prevIdx := smallestIdx - 1 726 if prevIdx < 0 { 727 prevIdx = linearRing.NumCoords() - 1 728 } 729 for prevIdx != smallestIdx { 730 a := linearRing.Coord(prevIdx) 731 if a.X() != smallest.X() || a.Y() != smallest.Y() { 732 break 733 } 734 prevIdx-- 735 if prevIdx < 0 { 736 prevIdx = linearRing.NumCoords() - 1 737 } 738 } 739 // Find the next point in the ring that is not the same as smallest. 740 nextIdx := smallestIdx + 1 741 if nextIdx >= linearRing.NumCoords() { 742 nextIdx = 0 743 } 744 for nextIdx != smallestIdx { 745 c := linearRing.Coord(nextIdx) 746 if c.X() != smallest.X() || c.Y() != smallest.Y() { 747 break 748 } 749 nextIdx++ 750 if nextIdx >= linearRing.NumCoords() { 751 nextIdx = 0 752 } 753 } 754 755 // We could do the cross product, but we are only interested in the sign. 756 // To find the sign, reorganize into the orientation matrix: 757 // 1 x_a y_a 758 // 1 x_b y_b 759 // 1 x_c y_c 760 // and find the determinant. 761 // https://en.wikipedia.org/wiki/Curve_orientation#Orientation_of_a_simple_polygon 762 a := linearRing.Coord(prevIdx) 763 b := smallest 764 c := linearRing.Coord(nextIdx) 765 766 // Explicitly use float64 conversion to disable "fused multiply and add" (FMA) to force 767 // identical behavior on all platforms. See https://golang.org/ref/spec#Floating_point_operators 768 areaSign := float64(a.X()*b.Y()) - float64(a.Y()*b.X()) + // nolint:unconvert 769 float64(a.Y()*c.X()) - float64(a.X()*c.Y()) + // nolint:unconvert 770 float64(b.X()*c.Y()) - float64(c.X()*b.Y()) // nolint:unconvert 771 // Note having an area sign of 0 means it is a flat polygon, which is invalid. 772 return areaSign > 0 773 } 774 775 // S2RegionsFromGeomT converts an geom representation of an object 776 // to s2 regions. 777 // As S2 does not really handle empty geometries well, we need to ingest emptyBehavior and 778 // react appropriately. 779 func S2RegionsFromGeomT(geomRepr geom.T, emptyBehavior EmptyBehavior) ([]s2.Region, error) { 780 var regions []s2.Region 781 if geomRepr.Empty() { 782 switch emptyBehavior { 783 case EmptyBehaviorOmit: 784 return nil, nil 785 case EmptyBehaviorError: 786 return nil, NewEmptyGeometryError() 787 default: 788 return nil, errors.AssertionFailedf("programmer error: unknown behavior") 789 } 790 } 791 switch repr := geomRepr.(type) { 792 case *geom.Point: 793 regions = []s2.Region{ 794 s2.PointFromLatLng(s2.LatLngFromDegrees(repr.Y(), repr.X())), 795 } 796 case *geom.LineString: 797 latLngs := make([]s2.LatLng, repr.NumCoords()) 798 for i := 0; i < repr.NumCoords(); i++ { 799 p := repr.Coord(i) 800 latLngs[i] = s2.LatLngFromDegrees(p.Y(), p.X()) 801 } 802 regions = []s2.Region{ 803 s2.PolylineFromLatLngs(latLngs), 804 } 805 case *geom.Polygon: 806 loops := make([]*s2.Loop, repr.NumLinearRings()) 807 // All loops must be oriented CCW for S2. 808 for ringIdx := 0; ringIdx < repr.NumLinearRings(); ringIdx++ { 809 linearRing := repr.LinearRing(ringIdx) 810 points := make([]s2.Point, linearRing.NumCoords()) 811 isCCW := IsLinearRingCCW(linearRing) 812 for pointIdx := 0; pointIdx < linearRing.NumCoords(); pointIdx++ { 813 p := linearRing.Coord(pointIdx) 814 pt := s2.PointFromLatLng(s2.LatLngFromDegrees(p.Y(), p.X())) 815 if isCCW { 816 points[pointIdx] = pt 817 } else { 818 points[len(points)-pointIdx-1] = pt 819 } 820 } 821 loops[ringIdx] = s2.LoopFromPoints(points) 822 } 823 regions = []s2.Region{ 824 s2.PolygonFromLoops(loops), 825 } 826 case *geom.GeometryCollection: 827 for _, geom := range repr.Geoms() { 828 subRegions, err := S2RegionsFromGeomT(geom, emptyBehavior) 829 if err != nil { 830 return nil, err 831 } 832 regions = append(regions, subRegions...) 833 } 834 case *geom.MultiPoint: 835 for i := 0; i < repr.NumPoints(); i++ { 836 subRegions, err := S2RegionsFromGeomT(repr.Point(i), emptyBehavior) 837 if err != nil { 838 return nil, err 839 } 840 regions = append(regions, subRegions...) 841 } 842 case *geom.MultiLineString: 843 for i := 0; i < repr.NumLineStrings(); i++ { 844 subRegions, err := S2RegionsFromGeomT(repr.LineString(i), emptyBehavior) 845 if err != nil { 846 return nil, err 847 } 848 regions = append(regions, subRegions...) 849 } 850 case *geom.MultiPolygon: 851 for i := 0; i < repr.NumPolygons(); i++ { 852 subRegions, err := S2RegionsFromGeomT(repr.Polygon(i), emptyBehavior) 853 if err != nil { 854 return nil, err 855 } 856 regions = append(regions, subRegions...) 857 } 858 } 859 return regions, nil 860 } 861 862 // normalizeLngLat normalizes geographical coordinates into a valid range. 863 func normalizeLngLat(lng float64, lat float64) (float64, float64) { 864 if lat > 90 || lat < -90 { 865 lat = NormalizeLatitudeDegrees(lat) 866 } 867 if lng > 180 || lng < -180 { 868 lng = NormalizeLongitudeDegrees(lng) 869 } 870 return lng, lat 871 } 872 873 // normalizeGeographyGeomT limits geography coordinates to spherical coordinates 874 // by converting geom.T coordinates inplace 875 func normalizeGeographyGeomT(t geom.T) { 876 switch repr := t.(type) { 877 case *geom.GeometryCollection: 878 for _, geom := range repr.Geoms() { 879 normalizeGeographyGeomT(geom) 880 } 881 default: 882 coords := repr.FlatCoords() 883 for i := 0; i < len(coords); i += repr.Stride() { 884 coords[i], coords[i+1] = normalizeLngLat(coords[i], coords[i+1]) 885 } 886 } 887 } 888 889 // validateGeomT validates the geom.T object across valid geom.T objects, 890 // returning an error if it is invalid. 891 func validateGeomT(t geom.T) error { 892 if t.Empty() { 893 return nil 894 } 895 switch t := t.(type) { 896 case *geom.Point: 897 case *geom.LineString: 898 if t.NumCoords() < 2 { 899 return pgerror.Newf( 900 pgcode.InvalidParameterValue, 901 "LineString must have at least 2 coordinates", 902 ) 903 } 904 case *geom.Polygon: 905 for i := 0; i < t.NumLinearRings(); i++ { 906 linearRing := t.LinearRing(i) 907 if linearRing.NumCoords() < 4 { 908 return pgerror.Newf( 909 pgcode.InvalidParameterValue, 910 "Polygon LinearRing must have at least 4 points, found %d at position %d", 911 linearRing.NumCoords(), 912 i+1, 913 ) 914 } 915 if !linearRing.Coord(0).Equal(linearRing.Layout(), linearRing.Coord(linearRing.NumCoords()-1)) { 916 return pgerror.Newf( 917 pgcode.InvalidParameterValue, 918 "Polygon LinearRing at position %d is not closed", 919 i+1, 920 ) 921 } 922 } 923 case *geom.MultiPoint: 924 case *geom.MultiLineString: 925 for i := 0; i < t.NumLineStrings(); i++ { 926 if err := validateGeomT(t.LineString(i)); err != nil { 927 return errors.Wrapf(err, "invalid MultiLineString component at position %d", i+1) 928 } 929 } 930 case *geom.MultiPolygon: 931 for i := 0; i < t.NumPolygons(); i++ { 932 if err := validateGeomT(t.Polygon(i)); err != nil { 933 return errors.Wrapf(err, "invalid MultiPolygon component at position %d", i+1) 934 } 935 } 936 case *geom.GeometryCollection: 937 // TODO(ayang): verify that the geometries all have the same Layout 938 for i := 0; i < t.NumGeoms(); i++ { 939 if err := validateGeomT(t.Geom(i)); err != nil { 940 return errors.Wrapf(err, "invalid GeometryCollection component at position %d", i+1) 941 } 942 } 943 default: 944 return pgerror.Newf( 945 pgcode.InvalidParameterValue, 946 "unknown geom.T type: %T", 947 t, 948 ) 949 } 950 return nil 951 } 952 953 // spatialObjectFromGeomT creates a geopb.SpatialObject from a geom.T. 954 func spatialObjectFromGeomT(t geom.T, soType geopb.SpatialObjectType) (geopb.SpatialObject, error) { 955 if err := validateGeomT(t); err != nil { 956 return geopb.SpatialObject{}, err 957 } 958 if soType == geopb.SpatialObjectType_GeographyType { 959 normalizeGeographyGeomT(t) 960 } 961 ret, err := ewkb.Marshal(t, DefaultEWKBEncodingFormat) 962 if err != nil { 963 return geopb.SpatialObject{}, err 964 } 965 shapeType, err := shapeTypeFromGeomT(t) 966 if err != nil { 967 return geopb.SpatialObject{}, err 968 } 969 bbox, err := boundingBoxFromGeomT(t, soType) 970 if err != nil { 971 return geopb.SpatialObject{}, err 972 } 973 return geopb.SpatialObject{ 974 Type: soType, 975 EWKB: geopb.EWKB(ret), 976 SRID: geopb.SRID(t.SRID()), 977 ShapeType: shapeType, 978 BoundingBox: bbox, 979 }, nil 980 } 981 982 func shapeTypeFromGeomT(t geom.T) (geopb.ShapeType, error) { 983 var shapeType geopb.ShapeType 984 switch t := t.(type) { 985 case *geom.Point: 986 shapeType = geopb.ShapeType_Point 987 case *geom.LineString: 988 shapeType = geopb.ShapeType_LineString 989 case *geom.Polygon: 990 shapeType = geopb.ShapeType_Polygon 991 case *geom.MultiPoint: 992 shapeType = geopb.ShapeType_MultiPoint 993 case *geom.MultiLineString: 994 shapeType = geopb.ShapeType_MultiLineString 995 case *geom.MultiPolygon: 996 shapeType = geopb.ShapeType_MultiPolygon 997 case *geom.GeometryCollection: 998 shapeType = geopb.ShapeType_GeometryCollection 999 default: 1000 return geopb.ShapeType_Unset, pgerror.Newf(pgcode.InvalidParameterValue, "unknown shape: %T", t) 1001 } 1002 switch t.Layout() { 1003 case geom.NoLayout: 1004 if gc, ok := t.(*geom.GeometryCollection); !ok || !gc.Empty() { 1005 return geopb.ShapeType_Unset, pgerror.Newf(pgcode.InvalidParameterValue, "no layout found on object") 1006 } 1007 case geom.XY: 1008 break 1009 case geom.XYM: 1010 shapeType = shapeType | geopb.MShapeTypeFlag 1011 case geom.XYZ: 1012 shapeType = shapeType | geopb.ZShapeTypeFlag 1013 case geom.XYZM: 1014 shapeType = shapeType | geopb.ZShapeTypeFlag | geopb.MShapeTypeFlag 1015 default: 1016 return geopb.ShapeType_Unset, pgerror.Newf(pgcode.InvalidParameterValue, "unknown layout: %s", t.Layout()) 1017 } 1018 return shapeType, nil 1019 } 1020 1021 // GeomTContainsEmpty returns whether a geom.T contains any empty element. 1022 func GeomTContainsEmpty(g geom.T) bool { 1023 if g.Empty() { 1024 return true 1025 } 1026 switch g := g.(type) { 1027 case *geom.MultiPoint: 1028 for i := 0; i < g.NumPoints(); i++ { 1029 if g.Point(i).Empty() { 1030 return true 1031 } 1032 } 1033 case *geom.MultiLineString: 1034 for i := 0; i < g.NumLineStrings(); i++ { 1035 if g.LineString(i).Empty() { 1036 return true 1037 } 1038 } 1039 case *geom.MultiPolygon: 1040 for i := 0; i < g.NumPolygons(); i++ { 1041 if g.Polygon(i).Empty() { 1042 return true 1043 } 1044 } 1045 case *geom.GeometryCollection: 1046 for i := 0; i < g.NumGeoms(); i++ { 1047 if GeomTContainsEmpty(g.Geom(i)) { 1048 return true 1049 } 1050 } 1051 } 1052 return false 1053 } 1054 1055 // compareSpatialObjectBytes compares the SpatialObject if they were serialized. 1056 // This is used for comparison operations, and must be kept consistent with the indexing 1057 // encoding. 1058 func compareSpatialObjectBytes(lhs *geopb.SpatialObject, rhs *geopb.SpatialObject) int { 1059 marshalledLHS, err := protoutil.Marshal(lhs) 1060 if err != nil { 1061 panic(err) 1062 } 1063 marshalledRHS, err := protoutil.Marshal(rhs) 1064 if err != nil { 1065 panic(err) 1066 } 1067 return bytes.Compare(marshalledLHS, marshalledRHS) 1068 } 1069 1070 const ( 1071 geomTSize = int64(unsafe.Sizeof(geom.T(nil))) 1072 geometryCollectionSize = int64(unsafe.Sizeof(geom.GeometryCollection{})) 1073 intSize = int64(unsafe.Sizeof(int(0))) 1074 floatSize = int64(unsafe.Sizeof(float64(0))) 1075 intSliceOverhead = int64(unsafe.Sizeof([]int{})) 1076 ) 1077 1078 // GeomTSize returns the best estimate for the memory footprint of the geom.T 1079 // object. 1080 func GeomTSize(g geom.T) int64 { 1081 if gc, ok := g.(*geom.GeometryCollection); ok { 1082 geoms := gc.Geoms() 1083 size := geometryCollectionSize + geomTSize*int64(cap(geoms)) 1084 for _, innerG := range geoms { 1085 size += GeomTSize(innerG) 1086 } 1087 return size 1088 } 1089 size := floatSize*int64(cap(g.FlatCoords())) + intSize*int64(cap(g.Ends())) 1090 if endss := g.Endss(); cap(endss) > 0 { 1091 size += intSliceOverhead * int64(cap(endss)) 1092 for _, ends := range endss { 1093 size += intSize * int64(cap(ends)) 1094 } 1095 } 1096 return size 1097 }