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 }