github.com/unigraph-dev/dgraph@v1.1.1-0.20200923154953-8b52b426f765/types/s2.go (about) 1 /* 2 * Copyright 2016-2018 Dgraph Labs, Inc. and Contributors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package types 18 19 import ( 20 "encoding/json" 21 22 "github.com/dgraph-io/dgraph/x" 23 "github.com/golang/geo/s2" 24 "github.com/pkg/errors" 25 geom "github.com/twpayne/go-geom" 26 "github.com/twpayne/go-geom/encoding/geojson" 27 ) 28 29 func edgesCrossPoints(l *s2.Loop, pts []s2.Point) bool { 30 n := len(pts) 31 for i := 0; i < n; i++ { 32 crosser := s2.NewChainEdgeCrosser(pts[i], pts[(i+1)%n], l.Vertex(0)) 33 for i := 1; i <= l.NumEdges(); i++ { // add vertex 0 twice as it is a closed loop 34 if crosser.EdgeOrVertexChainCrossing(l.Vertex(i)) { 35 return true 36 } 37 } 38 } 39 return false 40 } 41 42 func intersects(l *s2.Loop, loop *s2.Loop) bool { 43 // Quick check if the bounding boxes intersect 44 if !l.RectBound().Intersects(loop.RectBound()) { 45 return false 46 } 47 48 // Quick check to see if the first vertex is in the loop. 49 if l.ContainsPoint(loop.Vertex(0)) { 50 return true 51 } 52 53 // At this stage the one vertex is outside the loop. We check if any of the edges of the cell 54 // cross the loop. 55 if edgesCrossPoints(l, loop.Vertices()) { 56 return true 57 } 58 59 // At this stage we know that there is one point of the loop is outside us and the boundaries do 60 // not interesect. The only way for the loops to intersect is if it contains us. We test this 61 // by checking if an arbitrary vertex is inside the loop. 62 if loop.RectBound().Contains(l.RectBound()) { 63 return loop.ContainsPoint(l.Vertex(0)) 64 } 65 return false 66 } 67 68 func findVertex(a *s2.Loop, p s2.Point) int { 69 pts := a.Vertices() 70 for i := 0; i < len(pts); i++ { 71 if pts[i].ApproxEqual(p) { 72 return i 73 } 74 } 75 return -1 76 } 77 78 // Contains checks whether loop A contains loop B. 79 func Contains(a *s2.Loop, b *s2.Loop) bool { 80 // For this loop A to contains the given loop B, all of the following must 81 // be true: 82 // 83 // (1) There are no edge crossings between A and B except at vertices. 84 // 85 // (2) At every vertex that is shared between A and B, the local edge 86 // ordering implies that A contains B. 87 // 88 // (3) If there are no shared vertices, then A must contain a vertex of B 89 // and B must not contain a vertex of A. (An arbitrary vertex may be 90 // chosen in each case.) 91 // 92 // The second part of (3) is necessary to detect the case of two loops whose 93 // union is the entire sphere, i.e. two loops that contains each other's 94 // boundaries but not each other's interiors. 95 96 if !a.RectBound().Contains(b.RectBound()) { 97 return false 98 } 99 100 // Unless there are shared vertices, we need to check whether A contains a 101 // vertex of B. Since shared vertices are rare, it is more efficient to do 102 // this test up front as a quick rejection test. 103 if !a.ContainsPoint(b.Vertex(0)) && findVertex(a, b.Vertex(0)) < 0 { 104 return false 105 } 106 107 // Now check whether there are any edge crossings. 108 if edgesCrossPoints(a, b.Vertices()) { 109 return false 110 } 111 112 // At this point we know that the boundaries of A and B do not intersect, 113 // and that A contains a vertex of B. However we still need to check for 114 // the case mentioned above, where (A union B) is the entire sphere. 115 // Normally this check is very cheap due to the bounding box precondition. 116 if a.RectBound().Union(b.RectBound()).IsFull() { 117 if b.ContainsPoint(a.Vertex(0)) && findVertex(b, a.Vertex(0)) < 0 { 118 return false 119 } 120 } 121 return true 122 123 } 124 125 // Intersects returns true if the two loops intersect. 126 func Intersects(l1 *s2.Loop, l2 *s2.Loop) bool { 127 if l2.NumEdges() > l1.NumEdges() { 128 // Use the larger loop for edge indexing. 129 return intersects(l2, l1) 130 } 131 return intersects(l1, l2) 132 } 133 134 func convertToGeom(str string) (geom.T, error) { 135 // validate would ensure that we have a closed loop for all the polygons. We don't support open 136 // loop polygons. 137 closed := func(p *geom.Polygon) error { 138 coords := p.Coords() 139 if len(coords) == 0 { 140 return errors.Errorf("Got empty polygon.") 141 } 142 // Check that first ring is closed. 143 c := coords[0] 144 l := len(c) 145 if c[0][0] == c[l-1][0] && c[0][1] == c[l-1][1] { 146 return nil 147 } 148 return errors.Errorf("Last coord not same as first") 149 } 150 151 validate := func(g geom.T) (geom.T, error) { 152 switch v := g.(type) { 153 case *geom.MultiPolygon: 154 for i := 0; i < v.NumPolygons(); i++ { 155 if err := closed(v.Polygon(i)); err != nil { 156 return nil, err 157 } 158 } 159 case *geom.Polygon: 160 if err := closed(v); err != nil { 161 return nil, err 162 } 163 } 164 return g, nil 165 } 166 167 var g geojson.Geometry 168 if err := json.Unmarshal([]byte(str), &g); err == nil { 169 t, err := g.Decode() 170 if err != nil { 171 return nil, err 172 } 173 return validate(t) 174 } 175 176 s := x.WhiteSpace.Replace(str) 177 if len(s) < 5 { // [1,2] 178 return nil, errors.Errorf("Invalid coordinates") 179 } 180 var m json.RawMessage 181 var err error 182 183 if s[0:4] == "[[[[" { 184 g.Type = "MultiPolygon" 185 err = m.UnmarshalJSON([]byte(s)) 186 if err != nil { 187 return nil, errors.Wrapf(err, "Invalid coordinates") 188 } 189 g.Coordinates = &m 190 g1, err := g.Decode() 191 if err != nil { 192 return nil, errors.Wrapf(err, "Invalid coordinates") 193 } 194 return validate(g1) 195 } 196 197 if s[0:3] == "[[[" { 198 g.Type = "Polygon" 199 err = m.UnmarshalJSON([]byte(s)) 200 if err != nil { 201 return nil, errors.Wrapf(err, "Invalid coordinates") 202 } 203 g.Coordinates = &m 204 g1, err := g.Decode() 205 if err != nil { 206 return nil, errors.Wrapf(err, "Invalid coordinates") 207 } 208 return validate(g1) 209 } 210 211 if s[0] == '[' { 212 g.Type = "Point" 213 err = m.UnmarshalJSON([]byte(s)) 214 if err != nil { 215 return nil, errors.Wrapf(err, "Invalid coordinates") 216 } 217 g.Coordinates = &m 218 return g.Decode() 219 } 220 return nil, errors.Errorf("Invalid coordinates") 221 }