geoconvert/projections/
polar_stereographic.rs

1use 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}