github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/geo/geomfn/distance.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 "math" 15 16 "github.com/cockroachdb/cockroach/pkg/geo" 17 "github.com/cockroachdb/cockroach/pkg/geo/geodist" 18 "github.com/cockroachdb/errors" 19 "github.com/twpayne/go-geom" 20 "github.com/twpayne/go-geom/xy/lineintersector" 21 ) 22 23 // MinDistance returns the minimum distance between geometries A and B. 24 // This returns a geo.EmptyGeometryError if either A or B is EMPTY. 25 func MinDistance(a *geo.Geometry, b *geo.Geometry) (float64, error) { 26 if a.SRID() != b.SRID() { 27 return 0, geo.NewMismatchingSRIDsError(a, b) 28 } 29 return minDistanceInternal(a, b, 0, geo.EmptyBehaviorOmit) 30 } 31 32 // MaxDistance returns the maximum distance across every pair of points comprising 33 // geometries A and B. 34 func MaxDistance(a *geo.Geometry, b *geo.Geometry) (float64, error) { 35 if a.SRID() != b.SRID() { 36 return 0, geo.NewMismatchingSRIDsError(a, b) 37 } 38 return maxDistanceInternal(a, b, math.MaxFloat64, geo.EmptyBehaviorOmit) 39 } 40 41 // DWithin determines if any part of geometry A is within D units of geometry B. 42 func DWithin(a *geo.Geometry, b *geo.Geometry, d float64) (bool, error) { 43 if a.SRID() != b.SRID() { 44 return false, geo.NewMismatchingSRIDsError(a, b) 45 } 46 if d < 0 { 47 return false, errors.Newf("dwithin distance cannot be less than zero") 48 } 49 dist, err := minDistanceInternal(a, b, d, geo.EmptyBehaviorError) 50 if err != nil { 51 // In case of any empty geometries return false. 52 if geo.IsEmptyGeometryError(err) { 53 return false, nil 54 } 55 return false, err 56 } 57 return dist <= d, nil 58 } 59 60 // DFullyWithin determines whether the maximum distance across every pair of points 61 // comprising geometries A and B is within D units. 62 func DFullyWithin(a *geo.Geometry, b *geo.Geometry, d float64) (bool, error) { 63 if a.SRID() != b.SRID() { 64 return false, geo.NewMismatchingSRIDsError(a, b) 65 } 66 if d < 0 { 67 return false, errors.Newf("dwithin distance cannot be less than zero") 68 } 69 dist, err := maxDistanceInternal(a, b, d, geo.EmptyBehaviorError) 70 if err != nil { 71 // In case of any empty geometries return false. 72 if geo.IsEmptyGeometryError(err) { 73 return false, nil 74 } 75 return false, err 76 } 77 return dist <= d, nil 78 } 79 80 // maxDistanceInternal finds the maximum distance between two geometries. 81 // We can re-use the same algorithm as min-distance, allowing skips of checks that involve 82 // the interiors or intersections as those will always be less then the maximum min-distance. 83 func maxDistanceInternal( 84 a *geo.Geometry, b *geo.Geometry, stopAfterGT float64, emptyBehavior geo.EmptyBehavior, 85 ) (float64, error) { 86 u := newGeomMaxDistanceUpdater(stopAfterGT) 87 c := &geomDistanceCalculator{updater: u, boundingBoxIntersects: a.BoundingBoxIntersects(b)} 88 return distanceInternal(a, b, c, emptyBehavior) 89 } 90 91 // minDistanceInternal finds the minimum distance between two geometries. 92 // This implementation is done in-house, as compared to using GEOS. 93 func minDistanceInternal( 94 a *geo.Geometry, b *geo.Geometry, stopAfterLE float64, emptyBehavior geo.EmptyBehavior, 95 ) (float64, error) { 96 u := newGeomMinDistanceUpdater(stopAfterLE) 97 c := &geomDistanceCalculator{updater: u, boundingBoxIntersects: a.BoundingBoxIntersects(b)} 98 return distanceInternal(a, b, c, emptyBehavior) 99 } 100 101 // distanceInternal calculates the distance between two geometries using 102 // the DistanceCalculator operator. 103 // If there are any EMPTY Geometry objects, they will be ignored. It will return an 104 // EmptyGeometryError if A or B contains only EMPTY geometries, even if emptyBehavior 105 // is set to EmptyBehaviorOmit. 106 func distanceInternal( 107 a *geo.Geometry, b *geo.Geometry, c geodist.DistanceCalculator, emptyBehavior geo.EmptyBehavior, 108 ) (float64, error) { 109 aGeoms, err := flattenGeometry(a, emptyBehavior) 110 if err != nil { 111 return 0, err 112 } 113 bGeoms, err := flattenGeometry(b, emptyBehavior) 114 if err != nil { 115 return 0, err 116 } 117 // If either side has no geoms, then we error out. 118 if len(aGeoms) == 0 || len(bGeoms) == 0 { 119 return 0, geo.NewEmptyGeometryError() 120 } 121 122 for _, aGeom := range aGeoms { 123 aGeodist, err := geomToGeodist(aGeom) 124 if err != nil { 125 return 0, err 126 } 127 for _, bGeom := range bGeoms { 128 bGeodist, err := geomToGeodist(bGeom) 129 if err != nil { 130 return 0, err 131 } 132 earlyExit, err := geodist.ShapeDistance(c, aGeodist, bGeodist) 133 if err != nil { 134 return 0, err 135 } 136 if earlyExit { 137 return c.DistanceUpdater().Distance(), nil 138 } 139 } 140 } 141 return c.DistanceUpdater().Distance(), nil 142 } 143 144 // geomToGeodist converts a given geom object to a geodist shape. 145 func geomToGeodist(g geom.T) (geodist.Shape, error) { 146 switch g := g.(type) { 147 case *geom.Point: 148 return &geomGeodistPoint{Coord: g.Coords()}, nil 149 case *geom.LineString: 150 return &geomGeodistLineString{LineString: g}, nil 151 case *geom.Polygon: 152 return &geomGeodistPolygon{Polygon: g}, nil 153 } 154 return nil, errors.Newf("could not find shape: %T", g) 155 } 156 157 // geomGeodistPoint implements geodist.Point. 158 type geomGeodistPoint struct { 159 geom.Coord 160 } 161 162 var _ geodist.Point = (*geomGeodistPoint)(nil) 163 164 // IsShape implements the geodist.Point interface. 165 func (*geomGeodistPoint) IsShape() {} 166 167 // Point implements the geodist.Point interface. 168 func (*geomGeodistPoint) IsPoint() {} 169 170 // geomGeodistLineString implements geodist.LineString. 171 type geomGeodistLineString struct { 172 *geom.LineString 173 } 174 175 var _ geodist.LineString = (*geomGeodistLineString)(nil) 176 177 // IsShape implements the geodist.LineString interface. 178 func (*geomGeodistLineString) IsShape() {} 179 180 // LineString implements the geodist.LineString interface. 181 func (*geomGeodistLineString) IsLineString() {} 182 183 // Edge implements the geodist.LineString interface. 184 func (g *geomGeodistLineString) Edge(i int) geodist.Edge { 185 return geodist.Edge{ 186 V0: &geomGeodistPoint{Coord: g.LineString.Coord(i)}, 187 V1: &geomGeodistPoint{Coord: g.LineString.Coord(i + 1)}, 188 } 189 } 190 191 // NumEdges implements the geodist.LineString interface. 192 func (g *geomGeodistLineString) NumEdges() int { 193 return g.LineString.NumCoords() - 1 194 } 195 196 // Vertex implements the geodist.LineString interface. 197 func (g *geomGeodistLineString) Vertex(i int) geodist.Point { 198 return &geomGeodistPoint{Coord: g.LineString.Coord(i)} 199 } 200 201 // NumVertexes implements the geodist.LineString interface. 202 func (g *geomGeodistLineString) NumVertexes() int { 203 return g.LineString.NumCoords() 204 } 205 206 // geomGeodistLinearRing implements geodist.LinearRing. 207 type geomGeodistLinearRing struct { 208 *geom.LinearRing 209 } 210 211 var _ geodist.LinearRing = (*geomGeodistLinearRing)(nil) 212 213 // IsShape implements the geodist.LinearRing interface. 214 func (*geomGeodistLinearRing) IsShape() {} 215 216 // LinearRing implements the geodist.LinearRing interface. 217 func (*geomGeodistLinearRing) IsLinearRing() {} 218 219 // Edge implements the geodist.LinearRing interface. 220 func (g *geomGeodistLinearRing) Edge(i int) geodist.Edge { 221 return geodist.Edge{ 222 V0: &geomGeodistPoint{Coord: g.LinearRing.Coord(i)}, 223 V1: &geomGeodistPoint{Coord: g.LinearRing.Coord(i + 1)}, 224 } 225 } 226 227 // NumEdges implements the geodist.LinearRing interface. 228 func (g *geomGeodistLinearRing) NumEdges() int { 229 return g.LinearRing.NumCoords() - 1 230 } 231 232 // Vertex implements the geodist.LinearRing interface. 233 func (g *geomGeodistLinearRing) Vertex(i int) geodist.Point { 234 return &geomGeodistPoint{Coord: g.LinearRing.Coord(i)} 235 } 236 237 // NumVertexes implements the geodist.LinearRing interface. 238 func (g *geomGeodistLinearRing) NumVertexes() int { 239 return g.LinearRing.NumCoords() 240 } 241 242 // geomGeodistPolygon implements geodist.Polygon. 243 type geomGeodistPolygon struct { 244 *geom.Polygon 245 } 246 247 var _ geodist.Polygon = (*geomGeodistPolygon)(nil) 248 249 // IsShape implements the geodist.Polygon interface. 250 func (*geomGeodistPolygon) IsShape() {} 251 252 // Polygon implements the geodist.Polygon interface. 253 func (*geomGeodistPolygon) IsPolygon() {} 254 255 // LinearRing implements the geodist.Polygon interface. 256 func (g *geomGeodistPolygon) LinearRing(i int) geodist.LinearRing { 257 return &geomGeodistLinearRing{LinearRing: g.Polygon.LinearRing(i)} 258 } 259 260 // NumLinearRings implements the geodist.Polygon interface. 261 func (g *geomGeodistPolygon) NumLinearRings() int { 262 return g.Polygon.NumLinearRings() 263 } 264 265 // geomGeodistEdgeCrosser implements geodist.EdgeCrosser. 266 type geomGeodistEdgeCrosser struct { 267 strategy lineintersector.Strategy 268 edgeV0 geom.Coord 269 edgeV1 geom.Coord 270 nextEdgeV0 geom.Coord 271 } 272 273 var _ geodist.EdgeCrosser = (*geomGeodistEdgeCrosser)(nil) 274 275 // ChainCrossing implements geodist.EdgeCrosser. 276 func (c *geomGeodistEdgeCrosser) ChainCrossing(p geodist.Point) bool { 277 nextEdgeV1 := p.(*geomGeodistPoint).Coord 278 result := lineintersector.LineIntersectsLine( 279 c.strategy, 280 c.edgeV0, 281 c.edgeV1, 282 c.nextEdgeV0, 283 nextEdgeV1, 284 ) 285 c.nextEdgeV0 = nextEdgeV1 286 return result.HasIntersection() 287 } 288 289 // geomMinDistanceUpdater finds the minimum distance using geom calculations. 290 // Methods will return early if it finds a minimum distance <= stopAfterLE. 291 type geomMinDistanceUpdater struct { 292 currentValue float64 293 stopAfterLE float64 294 } 295 296 var _ geodist.DistanceUpdater = (*geomMinDistanceUpdater)(nil) 297 298 // newGeomMinDistanceUpdater returns a new geomMinDistanceUpdater with the 299 // correct arguments set up. 300 func newGeomMinDistanceUpdater(stopAfterLE float64) *geomMinDistanceUpdater { 301 return &geomMinDistanceUpdater{ 302 currentValue: math.MaxFloat64, 303 stopAfterLE: stopAfterLE, 304 } 305 } 306 307 // Distance implements the DistanceUpdater interface. 308 func (u *geomMinDistanceUpdater) Distance() float64 { 309 return u.currentValue 310 } 311 312 // Update implements the geodist.DistanceUpdater interface. 313 func (u *geomMinDistanceUpdater) Update(aInterface geodist.Point, bInterface geodist.Point) bool { 314 a := aInterface.(*geomGeodistPoint).Coord 315 b := bInterface.(*geomGeodistPoint).Coord 316 317 dist := coordNorm(coordSub(a, b)) 318 if dist < u.currentValue { 319 u.currentValue = dist 320 return dist <= u.stopAfterLE 321 } 322 return false 323 } 324 325 // OnIntersects implements the geodist.DistanceUpdater interface. 326 func (u *geomMinDistanceUpdater) OnIntersects() bool { 327 u.currentValue = 0 328 return true 329 } 330 331 // IsMaxDistance implements the geodist.DistanceUpdater interface. 332 func (u *geomMinDistanceUpdater) IsMaxDistance() bool { 333 return false 334 } 335 336 // geomMaxDistanceUpdater finds the maximum distance using geom calculations. 337 // Methods will return early if it finds a distance > stopAfterGT. 338 type geomMaxDistanceUpdater struct { 339 currentValue float64 340 stopAfterGT float64 341 } 342 343 var _ geodist.DistanceUpdater = (*geomMaxDistanceUpdater)(nil) 344 345 // newGeomMaxDistanceUpdater returns a new geomMaxDistanceUpdater with the 346 // correct arguments set up. 347 func newGeomMaxDistanceUpdater(stopAfterGT float64) *geomMaxDistanceUpdater { 348 return &geomMaxDistanceUpdater{ 349 currentValue: 0, 350 stopAfterGT: stopAfterGT, 351 } 352 } 353 354 // Distance implements the DistanceUpdater interface. 355 func (u *geomMaxDistanceUpdater) Distance() float64 { 356 return u.currentValue 357 } 358 359 // Update implements the geodist.DistanceUpdater interface. 360 func (u *geomMaxDistanceUpdater) Update(aInterface geodist.Point, bInterface geodist.Point) bool { 361 a := aInterface.(*geomGeodistPoint).Coord 362 b := bInterface.(*geomGeodistPoint).Coord 363 364 dist := coordNorm(coordSub(a, b)) 365 if dist > u.currentValue { 366 u.currentValue = dist 367 return dist > u.stopAfterGT 368 } 369 return false 370 } 371 372 // OnIntersects implements the geodist.DistanceUpdater interface. 373 func (u *geomMaxDistanceUpdater) OnIntersects() bool { 374 return false 375 } 376 377 // IsMaxDistance implements the geodist.DistanceUpdater interface. 378 func (u *geomMaxDistanceUpdater) IsMaxDistance() bool { 379 return true 380 } 381 382 // geomDistanceCalculator implements geodist.DistanceCalculator 383 type geomDistanceCalculator struct { 384 updater geodist.DistanceUpdater 385 boundingBoxIntersects bool 386 } 387 388 var _ geodist.DistanceCalculator = (*geomDistanceCalculator)(nil) 389 390 // DistanceUpdater implements geodist.DistanceCalculator. 391 func (c *geomDistanceCalculator) DistanceUpdater() geodist.DistanceUpdater { 392 return c.updater 393 } 394 395 // BoundingBoxIntersects implements geodist.DistanceCalculator. 396 func (c *geomDistanceCalculator) BoundingBoxIntersects() bool { 397 return c.boundingBoxIntersects 398 } 399 400 // NewEdgeCrosser implements geodist.DistanceCalculator. 401 func (c *geomDistanceCalculator) NewEdgeCrosser( 402 edge geodist.Edge, startPoint geodist.Point, 403 ) geodist.EdgeCrosser { 404 return &geomGeodistEdgeCrosser{ 405 strategy: &lineintersector.NonRobustLineIntersector{}, 406 edgeV0: edge.V0.(*geomGeodistPoint).Coord, 407 edgeV1: edge.V1.(*geomGeodistPoint).Coord, 408 nextEdgeV0: startPoint.(*geomGeodistPoint).Coord, 409 } 410 } 411 412 // side corresponds to the side in which a point is relative to a line. 413 type pointSide int 414 415 const ( 416 pointSideLeft pointSide = -1 417 pointSideOn pointSide = 0 418 pointSideRight pointSide = 1 419 ) 420 421 // findPointSide finds which side a point is relative to the infinite line 422 // given by the edge. 423 // Note this side is relative to the orientation of the line. 424 func findPointSide(p geom.Coord, eV0 geom.Coord, eV1 geom.Coord) pointSide { 425 // This is the equivalent of using the point-gradient formula 426 // and determining the sign, i.e. the sign of 427 // d = (x-x1)(y2-y1) - (y-y1)(x2-x1) 428 // where (x1,y1) and (x2,y2) is the edge and (x,y) is the point 429 sign := (p.X()-eV0.X())*(eV1.Y()-eV0.Y()) - (eV1.X()-eV0.X())*(p.Y()-eV0.Y()) 430 switch { 431 case sign == 0: 432 return pointSideOn 433 case sign > 0: 434 return pointSideRight 435 default: 436 return pointSideLeft 437 } 438 } 439 440 // PointInLinearRing implements geodist.DistanceCalculator. 441 func (c *geomDistanceCalculator) PointInLinearRing( 442 point geodist.Point, polygon geodist.LinearRing, 443 ) bool { 444 // This is done using the winding number algorithm, also known as the 445 // "non-zero rule". 446 // See: https://en.wikipedia.org/wiki/Point_in_polygon for intro. 447 // See: http://geomalgorithms.com/a03-_inclusion.html for algorithm. 448 // See also: https://en.wikipedia.org/wiki/Winding_number 449 // See also: https://en.wikipedia.org/wiki/Nonzero-rule 450 windingNumber := 0 451 p := point.(*geomGeodistPoint).Coord 452 for edgeIdx := 0; edgeIdx < polygon.NumEdges(); edgeIdx++ { 453 e := polygon.Edge(edgeIdx) 454 eV0 := e.V0.(*geomGeodistPoint).Coord 455 eV1 := e.V1.(*geomGeodistPoint).Coord 456 // Same vertex; none of these checks will pass. 457 if coordEqual(eV0, eV1) { 458 continue 459 } 460 yMin := math.Min(eV0.Y(), eV1.Y()) 461 yMax := math.Max(eV0.Y(), eV1.Y()) 462 // If the edge isn't on the same level as Y, this edge isn't worth considering. 463 if p.Y() > yMax || p.Y() < yMin { 464 continue 465 } 466 side := findPointSide(p, eV0, eV1) 467 // If the point is on the line if the edge was infinite, and the point is within the bounds 468 // of the line segment denoted by the edge, there is a covering. 469 if side == pointSideOn && 470 ((eV0.X() <= p.X() && p.X() < eV1.X()) || (eV1.X() <= p.X() && p.X() < eV0.X()) || 471 (eV0.Y() <= p.Y() && p.Y() < eV1.Y()) || (eV1.Y() <= p.Y() && p.Y() < eV0.Y())) { 472 return true 473 } 474 // If the point is left of the segment and the line is rising 475 // we have a circle going CCW, so increment. 476 // Note we only compare [start, end) as we do not want to double count points 477 // which are on the same X / Y axis as an edge vertex. 478 if side == pointSideLeft && eV0.Y() <= p.Y() && p.Y() < eV1.Y() { 479 windingNumber++ 480 } 481 // If the line is to the right of the segment and the 482 // line is falling, we a have a circle going CW so decrement. 483 // Note we only compare [start, end) as we do not want to double count points 484 // which are on the same X / Y axis as an edge vertex. 485 if side == pointSideRight && eV1.Y() <= p.Y() && p.Y() < eV0.Y() { 486 windingNumber-- 487 } 488 } 489 return windingNumber != 0 490 } 491 492 // ClosestPointToEdge implements geodist.DistanceCalculator. 493 func (c *geomDistanceCalculator) ClosestPointToEdge( 494 edge geodist.Edge, pointInterface geodist.Point, 495 ) (geodist.Point, bool) { 496 eV0 := edge.V0.(*geomGeodistPoint).Coord 497 eV1 := edge.V1.(*geomGeodistPoint).Coord 498 p := pointInterface.(*geomGeodistPoint).Coord 499 500 // Edge is a single point. Closest point must be any edge vertex. 501 if coordEqual(eV0, eV1) { 502 return edge.V0, coordEqual(eV0, p) 503 } 504 505 // From http://www.faqs.org/faqs/graphics/algorithms-faq/, section 1.02 506 // 507 // Let the point be C (Cx,Cy) and the line be AB (Ax,Ay) to (Bx,By). 508 // Let P be the point of perpendicular projection of C on AB. The parameter 509 // r, which indicates P's position along AB, is computed by the dot product 510 // of AC and AB divided by the square of the length of AB: 511 // 512 // (1) AC dot AB 513 // r = --------- 514 // ||AB||^2 515 // 516 // r has the following meaning: 517 // 518 // r=0 P = A 519 // r=1 P = B 520 // r<0 P is on the backward extension of AB 521 // r>1 P is on the forward extension of AB 522 // 0<r<1 P is interior to AB 523 if coordEqual(p, eV0) { 524 return pointInterface, true 525 } 526 if coordEqual(p, eV1) { 527 return pointInterface, true 528 } 529 530 ac := coordSub(p, eV0) 531 ab := coordSub(eV1, eV0) 532 533 r := coordDot(ac, ab) / coordNorm2(ab) 534 if r < 0 || r > 1 { 535 return pointInterface, false 536 } 537 return &geomGeodistPoint{Coord: coordAdd(eV0, coordMul(ab, r))}, true 538 }