|
15 | 15 | package earth |
16 | 16 |
|
17 | 17 | import ( |
18 | | - "math" |
19 | 18 | "testing" |
20 | | - |
21 | | - "github.com/golang/geo/s1" |
22 | | - "github.com/golang/geo/s2" |
23 | | - "github.com/google/go-units/unit" |
24 | 19 | ) |
25 | 20 |
|
26 | | -func float64Eq(x, y float64) bool { |
27 | | - if x == y { |
28 | | - return true |
29 | | - } |
30 | | - if math.Abs(x) > math.Abs(y) { |
31 | | - return math.Abs(1-y/x) < 1e-14 |
32 | | - } |
33 | | - return math.Abs(1-x/y) < 1e-14 |
34 | | -} |
35 | | - |
36 | | -var degreesToMeters = []struct { |
37 | | - angle s1.Angle |
38 | | - length unit.Length |
39 | | -}{ |
40 | | - {-89.93201943346866 * s1.Degree, -1e7 * unit.Meter}, |
41 | | - {-30 * s1.Degree, -3335853.035324518 * unit.Meter}, |
42 | | - {0 * s1.Degree, 0 * unit.Meter}, |
43 | | - {30 * s1.Degree, 3335853.035324518 * unit.Meter}, |
44 | | - {89.93201943346866 * s1.Degree, 1e7 * unit.Meter}, |
45 | | - {90 * s1.Degree, 10007559.105973555 * unit.Meter}, |
46 | | - {179.86403886693734 * s1.Degree, 2e7 * unit.Meter}, |
47 | | - {180 * s1.Degree, 20015118.21194711 * unit.Meter}, |
48 | | - {359.72807773387467 * s1.Degree, 4e7 * unit.Meter}, |
49 | | - {360 * s1.Degree, 40030236.42389422 * unit.Meter}, |
50 | | - {899.3201943346867 * s1.Degree, 1e8 * unit.Meter}, |
51 | | -} |
52 | | - |
53 | | -func TestAngleFromLength(t *testing.T) { |
54 | | - for _, test := range degreesToMeters { |
55 | | - if got, want := AngleFromLength(test.length), test.angle; !float64Eq(got.Radians(), want.Radians()) { |
56 | | - t.Errorf("AngleFromLength(%v) = %v, want %v", test.length, got, want) |
57 | | - } |
58 | | - } |
59 | | -} |
60 | | - |
61 | | -func TestLengthFromAngle(t *testing.T) { |
62 | | - for _, test := range degreesToMeters { |
63 | | - if got, want := LengthFromAngle(test.angle), test.length; !float64Eq(got.Meters(), want.Meters()) { |
64 | | - t.Errorf("LengthFromAngle(%v) = %v, want %v", test.angle, got, want) |
65 | | - } |
66 | | - } |
67 | | -} |
68 | | - |
69 | | -func TestLengthFromPoints(t *testing.T) { |
70 | | - tests := []struct { |
71 | | - x1, y1, z1 float64 |
72 | | - x2, y2, z2 float64 |
73 | | - length unit.Length |
74 | | - }{ |
75 | | - {1, 0, 0, 1, 0, 0, 0 * unit.Meter}, |
76 | | - {1, 0, 0, 0, 1, 0, 10007559.105973555 * unit.Meter}, |
77 | | - {1, 0, 0, 0, 1, 1, 10007559.105973555 * unit.Meter}, |
78 | | - {1, 0, 0, -1, 0, 0, 20015118.21194711 * unit.Meter}, |
79 | | - {1, 2, 3, 2, 3, -1, 7680820.247060414 * unit.Meter}, |
80 | | - } |
81 | | - for _, test := range tests { |
82 | | - p1 := s2.PointFromCoords(test.x1, test.y1, test.z1) |
83 | | - p2 := s2.PointFromCoords(test.x2, test.y2, test.z2) |
84 | | - if got, want := LengthFromPoints(p1, p2), test.length; !float64Eq(got.Meters(), want.Meters()) { |
85 | | - t.Errorf("LengthFromPoints(%v, %v) = %v, want %v", p1, p2, got, want) |
86 | | - } |
87 | | - } |
88 | | -} |
89 | | - |
90 | | -func TestLengthFromLatLngs(t *testing.T) { |
91 | | - tests := []struct { |
92 | | - lat1, lng1, lat2, lng2 float64 |
93 | | - length unit.Length |
94 | | - }{ |
95 | | - {90, 0, 90, 0, 0 * unit.Meter}, |
96 | | - {-37, 25, -66, -155, 8562022.790666264 * unit.Meter}, |
97 | | - {0, 165, 0, -80, 12787436.635410652 * unit.Meter}, |
98 | | - {47, -127, -47, 53, 20015118.077688109 * unit.Meter}, |
99 | | - {51.961951, -180.227156, 51.782383, 181.126878, 95.0783566198074 * unit.Kilometer}, |
100 | | - } |
101 | | - for _, test := range tests { |
102 | | - ll1 := s2.LatLngFromDegrees(test.lat1, test.lng1) |
103 | | - ll2 := s2.LatLngFromDegrees(test.lat2, test.lng2) |
104 | | - if got, want := LengthFromLatLngs(ll1, ll2), test.length; !float64Eq(got.Meters(), want.Meters()) { |
105 | | - t.Errorf("LengthFromLatLngs(%v, %v) = %v, want %v", ll1, ll2, got, want) |
106 | | - } |
107 | | - } |
108 | | -} |
109 | | - |
110 | | -var ( |
111 | | - earthArea = unit.Area(Radius.Meters()*Radius.Meters()) * math.Pi * 4 |
112 | | - steradiansToArea = []struct { |
113 | | - steradians float64 |
114 | | - area unit.Area |
115 | | - }{ |
116 | | - {1, earthArea / 4 / math.Pi}, |
117 | | - {4 * math.Pi, earthArea}, |
118 | | - {s2.PolygonFromLoops([]*s2.Loop{s2.FullLoop()}).Area(), earthArea}, |
119 | | - {s2.PolygonFromLoops([]*s2.Loop{s2.LoopFromPoints([]s2.Point{ |
120 | | - s2.PointFromLatLng(s2.LatLngFromDegrees(-90, 0)), |
121 | | - s2.PointFromLatLng(s2.LatLngFromDegrees(0, 0)), |
122 | | - s2.PointFromLatLng(s2.LatLngFromDegrees(90, 0)), |
123 | | - s2.PointFromLatLng(s2.LatLngFromDegrees(0, -90)), |
124 | | - })}).Area(), earthArea / 4}, |
125 | | - {s2.CellFromCellID(s2.CellIDFromFace(2)).ExactArea(), earthArea / 6}, |
126 | | - {s2.AvgAreaMetric.Value(10), 81.07281893380302 * unit.SquareKilometer}, // average area of level 10 cells |
127 | | - {s2.AvgAreaMetric.Value(20), 77.31706517582228 * unit.SquareMeter}, // average area of level 20 cells |
128 | | - {s2.AvgAreaMetric.Value(30), 73.73529927808979 * unit.SquareMillimeter}, // average area of level 30 cells |
129 | | - {s2.PolygonFromLoops([]*s2.Loop{s2.EmptyLoop()}).Area(), 0 * unit.SquareMeter}, |
130 | | - } |
131 | | -) |
132 | | - |
133 | | -func TestAreaFromSteradians(t *testing.T) { |
134 | | - for _, test := range steradiansToArea { |
135 | | - if got, want := AreaFromSteradians(test.steradians), test.area; !float64Eq(got.SquareMeters(), want.SquareMeters()) { |
136 | | - t.Errorf("AreaFromSteradians(%v) = %v, want %v", test.steradians, got, want) |
137 | | - } |
138 | | - } |
139 | | -} |
140 | | - |
141 | | -func TestSteradiansFromArea(t *testing.T) { |
142 | | - for _, test := range steradiansToArea { |
143 | | - if got, want := SteradiansFromArea(test.area), test.steradians; !float64Eq(got, want) { |
144 | | - t.Errorf("SteradiansFromArea(%v) = %v, want %v", test.area, got, want) |
145 | | - } |
| 21 | +func TestRadius(t *testing.T) { |
| 22 | + // Verify the Earth radius constant is approximately correct. |
| 23 | + if got, want := Radius.Kilometers(), 6371.01; got != want { |
| 24 | + t.Errorf("Radius.Kilometers() = %v, want %v", got, want) |
146 | 25 | } |
147 | 26 | } |
148 | 27 |
|
149 | | -func TestInitialBearingFromLatLngs(t *testing.T) { |
150 | | - for _, tc := range []struct { |
151 | | - name string |
152 | | - a, b s2.LatLng |
153 | | - want s1.Angle |
154 | | - }{ |
155 | | - {"Westward on equator", s2.LatLngFromDegrees(0, 50), |
156 | | - s2.LatLngFromDegrees(0, 100), s1.Degree * 90}, |
157 | | - {"Eastward on equator", s2.LatLngFromDegrees(0, 50), |
158 | | - s2.LatLngFromDegrees(0, 0), s1.Degree * -90}, |
159 | | - {"Northward on meridian", s2.LatLngFromDegrees(16, 28), |
160 | | - s2.LatLngFromDegrees(81, 28), s1.Degree * 0}, |
161 | | - {"Southward on meridian", s2.LatLngFromDegrees(24, 64), |
162 | | - s2.LatLngFromDegrees(-27, 64), s1.Degree * 180}, |
163 | | - {"Towards north pole", s2.LatLngFromDegrees(12, 76), |
164 | | - s2.LatLngFromDegrees(90, 50), s1.Degree * 0}, |
165 | | - {"Towards south pole", s2.LatLngFromDegrees(-35, 105), |
166 | | - s2.LatLngFromDegrees(-90, -120), s1.Degree * 180}, |
167 | | - {"Spain to Japan", s2.LatLngFromDegrees(40.4379332, -3.749576), |
168 | | - s2.LatLngFromDegrees(35.6733227, 139.6403486), s1.Degree * 29.2}, |
169 | | - {"Japan to Spain", s2.LatLngFromDegrees(35.6733227, 139.6403486), |
170 | | - s2.LatLngFromDegrees(40.4379332, -3.749576), s1.Degree * -27.2}, |
171 | | - } { |
172 | | - t.Run(tc.name, func(t *testing.T) { |
173 | | - got := InitialBearingFromLatLngs(tc.a, tc.b) |
174 | | - if diff := (got - tc.want).Abs(); diff > 0.01 { |
175 | | - t.Errorf("InitialBearingFromLatLngs(%s, %s): got %s, want %s, diff %s", tc.a, tc.b, got, tc.want, diff) |
176 | | - } |
177 | | - }) |
| 28 | +func TestLowestAltitude(t *testing.T) { |
| 29 | + // Mariana Trench depth |
| 30 | + if got, want := LowestAltitude.Meters(), -10898.0; got != want { |
| 31 | + t.Errorf("LowestAltitude.Meters() = %v, want %v", got, want) |
178 | 32 | } |
179 | 33 | } |
180 | 34 |
|
181 | | -func TestInitialBearingFromLatLngsUndefinedResultDoesNotCrash(t *testing.T) { |
182 | | - // InitialBearingFromLatLngs says if a == b, a == -b, or a is one of Earth's |
183 | | - // poles, the return value is undefined. Make sure it returns a real value |
184 | | - // (but don't assert what it is) rather than panicking or NaN. |
185 | | - // Bearing from a pole is undefined because 0° is north, but the observer |
186 | | - // can't face north from the north pole, so the calculation depends on the |
187 | | - // latitude value at the pole, even though 90°N 123°E and 90°N 45°W represent |
188 | | - // the same point. Bearing is undefined when a == b because the observer can |
189 | | - // point any direction and still be present. Bearing is undefined when |
190 | | - // a == -b (two antipodal points) because there are two possible paths. |
191 | | - for _, tc := range []struct { |
192 | | - name string |
193 | | - a, b s2.LatLng |
194 | | - }{ |
195 | | - {"North pole prime meridian to Null Island", s2.LatLngFromDegrees(90, 0), s2.LatLngFromDegrees(0, 0)}, |
196 | | - {"North pole facing east to Guatemala", s2.LatLngFromDegrees(90, 90), s2.LatLngFromDegrees(15, -90)}, |
197 | | - {"South pole facing west to McMurdo", s2.LatLngFromDegrees(-90, -90), s2.LatLngFromDegrees(-78, 166)}, |
198 | | - {"South pole anti-prime meridian to Null Island", s2.LatLngFromDegrees(-90, -180), s2.LatLngFromDegrees(0, 0)}, |
199 | | - {"Jakarta and antipode", s2.LatLngFromDegrees(-6.109, 106.668), s2.LatLngFromDegrees(6.109, -180+106.668)}, |
200 | | - {"Alert and antipode", s2.LatLngFromDegrees(82.499, -62.350), s2.LatLngFromDegrees(-82.499, 180-62.350)}, |
201 | | - } { |
202 | | - t.Run(tc.name, func(t *testing.T) { |
203 | | - got := InitialBearingFromLatLngs(tc.a, tc.b) |
204 | | - if math.IsNaN(got.Radians()) || math.IsInf(got.Radians(), 0) { |
205 | | - t.Errorf("InitialBearingFromLatLngs(%s, %s): got %s, want a real value", tc.a, tc.b, got) |
206 | | - } |
207 | | - }) |
| 35 | +func TestHighestAltitude(t *testing.T) { |
| 36 | + // Mount Everest height |
| 37 | + if got, want := HighestAltitude.Meters(), 8848.0; got != want { |
| 38 | + t.Errorf("HighestAltitude.Meters() = %v, want %v", got, want) |
208 | 39 | } |
209 | 40 | } |
0 commit comments