geoconvert/projections/
polar_stereographic.rs1use crate::{ThisOrThat, constants::{WGS84_A, WGS84_F, UPS_K0}, utility::{GeoMath, dms}, latlon::LatLon};
2
3const F: f64 = WGS84_F;
4const E2: f64 = F * (2.0 - F);
5
6pub(crate) struct PolarStereographic {
7 a: f64,
8 k0: f64,
9 es: f64,
10 c: f64,
11}
12
13impl PolarStereographic {
14 pub fn ups() -> PolarStereographic {
15
16 let es = (F < 0.0).ternary(-1.0, 1.0) * E2.abs().sqrt();
17 let c = (1.0 - F) * 1_f64.eatanhe(es).exp();
18
19 Self {
20 a: WGS84_A,
21 k0: UPS_K0,
22 es,
23 c,
24 }
25 }
26
27 #[allow(clippy::wrong_self_convention)]
28 pub fn from_latlon(&self, northp: bool, lat: f64, lon: f64) -> (f64, f64) {
29 let lat = lat * northp.ternary(1.0, -1.0);
30
31 let tau = lat.to_radians().tan();
32 let taup = tau.taupf(self.es);
33 let mut rho = 1_f64.hypot(taup) + taup.abs();
34 rho = (taup >= 0.0).ternary_lazy(|| (!lat.eps_eq(f64::from(dms::QD))).ternary_lazy(|| 1.0 / rho, || 0.0), || rho);
35 rho *= 2.0 * self.k0 * self.a / self.c;
36
37 let (mut x, mut y) = {
38 let (x, y) = lon.to_radians().sin_cos();
39 (x, y)
40 };
41
42 x *= rho;
43 y *= northp.ternary(-rho, rho);
44
45 (x, y)
46 }
47
48 pub fn to_latlon(&self, northp: bool, x: f64, y: f64) -> LatLon {
49 let rho = x.hypot(y);
50 let t = (rho != 0.0)
51 .ternary_lazy(
52 || rho / (2.0 * self.k0 * self.a / self.c),
53 || f64::EPSILON.powi(2)
54 );
55 let taup = (1.0 / t - t) / 2.;
56 let tau = taup.tauf(self.es);
57
58 let lat = northp.ternary(1.0, -1.0) * tau.atan().to_degrees();
59 let lon = x.atan2(northp.ternary(-y, y)).to_degrees();
60
61 LatLon {
62 latitude: lat,
63 longitude: lon,
64 }
65 }
66}