gistools/proj/project/
airy.rs1use crate::proj::{
2 CoordinateStep, LAT_B, NO_CUT, Proj, ProjMode, ProjValue, ProjectCoordinates,
3 TransformCoordinates,
4};
5use alloc::rc::Rc;
6use core::{cell::RefCell, f64::consts::FRAC_PI_2};
7use libm::{cos, fabs, log, sin, tan};
8
9#[derive(Debug, Default, Clone, Copy, PartialEq)]
11pub struct Airy {
12 p_halfpi: f64,
13 sinph0: f64,
14 cosph0: f64,
15 cb: f64,
16 mode: ProjMode,
17 no_cut: bool,
19}
20
21const EPS: f64 = 1e-10;
22
23#[derive(Debug, Clone, PartialEq)]
25pub struct AiryProjection {
26 proj: Rc<RefCell<Proj>>,
27 store: RefCell<Airy>,
28}
29impl ProjectCoordinates for AiryProjection {
30 fn code(&self) -> i64 {
31 -1
32 }
33 fn name(&self) -> &'static str {
34 "Airy"
35 }
36 fn names() -> &'static [&'static str] {
37 &["Airy", "airy"]
38 }
39}
40impl CoordinateStep for AiryProjection {
41 fn new(proj: Rc<RefCell<Proj>>) -> Self {
42 let mut store = Airy {
43 no_cut: proj.borrow().params.get(&NO_CUT).unwrap_or(&ProjValue::default()).bool(),
44 ..Default::default()
45 };
46 {
47 let proj = &mut proj.borrow_mut();
48 let beta =
49 0.5 * (FRAC_PI_2 - proj.params.get(&LAT_B).unwrap_or(&ProjValue::default()).f64());
50 if fabs(beta) < EPS {
51 store.cb = -0.5;
52 } else {
53 store.cb = 1. / tan(beta);
54 store.cb = store.cb * store.cb * log(cos(beta));
55 }
56
57 if fabs(fabs(proj.phi0) - FRAC_PI_2) < EPS {
58 if proj.phi0 < 0. {
59 store.p_halfpi = -FRAC_PI_2;
60 store.mode = ProjMode::SPole;
61 } else {
62 store.p_halfpi = FRAC_PI_2;
63 store.mode = ProjMode::NPole;
64 }
65 } else if fabs(proj.phi0) < EPS {
66 store.mode = ProjMode::Equit;
67 } else {
68 store.mode = ProjMode::Obliq;
69 store.sinph0 = sin(proj.phi0);
70 store.cosph0 = cos(proj.phi0);
71 }
72 proj.es = 0.;
73 }
74 AiryProjection { proj, store: store.into() }
75 }
76 fn forward<P: TransformCoordinates>(&self, p: &mut P) {
77 airy_s_forward(&mut self.store.borrow_mut(), &self.proj.borrow(), p);
78 }
79 fn inverse<P: TransformCoordinates>(&self, _p: &mut P) {
80 }
82}
83
84pub fn airy_s_forward<P: TransformCoordinates>(airy: &mut Airy, _proj: &Proj, p: &mut P) {
86 let sinlam = sin(p.lam());
87 let coslam = cos(p.lam());
88 match airy.mode {
89 ProjMode::Equit | ProjMode::Obliq => {
90 let sinphi = sin(p.phi());
91 let cosphi = cos(p.phi());
92 let mut cosz = cosphi * coslam;
93 if airy.mode == ProjMode::Obliq {
94 cosz = airy.sinph0 * sinphi + airy.cosph0 * cosz;
95 }
96 if !airy.no_cut && cosz < -EPS {
97 panic!("Coordinates are outside the projection domain");
98 }
99 let s = 1. - cosz;
100 let k_rho = if fabs(s) > EPS {
101 let t = 0.5 * (1. + cosz);
102 if t == 0. {
103 panic!("Coordinates are outside the projection domain");
104 }
105 -log(t) / s - airy.cb / t
106 } else {
107 0.5 - airy.cb
108 };
109 p.set_x(k_rho * cosphi * sinlam);
110 if airy.mode == ProjMode::Obliq {
111 p.set_y(k_rho * (airy.cosph0 * sinphi - airy.sinph0 * cosphi * coslam));
112 } else {
113 p.set_y(k_rho * sinphi);
114 }
115 }
116 ProjMode::SPole | ProjMode::NPole => {
117 p.set_phi(fabs(airy.p_halfpi - p.phi()));
118 if !airy.no_cut && (p.phi() - EPS) > FRAC_PI_2 {
119 panic!("Coordinates are outside the projection domain");
120 }
121 p.set_phi(p.phi() * 0.5);
122 if p.phi() > EPS {
123 let t = tan(p.phi());
124 let k_rho = -2. * (log(cos(p.phi())) / t + t * airy.cb);
125 p.set_x(k_rho * sinlam);
126 p.set_y(k_rho * coslam);
127 if airy.mode == ProjMode::NPole {
128 p.set_y(-p.y());
129 }
130 } else {
131 p.set_x(0.);
132 p.set_y(0.);
133 }
134 }
135 }
136}