gistools/proj/project/
labrd.rs1use crate::proj::{
2 AZIMUTH_PROJECTION_CENTRE, CoordinateStep, LABORDE, Proj, ProjValue, ProjectCoordinates,
3 TransformCoordinates,
4};
5use alloc::rc::Rc;
6use core::cell::RefCell;
7use libm::{atan, cos, exp, fabs, log, sin, sqrt, tan};
8
9const M_FORTPI: f64 = 4. / core::f64::consts::PI;
10const EPS: f64 = 1.0e-10;
11
12#[derive(Debug, Default, Clone, Copy, PartialEq)]
14pub struct LabordeData {
15 k_rg: f64,
16 p0s: f64,
17 a: f64,
18 c: f64,
19 ca: f64,
20 cb: f64,
21 cc: f64,
22 cd: f64,
23}
24
25#[derive(Debug, Clone, PartialEq)]
27pub struct LabordeProjection {
28 proj: Rc<RefCell<Proj>>,
29 store: RefCell<LabordeData>,
30}
31impl ProjectCoordinates for LabordeProjection {
32 fn code(&self) -> i64 {
33 LABORDE
34 }
35 fn name(&self) -> &'static str {
36 "Laborde"
37 }
38 fn names() -> &'static [&'static str] {
39 &["Laborde", "Laborde Oblique Mercator", "labrd"]
40 }
41}
42impl CoordinateStep for LabordeProjection {
43 fn new(proj: Rc<RefCell<Proj>>) -> Self {
44 let mut store = LabordeData::default();
45 {
46 let proj = &mut proj.borrow_mut();
47 if proj.phi0 == 0. {
48 panic!("Invalid value for lat_0: lat_0 should be different from 0");
49 }
50
51 let az = proj
52 .params
53 .get(&AZIMUTH_PROJECTION_CENTRE) .unwrap_or(&ProjValue::default())
55 .f64();
56 let sinp = sin(proj.phi0);
57 let mut t = 1. - proj.es * sinp * sinp;
58 let _n = 1. / sqrt(t);
59 let _r = proj.one_es * _n / t;
60 store.k_rg = proj.k0 * sqrt(_n * _r);
61 store.p0s = atan(sqrt(_r / _n) * tan(proj.phi0));
62 store.a = sinp / sin(store.p0s);
63 t = proj.e * sinp;
64 store.c = 0.5 * proj.e * store.a * log((1. + t) / (1. - t))
65 + -store.a * log(tan(M_FORTPI + 0.5 * proj.phi0))
66 + log(tan(M_FORTPI + 0.5 * store.p0s));
67 t = az + az;
68 store.cb = 1. / (12. * store.k_rg * store.k_rg);
69 store.ca = (1. - cos(t)) * store.cb;
70 store.cb *= sin(t);
71 store.cc = 3. * (store.ca * store.ca - store.cb * store.cb);
72 store.cd = 6. * store.ca * store.cb;
73 }
74 LabordeProjection { proj, store: store.into() }
75 }
76 fn forward<P: TransformCoordinates>(&self, p: &mut P) {
77 labrd_e_forward(&self.store.borrow(), &self.proj.borrow(), p);
78 }
79 fn inverse<P: TransformCoordinates>(&self, p: &mut P) {
80 labrd_e_inverse(&self.store.borrow(), &self.proj.borrow(), p);
81 }
82}
83
84pub fn labrd_e_forward<P: TransformCoordinates>(laborde: &LabordeData, proj: &Proj, p: &mut P) {
86 let phi = p.phi();
87 let lam = p.lam();
88
89 let mut v1 = laborde.a * log(tan(M_FORTPI + 0.5 * phi));
90 let mut t = proj.e * sin(phi);
91 let mut v2 = 0.5 * proj.e * laborde.a * log((1. + t) / (1. - t));
92 let ps = 2. * (atan(exp(v1 - v2 + laborde.c)) - M_FORTPI);
93 let i1 = ps - laborde.p0s;
94 let cosps = cos(ps);
95 let cosps2 = cosps * cosps;
96 let sinps = sin(ps);
97 let sinps2 = sinps * sinps;
98 let i4 = laborde.a * cosps;
99 let i2 = 0.5 * laborde.a * i4 * sinps;
100 let i3 = i2 * laborde.a * laborde.a * (5. * cosps2 - sinps2) / 12.;
101 let mut i6 = i4 * laborde.a * laborde.a;
102 let i5 = i6 * (cosps2 - sinps2) / 6.;
103 i6 *= laborde.a * laborde.a * (5. * cosps2 * cosps2 + sinps2 * (sinps2 - 18. * cosps2)) / 120.;
104 t = lam * lam;
105 let mut x = laborde.k_rg * lam * (i4 + t * (i5 + t * i6));
106 let mut y = laborde.k_rg * (i1 + t * (i2 + t * i3));
107 let x2 = x * x;
108 let y2 = y * y;
109 v1 = 3. * x * y2 - x * x2;
110 v2 = y * y2 - 3. * x2 * y;
111 x += laborde.ca * v1 + laborde.cb * v2;
112 y += laborde.ca * v2 - laborde.cb * v1;
113
114 p.set_x(x);
115 p.set_y(y);
116}
117
118pub fn labrd_e_inverse<P: TransformCoordinates>(laborde: &LabordeData, proj: &Proj, p: &mut P) {
120 let mut t;
122 let mut x = p.x();
125 let mut y = p.y();
126
127 let mut x2 = x * x;
128 let y2 = y * y;
129 let mut v1 = 3. * x * y2 - x * x2;
130 let mut v2 = y * y2 - 3. * x2 * y;
131 let v3 = x * (5. * y2 * y2 + x2 * (-10. * y2 + x2));
132 let v4 = y * (5. * x2 * x2 + y2 * (-10. * x2 + y2));
133 x += -laborde.ca * v1 - laborde.cb * v2 + laborde.cc * v3 + laborde.cd * v4;
134 y += laborde.cb * v1 - laborde.ca * v2 - laborde.cd * v3 + laborde.cc * v4;
135 let ps = laborde.p0s + y / laborde.k_rg;
136 let mut pe = ps + proj.phi0 - laborde.p0s;
137
138 for _ in 0..20 {
139 v1 = laborde.a * log(tan(M_FORTPI + 0.5 * pe));
140 let tpe = proj.e * sin(pe);
141 v2 = 0.5 * proj.e * laborde.a * log((1. + tpe) / (1. - tpe));
142 t = ps - 2. * (atan(exp(v1 - v2 + laborde.c)) - M_FORTPI);
143 pe += t;
144 if fabs(t) < EPS {
145 break;
146 }
147 }
148
149 t = proj.e * sin(pe);
150 t = 1. - t * t;
151 let re = proj.one_es / (t * sqrt(t));
152 t = tan(ps);
153 let t2 = t * t;
154 let s = laborde.k_rg * laborde.k_rg;
155 let mut d = re * proj.k0 * laborde.k_rg;
156 let i7 = t / (2. * d);
157 let i8 = t * (5. + 3. * t2) / (24. * d * s);
158 d = cos(ps) * laborde.k_rg * laborde.a;
159 let i9 = 1. / d;
160 d *= s;
161 let i10 = (1. + 2. * t2) / (6. * d);
162 let i11 = (5. + t2 * (28. + 24. * t2)) / (120. * d * s);
163 x2 = x * x;
164
165 p.set_phi(pe + x2 * (-i7 + i8 * x2));
166 p.set_lam(x * (i9 + x2 * (-i10 + x2 * i11)));
167}