github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/geo/geogfn/covers.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 geogfn
    12  
    13  import (
    14  	"fmt"
    15  
    16  	"github.com/cockroachdb/cockroach/pkg/geo"
    17  	"github.com/golang/geo/s2"
    18  )
    19  
    20  // Covers returns whether geography A covers geography B.
    21  //
    22  // This calculation is done on the sphere.
    23  //
    24  // Due to minor inaccuracies and lack of certain primitives in S2,
    25  // precision for Covers will be for up to 1cm.
    26  //
    27  // Current limitations (which are also limitations in PostGIS):
    28  // * POLYGON/LINESTRING only works as "contains" - if any point of the LINESTRING
    29  //   touches the boundary of the polygon, we will return false but should be true - e.g.
    30  //     SELECT st_covers(
    31  //       'multipolygon(((0.0 0.0, 1.0 0.0, 1.0 1.0, 0.0 1.0, 0.0 0.0)), ((1.0 0.0, 2.0 0.0, 2.0 1.0, 1.0 1.0, 1.0 0.0)))',
    32  //       'linestring(0.0 0.0, 1.0 0.0)'::geography
    33  //     );
    34  //
    35  // * Furthermore, LINESTRINGS that are covered in multiple POLYGONs inside
    36  //   MULTIPOLYGON but NOT within a single POLYGON in the MULTIPOLYGON
    37  //   currently return false but should be true, e.g.
    38  //     SELECT st_covers(
    39  //       'multipolygon(((0.0 0.0, 1.0 0.0, 1.0 1.0, 0.0 1.0, 0.0 0.0)), ((1.0 0.0, 2.0 0.0, 2.0 1.0, 1.0 1.0, 1.0 0.0)))',
    40  //       'linestring(0.0 0.0, 2.0 0.0)'::geography
    41  //     );
    42  func Covers(a *geo.Geography, b *geo.Geography) (bool, error) {
    43  	if a.SRID() != b.SRID() {
    44  		return false, geo.NewMismatchingSRIDsError(a, b)
    45  	}
    46  	return covers(a, b)
    47  }
    48  
    49  // covers is the internal calculation for Covers.
    50  func covers(a *geo.Geography, b *geo.Geography) (bool, error) {
    51  	// Ignore EMPTY regions in a.
    52  	aRegions, err := a.AsS2(geo.EmptyBehaviorOmit)
    53  	if err != nil {
    54  		return false, err
    55  	}
    56  	// If any of b is empty, we cannot cover it. Error and catch to return false.
    57  	bRegions, err := b.AsS2(geo.EmptyBehaviorError)
    58  	if err != nil {
    59  		if geo.IsEmptyGeometryError(err) {
    60  			return false, nil
    61  		}
    62  		return false, err
    63  	}
    64  
    65  	// We need to check each region in B is covered by at least
    66  	// one region of A.
    67  	bRegionsRemaining := make(map[int]struct{}, len(bRegions))
    68  	for i := range bRegions {
    69  		bRegionsRemaining[i] = struct{}{}
    70  	}
    71  	for _, aRegion := range aRegions {
    72  		for bRegionIdx := range bRegionsRemaining {
    73  			regionCovers, err := regionCovers(aRegion, bRegions[bRegionIdx])
    74  			if err != nil {
    75  				return false, err
    76  			}
    77  			if regionCovers {
    78  				delete(bRegionsRemaining, bRegionIdx)
    79  			}
    80  		}
    81  		if len(bRegionsRemaining) == 0 {
    82  			return true, nil
    83  		}
    84  	}
    85  	return false, nil
    86  }
    87  
    88  // CoveredBy returns whether geography A is covered by geography B.
    89  // See Covers for limitations.
    90  func CoveredBy(a *geo.Geography, b *geo.Geography) (bool, error) {
    91  	if a.SRID() != b.SRID() {
    92  		return false, geo.NewMismatchingSRIDsError(a, b)
    93  	}
    94  	return covers(b, a)
    95  }
    96  
    97  // regionCovers returns whether aRegion completely covers bRegion.
    98  func regionCovers(aRegion s2.Region, bRegion s2.Region) (bool, error) {
    99  	switch aRegion := aRegion.(type) {
   100  	case s2.Point:
   101  		switch bRegion := bRegion.(type) {
   102  		case s2.Point:
   103  			return aRegion.ContainsPoint(bRegion), nil
   104  		case *s2.Polyline:
   105  			return false, nil
   106  		case *s2.Polygon:
   107  			return false, nil
   108  		default:
   109  			return false, fmt.Errorf("unknown s2 type of b: %#v", bRegion)
   110  		}
   111  	case *s2.Polyline:
   112  		switch bRegion := bRegion.(type) {
   113  		case s2.Point:
   114  			return polylineCoversPoint(aRegion, bRegion), nil
   115  		case *s2.Polyline:
   116  			return polylineCoversPolyline(aRegion, bRegion), nil
   117  		case *s2.Polygon:
   118  			return false, nil
   119  		default:
   120  			return false, fmt.Errorf("unknown s2 type of b: %#v", bRegion)
   121  		}
   122  	case *s2.Polygon:
   123  		switch bRegion := bRegion.(type) {
   124  		case s2.Point:
   125  			return polygonCoversPoint(aRegion, bRegion), nil
   126  		case *s2.Polyline:
   127  			return polygonCoversPolyline(aRegion, bRegion), nil
   128  		case *s2.Polygon:
   129  			return polygonCoversPolygon(aRegion, bRegion), nil
   130  		default:
   131  			return false, fmt.Errorf("unknown s2 type of b: %#v", bRegion)
   132  		}
   133  	}
   134  	return false, fmt.Errorf("unknown s2 type of a: %#v", aRegion)
   135  }
   136  
   137  // polylineCoversPoints returns whether a polyline covers a given point.
   138  func polylineCoversPoint(a *s2.Polyline, b s2.Point) bool {
   139  	return a.IntersectsCell(s2.CellFromPoint(b))
   140  }
   141  
   142  // polylineCoversPointsWithIdx returns whether a polyline covers a given point.
   143  // If true, it will also return an index of the start of the edge where there
   144  // was an intersection.
   145  func polylineCoversPointWithIdx(a *s2.Polyline, b s2.Point) (bool, int) {
   146  	for edgeIdx := 0; edgeIdx < a.NumEdges(); edgeIdx++ {
   147  		if edgeCoversPoint(a.Edge(edgeIdx), b) {
   148  			return true, edgeIdx
   149  		}
   150  	}
   151  	return false, -1
   152  }
   153  
   154  // polygonCoversPoints returns whether a polygon covers a given point.
   155  func polygonCoversPoint(a *s2.Polygon, b s2.Point) bool {
   156  	// Account for the case where b is on the edge of the polygon.
   157  	return a.ContainsPoint(b) || a.IntersectsCell(s2.CellFromPoint(b))
   158  }
   159  
   160  // edgeCoversPoint determines whether a given edge contains a point.
   161  func edgeCoversPoint(e s2.Edge, p s2.Point) bool {
   162  	return (&s2.Polyline{e.V0, e.V1}).IntersectsCell(s2.CellFromPoint(p))
   163  }
   164  
   165  // polylineCoversPolyline returns whether polyline a covers polyline b.
   166  func polylineCoversPolyline(a *s2.Polyline, b *s2.Polyline) bool {
   167  	if polylineCoversPolylineOrdered(a, b) {
   168  		return true
   169  	}
   170  	// Check reverse ordering works as well.
   171  	reversedB := make([]s2.Point, len(*b))
   172  	for i, point := range *b {
   173  		reversedB[len(reversedB)-1-i] = point
   174  	}
   175  	newBAsPolyline := s2.Polyline(reversedB)
   176  	return polylineCoversPolylineOrdered(a, &newBAsPolyline)
   177  }
   178  
   179  // polylineCoversPolylineOrdered returns whether a polyline covers a polyline
   180  // in the same ordering.
   181  func polylineCoversPolylineOrdered(a *s2.Polyline, b *s2.Polyline) bool {
   182  	aCoversStartOfB, aCoverBStart := polylineCoversPointWithIdx(a, (*b)[0])
   183  	// We must first check that the start of B is contained by A.
   184  	if !aCoversStartOfB {
   185  		return false
   186  	}
   187  
   188  	aPoints := *a
   189  	bPoints := *b
   190  	// We have found "aIdx" which is the first edge in polyline A
   191  	// that includes the starting vertex of polyline "B".
   192  	// Start checking the covering from this edge.
   193  	aIdx := aCoverBStart
   194  	bIdx := 0
   195  
   196  	aEdge := s2.Edge{V0: aPoints[aIdx], V1: aPoints[aIdx+1]}
   197  	bEdge := s2.Edge{V0: bPoints[bIdx], V1: bPoints[bIdx+1]}
   198  	for {
   199  		aEdgeCoversBStart := edgeCoversPoint(aEdge, bEdge.V0)
   200  		aEdgeCoversBEnd := edgeCoversPoint(aEdge, bEdge.V1)
   201  		bEdgeCoversAEnd := edgeCoversPoint(bEdge, aEdge.V1)
   202  		if aEdgeCoversBStart && aEdgeCoversBEnd {
   203  			// If the edge A fully covers edge B, check the next edge.
   204  			bIdx++
   205  			// We are out of edges in B, and A keeps going or stops at the same point.
   206  			// This is a covering.
   207  			if bIdx == len(bPoints)-1 {
   208  				return true
   209  			}
   210  			bEdge = s2.Edge{V0: bPoints[bIdx], V1: bPoints[bIdx+1]}
   211  			// If A and B end at the same place, we need to move A forward.
   212  			if bEdgeCoversAEnd {
   213  				aIdx++
   214  				if aIdx == len(aPoints)-1 {
   215  					// At this point, B extends past A. return false.
   216  					return false
   217  				}
   218  				aEdge = s2.Edge{V0: aPoints[aIdx], V1: aPoints[aIdx+1]}
   219  			}
   220  			continue
   221  		}
   222  
   223  		if aEdgeCoversBStart {
   224  			// Edge A doesn't cover the end of B, but it does cover the start.
   225  			// If B doesn't cover the end of A, we're done.
   226  			if !bEdgeCoversAEnd {
   227  				return false
   228  			}
   229  			// If the end of edge B covers the end of A, that means that
   230  			// B is possibly longer than A. If that's the case, truncate B
   231  			// to be the end of A, and move A forward.
   232  			bEdge.V0 = aEdge.V1
   233  			aIdx++
   234  			if aIdx == len(aPoints)-1 {
   235  				// At this point, B extends past A. return false.
   236  				return false
   237  			}
   238  			aEdge = s2.Edge{V0: aPoints[aIdx], V1: aPoints[aIdx+1]}
   239  			continue
   240  		}
   241  
   242  		// Otherwise, we're doomed.
   243  		// Edge A does not contain edge B.
   244  		return false
   245  	}
   246  }
   247  
   248  // polygonCoversPolyline returns whether polygon a covers polyline b.
   249  func polygonCoversPolyline(a *s2.Polygon, b *s2.Polyline) bool {
   250  	// Check everything of polyline B is in the interior of polygon A.
   251  	for _, vertex := range *b {
   252  		if !polygonCoversPoint(a, vertex) {
   253  			return false
   254  		}
   255  	}
   256  	// Even if every point of polyline B is inside polygon A, they
   257  	// may form an edge which goes outside of polygon A and back in
   258  	// due to holes and concavities.
   259  	//
   260  	// As such, check if there are any intersections - if so,
   261  	// we do not consider it a covering.
   262  	//
   263  	// NOTE: this implementation has a limitation where a vertex of the line could
   264  	// be on the boundary and still technically be "covered" (using GEOS).
   265  	//
   266  	// However, PostGIS seems to consider this as non-covering so we can go
   267  	// with this for now.
   268  	// i.e. `
   269  	//   select st_covers(
   270  	//    'POLYGON((0.0 0.0, 1.0 0.0, 1.0 1.0, 0.0 1.0, 0.0 0.0))'::geography,
   271  	//    'LINESTRING(0.0 0.0, 1.0 1.0)'::geography);
   272  	// ` returns false, but should be true. This requires some more math to resolve.
   273  	return !polygonIntersectsPolylineEdge(a, b)
   274  }
   275  
   276  // polygonIntersectsPolylineEdge returns whether polygon a intersects with
   277  // polyline b by edge. It does not return true if the polyline is completely
   278  // within the polygon.
   279  func polygonIntersectsPolylineEdge(a *s2.Polygon, b *s2.Polyline) bool {
   280  	// Avoid using NumEdges / Edge of the Polygon type as it is not O(1).
   281  	for _, loop := range a.Loops() {
   282  		for loopEdgeIdx := 0; loopEdgeIdx < loop.NumEdges(); loopEdgeIdx++ {
   283  			loopEdge := loop.Edge(loopEdgeIdx)
   284  			crosser := s2.NewChainEdgeCrosser(loopEdge.V0, loopEdge.V1, (*b)[0])
   285  			for _, nextVertex := range (*b)[1:] {
   286  				if crosser.ChainCrossingSign(nextVertex) != s2.DoNotCross {
   287  					return true
   288  				}
   289  			}
   290  		}
   291  	}
   292  	return false
   293  }
   294  
   295  // polygonCoversPolygon returns whether polygon a intersects with polygon b.
   296  func polygonCoversPolygon(a *s2.Polygon, b *s2.Polygon) bool {
   297  	// We can rely on Contains here, as if the boundaries of A and B are on top
   298  	// of each other, it is still considered a containment as well as a covering.
   299  	return a.Contains(b)
   300  }