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