1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package s2
16
17 import (
18 "math"
19 "testing"
20
21 "github.com/golang/geo/r1"
22 "github.com/golang/geo/r3"
23 "github.com/golang/geo/s1"
24 )
25
26 func rectBoundForPoints(a, b Point) Rect {
27 bounder := NewRectBounder()
28 bounder.AddPoint(a)
29 bounder.AddPoint(b)
30 return bounder.RectBound()
31 }
32
33 func TestRectBounderMaxLatitudeSimple(t *testing.T) {
34 cubeLat := math.Asin(1 / math.Sqrt(3))
35 cubeLatRect := Rect{r1.IntervalFromPoint(-cubeLat).AddPoint(cubeLat),
36 s1.IntervalFromEndpoints(-math.Pi/4, math.Pi/4)}
37
38 tests := []struct {
39 a, b Point
40 want Rect
41 }{
42
43 {
44 a: Point{r3.Vector{1, 1, 1}},
45 b: Point{r3.Vector{1, -1, -1}},
46 want: cubeLatRect,
47 },
48 {
49 a: Point{r3.Vector{1, -1, 1}},
50 b: Point{r3.Vector{1, 1, -1}},
51 want: cubeLatRect,
52 },
53 }
54
55 for _, test := range tests {
56 if got := rectBoundForPoints(test.a, test.b); !rectsApproxEqual(got, test.want, rectErrorLat, rectErrorLng) {
57 t.Errorf("RectBounder for points (%v, %v) near max lat failed: got %v, want %v", test.a, test.b, got, test.want)
58 }
59 }
60 }
61
62 func TestRectBounderMaxLatitudeEdgeInterior(t *testing.T) {
63
64
65
66
67 tests := []struct {
68 got float64
69 want float64
70 }{
71
72 {
73 math.Pi/4 + 0.5*rectErrorLat,
74 rectBoundForPoints(Point{r3.Vector{1, 1, 1}}, Point{r3.Vector{1, -1, 1}}).Lat.Hi,
75 },
76
77 {
78 -math.Pi/4 - 0.5*rectErrorLat,
79 rectBoundForPoints(Point{r3.Vector{1, -1, -1}}, Point{r3.Vector{-1, -1, -1}}).Lat.Lo,
80 },
81
82 {
83 math.Pi/4 + 0.5*rectErrorLat,
84 rectBoundForPoints(Point{r3.Vector{1, -1, 1}}, Point{r3.Vector{1, 1, 1}}).Lat.Hi,
85 },
86
87 {
88 -math.Pi/4 - 0.5*rectErrorLat,
89 rectBoundForPoints(Point{r3.Vector{-1, 1, -1}}, Point{r3.Vector{-1, -1, -1}}).Lat.Lo,
90 },
91
92
93 {
94 math.Pi / 2,
95 rectBoundForPoints(Point{r3.Vector{.3, .4, 1}}, Point{r3.Vector{-.3, -.4, 1}}).Lat.Hi,
96 },
97 {
98 -math.Pi / 2,
99 rectBoundForPoints(Point{r3.Vector{.3, .4, -1}}, Point{r3.Vector{-.3, -.4, -1}}).Lat.Lo,
100 },
101 }
102
103 for _, test := range tests {
104 if !float64Eq(test.got, test.want) {
105 t.Errorf("RectBound for max lat on interior of edge failed; got %v want %v", test.got, test.want)
106 }
107 }
108 }
109
110 func TestRectBounderMaxLatitudeRandom(t *testing.T) {
111
112
113
114
115 for i := 0; i < 100; i++ {
116
117
118
119 u := randomPoint()
120 u.Z = dblEpsilon * 1e-6 * math.Pow(1e12, randomFloat64())
121
122 u = Point{u.Normalize()}
123 v := Point{PointFromCoords(0, 0, 1).PointCross(u).Normalize()}
124 w := Point{u.PointCross(v).Normalize()}
125
126
127
128 a := Point{u.Sub(v.Mul(randomFloat64())).Normalize()}
129 b := Point{u.Add(v.Mul(randomFloat64())).Normalize()}
130 abBound := rectBoundForPoints(a, b)
131 if !float64Near(latitude(u).Radians(), abBound.Lat.Hi, rectErrorLat) {
132 t.Errorf("bound for line AB not near enough to the latitude of point %v. got %v, want %v",
133 u, latitude(u).Radians(), abBound.Lat.Hi)
134 }
135
136
137
138 c := Point{w.Sub(v.Mul(randomFloat64())).Normalize()}
139 d := Point{w.Add(v.Mul(randomFloat64())).Normalize()}
140 cdBound := rectBoundForPoints(c, d)
141 if !float64Near(latitude(w).Radians(), cdBound.Lat.Hi, rectErrorLat) {
142 t.Errorf("bound for line CD not near enough to the lat of point %v. got %v, want %v",
143 v, latitude(w).Radians(), cdBound.Lat.Hi)
144 }
145 }
146 }
147
148 func TestRectBounderExpandForSubregions(t *testing.T) {
149
150 if !ExpandForSubregions(FullRect()).IsFull() {
151 t.Errorf("Subregion Bound of full rect should be full")
152 }
153 if !ExpandForSubregions(EmptyRect()).IsEmpty() {
154 t.Errorf("Subregion Bound of empty rect should be empty")
155 }
156
157 tests := []struct {
158 xLat, xLng, yLat, yLng float64
159 wantFull bool
160 }{
161
162
163 {3e-16, 0, 1e-14, math.Pi, true},
164 {9e-16, 0, 1e-14, math.Pi, false},
165 {1e-16, 7e-16, 1e-14, math.Pi, true},
166 {3e-16, 14e-16, 1e-14, math.Pi, false},
167 {1e-100, 14e-16, 1e-14, math.Pi, true},
168 {1e-100, 22e-16, 1e-14, math.Pi, false},
169
170
171
172
173 {-math.Pi / 2, -1e-15, math.Pi/2 - 7e-16, 0, true},
174 {-math.Pi / 2, -1e-15, math.Pi/2 - 30e-16, 0, false},
175 {-math.Pi/2 + 4e-16, 0, math.Pi/2 - 2e-16, 1e-7, true},
176 {-math.Pi/2 + 30e-16, 0, math.Pi / 2, 1e-7, false},
177 {-math.Pi/2 + 4e-16, 0, math.Pi/2 - 4e-16, math.Pi / 2, true},
178 {-math.Pi / 2, 0, math.Pi/2 - 30e-16, math.Pi / 2, false},
179
180
181
182
183
184
185 {-math.Pi / 2, 0, math.Pi/2 - 1e-8, math.Pi - 1e-7, true},
186 {-math.Pi / 2, 0, math.Pi/2 - 1e-7, math.Pi - 1e-7, false},
187 {-math.Pi/2 + 1e-12, -math.Pi + 1e-4, math.Pi / 2, 0, true},
188 {-math.Pi/2 + 1e-11, -math.Pi + 1e-4, math.Pi / 2, 0, true},
189 }
190
191 for _, tc := range tests {
192 in := RectFromLatLng(LatLng{s1.Angle(tc.xLat), s1.Angle(tc.xLng)})
193 in = in.AddPoint(LatLng{s1.Angle(tc.yLat), s1.Angle(tc.yLng)})
194 got := ExpandForSubregions(in)
195
196
197 if !got.Contains(in) {
198 t.Errorf("Subregion bound of (%f, %f, %f, %f) should contain original rect", tc.xLat, tc.xLng, tc.yLat, tc.yLng)
199 }
200 if in.Lat == validRectLatRange && in.Lat.ContainsInterval(got.Lat) {
201 t.Errorf("Subregion bound of (%f, %f, %f, %f) shouldn't be contained by original rect", tc.xLat, tc.xLng, tc.yLat, tc.yLng)
202 }
203
204
205
206 if got.IsFull() != tc.wantFull {
207 t.Errorf("Subregion Bound of (%f, %f, %f, %f).IsFull should be %t", tc.xLat, tc.xLng, tc.yLat, tc.yLng, tc.wantFull)
208 }
209 }
210
211 rectTests := []struct {
212 xLat, xLng, yLat, yLng float64
213 wantRect Rect
214 }{
215 {1.5, -math.Pi / 2, 1.5, math.Pi/2 - 2e-16, Rect{r1.Interval{1.5, 1.5}, s1.FullInterval()}},
216 {1.5, -math.Pi / 2, 1.5, math.Pi/2 - 7e-16, Rect{r1.Interval{1.5, 1.5}, s1.Interval{-math.Pi / 2, math.Pi/2 - 7e-16}}},
217
218 {-math.Pi/2 + 1e-15, 0, -math.Pi/2 + 1e-15, 0, Rect{r1.Interval{-math.Pi / 2, -math.Pi/2 + 1e-15}, s1.FullInterval()}},
219 {math.Pi/2 - 1e-15, 0, math.Pi/2 - 1e-15, 0, Rect{r1.Interval{math.Pi/2 - 1e-15, math.Pi / 2}, s1.FullInterval()}},
220 }
221
222 for _, tc := range rectTests {
223
224
225
226 in := RectFromLatLng(LatLng{s1.Angle(tc.xLat), s1.Angle(tc.xLng)})
227 in = in.AddPoint(LatLng{s1.Angle(tc.yLat), s1.Angle(tc.yLng)})
228 got := ExpandForSubregions(in)
229 if !rectsApproxEqual(got, tc.wantRect, rectErrorLat, rectErrorLng) {
230 t.Errorf("Subregion Bound of (%f, %f, %f, %f) = (%v) should be %v", tc.xLat, tc.xLng, tc.yLat, tc.yLng, got, tc.wantRect)
231 }
232 }
233 }
234
235
236
View as plain text