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  }