gistools/proj/project/
poly.rs1use crate::proj::{
2 _msfn, CoordinateStep, POLYCONIC, Proj, ProjMethod, ProjectCoordinates, TransformCoordinates,
3 enfn, mlfn,
4};
5use alloc::{rc::Rc, vec::Vec};
6use core::cell::RefCell;
7use libm::{asin, cos, fabs, sin, sqrt, tan};
8
9#[derive(Debug, Default, Clone, PartialEq)]
11pub struct PolyData {
12 ml0: f64,
13 en: Vec<f64>,
14}
15
16const TOL: f64 = 1e-10;
17const CONV: f64 = 1e-10;
18const N_ITER: usize = 10;
19const I_ITER: usize = 20;
20const ITOL: f64 = 1e-12;
21
22#[derive(Debug, Clone, PartialEq)]
55pub struct PolyconicProjection {
56 proj: Rc<RefCell<Proj>>,
57 store: RefCell<PolyData>,
58 method: ProjMethod,
59}
60impl ProjectCoordinates for PolyconicProjection {
61 fn code(&self) -> i64 {
62 POLYCONIC
63 }
64 fn name(&self) -> &'static str {
65 "Polyconic"
66 }
67 fn names() -> &'static [&'static str] {
68 &["Polyconic", "Polyconic (American)", "poly"]
69 }
70}
71impl CoordinateStep for PolyconicProjection {
72 fn new(proj: Rc<RefCell<Proj>>) -> Self {
73 let mut store = PolyData::default();
74 let method: ProjMethod;
75 {
76 let proj = &mut proj.borrow_mut();
77
78 method = if proj.es != 0.0 {
79 store.en = enfn(proj.n);
80 store.ml0 = mlfn(proj.phi0, sin(proj.phi0), cos(proj.phi0), &store.en);
81 ProjMethod::Ellipsoidal
82 } else {
83 store.ml0 = -proj.phi0;
84 ProjMethod::Spheroidal
85 }
86 }
87
88 PolyconicProjection { proj, store: store.into(), method }
89 }
90 fn forward<P: TransformCoordinates>(&self, p: &mut P) {
91 if self.method == ProjMethod::Spheroidal {
92 poly_s_forward(&self.store.borrow(), &self.proj.borrow(), p);
93 } else {
94 poly_e_forward(&self.store.borrow(), &self.proj.borrow(), p);
95 }
96 }
97 fn inverse<P: TransformCoordinates>(&self, p: &mut P) {
98 if self.method == ProjMethod::Spheroidal {
99 poly_s_inverse(&self.proj.borrow(), p);
100 } else {
101 poly_e_inverse(&self.store.borrow(), &self.proj.borrow(), p);
102 }
103 }
104}
105
106pub fn poly_e_forward<P: TransformCoordinates>(poly: &PolyData, proj: &Proj, p: &mut P) {
108 if fabs(p.phi()) <= TOL {
109 p.set_x(p.lam());
110 p.set_y(-poly.ml0);
111 } else {
112 let sp = sin(p.phi());
113 let cp = cos(p.phi());
114 let ms = if fabs(cp) > TOL { _msfn(sp, cp, proj.es) / sp } else { 0. };
115 p.set_lam(p.lam() * sp);
116 p.set_x(ms * sin(p.lam()));
117 p.set_y((mlfn(p.phi(), sp, cp, &poly.en) - poly.ml0) + ms * (1. - cos(p.lam())));
118 }
119}
120
121pub fn poly_s_forward<P: TransformCoordinates>(poly: &PolyData, proj: &Proj, p: &mut P) {
123 if fabs(p.phi()) <= TOL {
124 p.set_x(p.lam());
125 p.set_y(poly.ml0);
126 } else {
127 let cot = 1. / tan(p.phi());
128 let e = p.lam() * sin(p.phi());
129 p.set_x(sin(e) * cot);
130 p.set_y(p.phi() - proj.phi0 + cot * (1. - cos(e)));
131 }
132}
133
134pub fn poly_e_inverse<P: TransformCoordinates>(poly: &PolyData, proj: &Proj, p: &mut P) {
136 let x = p.x();
137 let mut y = p.y();
138 let mut phi;
139 let lam;
140 y += poly.ml0;
141 if fabs(y) <= TOL {
142 lam = x;
143 phi = 0.;
144 } else {
145 let r = y * y + x * x;
146 phi = y;
147 let mut i = I_ITER;
148 while i > 0 {
149 let sp = sin(phi);
150 let cp = cos(phi);
151 let s2ph = sp * cp;
152 if fabs(cp) < ITOL {
153 panic!("Coordinate outside projection domain");
154 }
155 let mut mlp = sqrt(1. - proj.es * sp * sp);
156 let c = sp * mlp / cp;
157 let ml = mlfn(phi, sp, cp, &poly.en);
158 let mlb = ml * ml + r;
159 mlp = proj.one_es / (mlp * mlp * mlp);
160 let d_phi = (ml + ml + c * mlb - 2. * y * (c * ml + 1.))
161 / (proj.es * s2ph * (mlb - 2. * y * ml) / c
162 + 2. * (y - ml) * (c * mlp - 1. / s2ph)
163 - mlp
164 - mlp);
165 phi += d_phi;
166 if fabs(d_phi) <= ITOL {
167 break;
168 }
169 i -= 1;
170 }
171 if i == 0 {
172 panic!("Coordinate outside projection domain");
173 }
174 let c = sin(phi);
175 lam = asin(x * tan(phi) * sqrt(1. - proj.es * c * c)) / sin(phi);
176 }
177
178 p.set_lam(lam);
179 p.set_phi(phi);
180}
181
182pub fn poly_s_inverse<P: TransformCoordinates>(proj: &Proj, p: &mut P) {
184 let x = p.x();
185 let y = proj.phi0 + p.y();
186 if fabs(y) <= TOL {
187 p.set_lam(x);
188 p.set_phi(0.);
189 } else {
190 p.set_phi(y);
191 let b = x * x + y * y;
192 let mut i = N_ITER;
193 loop {
194 let tp = tan(p.phi());
195 let dphi = (y * (p.phi() * tp + 1.) - p.phi() - 0.5 * (p.phi() * p.phi() + b) * tp)
196 / ((p.phi() - y) / tp - 1.);
197 p.set_phi(p.phi() - dphi);
198 if fabs(dphi) <= CONV {
199 break;
200 }
201 i -= 1;
202 if i == 0 {
203 panic!("Coordinate outside projection domain");
204 }
205 }
206 p.set_lam(asin(x * tan(p.phi())) / sin(p.phi()));
207 }
208}