github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/geo/geogfn/intersects.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 // Intersects returns whether geography A intersects geography B. 21 // This calculation is done on the sphere. 22 // Precision of intersect measurements is up to 1cm. 23 func Intersects(a *geo.Geography, b *geo.Geography) (bool, error) { 24 if a.SRID() != b.SRID() { 25 return false, geo.NewMismatchingSRIDsError(a, b) 26 } 27 28 aRegions, err := a.AsS2(geo.EmptyBehaviorOmit) 29 if err != nil { 30 return false, err 31 } 32 bRegions, err := b.AsS2(geo.EmptyBehaviorOmit) 33 if err != nil { 34 return false, err 35 } 36 // If any of aRegions intersects any of bRegions, return true. 37 for _, aRegion := range aRegions { 38 for _, bRegion := range bRegions { 39 intersects, err := singleRegionIntersects(aRegion, bRegion) 40 if err != nil { 41 return false, err 42 } 43 if intersects { 44 return true, nil 45 } 46 } 47 } 48 return false, nil 49 } 50 51 // singleRegionIntersects returns true if aRegion intersects bRegion. 52 func singleRegionIntersects(aRegion s2.Region, bRegion s2.Region) (bool, error) { 53 switch aRegion := aRegion.(type) { 54 case s2.Point: 55 switch bRegion := bRegion.(type) { 56 case s2.Point: 57 return aRegion.IntersectsCell(s2.CellFromPoint(bRegion)), nil 58 case *s2.Polyline: 59 return bRegion.IntersectsCell(s2.CellFromPoint(aRegion)), nil 60 case *s2.Polygon: 61 return bRegion.IntersectsCell(s2.CellFromPoint(aRegion)), nil 62 default: 63 return false, fmt.Errorf("unknown s2 type of b: %#v", bRegion) 64 } 65 case *s2.Polyline: 66 switch bRegion := bRegion.(type) { 67 case s2.Point: 68 return aRegion.IntersectsCell(s2.CellFromPoint(bRegion)), nil 69 case *s2.Polyline: 70 return polylineIntersectsPolyline(aRegion, bRegion), nil 71 case *s2.Polygon: 72 return polygonIntersectsPolyline(bRegion, aRegion), nil 73 default: 74 return false, fmt.Errorf("unknown s2 type of b: %#v", bRegion) 75 } 76 case *s2.Polygon: 77 switch bRegion := bRegion.(type) { 78 case s2.Point: 79 return aRegion.IntersectsCell(s2.CellFromPoint(bRegion)), nil 80 case *s2.Polyline: 81 return polygonIntersectsPolyline(aRegion, bRegion), nil 82 case *s2.Polygon: 83 return aRegion.Intersects(bRegion), nil 84 default: 85 return false, fmt.Errorf("unknown s2 type of b: %#v", bRegion) 86 } 87 } 88 return false, fmt.Errorf("unknown s2 type of a: %#v", aRegion) 89 } 90 91 // polylineIntersectsPolyline returns whether polyline a intersects with 92 // polyline b. 93 func polylineIntersectsPolyline(a *s2.Polyline, b *s2.Polyline) bool { 94 for aEdgeIdx := 0; aEdgeIdx < a.NumEdges(); aEdgeIdx++ { 95 edge := a.Edge(aEdgeIdx) 96 crosser := s2.NewChainEdgeCrosser(edge.V0, edge.V1, (*b)[0]) 97 for _, nextVertex := range (*b)[1:] { 98 crossing := crosser.ChainCrossingSign(nextVertex) 99 if crossing != s2.DoNotCross { 100 return true 101 } 102 } 103 } 104 return false 105 } 106 107 // polygonIntersectsPolyline returns whether polygon a intersects with 108 // polyline b. 109 func polygonIntersectsPolyline(a *s2.Polygon, b *s2.Polyline) bool { 110 // Check if the polygon contains any vertex of the line b. 111 for _, vertex := range *b { 112 if a.IntersectsCell(s2.CellFromPoint(vertex)) { 113 return true 114 } 115 } 116 // Here the polygon does not contain any vertex of the polyline. 117 // The polyline can intersect the polygon if a line goes through the polygon 118 // with both vertexes that are not in the interior of the polygon. 119 // This technique works for holes touching, or holes touching the exterior 120 // as the point in which the holes touch is considered an intersection. 121 for _, loop := range a.Loops() { 122 for loopEdgeIdx := 0; loopEdgeIdx < loop.NumEdges(); loopEdgeIdx++ { 123 loopEdge := loop.Edge(loopEdgeIdx) 124 crosser := s2.NewChainEdgeCrosser(loopEdge.V0, loopEdge.V1, (*b)[0]) 125 for _, nextVertex := range (*b)[1:] { 126 crossing := crosser.ChainCrossingSign(nextVertex) 127 if crossing != s2.DoNotCross { 128 return true 129 } 130 } 131 } 132 } 133 return false 134 }