1#![allow(non_snake_case)]
2#![allow(clippy::excessive_precision)]
3
4use crate::geodesic_capability as caps;
5use crate::geodesic_line;
6use crate::geomath;
7use std::sync;
8
9use std::f64::consts::{FRAC_1_SQRT_2, PI};
10
11pub const WGS84_A: f64 = 6378137.0;
12pub const WGS84_F: f64 = 1.0 / ((298257223563f64) / 1000000000.0);
16
17#[derive(Copy, Clone, PartialEq, PartialOrd, Debug)]
18pub struct Geodesic {
19 pub a: f64,
20 pub f: f64,
21 pub _f1: f64,
22 pub _e2: f64,
23 pub _ep2: f64,
24 _n: f64,
25 pub _b: f64,
26 pub _c2: f64,
27 _etol2: f64,
28 _A3x: [f64; GEODESIC_ORDER],
29 _C3x: [f64; _nC3x_],
30 _C4x: [f64; _nC4x_],
31
32 _nC3x_: usize,
33 _nC4x_: usize,
34 maxit1_: u64,
35 maxit2_: u64,
36
37 pub tiny_: f64,
38 tol0_: f64,
39 tol1_: f64,
40 _tol2_: f64,
41 tolb_: f64,
42 xthresh_: f64,
43}
44
45static WGS84_GEOD: sync::OnceLock<Geodesic> = sync::OnceLock::new();
46
47impl Geodesic {
48 pub fn wgs84() -> Self {
49 *WGS84_GEOD.get_or_init(|| Geodesic::new(WGS84_A, WGS84_F))
50 }
51
52 pub fn equatorial_radius(&self) -> f64 {
53 self.a
54 }
55
56 pub fn flattening(&self) -> f64 {
57 self.f
58 }
59}
60
61const COEFF_A3: [f64; 18] = [
62 -3.0, 128.0, -2.0, -3.0, 64.0, -1.0, -3.0, -1.0, 16.0, 3.0, -1.0, -2.0, 8.0, 1.0, -1.0, 2.0,
63 1.0, 1.0,
64];
65
66const COEFF_C3: [f64; 45] = [
67 3.0, 128.0, 2.0, 5.0, 128.0, -1.0, 3.0, 3.0, 64.0, -1.0, 0.0, 1.0, 8.0, -1.0, 1.0, 4.0, 5.0,
68 256.0, 1.0, 3.0, 128.0, -3.0, -2.0, 3.0, 64.0, 1.0, -3.0, 2.0, 32.0, 7.0, 512.0, -10.0, 9.0,
69 384.0, 5.0, -9.0, 5.0, 192.0, 7.0, 512.0, -14.0, 7.0, 512.0, 21.0, 2560.0,
70];
71
72const COEFF_C4: [f64; 77] = [
73 97.0, 15015.0, 1088.0, 156.0, 45045.0, -224.0, -4784.0, 1573.0, 45045.0, -10656.0, 14144.0,
74 -4576.0, -858.0, 45045.0, 64.0, 624.0, -4576.0, 6864.0, -3003.0, 15015.0, 100.0, 208.0, 572.0,
75 3432.0, -12012.0, 30030.0, 45045.0, 1.0, 9009.0, -2944.0, 468.0, 135135.0, 5792.0, 1040.0,
76 -1287.0, 135135.0, 5952.0, -11648.0, 9152.0, -2574.0, 135135.0, -64.0, -624.0, 4576.0, -6864.0,
77 3003.0, 135135.0, 8.0, 10725.0, 1856.0, -936.0, 225225.0, -8448.0, 4992.0, -1144.0, 225225.0,
78 -1440.0, 4160.0, -4576.0, 1716.0, 225225.0, -136.0, 63063.0, 1024.0, -208.0, 105105.0, 3584.0,
79 -3328.0, 1144.0, 315315.0, -128.0, 135135.0, -2560.0, 832.0, 405405.0, 128.0, 99099.0,
80];
81
82pub const GEODESIC_ORDER: usize = 6;
83pub(crate) const CARR_SIZE: usize = GEODESIC_ORDER + 1;
84
85#[allow(non_upper_case_globals)]
86const _nC3x_: usize = 15;
87#[allow(non_upper_case_globals)]
88const _nC4x_: usize = 21;
89
90impl Geodesic {
91 pub fn new(a: f64, f: f64) -> Self {
92 let maxit1_ = 20;
93 let maxit2_ = maxit1_ + geomath::DIGITS + 10;
94 let tiny_ = f64::MIN_POSITIVE.sqrt();
95 let tol0_ = f64::EPSILON;
96 let tol1_ = 200.0 * tol0_;
97 let _tol2_ = tol0_.sqrt();
98 let tolb_ = tol0_ * _tol2_;
99 let xthresh_ = 1000.0 * _tol2_;
100
101 let _f1 = 1.0 - f;
102 let _e2 = f * (2.0 - f);
103 let _ep2 = _e2 / geomath::sq(_f1);
104 let _n = f / (2.0 - f);
105 let _b = a * _f1;
106 let _c2 = (geomath::sq(a)
107 + geomath::sq(_b)
108 * (if _e2 == 0.0 {
109 1.0
110 } else {
111 geomath::eatanhe(1.0, (if f < 0.0 { -1.0 } else { 1.0 }) * _e2.abs().sqrt())
112 / _e2
113 }))
114 / 2.0;
115 let _etol2 = 0.1 * _tol2_ / (f.abs().max(0.001) * (1.0 - f / 2.0).min(1.0) / 2.0).sqrt();
116
117 let mut _A3x: [f64; GEODESIC_ORDER] = [0.0; GEODESIC_ORDER];
118 let mut _C3x: [f64; _nC3x_] = [0.0; _nC3x_];
119 let mut _C4x: [f64; _nC4x_] = [0.0; _nC4x_];
120
121 let mut o: usize = 0;
123 for (k, j) in (0..GEODESIC_ORDER).rev().enumerate() {
124 let m = j.min(GEODESIC_ORDER - j - 1);
125 _A3x[k] = geomath::polyval(m, &COEFF_A3[o..], _n) / COEFF_A3[o + m + 1];
126 o += m + 2;
127 }
128
129 let mut o = 0;
131 let mut k = 0;
132 for l in 1..GEODESIC_ORDER {
133 for j in (l..GEODESIC_ORDER).rev() {
134 let m = j.min(GEODESIC_ORDER - j - 1);
135 _C3x[k] = geomath::polyval(m, &COEFF_C3[o..], _n) / COEFF_C3[o + m + 1];
136 k += 1;
137 o += m + 2;
138 }
139 }
140
141 let mut o = 0;
143 let mut k = 0;
144 for l in 0..GEODESIC_ORDER {
145 for j in (l..GEODESIC_ORDER).rev() {
146 let m = GEODESIC_ORDER - j - 1;
147 _C4x[k] = geomath::polyval(m, &COEFF_C4[o..], _n) / COEFF_C4[o + m + 1];
148 k += 1;
149 o += m + 2;
150 }
151 }
152
153 Geodesic {
154 a,
155 f,
156 _f1,
157 _e2,
158 _ep2,
159 _n,
160 _b,
161 _c2,
162 _etol2,
163 _A3x,
164 _C3x,
165 _C4x,
166
167 _nC3x_,
168 _nC4x_,
169 maxit1_,
170 maxit2_,
171
172 tiny_,
173 tol0_,
174 tol1_,
175 _tol2_,
176 tolb_,
177 xthresh_,
178 }
179 }
180
181 pub fn _A3f(&self, eps: f64) -> f64 {
182 geomath::polyval(GEODESIC_ORDER - 1, &self._A3x, eps)
183 }
184
185 pub fn _C3f(&self, eps: f64, c: &mut [f64; GEODESIC_ORDER]) {
186 let mut mult = 1.0;
187 let mut o = 0;
188 #[allow(clippy::needless_range_loop)]
191 for l in 1..GEODESIC_ORDER {
192 let m = GEODESIC_ORDER - l - 1;
193 mult *= eps;
194 c[l] = mult * geomath::polyval(m, &self._C3x[o..], eps);
195 o += m + 1;
196 }
197 }
198
199 pub fn _C4f(&self, eps: f64, c: &mut [f64; GEODESIC_ORDER]) {
200 let mut mult = 1.0;
201 let mut o = 0;
202 #[allow(clippy::needless_range_loop)]
205 for l in 0..GEODESIC_ORDER {
206 let m = GEODESIC_ORDER - l - 1;
207 c[l] = mult * geomath::polyval(m, &self._C4x[o..], eps);
208 o += m + 1;
209 mult *= eps;
210 }
211 }
212
213 #[allow(clippy::too_many_arguments)]
214 pub fn _Lengths(
215 &self,
216 eps: f64,
217 sig12: f64,
218 ssig1: f64,
219 csig1: f64,
220 dn1: f64,
221 ssig2: f64,
222 csig2: f64,
223 dn2: f64,
224 cbet1: f64,
225 cbet2: f64,
226 outmask: u64,
227 C1a: &mut [f64; CARR_SIZE],
228 C2a: &mut [f64; CARR_SIZE],
229 ) -> (f64, f64, f64, f64, f64) {
230 let outmask = outmask & caps::OUT_MASK;
231 let mut s12b = f64::NAN;
232 let mut m12b = f64::NAN;
233 let mut m0 = f64::NAN;
234 let mut M12 = f64::NAN;
235 let mut M21 = f64::NAN;
236
237 let mut A1 = 0.0;
238 let mut A2 = 0.0;
239 let mut m0x = 0.0;
240 let mut J12 = 0.0;
241
242 if outmask & (caps::DISTANCE | caps::REDUCEDLENGTH | caps::GEODESICSCALE) != 0 {
243 A1 = geomath::_A1m1f(eps);
244 geomath::_C1f(eps, C1a);
245 if outmask & (caps::REDUCEDLENGTH | caps::GEODESICSCALE) != 0 {
246 A2 = geomath::_A2m1f(eps);
247 geomath::_C2f(eps, C2a);
248 m0x = A1 - A2;
249 A2 += 1.0;
250 }
251 A1 += 1.0;
252 }
253 if outmask & caps::DISTANCE != 0 {
254 let B1 = geomath::sin_cos_series(true, ssig2, csig2, C1a)
255 - geomath::sin_cos_series(true, ssig1, csig1, C1a);
256 s12b = A1 * (sig12 + B1);
257 if outmask & (caps::REDUCEDLENGTH | caps::GEODESICSCALE) != 0 {
258 let B2 = geomath::sin_cos_series(true, ssig2, csig2, C2a)
259 - geomath::sin_cos_series(true, ssig1, csig1, C2a);
260 J12 = m0x * sig12 + (A1 * B1 - A2 * B2);
261 }
262 } else if outmask & (caps::REDUCEDLENGTH | caps::GEODESICSCALE) != 0 {
263 for l in 1..=GEODESIC_ORDER {
264 C2a[l] = A1 * C1a[l] - A2 * C2a[l];
265 }
266 J12 = m0x * sig12
267 + (geomath::sin_cos_series(true, ssig2, csig2, C2a)
268 - geomath::sin_cos_series(true, ssig1, csig1, C2a));
269 }
270 if outmask & caps::REDUCEDLENGTH != 0 {
271 m0 = m0x;
272 m12b = dn2 * (csig1 * ssig2) - dn1 * (ssig1 * csig2) - csig1 * csig2 * J12;
274 }
275 if outmask & caps::GEODESICSCALE != 0 {
276 let csig12 = csig1 * csig2 + ssig1 * ssig2;
277 let t = self._ep2 * (cbet1 - cbet2) * (cbet1 + cbet2) / (dn1 + dn2);
278 M12 = csig12 + (t * ssig2 - csig2 * J12) * ssig1 / dn1;
279 M21 = csig12 - (t * ssig1 - csig1 * J12) * ssig2 / dn2;
280 }
281 (s12b, m12b, m0, M12, M21)
282 }
283
284 #[allow(clippy::too_many_arguments)]
285 pub fn _InverseStart(
286 &self,
287 sbet1: f64,
288 cbet1: f64,
289 dn1: f64,
290 sbet2: f64,
291 cbet2: f64,
292 dn2: f64,
293 lam12: f64,
294 slam12: f64,
295 clam12: f64,
296 C1a: &mut [f64; CARR_SIZE],
297 C2a: &mut [f64; CARR_SIZE],
298 ) -> (f64, f64, f64, f64, f64, f64) {
299 let mut sig12 = -1.0;
300 let mut salp2 = f64::NAN;
301 let mut calp2 = f64::NAN;
302 let mut dnm = f64::NAN;
303
304 let mut somg12: f64;
305 let mut comg12: f64;
306
307 let sbet12 = sbet2 * cbet1 - cbet2 * sbet1;
308 let cbet12 = cbet2 * cbet1 + sbet2 * sbet1;
309
310 let mut sbet12a = sbet2 * cbet1;
311 sbet12a += cbet2 * sbet1;
312
313 let shortline = cbet12 >= 0.0 && sbet12 < 0.5 && cbet2 * lam12 < 0.5;
314 if shortline {
315 let mut sbetm2 = geomath::sq(sbet1 + sbet2);
316 sbetm2 /= sbetm2 + geomath::sq(cbet1 + cbet2);
317 dnm = (1.0 + self._ep2 * sbetm2).sqrt();
318 let omg12 = lam12 / (self._f1 * dnm);
319 somg12 = omg12.sin();
320 comg12 = omg12.cos();
321 } else {
322 somg12 = slam12;
323 comg12 = clam12;
324 }
325
326 let mut salp1 = cbet2 * somg12;
327
328 let mut calp1 = if comg12 >= 0.0 {
329 sbet12 + cbet2 * sbet1 * geomath::sq(somg12) / (1.0 + comg12)
330 } else {
331 sbet12a - cbet2 * sbet1 * geomath::sq(somg12) / (1.0 - comg12)
332 };
333
334 let ssig12 = salp1.hypot(calp1);
335 let csig12 = sbet1 * sbet2 + cbet1 * cbet2 * comg12;
336
337 if shortline && ssig12 < self._etol2 {
338 salp2 = cbet1 * somg12;
339 calp2 = sbet12
340 - cbet1
341 * sbet2
342 * (if comg12 >= 0.0 {
343 geomath::sq(somg12) / (1.0 + comg12)
344 } else {
345 1.0 - comg12
346 });
347 geomath::norm(&mut salp2, &mut calp2);
348 sig12 = ssig12.atan2(csig12);
349 } else if self._n.abs() > 0.1
350 || csig12 >= 0.0
351 || ssig12 >= 6.0 * self._n.abs() * PI * geomath::sq(cbet1)
352 {
353 } else {
354 let x: f64;
355 let y: f64;
356 let betscale: f64;
357 let lamscale: f64;
358 let lam12x = (-slam12).atan2(-clam12);
359 if self.f >= 0.0 {
360 let k2 = geomath::sq(sbet1) * self._ep2;
361 let eps = k2 / (2.0 * (1.0 + (1.0 + k2).sqrt()) + k2);
362 lamscale = self.f * cbet1 * self._A3f(eps) * PI;
363 betscale = lamscale * cbet1;
364 x = lam12x / lamscale;
365 y = sbet12a / betscale;
366 } else {
367 let cbet12a = cbet2 * cbet1 - sbet2 * sbet1;
368 let bet12a = sbet12a.atan2(cbet12a);
369 let (_, m12b, m0, _, _) = self._Lengths(
370 self._n,
371 PI + bet12a,
372 sbet1,
373 -cbet1,
374 dn1,
375 sbet2,
376 cbet2,
377 dn2,
378 cbet1,
379 cbet2,
380 caps::REDUCEDLENGTH,
381 C1a,
382 C2a,
383 );
384 x = -1.0 + m12b / (cbet1 * cbet2 * m0 * PI);
385 betscale = if x < -0.01 {
386 sbet12a / x
387 } else {
388 -self.f * geomath::sq(cbet1) * PI
389 };
390 lamscale = betscale / cbet1;
391 y = lam12x / lamscale;
392 }
393 if y > -self.tol1_ && x > -1.0 - self.xthresh_ {
394 if self.f >= 0.0 {
395 salp1 = (-x).min(1.0);
396 calp1 = -(1.0 - geomath::sq(salp1)).sqrt()
397 } else {
398 calp1 = x.max(if x > -self.tol1_ { 0.0 } else { -1.0 });
399 salp1 = (1.0 - geomath::sq(calp1)).sqrt();
400 }
401 } else {
402 let k = geomath::astroid(x, y);
403 let omg12a = lamscale
404 * if self.f >= 0.0 {
405 -x * k / (1.0 + k)
406 } else {
407 -y * (1.0 + k) / k
408 };
409 somg12 = omg12a.sin();
410 comg12 = -(omg12a.cos());
411 salp1 = cbet2 * somg12;
412 calp1 = sbet12a - cbet2 * sbet1 * geomath::sq(somg12) / (1.0 - comg12);
413 }
414 }
415
416 if salp1 > 0.0 || salp1.is_nan() {
417 geomath::norm(&mut salp1, &mut calp1);
418 } else {
419 salp1 = 1.0;
420 calp1 = 0.0;
421 };
422 (sig12, salp1, calp1, salp2, calp2, dnm)
423 }
424
425 #[allow(clippy::too_many_arguments)]
426 pub fn _Lambda12(
427 &self,
428 sbet1: f64,
429 cbet1: f64,
430 dn1: f64,
431 sbet2: f64,
432 cbet2: f64,
433 dn2: f64,
434 salp1: f64,
435 mut calp1: f64,
436 slam120: f64,
437 clam120: f64,
438 diffp: bool,
439 C1a: &mut [f64; CARR_SIZE],
440 C2a: &mut [f64; CARR_SIZE],
441 C3a: &mut [f64; GEODESIC_ORDER],
442 ) -> (f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64) {
443 if sbet1 == 0.0 && calp1 == 0.0 {
444 calp1 = -self.tiny_;
445 }
446 let salp0 = salp1 * cbet1;
447 let calp0 = calp1.hypot(salp1 * sbet1);
448
449 let mut ssig1 = sbet1;
450 let somg1 = salp0 * sbet1;
451 let mut csig1 = calp1 * cbet1;
452 let comg1 = calp1 * cbet1;
453 geomath::norm(&mut ssig1, &mut csig1);
454
455 let salp2 = if cbet2 != cbet1 { salp0 / cbet2 } else { salp1 };
456 let calp2 = if cbet2 != cbet1 || sbet2.abs() != -sbet1 {
457 (geomath::sq(calp1 * cbet1)
458 + if cbet1 < -sbet1 {
459 (cbet2 - cbet1) * (cbet1 + cbet2)
460 } else {
461 (sbet1 - sbet2) * (sbet1 + sbet2)
462 })
463 .sqrt()
464 / cbet2
465 } else {
466 calp1.abs()
467 };
468 let mut ssig2 = sbet2;
469 let somg2 = salp0 * sbet2;
470 let mut csig2 = calp2 * cbet2;
471 let comg2 = calp2 * cbet2;
472 geomath::norm(&mut ssig2, &mut csig2);
473
474 let sig12 = ((csig1 * ssig2 - ssig1 * csig2).max(0.0)).atan2(csig1 * csig2 + ssig1 * ssig2);
475 let somg12 = (comg1 * somg2 - somg1 * comg2).max(0.0);
476 let comg12 = comg1 * comg2 + somg1 * somg2;
477 let eta = (somg12 * clam120 - comg12 * slam120).atan2(comg12 * clam120 + somg12 * slam120);
478
479 let k2 = geomath::sq(calp0) * self._ep2;
480 let eps = k2 / (2.0 * (1.0 + (1.0 + k2).sqrt()) + k2);
481 self._C3f(eps, C3a);
482 let B312 = geomath::sin_cos_series(true, ssig2, csig2, C3a)
483 - geomath::sin_cos_series(true, ssig1, csig1, C3a);
484 let domg12 = -self.f * self._A3f(eps) * salp0 * (sig12 + B312);
485 let lam12 = eta + domg12;
486
487 let mut dlam12: f64;
488 if diffp {
489 if calp2 == 0.0 {
490 dlam12 = -2.0 * self._f1 * dn1 / sbet1;
491 } else {
492 let res = self._Lengths(
493 eps,
494 sig12,
495 ssig1,
496 csig1,
497 dn1,
498 ssig2,
499 csig2,
500 dn2,
501 cbet1,
502 cbet2,
503 caps::REDUCEDLENGTH,
504 C1a,
505 C2a,
506 );
507 dlam12 = res.1;
508 dlam12 *= self._f1 / (calp2 * cbet2);
509 }
510 } else {
511 dlam12 = f64::NAN;
512 }
513 (
514 lam12, salp2, calp2, sig12, ssig1, csig1, ssig2, csig2, eps, domg12, dlam12,
515 )
516 }
517
518 pub fn _gen_inverse_azi(
520 &self,
521 lat1: f64,
522 lon1: f64,
523 lat2: f64,
524 lon2: f64,
525 outmask: u64,
526 ) -> (f64, f64, f64, f64, f64, f64, f64, f64) {
527 let mut azi1 = f64::NAN;
528 let mut azi2 = f64::NAN;
529 let outmask = outmask & caps::OUT_MASK;
530
531 let (a12, s12, salp1, calp1, salp2, calp2, m12, M12, M21, S12) =
532 self._gen_inverse(lat1, lon1, lat2, lon2, outmask);
533 if outmask & caps::AZIMUTH != 0 {
534 azi1 = geomath::atan2d(salp1, calp1);
535 azi2 = geomath::atan2d(salp2, calp2);
536 }
537 (a12, s12, azi1, azi2, m12, M12, M21, S12)
538 }
539
540 pub fn _gen_inverse(
542 &self,
543 lat1: f64,
544 lon1: f64,
545 lat2: f64,
546 lon2: f64,
547 outmask: u64,
548 ) -> (f64, f64, f64, f64, f64, f64, f64, f64, f64, f64) {
549 let mut lat1 = lat1;
550 let mut lat2 = lat2;
551 let mut a12 = f64::NAN;
552 let mut s12 = f64::NAN;
553 let mut m12 = f64::NAN;
554 let mut M12 = f64::NAN;
555 let mut M21 = f64::NAN;
556 let mut S12 = f64::NAN;
557 let outmask = outmask & caps::OUT_MASK;
558
559 let (mut lon12, mut lon12s) = geomath::ang_diff(lon1, lon2);
560 let mut lonsign = if lon12 >= 0.0 { 1.0 } else { -1.0 };
561
562 lon12 = lonsign * geomath::ang_round(lon12);
563 lon12s = geomath::ang_round((180.0 - lon12) - lonsign * lon12s);
564 let lam12 = lon12.to_radians();
565 let slam12: f64;
566 let mut clam12: f64;
567 if lon12 > 90.0 {
568 let res = geomath::sincosd(lon12s);
569 slam12 = res.0;
570 clam12 = res.1;
571 clam12 = -clam12;
572 } else {
573 let res = geomath::sincosd(lon12);
574 slam12 = res.0;
575 clam12 = res.1;
576 };
577 lat1 = geomath::ang_round(geomath::lat_fix(lat1));
578 lat2 = geomath::ang_round(geomath::lat_fix(lat2));
579
580 let swapp = if lat1.abs() < lat2.abs() { -1.0 } else { 1.0 };
581 if swapp < 0.0 {
582 lonsign *= -1.0;
583 std::mem::swap(&mut lat2, &mut lat1);
584 }
585 let latsign = if lat1 < 0.0 { 1.0 } else { -1.0 };
586 lat1 *= latsign;
587 lat2 *= latsign;
588
589 let (mut sbet1, mut cbet1) = geomath::sincosd(lat1);
590 sbet1 *= self._f1;
591
592 geomath::norm(&mut sbet1, &mut cbet1);
593 cbet1 = cbet1.max(self.tiny_);
594
595 let (mut sbet2, mut cbet2) = geomath::sincosd(lat2);
596 sbet2 *= self._f1;
597
598 geomath::norm(&mut sbet2, &mut cbet2);
599 cbet2 = cbet2.max(self.tiny_);
600
601 if cbet1 < -sbet1 {
602 if cbet2 == cbet1 {
603 sbet2 = if sbet2 < 0.0 { sbet1 } else { -sbet1 };
604 }
605 } else if sbet2.abs() == -sbet1 {
606 cbet2 = cbet1;
607 }
608
609 let dn1 = (1.0 + self._ep2 * geomath::sq(sbet1)).sqrt();
610 let dn2 = (1.0 + self._ep2 * geomath::sq(sbet2)).sqrt();
611
612 let mut C1a: [f64; CARR_SIZE] = [0.0; CARR_SIZE];
613 let mut C2a: [f64; CARR_SIZE] = [0.0; CARR_SIZE];
614 let mut C3a: [f64; GEODESIC_ORDER] = [0.0; GEODESIC_ORDER];
615
616 let mut meridian = lat1 == -90.0 || slam12 == 0.0;
617 let mut calp1 = 0.0;
618 let mut salp1 = 0.0;
619 let mut calp2 = 0.0;
620 let mut salp2 = 0.0;
621 let mut ssig1 = 0.0;
622 let mut csig1 = 0.0;
623 let mut ssig2 = 0.0;
624 let mut csig2 = 0.0;
625 let mut sig12: f64;
626 let mut s12x = 0.0;
627 let mut m12x = 0.0;
628
629 if meridian {
630 calp1 = clam12;
631 salp1 = slam12;
632 calp2 = 1.0;
633 salp2 = 0.0;
634
635 ssig1 = sbet1;
636 csig1 = calp1 * cbet1;
637 ssig2 = sbet2;
638 csig2 = calp2 * cbet2;
639
640 sig12 = ((csig1 * ssig2 - ssig1 * csig2).max(0.0)).atan2(csig1 * csig2 + ssig1 * ssig2);
641 let res = self._Lengths(
642 self._n,
643 sig12,
644 ssig1,
645 csig1,
646 dn1,
647 ssig2,
648 csig2,
649 dn2,
650 cbet1,
651 cbet2,
652 outmask | caps::DISTANCE | caps::REDUCEDLENGTH,
653 &mut C1a,
654 &mut C2a,
655 );
656 s12x = res.0;
657 m12x = res.1;
658 M12 = res.3;
659 M21 = res.4;
660
661 if sig12 < 1.0 || m12x >= 0.0 {
662 if sig12 < 3.0 * self.tiny_ {
663 sig12 = 0.0;
664 m12x = 0.0;
665 s12x = 0.0;
666 }
667 m12x *= self._b;
668 s12x *= self._b;
669 a12 = sig12.to_degrees();
670 } else {
671 meridian = false;
672 }
673 }
674
675 let mut somg12 = 2.0;
676 let mut comg12 = 0.0;
677 let mut omg12 = 0.0;
678 let dnm: f64;
679 let mut eps = 0.0;
680 if !meridian && sbet1 == 0.0 && (self.f <= 0.0 || lon12s >= self.f * 180.0) {
681 calp1 = 0.0;
682 calp2 = 0.0;
683 salp1 = 1.0;
684 salp2 = 1.0;
685
686 s12x = self.a * lam12;
687 sig12 = lam12 / self._f1;
688 omg12 = lam12 / self._f1;
689 m12x = self._b * sig12.sin();
690 if outmask & caps::GEODESICSCALE != 0 {
691 M12 = sig12.cos();
692 M21 = sig12.cos();
693 }
694 a12 = lon12 / self._f1;
695 } else if !meridian {
696 let res = self._InverseStart(
697 sbet1, cbet1, dn1, sbet2, cbet2, dn2, lam12, slam12, clam12, &mut C1a, &mut C2a,
698 );
699 sig12 = res.0;
700 salp1 = res.1;
701 calp1 = res.2;
702 salp2 = res.3;
703 calp2 = res.4;
704 dnm = res.5;
705
706 if sig12 >= 0.0 {
707 s12x = sig12 * self._b * dnm;
708 m12x = geomath::sq(dnm) * self._b * (sig12 / dnm).sin();
709 if outmask & caps::GEODESICSCALE != 0 {
710 M12 = (sig12 / dnm).cos();
711 M21 = (sig12 / dnm).cos();
712 }
713 a12 = sig12.to_degrees();
714 omg12 = lam12 / (self._f1 * dnm);
715 } else {
716 let mut tripn = false;
717 let mut tripb = false;
718 let mut salp1a = self.tiny_;
719 let mut calp1a = 1.0;
720 let mut salp1b = self.tiny_;
721 let mut calp1b = -1.0;
722 let mut domg12 = 0.0;
723 for numit in 0..self.maxit2_ {
724 let res = self._Lambda12(
725 sbet1,
726 cbet1,
727 dn1,
728 sbet2,
729 cbet2,
730 dn2,
731 salp1,
732 calp1,
733 slam12,
734 clam12,
735 numit < self.maxit1_,
736 &mut C1a,
737 &mut C2a,
738 &mut C3a,
739 );
740 let v = res.0;
741 salp2 = res.1;
742 calp2 = res.2;
743 sig12 = res.3;
744 ssig1 = res.4;
745 csig1 = res.5;
746 ssig2 = res.6;
747 csig2 = res.7;
748 eps = res.8;
749 domg12 = res.9;
750 let dv = res.10;
751
752 if tripb
753 || v.abs() < if tripn { 8.0 } else { 1.0 } * self.tol0_
754 || v.abs().is_nan()
755 {
756 break;
757 };
758 if v > 0.0 && (numit > self.maxit1_ || calp1 / salp1 > calp1b / salp1b) {
759 salp1b = salp1;
760 calp1b = calp1;
761 } else if v < 0.0 && (numit > self.maxit1_ || calp1 / salp1 < calp1a / salp1a) {
762 salp1a = salp1;
763 calp1a = calp1;
764 }
765 if numit < self.maxit1_ && dv > 0.0 {
766 let dalp1 = -v / dv;
767 let sdalp1 = dalp1.sin();
768 let cdalp1 = dalp1.cos();
769 let nsalp1 = salp1 * cdalp1 + calp1 * sdalp1;
770 if nsalp1 > 0.0 && dalp1.abs() < PI {
771 calp1 = calp1 * cdalp1 - salp1 * sdalp1;
772 salp1 = nsalp1;
773 geomath::norm(&mut salp1, &mut calp1);
774 tripn = v.abs() <= 16.0 * self.tol0_;
775 continue;
776 }
777 }
778
779 salp1 = (salp1a + salp1b) / 2.0;
780 calp1 = (calp1a + calp1b) / 2.0;
781 geomath::norm(&mut salp1, &mut calp1);
782 tripn = false;
783 tripb = (salp1a - salp1).abs() + (calp1a - calp1) < self.tolb_
784 || (salp1 - salp1b).abs() + (calp1 - calp1b) < self.tolb_;
785 }
786 let lengthmask = outmask
787 | if outmask & (caps::REDUCEDLENGTH | caps::GEODESICSCALE) != 0 {
788 caps::DISTANCE
789 } else {
790 caps::EMPTY
791 };
792 let res = self._Lengths(
793 eps, sig12, ssig1, csig1, dn1, ssig2, csig2, dn2, cbet1, cbet2, lengthmask,
794 &mut C1a, &mut C2a,
795 );
796 s12x = res.0;
797 m12x = res.1;
798 M12 = res.3;
799 M21 = res.4;
800
801 m12x *= self._b;
802 s12x *= self._b;
803 a12 = sig12.to_degrees();
804 if outmask & caps::AREA != 0 {
805 let sdomg12 = domg12.sin();
806 let cdomg12 = domg12.cos();
807 somg12 = slam12 * cdomg12 - clam12 * sdomg12;
808 comg12 = clam12 * cdomg12 + slam12 * sdomg12;
809 }
810 }
811 }
812 if outmask & caps::DISTANCE != 0 {
813 s12 = 0.0 + s12x;
814 }
815 if outmask & caps::REDUCEDLENGTH != 0 {
816 m12 = 0.0 + m12x;
817 }
818 if outmask & caps::AREA != 0 {
819 let salp0 = salp1 * cbet1;
820 let calp0 = calp1.hypot(salp1 * sbet1);
821 if calp0 != 0.0 && salp0 != 0.0 {
822 ssig1 = sbet1;
823 csig1 = calp1 * cbet1;
824 ssig2 = sbet2;
825 csig2 = calp2 * cbet2;
826 let k2 = geomath::sq(calp0) * self._ep2;
827 eps = k2 / (2.0 * (1.0 + (1.0 + k2).sqrt()) + k2);
828 let A4 = geomath::sq(self.a) * calp0 * salp0 * self._e2;
829 geomath::norm(&mut ssig1, &mut csig1);
830 geomath::norm(&mut ssig2, &mut csig2);
831 let mut C4a: [f64; GEODESIC_ORDER] = [0.0; GEODESIC_ORDER];
832 self._C4f(eps, &mut C4a);
833 let B41 = geomath::sin_cos_series(false, ssig1, csig1, &C4a);
834 let B42 = geomath::sin_cos_series(false, ssig2, csig2, &C4a);
835 S12 = A4 * (B42 - B41);
836 } else {
837 S12 = 0.0;
838 }
839
840 if !meridian && somg12 > 1.0 {
841 somg12 = omg12.sin();
842 comg12 = omg12.cos();
843 }
844
845 let alp12: f64;
848 if !meridian && comg12 > -FRAC_1_SQRT_2 && sbet2 - sbet1 < 1.75 {
849 let domg12 = 1.0 + comg12;
850 let dbet1 = 1.0 + cbet1;
851 let dbet2 = 1.0 + cbet2;
852 alp12 = 2.0
853 * (somg12 * (sbet1 * dbet2 + sbet2 * dbet1))
854 .atan2(domg12 * (sbet1 * sbet2 + dbet1 * dbet2));
855 } else {
856 let mut salp12 = salp2 * calp1 - calp2 * salp1;
857 let mut calp12 = calp2 * calp1 + salp2 * salp1;
858
859 if salp12 == 0.0 && calp12 < 0.0 {
860 salp12 = self.tiny_ * calp1;
861 calp12 = -1.0;
862 }
863 alp12 = salp12.atan2(calp12);
864 }
865 S12 += self._c2 * alp12;
866 S12 *= swapp * lonsign * latsign;
867 S12 += 0.0;
868 }
869
870 if swapp < 0.0 {
871 std::mem::swap(&mut salp2, &mut salp1);
872
873 std::mem::swap(&mut calp2, &mut calp1);
874
875 if outmask & caps::GEODESICSCALE != 0 {
876 std::mem::swap(&mut M21, &mut M12);
877 }
878 }
879 salp1 *= swapp * lonsign;
880 calp1 *= swapp * latsign;
881 salp2 *= swapp * lonsign;
882 calp2 *= swapp * latsign;
883 (a12, s12, salp1, calp1, salp2, calp2, m12, M12, M21, S12)
884 }
885
886 pub fn _gen_direct(
888 &self,
889 lat1: f64,
890 lon1: f64,
891 azi1: f64,
892 arcmode: bool,
893 s12_a12: f64,
894 mut outmask: u64,
895 ) -> (f64, f64, f64, f64, f64, f64, f64, f64, f64) {
896 if !arcmode {
897 outmask |= caps::DISTANCE_IN
898 };
899
900 let line =
901 geodesic_line::GeodesicLine::new(self, lat1, lon1, azi1, Some(outmask), None, None);
902 line._gen_position(arcmode, s12_a12, outmask)
903 }
904
905 pub fn area(&self) -> f64 {
907 self._c2 * 4.0 * std::f64::consts::PI
908 }
909}
910
911pub trait DirectGeodesic<T> {
953 fn direct(&self, lat1: f64, lon1: f64, azi1: f64, s12: f64) -> T;
954}
955
956impl DirectGeodesic<(f64, f64)> for Geodesic {
957 fn direct(&self, lat1: f64, lon1: f64, azi1: f64, s12: f64) -> (f64, f64) {
963 let capabilities = caps::LATITUDE | caps::LONGITUDE;
964 let (_a12, lat2, lon2, _azi2, _s12, _m12, _M12, _M21, _S12) =
965 self._gen_direct(lat1, lon1, azi1, false, s12, capabilities);
966
967 (lat2, lon2)
968 }
969}
970
971impl DirectGeodesic<(f64, f64, f64)> for Geodesic {
972 fn direct(&self, lat1: f64, lon1: f64, azi1: f64, s12: f64) -> (f64, f64, f64) {
979 let capabilities = caps::LATITUDE | caps::LONGITUDE | caps::AZIMUTH;
980 let (_a12, lat2, lon2, azi2, _s12, _m12, _M12, _M21, _S12) =
981 self._gen_direct(lat1, lon1, azi1, false, s12, capabilities);
982
983 (lat2, lon2, azi2)
984 }
985}
986
987impl DirectGeodesic<(f64, f64, f64, f64)> for Geodesic {
988 fn direct(&self, lat1: f64, lon1: f64, azi1: f64, s12: f64) -> (f64, f64, f64, f64) {
996 let capabilities = caps::LATITUDE | caps::LONGITUDE | caps::AZIMUTH | caps::REDUCEDLENGTH;
997 let (_a12, lat2, lon2, azi2, _s12, m12, _M12, _M21, _S12) =
998 self._gen_direct(lat1, lon1, azi1, false, s12, capabilities);
999
1000 (lat2, lon2, azi2, m12)
1001 }
1002}
1003
1004impl DirectGeodesic<(f64, f64, f64, f64, f64)> for Geodesic {
1005 fn direct(&self, lat1: f64, lon1: f64, azi1: f64, s12: f64) -> (f64, f64, f64, f64, f64) {
1014 let capabilities = caps::LATITUDE | caps::LONGITUDE | caps::AZIMUTH | caps::GEODESICSCALE;
1015 let (_a12, lat2, lon2, azi2, _s12, _m12, M12, M21, _S12) =
1016 self._gen_direct(lat1, lon1, azi1, false, s12, capabilities);
1017
1018 (lat2, lon2, azi2, M12, M21)
1019 }
1020}
1021
1022impl DirectGeodesic<(f64, f64, f64, f64, f64, f64)> for Geodesic {
1023 fn direct(&self, lat1: f64, lon1: f64, azi1: f64, s12: f64) -> (f64, f64, f64, f64, f64, f64) {
1033 let capabilities = caps::LATITUDE
1034 | caps::LONGITUDE
1035 | caps::AZIMUTH
1036 | caps::REDUCEDLENGTH
1037 | caps::GEODESICSCALE;
1038 let (_a12, lat2, lon2, azi2, _s12, m12, M12, M21, _S12) =
1039 self._gen_direct(lat1, lon1, azi1, false, s12, capabilities);
1040
1041 (lat2, lon2, azi2, m12, M12, M21)
1042 }
1043}
1044
1045impl DirectGeodesic<(f64, f64, f64, f64, f64, f64, f64, f64)> for Geodesic {
1046 fn direct(
1058 &self,
1059 lat1: f64,
1060 lon1: f64,
1061 azi1: f64,
1062 s12: f64,
1063 ) -> (f64, f64, f64, f64, f64, f64, f64, f64) {
1064 let capabilities = caps::LATITUDE
1065 | caps::LONGITUDE
1066 | caps::AZIMUTH
1067 | caps::REDUCEDLENGTH
1068 | caps::GEODESICSCALE
1069 | caps::AREA;
1070 let (a12, lat2, lon2, azi2, _s12, m12, M12, M21, S12) =
1071 self._gen_direct(lat1, lon1, azi1, false, s12, capabilities);
1072
1073 (lat2, lon2, azi2, m12, M12, M21, S12, a12)
1074 }
1075}
1076
1077pub trait InverseGeodesic<T> {
1126 fn inverse(&self, lat1: f64, lon1: f64, lat2: f64, lon2: f64) -> T;
1127}
1128
1129impl InverseGeodesic<f64> for Geodesic {
1130 fn inverse(&self, lat1: f64, lon1: f64, lat2: f64, lon2: f64) -> f64 {
1135 let capabilities = caps::DISTANCE;
1136 let (_a12, s12, _azi1, _azi2, _m12, _M12, _M21, _S12) =
1137 self._gen_inverse_azi(lat1, lon1, lat2, lon2, capabilities);
1138
1139 s12
1140 }
1141}
1142
1143impl InverseGeodesic<(f64, f64)> for Geodesic {
1144 fn inverse(&self, lat1: f64, lon1: f64, lat2: f64, lon2: f64) -> (f64, f64) {
1150 let capabilities = caps::DISTANCE;
1151 let (a12, s12, _azi1, _azi2, _m12, _M12, _M21, _S12) =
1152 self._gen_inverse_azi(lat1, lon1, lat2, lon2, capabilities);
1153
1154 (s12, a12)
1155 }
1156}
1157
1158impl InverseGeodesic<(f64, f64, f64)> for Geodesic {
1159 fn inverse(&self, lat1: f64, lon1: f64, lat2: f64, lon2: f64) -> (f64, f64, f64) {
1166 let capabilities = caps::AZIMUTH;
1167 let (a12, _s12, azi1, azi2, _m12, _M12, _M21, _S12) =
1168 self._gen_inverse_azi(lat1, lon1, lat2, lon2, capabilities);
1169
1170 (azi1, azi2, a12)
1171 }
1172}
1173
1174impl InverseGeodesic<(f64, f64, f64, f64)> for Geodesic {
1175 fn inverse(&self, lat1: f64, lon1: f64, lat2: f64, lon2: f64) -> (f64, f64, f64, f64) {
1183 let capabilities = caps::DISTANCE | caps::AZIMUTH;
1184 let (a12, s12, azi1, azi2, _m12, _M12, _M21, _S12) =
1185 self._gen_inverse_azi(lat1, lon1, lat2, lon2, capabilities);
1186
1187 (s12, azi1, azi2, a12)
1188 }
1189}
1190
1191impl InverseGeodesic<(f64, f64, f64, f64, f64)> for Geodesic {
1192 fn inverse(&self, lat1: f64, lon1: f64, lat2: f64, lon2: f64) -> (f64, f64, f64, f64, f64) {
1201 let capabilities = caps::DISTANCE | caps::AZIMUTH | caps::REDUCEDLENGTH;
1202 let (a12, s12, azi1, azi2, m12, _M12, _M21, _S12) =
1203 self._gen_inverse_azi(lat1, lon1, lat2, lon2, capabilities);
1204
1205 (s12, azi1, azi2, m12, a12)
1206 }
1207}
1208
1209impl InverseGeodesic<(f64, f64, f64, f64, f64, f64)> for Geodesic {
1210 fn inverse(
1220 &self,
1221 lat1: f64,
1222 lon1: f64,
1223 lat2: f64,
1224 lon2: f64,
1225 ) -> (f64, f64, f64, f64, f64, f64) {
1226 let capabilities = caps::DISTANCE | caps::AZIMUTH | caps::GEODESICSCALE;
1227 let (a12, s12, azi1, azi2, _m12, M12, M21, _S12) =
1228 self._gen_inverse_azi(lat1, lon1, lat2, lon2, capabilities);
1229
1230 (s12, azi1, azi2, M12, M21, a12)
1231 }
1232}
1233
1234impl InverseGeodesic<(f64, f64, f64, f64, f64, f64, f64)> for Geodesic {
1235 fn inverse(
1246 &self,
1247 lat1: f64,
1248 lon1: f64,
1249 lat2: f64,
1250 lon2: f64,
1251 ) -> (f64, f64, f64, f64, f64, f64, f64) {
1252 let capabilities =
1253 caps::DISTANCE | caps::AZIMUTH | caps::REDUCEDLENGTH | caps::GEODESICSCALE;
1254 let (a12, s12, azi1, azi2, m12, M12, M21, _S12) =
1255 self._gen_inverse_azi(lat1, lon1, lat2, lon2, capabilities);
1256
1257 (s12, azi1, azi2, m12, M12, M21, a12)
1258 }
1259}
1260
1261impl InverseGeodesic<(f64, f64, f64, f64, f64, f64, f64, f64)> for Geodesic {
1262 fn inverse(
1274 &self,
1275 lat1: f64,
1276 lon1: f64,
1277 lat2: f64,
1278 lon2: f64,
1279 ) -> (f64, f64, f64, f64, f64, f64, f64, f64) {
1280 let capabilities =
1281 caps::DISTANCE | caps::AZIMUTH | caps::REDUCEDLENGTH | caps::GEODESICSCALE | caps::AREA;
1282 let (a12, s12, azi1, azi2, m12, M12, M21, S12) =
1283 self._gen_inverse_azi(lat1, lon1, lat2, lon2, capabilities);
1284
1285 (s12, azi1, azi2, m12, M12, M21, S12, a12)
1286 }
1287}
1288
1289#[cfg(test)]
1290mod tests {
1291 use super::*;
1292 use crate::geodesic_line::GeodesicLine;
1293 use approx::assert_relative_eq;
1294 use std::io::BufRead;
1295
1296 #[allow(clippy::type_complexity)]
1297 const TESTCASES: &[(f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64)] = &[
1298 (
1299 35.60777,
1300 -139.44815,
1301 111.098748429560326,
1302 -11.17491,
1303 -69.95921,
1304 129.289270889708762,
1305 8935244.5604818305,
1306 80.50729714281974,
1307 6273170.2055303837,
1308 0.16606318447386067,
1309 0.16479116945612937,
1310 12841384694976.432,
1311 ),
1312 (
1313 55.52454,
1314 106.05087,
1315 22.020059880982801,
1316 77.03196,
1317 197.18234,
1318 109.112041110671519,
1319 4105086.1713924406,
1320 36.892740690445894,
1321 3828869.3344387607,
1322 0.80076349608092607,
1323 0.80101006984201008,
1324 61674961290615.615,
1325 ),
1326 (
1327 -21.97856,
1328 142.59065,
1329 -32.44456876433189,
1330 41.84138,
1331 98.56635,
1332 -41.84359951440466,
1333 8394328.894657671,
1334 75.62930491011522,
1335 6161154.5773110616,
1336 0.24816339233950381,
1337 0.24930251203627892,
1338 -6637997720646.717,
1339 ),
1340 (
1341 -66.99028,
1342 112.2363,
1343 173.73491240878403,
1344 -12.70631,
1345 285.90344,
1346 2.512956620913668,
1347 11150344.2312080241,
1348 100.278634181155759,
1349 6289939.5670446687,
1350 -0.17199490274700385,
1351 -0.17722569526345708,
1352 -121287239862139.744,
1353 ),
1354 (
1355 -17.42761,
1356 173.34268,
1357 -159.033557661192928,
1358 -15.84784,
1359 5.93557,
1360 -20.787484651536988,
1361 16076603.1631180673,
1362 144.640108810286253,
1363 3732902.1583877189,
1364 -0.81273638700070476,
1365 -0.81299800519154474,
1366 97825992354058.708,
1367 ),
1368 (
1369 32.84994,
1370 48.28919,
1371 150.492927788121982,
1372 -56.28556,
1373 202.29132,
1374 48.113449399816759,
1375 16727068.9438164461,
1376 150.565799985466607,
1377 3147838.1910180939,
1378 -0.87334918086923126,
1379 -0.86505036767110637,
1380 -72445258525585.010,
1381 ),
1382 (
1383 6.96833,
1384 52.74123,
1385 92.581585386317712,
1386 -7.39675,
1387 206.17291,
1388 90.721692165923907,
1389 17102477.2496958388,
1390 154.147366239113561,
1391 2772035.6169917581,
1392 -0.89991282520302447,
1393 -0.89986892177110739,
1394 -1311796973197.995,
1395 ),
1396 (
1397 -50.56724,
1398 -16.30485,
1399 -105.439679907590164,
1400 -33.56571,
1401 -94.97412,
1402 -47.348547835650331,
1403 6455670.5118668696,
1404 58.083719495371259,
1405 5409150.7979815838,
1406 0.53053508035997263,
1407 0.52988722644436602,
1408 41071447902810.047,
1409 ),
1410 (
1411 -58.93002,
1412 -8.90775,
1413 140.965397902500679,
1414 -8.91104,
1415 133.13503,
1416 19.255429433416599,
1417 11756066.0219864627,
1418 105.755691241406877,
1419 6151101.2270708536,
1420 -0.26548622269867183,
1421 -0.27068483874510741,
1422 -86143460552774.735,
1423 ),
1424 (
1425 -68.82867,
1426 -74.28391,
1427 93.774347763114881,
1428 -50.63005,
1429 -8.36685,
1430 34.65564085411343,
1431 3956936.926063544,
1432 35.572254987389284,
1433 3708890.9544062657,
1434 0.81443963736383502,
1435 0.81420859815358342,
1436 -41845309450093.787,
1437 ),
1438 (
1439 -10.62672,
1440 -32.0898,
1441 -86.426713286747751,
1442 5.883,
1443 -134.31681,
1444 -80.473780971034875,
1445 11470869.3864563009,
1446 103.387395634504061,
1447 6184411.6622659713,
1448 -0.23138683500430237,
1449 -0.23155097622286792,
1450 4198803992123.548,
1451 ),
1452 (
1453 -21.76221,
1454 166.90563,
1455 29.319421206936428,
1456 48.72884,
1457 213.97627,
1458 43.508671946410168,
1459 9098627.3986554915,
1460 81.963476716121964,
1461 6299240.9166992283,
1462 0.13965943368590333,
1463 0.14152969707656796,
1464 10024709850277.476,
1465 ),
1466 (
1467 -19.79938,
1468 -174.47484,
1469 71.167275780171533,
1470 -11.99349,
1471 -154.35109,
1472 65.589099775199228,
1473 2319004.8601169389,
1474 20.896611684802389,
1475 2267960.8703918325,
1476 0.93427001867125849,
1477 0.93424887135032789,
1478 -3935477535005.785,
1479 ),
1480 (
1481 -11.95887,
1482 -116.94513,
1483 92.712619830452549,
1484 4.57352,
1485 7.16501,
1486 78.64960934409585,
1487 13834722.5801401374,
1488 124.688684161089762,
1489 5228093.177931598,
1490 -0.56879356755666463,
1491 -0.56918731952397221,
1492 -9919582785894.853,
1493 ),
1494 (
1495 -87.85331,
1496 85.66836,
1497 -65.120313040242748,
1498 66.48646,
1499 16.09921,
1500 -4.888658719272296,
1501 17286615.3147144645,
1502 155.58592449699137,
1503 2635887.4729110181,
1504 -0.90697975771398578,
1505 -0.91095608883042767,
1506 42667211366919.534,
1507 ),
1508 (
1509 1.74708,
1510 128.32011,
1511 -101.584843631173858,
1512 -11.16617,
1513 11.87109,
1514 -86.325793296437476,
1515 12942901.1241347408,
1516 116.650512484301857,
1517 5682744.8413270572,
1518 -0.44857868222697644,
1519 -0.44824490340007729,
1520 10763055294345.653,
1521 ),
1522 (
1523 -25.72959,
1524 -144.90758,
1525 -153.647468693117198,
1526 -57.70581,
1527 -269.17879,
1528 -48.343983158876487,
1529 9413446.7452453107,
1530 84.664533838404295,
1531 6356176.6898881281,
1532 0.09492245755254703,
1533 0.09737058264766572,
1534 74515122850712.444,
1535 ),
1536 (
1537 -41.22777,
1538 122.32875,
1539 14.285113402275739,
1540 -7.57291,
1541 130.37946,
1542 10.805303085187369,
1543 3812686.035106021,
1544 34.34330804743883,
1545 3588703.8812128856,
1546 0.82605222593217889,
1547 0.82572158200920196,
1548 -2456961531057.857,
1549 ),
1550 (
1551 11.01307,
1552 138.25278,
1553 79.43682622782374,
1554 6.62726,
1555 247.05981,
1556 103.708090215522657,
1557 11911190.819018408,
1558 107.341669954114577,
1559 6070904.722786735,
1560 -0.29767608923657404,
1561 -0.29785143390252321,
1562 17121631423099.696,
1563 ),
1564 (
1565 -29.47124,
1566 95.14681,
1567 -163.779130441688382,
1568 -27.46601,
1569 -69.15955,
1570 -15.909335945554969,
1571 13487015.8381145492,
1572 121.294026715742277,
1573 5481428.9945736388,
1574 -0.51527225545373252,
1575 -0.51556587964721788,
1576 104679964020340.318,
1577 ),
1578 ];
1579
1580 #[test]
1581 fn test_inverse_and_direct() -> Result<(), String> {
1582 let geod = Geodesic::wgs84();
1584 let (_a12, s12, _azi1, _azi2, _m12, _M12, _M21, _S12) =
1585 geod._gen_inverse_azi(0.0, 0.0, 1.0, 1.0, caps::STANDARD);
1586 assert_eq!(s12, 156899.56829134026);
1587
1588 for (lat1, lon1, azi1, lat2, lon2, azi2, s12, a12, m12, M12, M21, S12) in TESTCASES.iter() {
1590 let (
1591 computed_a12,
1592 computed_s12,
1593 computed_azi1,
1594 computed_azi2,
1595 computed_m12,
1596 computed_M12,
1597 computed_M21,
1598 computed_S12,
1599 ) = geod._gen_inverse_azi(*lat1, *lon1, *lat2, *lon2, caps::ALL | caps::LONG_UNROLL);
1600 assert_relative_eq!(computed_azi1, azi1, epsilon = 1e-13f64);
1601 assert_relative_eq!(computed_azi2, azi2, epsilon = 1e-13f64);
1602 assert_relative_eq!(computed_s12, s12, epsilon = 1e-8f64);
1603 assert_relative_eq!(computed_a12, a12, epsilon = 1e-13f64);
1604 assert_relative_eq!(computed_m12, m12, epsilon = 1e-8f64);
1605 assert_relative_eq!(computed_M12, M12, epsilon = 1e-15f64);
1606 assert_relative_eq!(computed_M21, M21, epsilon = 1e-15f64);
1607 assert_relative_eq!(computed_S12, S12, epsilon = 0.1f64);
1608 }
1609
1610 for (lat1, lon1, azi1, lat2, lon2, azi2, s12, a12, m12, M12, M21, S12) in TESTCASES.iter() {
1612 let (
1613 computed_a12,
1614 computed_lat2,
1615 computed_lon2,
1616 computed_azi2,
1617 _computed_s12,
1618 computed_m12,
1619 computed_M12,
1620 computed_M21,
1621 computed_S12,
1622 ) = geod._gen_direct(
1623 *lat1,
1624 *lon1,
1625 *azi1,
1626 false,
1627 *s12,
1628 caps::ALL | caps::LONG_UNROLL,
1629 );
1630 assert_relative_eq!(computed_lat2, lat2, epsilon = 1e-13f64);
1631 assert_relative_eq!(computed_lon2, lon2, epsilon = 1e-13f64);
1632 assert_relative_eq!(computed_azi2, azi2, epsilon = 1e-13f64);
1633 assert_relative_eq!(computed_a12, a12, epsilon = 1e-13f64);
1634 assert_relative_eq!(computed_m12, m12, epsilon = 1e-8f64);
1635 assert_relative_eq!(computed_M12, M12, epsilon = 1e-15f64);
1636 assert_relative_eq!(computed_M21, M21, epsilon = 1e-15f64);
1637 assert_relative_eq!(computed_S12, S12, epsilon = 0.1f64);
1638 }
1639 Ok(())
1640 }
1641
1642 #[test]
1643 fn test_arcdirect() {
1644 let geod = Geodesic::wgs84();
1646 for (lat1, lon1, azi1, lat2, lon2, azi2, s12, a12, m12, M12, M21, S12) in TESTCASES.iter() {
1647 let (
1648 _computed_a12,
1649 computed_lat2,
1650 computed_lon2,
1651 computed_azi2,
1652 computed_s12,
1653 computed_m12,
1654 computed_M12,
1655 computed_M21,
1656 computed_S12,
1657 ) = geod._gen_direct(
1658 *lat1,
1659 *lon1,
1660 *azi1,
1661 true,
1662 *a12,
1663 caps::ALL | caps::LONG_UNROLL,
1664 );
1665 assert_relative_eq!(computed_lat2, lat2, epsilon = 1e-13);
1666 assert_relative_eq!(computed_lon2, lon2, epsilon = 1e-13);
1667 assert_relative_eq!(computed_azi2, azi2, epsilon = 1e-13);
1668 assert_relative_eq!(computed_s12, s12, epsilon = 1e-8);
1669 assert_relative_eq!(computed_m12, m12, epsilon = 1e-8);
1670 assert_relative_eq!(computed_M12, M12, epsilon = 1e-15);
1671 assert_relative_eq!(computed_M21, M21, epsilon = 1e-15);
1672 assert_relative_eq!(computed_S12, S12, epsilon = 0.1);
1673 }
1674 }
1675
1676 #[test]
1677 fn test_geninverse() {
1678 let geod = Geodesic::wgs84();
1679 let res = geod._gen_inverse(0.0, 0.0, 1.0, 1.0, caps::STANDARD);
1680 assert_eq!(res.0, 1.4141938478710363);
1681 assert_eq!(res.1, 156899.56829134026);
1682 assert_eq!(res.2, 0.7094236375834774);
1683 assert_eq!(res.3, 0.7047823085448635);
1684 assert_eq!(res.4, 0.7095309793242709);
1685 assert_eq!(res.5, 0.7046742434480923);
1686 assert!(res.6.is_nan());
1687 assert!(res.7.is_nan());
1688 assert!(res.8.is_nan());
1689 assert!(res.9.is_nan());
1690 }
1691
1692 #[test]
1693 fn test_inverse_start() {
1694 let geod = Geodesic::wgs84();
1695 let res = geod._InverseStart(
1696 -0.017393909556108908,
1697 0.9998487145115275,
1698 1.0000010195104125,
1699 -0.0,
1700 1.0,
1701 1.0,
1702 0.017453292519943295,
1703 0.01745240643728351,
1704 0.9998476951563913,
1705 &mut [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0],
1706 &mut [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0],
1707 );
1708 assert_eq!(res.0, -1.0);
1709 assert_relative_eq!(res.1, 0.7095310092765433, epsilon = 1e-13);
1710 assert_relative_eq!(res.2, 0.7046742132893822, epsilon = 1e-13);
1711 assert!(res.3.is_nan());
1712 assert!(res.4.is_nan());
1713 assert_eq!(res.5, 1.0000002548969817);
1714
1715 let res = geod._InverseStart(
1716 -0.017393909556108908,
1717 0.9998487145115275,
1718 1.0000010195104125,
1719 -0.0,
1720 1.0,
1721 1.0,
1722 0.017453292519943295,
1723 0.01745240643728351,
1724 0.9998476951563913,
1725 &mut [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0],
1726 &mut [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0],
1727 );
1728 assert_eq!(res.0, -1.0);
1729 assert_relative_eq!(res.1, 0.7095310092765433, epsilon = 1e-13);
1730 assert_relative_eq!(res.2, 0.7046742132893822, epsilon = 1e-13);
1731 assert!(res.3.is_nan());
1732 assert!(res.4.is_nan());
1733 assert_eq!(res.5, 1.0000002548969817);
1734 }
1735
1736 #[test]
1737 fn test_lambda12() {
1738 let geod = Geodesic::wgs84();
1739 let res1 = geod._Lambda12(
1740 -0.017393909556108908,
1741 0.9998487145115275,
1742 1.0000010195104125,
1743 -0.0,
1744 1.0,
1745 1.0,
1746 0.7095310092765433,
1747 0.7046742132893822,
1748 0.01745240643728351,
1749 0.9998476951563913,
1750 true,
1751 &mut [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0],
1752 &mut [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0],
1753 &mut [0.0, 1.0, 2.0, 3.0, 4.0, 5.0],
1754 );
1755 assert_eq!(res1.0, 1.4834408705897495e-09);
1756 assert_eq!(res1.1, 0.7094236675312185);
1757 assert_eq!(res1.2, 0.7047822783999007);
1758 assert_eq!(res1.3, 0.024682339962725352);
1759 assert_eq!(res1.4, -0.024679833885152578);
1760 assert_eq!(res1.5, 0.9996954065111039);
1761 assert_eq!(res1.6, -0.0);
1762 assert_eq!(res1.7, 1.0);
1763 assert_relative_eq!(res1.8, 0.0008355095326524276, epsilon = 1e-13);
1764 assert_eq!(res1.9, -5.8708496511415445e-05);
1765 assert_eq!(res1.10, 0.034900275148485);
1766
1767 let res2 = geod._Lambda12(
1768 -0.017393909556108908,
1769 0.9998487145115275,
1770 1.0000010195104125,
1771 -0.0,
1772 1.0,
1773 1.0,
1774 0.7095309793242709,
1775 0.7046742434480923,
1776 0.01745240643728351,
1777 0.9998476951563913,
1778 true,
1779 &mut [
1780 0.0,
1781 -0.00041775465696698233,
1782 -4.362974596862037e-08,
1783 -1.2151022357848552e-11,
1784 -4.7588881620421004e-15,
1785 -2.226614930167366e-18,
1786 -1.1627237498131586e-21,
1787 ],
1788 &mut [
1789 0.0,
1790 -0.0008355098973052918,
1791 -1.7444619952659748e-07,
1792 -7.286557795511902e-11,
1793 -3.80472772706481e-14,
1794 -2.2251271876594078e-17,
1795 1.2789961247944744e-20,
1796 ],
1797 &mut [
1798 0.0,
1799 0.00020861391868413911,
1800 4.3547247296823945e-08,
1801 1.515432276542012e-11,
1802 6.645637323698485e-15,
1803 3.3399223952510497e-18,
1804 ],
1805 );
1806 assert_eq!(res2.0, 6.046459990680098e-17);
1807 assert_eq!(res2.1, 0.7094236375834774);
1808 assert_eq!(res2.2, 0.7047823085448635);
1809 assert_eq!(res2.3, 0.024682338906797385);
1810 assert_eq!(res2.4, -0.02467983282954624);
1811 assert_eq!(res2.5, 0.9996954065371639);
1812 assert_eq!(res2.6, -0.0);
1813 assert_eq!(res2.7, 1.0);
1814 assert_relative_eq!(res2.8, 0.0008355096040059597, epsilon = 1e-18);
1815 assert_eq!(res2.9, -5.870849152149326e-05);
1816 assert_eq!(res2.10, 0.03490027216297455);
1817 }
1818
1819 #[test]
1820 fn test_lengths() {
1821 let geod = Geodesic::wgs84();
1823 let mut c1a = [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0];
1824 let mut c2a = [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0];
1825 let res1 = geod._Lengths(
1826 0.0008355095326524276,
1827 0.024682339962725352,
1828 -0.024679833885152578,
1829 0.9996954065111039,
1830 1.0000010195104125,
1831 -0.0,
1832 1.0,
1833 1.0,
1834 0.9998487145115275,
1835 1.0,
1836 4101,
1837 &mut c1a,
1838 &mut c2a,
1839 );
1840 assert!(res1.0.is_nan());
1841 assert_eq!(res1.1, 0.024679842274314294);
1842 assert_eq!(res1.2, 0.0016717180169067588);
1843 assert!(res1.3.is_nan());
1844 assert!(res1.4.is_nan());
1845
1846 let res2 = geod._Lengths(
1847 0.0008355096040059597,
1848 0.024682338906797385,
1849 -0.02467983282954624,
1850 0.9996954065371639,
1851 1.0000010195104125,
1852 -0.0,
1853 1.0,
1854 1.0,
1855 0.9998487145115275,
1856 1.0,
1857 4101,
1858 &mut [
1859 0.0,
1860 -0.00041775465696698233,
1861 -4.362974596862037e-08,
1862 -1.2151022357848552e-11,
1863 -4.7588881620421004e-15,
1864 -2.226614930167366e-18,
1865 -1.1627237498131586e-21,
1866 ],
1867 &mut [
1868 0.0,
1869 -0.0008355098973052918,
1870 -1.7444619952659748e-07,
1871 -7.286557795511902e-11,
1872 -3.80472772706481e-14,
1873 -2.2251271876594078e-17,
1874 1.2789961247944744e-20,
1875 ],
1876 );
1877 assert!(res2.0.is_nan());
1878 assert_eq!(res2.1, 0.02467984121870759);
1879 assert_eq!(res2.2, 0.0016717181597332804);
1880 assert!(res2.3.is_nan());
1881 assert!(res2.4.is_nan());
1882
1883 let res3 = geod._Lengths(
1884 0.0008355096040059597,
1885 0.024682338906797385,
1886 -0.02467983282954624,
1887 0.9996954065371639,
1888 1.0000010195104125,
1889 -0.0,
1890 1.0,
1891 1.0,
1892 0.9998487145115275,
1893 1.0,
1894 1920,
1895 &mut [
1896 0.0,
1897 -0.00041775469264372037,
1898 -4.362975342068502e-08,
1899 -1.215102547098435e-11,
1900 -4.758889787701359e-15,
1901 -2.2266158809456692e-18,
1902 -1.1627243456014359e-21,
1903 ],
1904 &mut [
1905 0.0,
1906 -0.0008355099686589174,
1907 -1.744462293162189e-07,
1908 -7.286559662008413e-11,
1909 -3.804729026574989e-14,
1910 -2.2251281376754273e-17,
1911 1.2789967801615795e-20,
1912 ],
1913 );
1914 assert_eq!(res3.0, 0.024682347295447677);
1915 assert!(res3.1.is_nan());
1916 assert!(res3.2.is_nan());
1917 assert!(res3.3.is_nan());
1918 assert!(res3.4.is_nan());
1919
1920 let res = geod._Lengths(
1921 0.0007122620325664751,
1922 1.405117407023628,
1923 -0.8928657853278468,
1924 0.45032287238256896,
1925 1.0011366173804046,
1926 0.2969032234925426,
1927 0.9549075745221299,
1928 1.0001257451360057,
1929 0.8139459053827204,
1930 0.9811634781422108,
1931 1920,
1932 &mut [
1933 0.0,
1934 -0.0003561309485314716,
1935 -3.170731714689771e-08,
1936 -7.527972480734327e-12,
1937 -2.5133854116682488e-15,
1938 -1.0025061462383107e-18,
1939 -4.462794158625518e-22,
1940 ],
1941 &mut [
1942 0.0,
1943 -0.0007122622584701569,
1944 -1.2678416507678478e-07,
1945 -4.514641118748122e-11,
1946 -2.0096353119518367e-14,
1947 -1.0019350865558619e-17,
1948 4.90907357448807e-21,
1949 ],
1950 );
1951 assert_eq!(res.0, 1.4056304412645388);
1952 assert!(res.1.is_nan());
1953 assert!(res.2.is_nan());
1954 assert!(res.3.is_nan());
1955 assert!(res.4.is_nan());
1956 }
1957
1958 #[test]
1959 fn test_goed__C4f() {
1960 let geod = Geodesic::wgs84();
1961 let mut c = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0];
1962 geod._C4f(0.12, &mut c);
1963 assert_eq!(
1964 c,
1965 [
1966 0.6420952961066771,
1967 0.0023680700061156517,
1968 9.96704067834604e-05,
1969 5.778187189466089e-06,
1970 3.9979026199316593e-07,
1971 3.2140078103714466e-08,
1972 ]
1973 );
1974 }
1975
1976 #[test]
1977 fn test_goed__C3f() {
1978 let geod = Geodesic::wgs84();
1979 let mut c = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0];
1980 geod._C3f(0.12, &mut c);
1981 assert_eq!(
1982 c,
1983 [
1984 1.0,
1985 0.031839442894193756,
1986 0.0009839921354137713,
1987 5.0055242248766214e-05,
1988 3.1656788204092044e-06,
1989 2.0412e-07,
1990 ]
1991 );
1992 }
1993
1994 #[test]
1995 fn test_goed__A3f() {
1996 let geod = Geodesic::wgs84();
1997 assert_eq!(geod._A3f(0.12), 0.9363788874000158);
1998 }
1999
2000 #[test]
2001 fn test_geod_init() {
2002 let geod = Geodesic::wgs84();
2005 assert_eq!(geod.a, 6378137.0, "geod.a wrong");
2006 assert_eq!(geod.f, 0.0033528106647474805, "geod.f wrong");
2007 assert_eq!(geod._f1, 0.9966471893352525, "geod._f1 wrong");
2008 assert_eq!(geod._e2, 0.0066943799901413165, "geod._e2 wrong");
2009 assert_eq!(geod._ep2, 0.006739496742276434, "geod._ep2 wrong");
2010 assert_eq!(geod._n, 0.0016792203863837047, "geod._n wrong");
2011 assert_eq!(geod._b, 6356752.314245179, "geod._b wrong");
2012 assert_eq!(geod._c2, 40589732499314.76, "geod._c2 wrong");
2013 assert_eq!(geod._etol2, 3.6424611488788524e-08, "geod._etol2 wrong");
2014 assert_eq!(
2015 geod._A3x,
2016 [
2017 -0.0234375,
2018 -0.046927475637074494,
2019 -0.06281503005876607,
2020 -0.2502088451303832,
2021 -0.49916038980680816,
2022 1.0
2023 ],
2024 "geod._A3x wrong"
2025 );
2026
2027 assert_eq!(
2028 geod._C3x,
2029 [
2030 0.0234375,
2031 0.03908873781853724,
2032 0.04695366939653196,
2033 0.12499964752736174,
2034 0.24958019490340408,
2035 0.01953125,
2036 0.02345061890926862,
2037 0.046822392185686165,
2038 0.062342661206936094,
2039 0.013671875,
2040 0.023393770302437927,
2041 0.025963026642854565,
2042 0.013671875,
2043 0.01362595881755982,
2044 0.008203125
2045 ],
2046 "geod._C3x wrong"
2047 );
2048 assert_eq!(
2049 geod._C4x,
2050 [
2051 0.00646020646020646,
2052 0.0035037627212872787,
2053 0.034742279454780166,
2054 -0.01921732223244865,
2055 -0.19923321555984239,
2056 0.6662190894642603,
2057 0.000111000111000111,
2058 0.003426620602971002,
2059 -0.009510765372597735,
2060 -0.01893413691235592,
2061 0.0221370239510936,
2062 0.0007459207459207459,
2063 -0.004142006291321442,
2064 -0.00504225176309005,
2065 0.007584982177746079,
2066 -0.0021565735851450138,
2067 -0.001962613370670692,
2068 0.0036104265913438913,
2069 -0.0009472009472009472,
2070 0.0020416649913317735,
2071 0.0012916376552740189
2072 ],
2073 "geod._C4x wrong"
2074 );
2075 }
2076
2077 #[test]
2086 fn test_std_geodesic_geodsolve0() {
2087 let geod = Geodesic::wgs84();
2088 let (s12, azi1, azi2, _a12) = geod.inverse(40.6, -73.8, 49.01666667, 2.55);
2089 assert_relative_eq!(azi1, 53.47022, epsilon = 0.5e-5);
2090 assert_relative_eq!(azi2, 111.59367, epsilon = 0.5e-5);
2091 assert_relative_eq!(s12, 5853226.0, epsilon = 0.5);
2092 }
2093
2094 #[test]
2095 fn test_std_geodesic_geodsolve1() {
2096 let geod = Geodesic::wgs84();
2097 let (lat2, lon2, azi2) = geod.direct(40.63972222, -73.77888889, 53.5, 5850e3);
2098 assert_relative_eq!(lat2, 49.01467, epsilon = 0.5e-5);
2099 assert_relative_eq!(lon2, 2.56106, epsilon = 0.5e-5);
2100 assert_relative_eq!(azi2, 111.62947, epsilon = 0.5e-5);
2101 }
2102
2103 #[test]
2104 fn test_std_geodesic_geodsolve2() {
2105 let geod = Geodesic::new(6.4e6, -1f64 / 150.0);
2107 let (s12, azi1, azi2, _a12) = geod.inverse(0.07476, 0.0, -0.07476, 180.0);
2108 assert_relative_eq!(azi1, 90.00078, epsilon = 0.5e-5);
2109 assert_relative_eq!(azi2, 90.00078, epsilon = 0.5e-5);
2110 assert_relative_eq!(s12, 20106193.0, epsilon = 0.5);
2111 let (s12, azi1, azi2, _a12) = geod.inverse(0.1, 0.0, -0.1, 180.0);
2112 assert_relative_eq!(azi1, 90.00105, epsilon = 0.5e-5);
2113 assert_relative_eq!(azi2, 90.00105, epsilon = 0.5e-5);
2114 assert_relative_eq!(s12, 20106193.0, epsilon = 0.5);
2115 }
2116
2117 #[test]
2118 fn test_std_geodesic_geodsolve4() {
2119 let geod = Geodesic::wgs84();
2121 let s12: f64 = geod.inverse(36.493349428792, 0.0, 36.49334942879201, 0.0000008);
2122 assert_relative_eq!(s12, 0.072, epsilon = 0.5e-3);
2123 }
2124
2125 #[test]
2126 fn test_std_geodesic_geodsolve5() {
2127 let geod = Geodesic::wgs84();
2129 let (lat2, lon2, azi2) = geod.direct(0.01777745589997, 30.0, 0.0, 10e6);
2130 assert_relative_eq!(lat2, 90.0, epsilon = 0.5e-5);
2131 if lon2 < 0.0 {
2132 assert_relative_eq!(lon2, -150.0, epsilon = 0.5e-5);
2133 assert_relative_eq!(azi2.abs(), 180.0, epsilon = 0.5e-5);
2134 } else {
2135 assert_relative_eq!(lon2, 30.0, epsilon = 0.5e-5);
2136 assert_relative_eq!(azi2, 0.0, epsilon = 0.5e-5);
2137 }
2138 }
2139
2140 #[test]
2141 fn test_std_geodesic_geodsolve6() {
2142 let geod = Geodesic::wgs84();
2145 let s12: f64 = geod.inverse(
2146 88.202499451857,
2147 0.0,
2148 -88.202499451857,
2149 179.981022032992859592,
2150 );
2151 assert_relative_eq!(s12, 20003898.214, epsilon = 0.5e-3);
2152 let s12: f64 = geod.inverse(
2153 89.333123580033,
2154 0.0,
2155 -89.333123580032997687,
2156 179.99295812360148422,
2157 );
2158 assert_relative_eq!(s12, 20003926.881, epsilon = 0.5e-3);
2159 }
2160
2161 #[test]
2162 fn test_std_geodesic_geodsolve9() {
2163 let geod = Geodesic::wgs84();
2165 let s12: f64 = geod.inverse(
2166 56.320923501171,
2167 0.0,
2168 -56.320923501171,
2169 179.664747671772880215,
2170 );
2171 assert_relative_eq!(s12, 19993558.287, epsilon = 0.5e-3);
2172 }
2173
2174 #[test]
2175 fn test_std_geodesic_geodsolve10() {
2176 let geod = Geodesic::wgs84();
2179 let s12: f64 = geod.inverse(
2180 52.784459512564,
2181 0.0,
2182 -52.784459512563990912,
2183 179.634407464943777557,
2184 );
2185 assert_relative_eq!(s12, 19991596.095, epsilon = 0.5e-3);
2186 }
2187
2188 #[test]
2189 fn test_std_geodesic_geodsolve11() {
2190 let geod = Geodesic::wgs84();
2193 let s12: f64 = geod.inverse(
2194 48.522876735459,
2195 0.0,
2196 -48.52287673545898293,
2197 179.599720456223079643,
2198 );
2199 assert_relative_eq!(s12, 19989144.774, epsilon = 0.5e-3);
2200 }
2201
2202 #[test]
2203 fn test_std_geodesic_geodsolve12() {
2204 let geod = Geodesic::new(89.8, -1.83);
2208 let (s12, azi1, azi2, _a12) = geod.inverse(0.0, 0.0, -10.0, 160.0);
2209 assert_relative_eq!(azi1, 120.27, epsilon = 1e-2);
2210 assert_relative_eq!(azi2, 105.15, epsilon = 1e-2);
2211 assert_relative_eq!(s12, 266.7, epsilon = 1e-1);
2212 }
2213
2214 #[test]
2215 fn test_std_geodesic_geodsolve14() {
2216 let geod = Geodesic::wgs84();
2218 let (s12, azi1, azi2, _a12) = geod.inverse(0.0, 0.0, 1.0, f64::NAN);
2219 assert!(azi1.is_nan());
2220 assert!(azi2.is_nan());
2221 assert!(s12.is_nan());
2222 }
2223
2224 #[test]
2225 fn test_std_geodesic_geodsolve15() {
2226 let geod = Geodesic::new(6.4e6, -1f64 / 150.0);
2229 let (_lat2, _lon2, _azi2, _m12, _M12, _M21, S12, _a12) = geod.direct(1.0, 2.0, 3.0, 4.0);
2230 assert_relative_eq!(S12, 23700.0, epsilon = 0.5);
2231 }
2232
2233 #[test]
2234 fn test_std_geodesic_geodsolve17() {
2235 let geod = Geodesic::new(6.4e6, -1f64 / 150.0);
2237 let (_a12, lat2, lon2, azi2, _s12, _m12, _M12, _M21, _S12) = geod._gen_direct(
2238 40.0,
2239 -75.0,
2240 -10.0,
2241 false,
2242 2e7,
2243 caps::STANDARD | caps::LONG_UNROLL,
2244 );
2245 assert_relative_eq!(lat2, -39.0, epsilon = 1.0);
2246 assert_relative_eq!(lon2, -254.0, epsilon = 1.0);
2247 assert_relative_eq!(azi2, -170.0, epsilon = 1.0);
2248
2249 let line = GeodesicLine::new(&geod, 40.0, -75.0, -10.0, None, None, None);
2250 let (_a12, lat2, lon2, azi2, _s12, _m12, _M12, _M21, _S12) =
2251 line._gen_position(false, 2e7, caps::STANDARD | caps::LONG_UNROLL);
2252 assert_relative_eq!(lat2, -39.0, epsilon = 1.0);
2253 assert_relative_eq!(lon2, -254.0, epsilon = 1.0);
2254 assert_relative_eq!(azi2, -170.0, epsilon = 1.0);
2255
2256 let (lat2, lon2, azi2) = geod.direct(40.0, -75.0, -10.0, 2e7);
2257 assert_relative_eq!(lat2, -39.0, epsilon = 1.0);
2258 assert_relative_eq!(lon2, 105.0, epsilon = 1.0);
2259 assert_relative_eq!(azi2, -170.0, epsilon = 1.0);
2260
2261 let (_a12, lat2, lon2, azi2, _s12, _m12, _M12, _M21, _S12) =
2262 line._gen_position(false, 2e7, caps::STANDARD);
2263 assert_relative_eq!(lat2, -39.0, epsilon = 1.0);
2264 assert_relative_eq!(lon2, 105.0, epsilon = 1.0);
2265 assert_relative_eq!(azi2, -170.0, epsilon = 1.0);
2266 }
2267
2268 #[test]
2269 fn test_std_geodesic_geodsolve26() {
2270 let geod = Geodesic::new(6.4e6, 0.0);
2272 let (_a12, _s12, _salp1, _calp1, _salp2, _calp2, _m12, _M12, _M21, S12) =
2273 geod._gen_inverse(1.0, 2.0, 3.0, 4.0, caps::AREA);
2274 assert_relative_eq!(S12, 49911046115.0, epsilon = 0.5);
2275 }
2276
2277 #[test]
2278 fn test_std_geodesic_geodsolve28() {
2279 let geod = Geodesic::new(6.4e6, 0.1);
2282 let (a12, _lat2, _lon2, _azi2, _s12, _m12, _M12, _M21, _S12) =
2283 geod._gen_direct(1.0, 2.0, 10.0, false, 5e6, caps::STANDARD);
2284 assert_relative_eq!(a12, 48.55570690, epsilon = 0.5e-8);
2285 }
2286
2287 #[test]
2288 fn test_std_geodesic_geodsolve29() {
2289 let geod = Geodesic::wgs84();
2291 let (_a12, s12, _salp1, _calp1, _salp2, _calp2, _m12, _M12, _M21, _S12) =
2292 geod._gen_inverse(0.0, 539.0, 0.0, 181.0, caps::STANDARD);
2293 assert_relative_eq!(s12, 222639.0, epsilon = 0.5);
2298 let (_a12, s12, _salp1, _calp1, _salp2, _calp2, _m12, _M12, _M21, _S12) =
2299 geod._gen_inverse(0.0, 539.0, 0.0, 181.0, caps::STANDARD | caps::LONG_UNROLL);
2300 assert_relative_eq!(s12, 222639.0, epsilon = 0.5);
2303 }
2304
2305 #[test]
2306 fn test_std_geodesic_geodsolve33() {
2307 let geod = Geodesic::wgs84();
2311 let (s12, azi1, azi2, _a12) = geod.inverse(0.0, 0.0, 0.0, 179.0);
2312 assert_relative_eq!(azi1, 90.0, epsilon = 0.5e-5);
2313 assert_relative_eq!(azi2, 90.0, epsilon = 0.5e-5);
2314 assert_relative_eq!(s12, 19926189.0, epsilon = 0.5);
2315 let (s12, azi1, azi2, _a12) = geod.inverse(0.0, 0.0, 0.0, 179.5);
2316 assert_relative_eq!(azi1, 55.96650, epsilon = 0.5e-5);
2317 assert_relative_eq!(azi2, 124.03350, epsilon = 0.5e-5);
2318 assert_relative_eq!(s12, 19980862.0, epsilon = 0.5);
2319 let (s12, azi1, azi2, _a12) = geod.inverse(0.0, 0.0, 0.0, 180.0);
2320 assert_relative_eq!(azi1, 0.0, epsilon = 0.5e-5);
2321 assert_relative_eq!(azi2.abs(), 180.0, epsilon = 0.5e-5);
2322 assert_relative_eq!(s12, 20003931.0, epsilon = 0.5);
2323 let (s12, azi1, azi2, _a12) = geod.inverse(0.0, 0.0, 1.0, 180.0);
2324 assert_relative_eq!(azi1, 0.0, epsilon = 0.5e-5);
2325 assert_relative_eq!(azi2.abs(), 180.0, epsilon = 0.5e-5);
2326 assert_relative_eq!(s12, 19893357.0, epsilon = 0.5);
2327
2328 let geod = Geodesic::new(6.4e6, 0.0);
2329 let (s12, azi1, azi2, _a12) = geod.inverse(0.0, 0.0, 0.0, 179.0);
2330 assert_relative_eq!(azi1, 90.0, epsilon = 0.5e-5);
2331 assert_relative_eq!(azi2, 90.0, epsilon = 0.5e-5);
2332 assert_relative_eq!(s12, 19994492.0, epsilon = 0.5);
2333 let (s12, azi1, azi2, _a12) = geod.inverse(0.0, 0.0, 0.0, 180.0);
2334 assert_relative_eq!(azi1, 0.0, epsilon = 0.5e-5);
2335 assert_relative_eq!(azi2.abs(), 180.0, epsilon = 0.5e-5);
2336 assert_relative_eq!(s12, 20106193.0, epsilon = 0.5);
2337 let (s12, azi1, azi2, _a12) = geod.inverse(0.0, 0.0, 1.0, 180.0);
2338 assert_relative_eq!(azi1, 0.0, epsilon = 0.5e-5);
2339 assert_relative_eq!(azi2.abs(), 180.0, epsilon = 0.5e-5);
2340 assert_relative_eq!(s12, 19994492.0, epsilon = 0.5);
2341
2342 let geod = Geodesic::new(6.4e6, -1.0 / 300.0);
2343 let (s12, azi1, azi2, _a12) = geod.inverse(0.0, 0.0, 0.0, 179.0);
2344 assert_relative_eq!(azi1, 90.0, epsilon = 0.5e-5);
2345 assert_relative_eq!(azi2, 90.0, epsilon = 0.5e-5);
2346 assert_relative_eq!(s12, 19994492.0, epsilon = 0.5);
2347 let (s12, azi1, azi2, _a12) = geod.inverse(0.0, 0.0, 0.0, 180.0);
2348 assert_relative_eq!(azi1, 90.0, epsilon = 0.5e-5);
2349 assert_relative_eq!(azi2, 90.0, epsilon = 0.5e-5);
2350 assert_relative_eq!(s12, 20106193.0, epsilon = 0.5);
2351 let (s12, azi1, azi2, _a12) = geod.inverse(0.0, 0.0, 0.5, 180.0);
2352 assert_relative_eq!(azi1, 33.02493, epsilon = 0.5e-5);
2353 assert_relative_eq!(azi2, 146.97364, epsilon = 0.5e-5);
2354 assert_relative_eq!(s12, 20082617.0, epsilon = 0.5);
2355 let (s12, azi1, azi2, _a12) = geod.inverse(0.0, 0.0, 1.0, 180.0);
2356 assert_relative_eq!(azi1, 0.0, epsilon = 0.5e-5);
2357 assert_relative_eq!(azi2.abs(), 180.0, epsilon = 0.5e-5);
2358 assert_relative_eq!(s12, 20027270.0, epsilon = 0.5);
2359 }
2360
2361 #[test]
2362 fn test_std_geodesic_geodsolve55() {
2363 let geod = Geodesic::wgs84();
2366 let (s12, azi1, azi2, _a12) = geod.inverse(f64::NAN, 0.0, 0.0, 90.0);
2367 assert!(azi1.is_nan());
2368 assert!(azi2.is_nan());
2369 assert!(s12.is_nan());
2370 let (s12, azi1, azi2, _a12) = geod.inverse(f64::NAN, 0.0, 90.0, 3.0);
2371 assert!(azi1.is_nan());
2372 assert!(azi2.is_nan());
2373 assert!(s12.is_nan());
2374 }
2375
2376 #[test]
2377 fn test_std_geodesic_geodsolve59() {
2378 let geod = Geodesic::wgs84();
2380 let (s12, azi1, azi2, _a12) = geod.inverse(5.0, 0.00000000000001, 10.0, 180.0);
2381 assert_relative_eq!(azi1, 0.000000000000035, epsilon = 1.5e-14);
2382 assert_relative_eq!(azi2, 179.99999999999996, epsilon = 1.5e-14);
2383 assert_relative_eq!(s12, 18345191.174332713, epsilon = 5e-9);
2384 }
2385
2386 #[test]
2387 fn test_std_geodesic_geodsolve61() {
2388 let geod = Geodesic::wgs84();
2390 let (_a12, lat2, lon2, azi2, _s12, _m12, _M12, _M21, _S12) = geod._gen_direct(
2391 45.0,
2392 0.0,
2393 -0.000000000000000003,
2394 false,
2395 1e7,
2396 caps::STANDARD | caps::LONG_UNROLL,
2397 );
2398 assert_relative_eq!(lat2, 45.30632, epsilon = 0.5e-5);
2399 assert_relative_eq!(lon2, -180.0, epsilon = 0.5e-5);
2400 assert_relative_eq!(azi2.abs(), 180.0, epsilon = 0.5e-5);
2401 }
2409
2410 #[test]
2435 fn test_std_geodesic_geodsolve73() {
2436 let geod = Geodesic::wgs84();
2442 let (lat2, lon2, azi2) = geod.direct(90.0, 10.0, 180.0, -1e6);
2443 assert_relative_eq!(lat2, 81.04623, epsilon = 0.5e-5);
2444 assert_relative_eq!(lon2, -170.0, epsilon = 0.5e-5);
2445 assert_relative_eq!(azi2, 0.0, epsilon = 0.5e-5);
2446 assert!(azi2.is_sign_positive());
2447 }
2448
2449 #[test]
2450 fn test_std_geodesic_geodsolve74() {
2451 let geod = Geodesic::wgs84();
2454 let (a12, s12, azi1, azi2, m12, M12, M21, S12) =
2455 geod._gen_inverse_azi(54.1589, 15.3872, 54.1591, 15.3877, caps::ALL);
2456 assert_relative_eq!(azi1, 55.723110355, epsilon = 5e-9);
2457 assert_relative_eq!(azi2, 55.723515675, epsilon = 5e-9);
2458 assert_relative_eq!(s12, 39.527686385, epsilon = 5e-9);
2459 assert_relative_eq!(a12, 0.000355495, epsilon = 5e-9);
2460 assert_relative_eq!(m12, 39.527686385, epsilon = 5e-9);
2461 assert_relative_eq!(M12, 0.999999995, epsilon = 5e-9);
2462 assert_relative_eq!(M21, 0.999999995, epsilon = 5e-9);
2463 assert_relative_eq!(S12, 286698586.30197, epsilon = 5e-4);
2464 }
2465
2466 #[test]
2467 fn test_std_geodesic_geodsolve76() {
2468 let geod = Geodesic::wgs84();
2471 let (s12, azi1, azi2, _a12) = geod.inverse(
2472 -(41.0 + 19.0 / 60.0),
2473 174.0 + 49.0 / 60.0,
2474 40.0 + 58.0 / 60.0,
2475 -(5.0 + 30.0 / 60.0),
2476 );
2477 assert_relative_eq!(azi1, 160.39137649664, epsilon = 0.5e-11);
2478 assert_relative_eq!(azi2, 19.50042925176, epsilon = 0.5e-11);
2479 assert_relative_eq!(s12, 19960543.857179, epsilon = 0.5e-6);
2480 }
2481
2482 #[test]
2483 fn test_std_geodesic_geodsolve78() {
2484 let geod = Geodesic::wgs84();
2486 let (s12, azi1, azi2, _a12) = geod.inverse(27.2, 0.0, -27.1, 179.5);
2487 assert_relative_eq!(azi1, 45.82468716758, epsilon = 0.5e-11);
2488 assert_relative_eq!(azi2, 134.22776532670, epsilon = 0.5e-11);
2489 assert_relative_eq!(s12, 19974354.765767, epsilon = 0.5e-6);
2490 }
2491
2492 #[test]
2493 fn test_std_geodesic_geodsolve80() {
2494 let geod = Geodesic::wgs84();
2497 let (_a12, _s12, _salp1, _calp1, _salp2, _calp2, _m12, M12, M21, _S12) =
2498 geod._gen_inverse(0.0, 0.0, 0.0, 90.0, caps::GEODESICSCALE);
2499 assert_relative_eq!(M12, -0.00528427534, epsilon = 0.5e-10);
2500 assert_relative_eq!(M21, -0.00528427534, epsilon = 0.5e-10);
2501
2502 let (_a12, _s12, _salp1, _calp1, _salp2, _calp2, _m12, M12, M21, _S12) =
2503 geod._gen_inverse(0.0, 0.0, 1e-6, 1e-6, caps::GEODESICSCALE);
2504 assert_relative_eq!(M12, 1.0, epsilon = 0.5e-10);
2505 assert_relative_eq!(M21, 1.0, epsilon = 0.5e-10);
2506
2507 let (a12, s12, azi1, azi2, m12, M12, M21, S12) =
2508 geod._gen_inverse_azi(20.001, 0.0, 20.001, 0.0, caps::ALL);
2509 assert_relative_eq!(a12, 0.0, epsilon = 1e-13);
2510 assert_relative_eq!(s12, 0.0, epsilon = 1e-8);
2511 assert_relative_eq!(azi1, 180.0, epsilon = 1e-13);
2512 assert_relative_eq!(azi2, 180.0, epsilon = 1e-13);
2513 assert_relative_eq!(m12, 0.0, epsilon = 1e-8);
2514 assert_relative_eq!(M12, 1.0, epsilon = 1e-15);
2515 assert_relative_eq!(M21, 1.0, epsilon = 1e-15);
2516 assert_relative_eq!(S12, 0.0, epsilon = 1e-10);
2517
2518 let (a12, s12, azi1, azi2, m12, M12, M21, S12) =
2519 geod._gen_inverse_azi(90.0, 0.0, 90.0, 180.0, caps::ALL);
2520 assert_relative_eq!(a12, 0.0, epsilon = 1e-13);
2521 assert_relative_eq!(s12, 0.0, epsilon = 1e-8);
2522 assert_relative_eq!(azi1, 0.0, epsilon = 1e-13);
2523 assert_relative_eq!(azi2, 180.0, epsilon = 1e-13);
2524 assert_relative_eq!(m12, 0.0, epsilon = 1e-8);
2525 assert_relative_eq!(M12, 1.0, epsilon = 1e-15);
2526 assert_relative_eq!(M21, 1.0, epsilon = 1e-15);
2527 assert_relative_eq!(S12, 127516405431022.0, epsilon = 0.5);
2528
2529 let line = GeodesicLine::new(&geod, 1.0, 2.0, 90.0, Some(caps::LATITUDE), None, None);
2531 let (a12, _lat2, _lon2, _azi2, _s12, _m12, _M12, _M21, _S12) =
2532 line._gen_position(false, 1000.0, caps::CAP_NONE);
2533 assert!(a12.is_nan());
2534 }
2535
2536 #[test]
2537 fn test_std_geodesic_geodsolve84() {
2538 let geod = Geodesic::wgs84();
2541 let (lat2, lon2, azi2) = geod.direct(0.0, 0.0, 90.0, f64::INFINITY);
2542 assert!(lat2.is_nan());
2543 assert!(lon2.is_nan());
2544 assert!(azi2.is_nan());
2545 let (lat2, lon2, azi2) = geod.direct(0.0, 0.0, 90.0, f64::NAN);
2546 assert!(lat2.is_nan());
2547 assert!(lon2.is_nan());
2548 assert!(azi2.is_nan());
2549 let (lat2, lon2, azi2) = geod.direct(0.0, 0.0, f64::INFINITY, 1000.0);
2550 assert!(lat2.is_nan());
2551 assert!(lon2.is_nan());
2552 assert!(azi2.is_nan());
2553 let (lat2, lon2, azi2) = geod.direct(0.0, 0.0, f64::NAN, 1000.0);
2554 assert!(lat2.is_nan());
2555 assert!(lon2.is_nan());
2556 assert!(azi2.is_nan());
2557 let (lat2, lon2, azi2) = geod.direct(0.0, f64::INFINITY, 90.0, 1000.0);
2558 assert_eq!(lat2, 0.0);
2559 assert!(lon2.is_nan());
2560 assert_eq!(azi2, 90.0);
2561 let (lat2, lon2, azi2) = geod.direct(0.0, f64::NAN, 90.0, 1000.0);
2562 assert_eq!(lat2, 0.0);
2563 assert!(lon2.is_nan());
2564 assert_eq!(azi2, 90.0);
2565 let (lat2, lon2, azi2) = geod.direct(f64::INFINITY, 0.0, 90.0, 1000.0);
2566 assert!(lat2.is_nan());
2567 assert!(lon2.is_nan());
2568 assert!(azi2.is_nan());
2569 let (lat2, lon2, azi2) = geod.direct(f64::NAN, 0.0, 90.0, 1000.0);
2570 assert!(lat2.is_nan());
2571 assert!(lon2.is_nan());
2572 assert!(azi2.is_nan());
2573 }
2574
2575 static FULL_TEST_PATH: &str = "test_fixtures/test_data_unzipped/GeodTest.dat";
2593 static SHORT_TEST_PATH: &str = "test_fixtures/test_data_unzipped/GeodTest-short.dat";
2594 static BUILTIN_TEST_PATH: &str = "test_fixtures/GeodTest-100.dat";
2595 fn test_input_path() -> &'static str {
2596 if cfg!(feature = "test_full") {
2597 FULL_TEST_PATH
2598 } else if cfg!(feature = "test_short") {
2599 SHORT_TEST_PATH
2600 } else {
2601 BUILTIN_TEST_PATH
2602 }
2603 }
2604
2605 fn geodtest_basic<T>(path: &str, f: T)
2606 where
2607 T: Fn(usize, &(f64, f64, f64, f64, f64, f64, f64, f64, f64, f64)),
2608 {
2609 let dir_base = std::env::current_dir().expect("Failed to determine current directory");
2610 let path_base = dir_base.as_path();
2611 let pathbuf = std::path::Path::new(path_base).join(path);
2612 let path = pathbuf.as_path();
2613 let file = match std::fs::File::open(path) {
2614 Ok(val) => val,
2615 Err(_error) => {
2616 let path_str = path
2617 .to_str()
2618 .expect("Failed to convert GeodTest path to string during error reporting");
2619 panic!("Failed to open test input file. Run `script/download-test-data.sh` to download test input to: {}\nFor details see https://geographiclib.sourceforge.io/html/geodesic.html#testgeod", path_str)
2620 }
2621 };
2622 let reader = std::io::BufReader::new(file);
2623 reader.lines().enumerate().for_each(|(i, line)| {
2624 let line_safe = line.expect("Failed to read line");
2625 let items: Vec<f64> = line_safe
2626 .split(' ')
2627 .enumerate()
2628 .map(|(j, item)| match item.parse::<f64>() {
2629 Ok(parsed) => parsed,
2630 Err(_error) => {
2631 panic!("Error parsing item {} on line {}: {}", j + 1, i + 1, item)
2632 }
2633 })
2634 .collect();
2635 assert_eq!(items.len(), 10);
2636 let tuple = (
2637 items[0], items[1], items[2], items[3], items[4], items[5], items[6], items[7],
2638 items[8], items[9],
2639 );
2640 f(i + 1, &tuple); });
2642 }
2643
2644 #[test]
2645 fn test_geodtest_geodesic_direct12() {
2646 let g = std::sync::Arc::new(std::sync::Mutex::new(Geodesic::wgs84()));
2647
2648 geodtest_basic(
2649 test_input_path(),
2650 |_line_num, &(lat1, lon1, azi1, lat2, lon2, azi2, s12, a12, m12, S12)| {
2651 let g = g.lock().unwrap();
2652 let (lat2_out, lon2_out, azi2_out, m12_out, _M12_out, _M21_out, S12_out, a12_out) =
2653 g.direct(lat1, lon1, azi1, s12);
2654 assert_relative_eq!(lat2, lat2_out, epsilon = 1e-13);
2655 assert_relative_eq!(lon2, lon2_out, epsilon = 2e-8);
2656 assert_relative_eq!(azi2, azi2_out, epsilon = 2e-8);
2657 assert_relative_eq!(m12, m12_out, epsilon = 9e-9);
2658 assert_relative_eq!(S12, S12_out, epsilon = 2e4); assert_relative_eq!(a12, a12_out, epsilon = 9e-14);
2660 },
2661 );
2662 }
2663
2664 #[test]
2665 fn test_geodtest_geodesic_direct21() {
2666 let g = std::sync::Arc::new(std::sync::Mutex::new(Geodesic::wgs84()));
2667
2668 geodtest_basic(
2669 test_input_path(),
2670 |_line_num, &(lat1, lon1, azi1, lat2, lon2, azi2, s12, a12, m12, S12)| {
2671 let g = g.lock().unwrap();
2672 let (lat1, lon1, azi1, lat2, lon2, azi2, s12, a12, m12, S12) =
2674 (lat2, lon2, azi2, lat1, lon1, azi1, -s12, -a12, -m12, -S12);
2675 let (lat2_out, lon2_out, azi2_out, m12_out, _M12_out, _M21_out, S12_out, a12_out) =
2676 g.direct(lat1, lon1, azi1, s12);
2677 assert_relative_eq!(lat2, lat2_out, epsilon = 8e-14);
2678 assert_relative_eq!(lon2, lon2_out, epsilon = 4e-6);
2679 assert_relative_eq!(azi2, azi2_out, epsilon = 4e-6);
2680 assert_relative_eq!(m12, m12_out, epsilon = 1e-8);
2681 assert_relative_eq!(S12, S12_out, epsilon = 3e6); assert_relative_eq!(a12, a12_out, epsilon = 9e-14);
2683 },
2684 );
2685 }
2686
2687 #[test]
2688 fn test_geodtest_geodesic_inverse12() {
2689 let g = std::sync::Arc::new(std::sync::Mutex::new(Geodesic::wgs84()));
2690
2691 geodtest_basic(
2692 test_input_path(),
2693 |line_num, &(lat1, lon1, azi1, lat2, lon2, azi2, s12, a12, m12, S12)| {
2694 let g = g.lock().unwrap();
2695 let (s12_out, azi1_out, azi2_out, m12_out, _M12_out, _M21_out, S12_out, a12_out) =
2696 g.inverse(lat1, lon1, lat2, lon2);
2697 assert_relative_eq!(s12, s12_out, epsilon = 8e-9);
2698 assert_relative_eq!(azi1, azi1_out, epsilon = 2e-2);
2699 assert_relative_eq!(azi2, azi2_out, epsilon = 2e-2);
2700 assert_relative_eq!(m12, m12_out, epsilon = 5e-5);
2701 if line_num != 400001 {
2706 assert_relative_eq!(S12, S12_out, epsilon = 3e10); }
2708 assert_relative_eq!(a12, a12_out, epsilon = 2e-10);
2709 },
2710 );
2711 }
2712
2713 #[test]
2714 fn test_turnaround() {
2715 let g = Geodesic::wgs84();
2716
2717 let start = (0.0, 0.0);
2718 let destination = (0.0, 1.0);
2719
2720 let (distance, azi1, _, _) = g.inverse(start.0, start.1, destination.0, destination.1);
2721
2722 assert_eq!(azi1, 90.0);
2724
2725 let turn_around = azi1 + 180.0;
2727
2728 assert_eq!(turn_around, 270.0);
2730
2731 let (lat, lon) = g.direct(destination.0, destination.1, turn_around, distance);
2733 assert_relative_eq!(lat, start.0, epsilon = 1.0e-3);
2734 assert_relative_eq!(lon, start.1, epsilon = 1.0e-3);
2735 }
2736}