1#![allow(non_snake_case)]
2
3use crate::geodesic::{self, GEODESIC_ORDER};
4use crate::geodesic_capability as caps;
5use crate::geomath;
6use std::collections::HashMap;
7
8#[derive(Copy, Clone, PartialEq, PartialOrd, Debug)]
12pub struct GeodesicLine {
13 tiny_: f64, _A1m1: f64,
15 _A2m1: f64,
16 _A3c: f64,
17 _A4: f64,
18 _B11: f64,
19 _B21: f64,
20 _B31: f64,
21 _B41: f64,
22 _C1a: [f64; GEODESIC_ORDER + 1],
23 _C1pa: [f64; GEODESIC_ORDER + 1],
24 _C2a: [f64; GEODESIC_ORDER + 1],
25 _C3a: [f64; GEODESIC_ORDER],
26 _C4a: [f64; GEODESIC_ORDER],
27 _b: f64,
28 _c2: f64,
29 _calp0: f64,
30 _csig1: f64,
31 _comg1: f64,
32 _ctau1: f64,
33 _dn1: f64,
34 _f1: f64,
35 _k2: f64,
36 _salp0: f64,
37 _somg1: f64,
38 _ssig1: f64,
39 _stau1: f64,
40 _a13: f64,
41 _a: f64,
42 azi1: f64,
43 calp1: f64,
44 caps: u64,
45 f: f64,
46 lat1: f64,
47 lon1: f64,
48 _s13: f64,
49 salp1: f64,
50}
51
52impl GeodesicLine {
53 pub fn new(
74 geod: &geodesic::Geodesic,
75 lat1: f64,
76 lon1: f64,
77 azi1: f64,
78 caps: Option<u64>,
79 salp1: Option<f64>,
80 calp1: Option<f64>,
81 ) -> Self {
82 let caps = match caps {
83 None => caps::STANDARD | caps::DISTANCE_IN,
84 Some(caps) => caps,
85 };
86 let salp1 = match salp1 {
87 None => f64::NAN,
88 Some(salp1) => salp1,
89 };
90 let calp1 = match calp1 {
91 None => f64::NAN,
92 Some(calp1) => calp1,
93 };
94
95 let tiny_ = geomath::get_min_val().sqrt();
97
98 let _a = geod.a;
99 let f = geod.f;
100 let _b = geod._b;
101 let _c2 = geod._c2;
102 let _f1 = geod._f1;
103 let caps = caps | caps::LATITUDE | caps::AZIMUTH | caps::LONG_UNROLL;
104 let (azi1, salp1, calp1) = if salp1.is_nan() || calp1.is_nan() {
105 let azi1 = geomath::ang_normalize(azi1);
106 let (salp1, calp1) = geomath::sincosd(geomath::ang_round(azi1));
107 (azi1, salp1, calp1)
108 } else {
109 (azi1, salp1, calp1)
110 };
111 let lat1 = geomath::lat_fix(lat1);
112
113 let (mut sbet1, mut cbet1) = geomath::sincosd(geomath::ang_round(lat1));
114 sbet1 *= _f1;
115 geomath::norm(&mut sbet1, &mut cbet1);
116 cbet1 = tiny_.max(cbet1);
117 let _dn1 = (1.0 + geod._ep2 * geomath::sq(sbet1)).sqrt();
118 let _salp0 = salp1 * cbet1;
119 let _calp0 = calp1.hypot(salp1 * sbet1);
120 let mut _ssig1 = sbet1;
121 let _somg1 = _salp0 * sbet1;
122 let mut _csig1 = if sbet1 != 0.0 || calp1 != 0.0 {
123 cbet1 * calp1
124 } else {
125 1.0
126 };
127 let _comg1 = _csig1;
128 geomath::norm(&mut _ssig1, &mut _csig1);
129 let _k2 = geomath::sq(_calp0) * geod._ep2;
130 let eps = _k2 / (2.0 * (1.0 + (1.0 + _k2).sqrt()) + _k2);
131
132 let mut _A1m1 = 0.0;
133 let mut _C1a: [f64; GEODESIC_ORDER + 1] = [0.0; GEODESIC_ORDER + 1];
134 let mut _B11 = 0.0;
135 let mut _stau1 = 0.0;
136 let mut _ctau1 = 0.0;
137 if caps & caps::CAP_C1 != 0 {
138 _A1m1 = geomath::_A1m1f(eps, geod.GEODESIC_ORDER);
139 geomath::_C1f(eps, &mut _C1a, geod.GEODESIC_ORDER);
140 _B11 = geomath::sin_cos_series(true, _ssig1, _csig1, &_C1a);
141 let s = _B11.sin();
142 let c = _B11.cos();
143 _stau1 = _ssig1 * c + _csig1 * s;
144 _ctau1 = _csig1 * c - _ssig1 * s;
145 }
146
147 let mut _C1pa: [f64; GEODESIC_ORDER + 1] = [0.0; GEODESIC_ORDER + 1];
148 if caps & caps::CAP_C1p != 0 {
149 geomath::_C1pf(eps, &mut _C1pa, geod.GEODESIC_ORDER);
150 }
151
152 let mut _A2m1 = 0.0;
153 let mut _C2a: [f64; GEODESIC_ORDER + 1] = [0.0; GEODESIC_ORDER + 1];
154 let mut _B21 = 0.0;
155 if caps & caps::CAP_C2 != 0 {
156 _A2m1 = geomath::_A2m1f(eps, geod.GEODESIC_ORDER);
157 geomath::_C2f(eps, &mut _C2a, geod.GEODESIC_ORDER);
158 _B21 = geomath::sin_cos_series(true, _ssig1, _csig1, &_C2a);
159 }
160
161 let mut _C3a: [f64; GEODESIC_ORDER] = [0.0; GEODESIC_ORDER];
162 let mut _A3c = 0.0;
163 let mut _B31 = 0.0;
164 if caps & caps::CAP_C3 != 0 {
165 geod._C3f(eps, &mut _C3a);
166 _A3c = -f * _salp0 * geod._A3f(eps);
167 _B31 = geomath::sin_cos_series(true, _ssig1, _csig1, &_C3a);
168 }
169
170 let mut _C4a: [f64; GEODESIC_ORDER] = [0.0; GEODESIC_ORDER];
171 let mut _A4 = 0.0;
172 let mut _B41 = 0.0;
173 if caps & caps::CAP_C4 != 0 {
174 geod._C4f(eps, &mut _C4a);
175 _A4 = geomath::sq(_a) * _calp0 * _salp0 * geod._e2;
176 _B41 = geomath::sin_cos_series(false, _ssig1, _csig1, &_C4a);
177 }
178
179 let _s13 = f64::NAN;
180 let _a13 = f64::NAN;
181
182 GeodesicLine {
183 tiny_,
184 _A1m1,
185 _A2m1,
186 _A3c,
187 _A4,
188 _B11,
189 _B21,
190 _B31,
191 _B41,
192 _C1a,
193 _C1pa,
194 _comg1,
195 _C2a,
196 _C3a,
197 _C4a,
198 _b,
199 _c2,
200 _calp0,
201 _csig1,
202 _ctau1,
203 _dn1,
204 _f1,
205 _k2,
206 _salp0,
207 _somg1,
208 _ssig1,
209 _stau1,
210 _a,
211 _a13,
212 azi1,
213 calp1,
214 caps,
215 f,
216 lat1,
217 lon1,
218 _s13,
219 salp1,
220 }
221 }
222
223 pub fn _gen_position(
264 &self,
265 arcmode: bool,
266 s12_a12: f64,
267 outmask: u64,
268 ) -> (f64, f64, f64, f64, f64, f64, f64, f64, f64) {
269 let mut a12 = f64::NAN;
270 let mut lat2 = f64::NAN;
271 let mut lon2 = f64::NAN;
272 let mut azi2 = f64::NAN;
273 let mut s12 = f64::NAN;
274 let mut m12 = f64::NAN;
275 let mut M12 = f64::NAN;
276 let mut M21 = f64::NAN;
277 let mut S12 = f64::NAN;
278 let outmask = outmask & (self.caps & caps::OUT_MASK);
279 if !(arcmode || (self.caps & (caps::OUT_MASK & caps::DISTANCE_IN) != 0)) {
280 return (a12, lat2, lon2, azi2, s12, m12, M12, M21, S12);
281 }
282
283 let mut B12 = 0.0;
284 let mut AB1 = 0.0;
285 let mut sig12: f64;
286 let mut ssig12: f64;
287 let mut csig12: f64;
288 let mut ssig2: f64;
289 let mut csig2: f64;
290 if arcmode {
291 sig12 = s12_a12.to_radians();
292 let res = geomath::sincosd(s12_a12);
293 ssig12 = res.0;
294 csig12 = res.1;
295 } else {
296 let tau12 = s12_a12 / (self._b * (1.0 + self._A1m1));
298
299 let s = tau12.sin();
300 let c = tau12.cos();
301
302 B12 = -geomath::sin_cos_series(
303 true,
304 self._stau1 * c + self._ctau1 * s,
305 self._ctau1 * c - self._stau1 * s,
306 &self._C1pa,
307 );
308 sig12 = tau12 - (B12 - self._B11);
309 ssig12 = sig12.sin();
310 csig12 = sig12.cos();
311 if self.f.abs() > 0.01 {
312 ssig2 = self._ssig1 * csig12 + self._csig1 * ssig12;
313 csig2 = self._csig1 * csig12 - self._ssig1 * ssig12;
314 B12 = geomath::sin_cos_series(true, ssig2, csig2, &self._C1a);
315 let serr = (1.0 + self._A1m1) * (sig12 + (B12 - self._B11)) - s12_a12 / self._b;
316 sig12 -= serr / (1.0 + self._k2 * geomath::sq(ssig2)).sqrt();
317 ssig12 = sig12.sin();
318 csig12 = sig12.cos();
319 }
320 };
321 ssig2 = self._ssig1 * csig12 + self._csig1 * ssig12;
322 csig2 = self._csig1 * csig12 - self._ssig1 * ssig12;
323 let dn2 = (1.0 + self._k2 * geomath::sq(ssig2)).sqrt();
324 if outmask & (caps::DISTANCE | caps::REDUCEDLENGTH | caps::GEODESICSCALE) != 0 {
325 if arcmode || self.f.abs() > 0.01 {
326 B12 = geomath::sin_cos_series(true, ssig2, csig2, &self._C1a);
327 }
328 AB1 = (1.0 + self._A1m1) * (B12 - self._B11);
329 }
330
331 let sbet2 = self._calp0 * ssig2;
332 let mut cbet2 = self._salp0.hypot(self._calp0 * csig2);
333 if cbet2 == 0.0 {
334 cbet2 = self.tiny_;
335 csig2 = self.tiny_;
336 }
337 let salp2 = self._salp0;
338 let calp2 = self._calp0 * csig2;
339 if outmask & caps::DISTANCE != 0 {
340 s12 = if arcmode {
341 self._b * ((1.0 + self._A1m1) * sig12 + AB1)
342 } else {
343 s12_a12
344 }
345 }
346 if outmask & caps::LONGITUDE != 0 {
347 let somg2 = self._salp0 * ssig2;
348 let comg2 = csig2;
349 let E = 1.0_f64.copysign(self._salp0);
350 let omg12 = if outmask & caps::LONG_UNROLL != 0 {
351 E * (sig12 - (ssig2.atan2(csig2) - self._ssig1.atan2(self._csig1))
352 + ((E * somg2).atan2(comg2) - (E * self._somg1).atan2(self._comg1)))
353 } else {
354 (somg2 * self._comg1 - comg2 * self._somg1)
355 .atan2(comg2 * self._comg1 + somg2 * self._somg1)
356 };
357 let lam12 = omg12
358 + self._A3c
359 * (sig12
360 + (geomath::sin_cos_series(true, ssig2, csig2, &self._C3a) - self._B31));
361 let lon12 = lam12.to_degrees();
362 lon2 = if outmask & caps::LONG_UNROLL != 0 {
363 self.lon1 + lon12
364 } else {
365 geomath::ang_normalize(
366 geomath::ang_normalize(self.lon1) + geomath::ang_normalize(lon12),
367 )
368 };
369 };
370
371 if outmask & caps::LATITUDE != 0 {
372 lat2 = geomath::atan2d(sbet2, self._f1 * cbet2);
373 }
374 if outmask & caps::AZIMUTH != 0 {
375 azi2 = geomath::atan2d(salp2, calp2);
376 }
377 if outmask & (caps::REDUCEDLENGTH | caps::GEODESICSCALE) != 0 {
378 let B22 = geomath::sin_cos_series(true, ssig2, csig2, &self._C2a);
379 let AB2 = (1.0 + self._A2m1) * (B22 - self._B21);
380 let J12 = (self._A1m1 - self._A2m1) * sig12 + (AB1 - AB2);
381 if outmask & caps::REDUCEDLENGTH != 0 {
382 m12 = self._b
383 * ((dn2 * (self._csig1 * ssig2) - self._dn1 * (self._ssig1 * csig2))
384 - self._csig1 * csig2 * J12);
385 }
386 if outmask & caps::GEODESICSCALE != 0 {
387 let t =
388 self._k2 * (ssig2 - self._ssig1) * (ssig2 + self._ssig1) / (self._dn1 + dn2);
389 M12 = csig12 + (t * ssig2 - csig2 * J12) * self._ssig1 / self._dn1;
390 M21 = csig12 - (t * self._ssig1 - self._csig1 * J12) * ssig2 / dn2;
391 }
392 }
393 if outmask & caps::AREA != 0 {
394 let B42 = geomath::sin_cos_series(false, ssig2, csig2, &self._C4a);
395 let salp12: f64;
396 let calp12: f64;
397 if self._calp0 == 0.0 || self._salp0 == 0.0 {
398 salp12 = salp2 * self.calp1 - calp2 * self.salp1;
399 calp12 = calp2 * self.calp1 + salp2 * self.salp1;
400 } else {
401 salp12 = self._calp0
402 * self._salp0
403 * (if csig12 <= 0.0 {
404 self._csig1 * (1.0 - csig12) + ssig12 * self._ssig1
405 } else {
406 ssig12 * (self._csig1 * ssig12 / (1.0 + csig12) + self._ssig1)
407 });
408 calp12 = geomath::sq(self._salp0) + geomath::sq(self._calp0) * self._csig1 * csig2;
409 }
410 S12 = self._c2 * salp12.atan2(calp12) + self._A4 * (B42 - self._B41);
411 }
412 a12 = if arcmode { s12_a12 } else { sig12.to_degrees() };
413 (a12, lat2, lon2, azi2, s12, m12, M12, M21, S12)
414 }
415
416 #[allow(dead_code)]
418 pub fn Position(&self, s12: f64, outmask: Option<u64>) -> HashMap<String, f64> {
419 let outmask = match outmask {
420 Some(outmask) => outmask,
421 None => caps::STANDARD,
422 };
423 let mut result: HashMap<String, f64> = HashMap::new();
424 result.insert("lat1".to_string(), self.lat1);
425 result.insert("azi1".to_string(), self.azi1);
426 result.insert("s12".to_string(), s12);
427 let lon1 = if outmask & caps::LONG_UNROLL != 0 {
428 self.lon1
429 } else {
430 geomath::ang_normalize(self.lon1)
431 };
432 result.insert("lon1".to_string(), lon1);
433
434 let (a12, lat2, lon2, azi2, _s12, m12, M12, M21, S12) =
435 self._gen_position(false, s12, outmask);
436 let outmask = outmask & caps::OUT_MASK;
437 result.insert("a12".to_string(), a12);
438 if outmask & caps::LATITUDE != 0 {
439 result.insert("lat2".to_string(), lat2);
440 }
441 if outmask & caps::LONGITUDE != 0 {
442 result.insert("lon2".to_string(), lon2);
443 }
444 if outmask & caps::AZIMUTH != 0 {
445 result.insert("azi2".to_string(), azi2);
446 }
447 if outmask & caps::REDUCEDLENGTH != 0 {
448 result.insert("m12".to_string(), m12);
449 }
450 if outmask & caps::GEODESICSCALE != 0 {
451 result.insert("M12".to_string(), M12);
452 result.insert("M21".to_string(), M21);
453 }
454 if outmask & caps::AREA != 0 {
455 result.insert("S12".to_string(), S12);
456 }
457 result
458 }
459}
460
461#[cfg(test)]
462mod tests {
463 use super::*;
464 use geodesic::Geodesic;
465
466 #[test]
467 fn test_gen_position() {
468 let geod = Geodesic::wgs84();
469 let gl = GeodesicLine::new(&geod, 0.0, 0.0, 10.0, None, None, None);
470 let res = gl._gen_position(false, 150.0, 3979);
471 assert_eq!(res.0, 0.0013520059461334633);
472 assert_eq!(res.1, 0.0013359451088740494);
473 assert_eq!(res.2, 0.00023398621812867812);
474 assert_eq!(res.3, 10.000000002727887);
475 assert_eq!(res.4, 150.0);
476 assert!(res.5.is_nan());
477 assert!(res.6.is_nan());
478 assert!(res.7.is_nan());
479 assert!(res.8.is_nan());
480 }
481
482 #[test]
483 fn test_init() {
484 let geod = Geodesic::wgs84();
485 let gl = GeodesicLine::new(&geod, 0.0, 0.0, 0.0, None, None, None);
486 assert_eq!(gl._a, 6378137.0);
487 assert_eq!(gl.f, 0.0033528106647474805);
488 assert_eq!(gl._b, 6356752.314245179);
489 assert_eq!(gl._c2, 40589732499314.76);
490 assert_eq!(gl._f1, 0.9966471893352525);
491 assert_eq!(gl.caps, 36747);
492 assert_eq!(gl.lat1, 0.0);
493 assert_eq!(gl.lon1, 0.0);
494 assert_eq!(gl.azi1, 0.0);
495 assert_eq!(gl.salp1, 0.0);
496 assert_eq!(gl.calp1, 1.0);
497 assert_eq!(gl._dn1, 1.0);
498 assert_eq!(gl._salp0, 0.0);
499 assert_eq!(gl._calp0, 1.0);
500 assert_eq!(gl._ssig1, 0.0);
501 assert_eq!(gl._somg1, 0.0);
502 assert_eq!(gl._csig1, 1.0);
503 assert_eq!(gl._comg1, 1.0);
504 assert_eq!(gl._k2, geod._ep2);
505 assert!(gl._s13.is_nan());
506 assert!(gl._a13.is_nan());
507 }
508}