gistools/proj/convert/
cart.rs1use crate::proj::{
2 CoordinateStep, IoUnits, Proj, ProjectCoordinates, ProjectionTransform, Step,
3 TransformCoordinates,
4};
5use alloc::rc::Rc;
6use core::{cell::RefCell, f64::consts::FRAC_PI_2};
7use libm::{atan, atan2, cos, fabs, sin, sqrt};
8
9#[derive(Debug, Clone, PartialEq)]
63pub struct CartesianConverter {
64 proj: Rc<RefCell<Proj>>,
65}
66impl ProjectCoordinates for CartesianConverter {
67 fn code(&self) -> i64 {
68 -1
69 }
70
71 fn name(&self) -> &'static str {
72 "cartesian"
73 }
74
75 fn names() -> &'static [&'static str] {
76 &["cartesian", "cart"]
77 }
78}
79impl CoordinateStep for CartesianConverter {
80 fn new(proj: Rc<RefCell<Proj>>) -> Self {
81 {
82 let proj = &mut proj.borrow_mut();
83 proj.left = IoUnits::RADIANS;
84 proj.right = IoUnits::CARTESIAN;
85 proj.is_ll = true;
86 }
87 CartesianConverter { proj }
88 }
89 fn forward<P: TransformCoordinates>(&self, coords: &mut P) {
91 cartesian(&self.proj.borrow(), coords);
92 }
93 fn inverse<P: TransformCoordinates>(&self, coords: &mut P) {
95 geodetic(&self.proj.borrow(), coords);
96 }
97}
98impl From<CartesianConverter> for ProjectionTransform {
99 fn from(c: CartesianConverter) -> ProjectionTransform {
100 ProjectionTransform {
101 proj: c.proj.clone(),
102 method: Step::Cartesian(c.into()),
103 ..Default::default()
104 }
105 }
106}
107
108pub fn normal_radius_of_curvature(a: f64, es: f64, sinphi: f64) -> f64 {
111 if es == 0. {
112 return a;
113 }
114 a / sqrt(1. - es * sinphi * sinphi)
116}
117
118pub fn geocentric_radius(a: f64, b_div_a: f64, cosphi: f64, sinphi: f64) -> f64 {
124 let cosphi_squared = cosphi * cosphi;
129 let sinphi_squared = sinphi * sinphi;
130 let b_div_a_squared = b_div_a * b_div_a;
131 let b_div_a_squared_mul_sinphi_squared = b_div_a_squared * sinphi_squared;
132 a * sqrt(
133 (cosphi_squared + b_div_a_squared * b_div_a_squared_mul_sinphi_squared)
134 / (cosphi_squared + b_div_a_squared_mul_sinphi_squared),
135 )
136}
137
138pub fn cartesian<P: TransformCoordinates>(proj: &Proj, coords: &mut P) {
140 let cosphi: f64 = cos(coords.phi());
141 let sinphi = sin(coords.phi());
142 let n = normal_radius_of_curvature(proj.a, proj.es, sinphi);
143
144 let z = coords.z();
146 let lam = coords.lam();
147 coords.set_x((n + z) * cosphi * cos(lam));
148 coords.set_y((n + z) * cosphi * sin(lam));
149 coords.set_z((n * (1. - proj.es) + z) * sinphi);
150}
151
152pub fn geodetic<P: TransformCoordinates>(proj: &Proj, coords: &mut P) {
154 let phi;
155 let x_div_a = coords.x() * proj.ra;
157 let y_div_a = coords.y() * proj.ra;
158 let z_div_a = coords.z() * proj.ra;
159
160 let p_div_a = sqrt(x_div_a * x_div_a + y_div_a * y_div_a);
162
163 let b_div_a = 1. - proj.f; let p_div_a_b_div_a = p_div_a * b_div_a;
165 let norm = sqrt(z_div_a * z_div_a + p_div_a_b_div_a * p_div_a_b_div_a);
166 let c;
167 let s;
168 if norm != 0. {
169 let inv_norm = 1.0 / norm;
170 c = p_div_a_b_div_a * inv_norm;
171 s = z_div_a * inv_norm;
172 } else {
173 c = 1.;
174 s = 0.;
175 }
176
177 let y_phi = z_div_a + proj.e2s * b_div_a * s * s * s;
178 let x_phi = p_div_a - proj.es * c * c * c;
179 let norm_phi = sqrt(y_phi * y_phi + x_phi * x_phi);
180 let mut cosphi;
181 let mut sinphi;
182 if norm_phi != 0. {
183 let inv_norm_phi = 1.0 / norm_phi;
184 cosphi = x_phi * inv_norm_phi;
185 sinphi = y_phi * inv_norm_phi;
186 } else {
187 cosphi = 1.;
188 sinphi = 0.;
189 }
190 if x_phi <= 0. {
191 phi = if coords.z() >= 0. { FRAC_PI_2 } else { -FRAC_PI_2 };
196 cosphi = 0.;
197 sinphi = if coords.z() >= 0. { 1. } else { -1. };
198 } else {
199 phi = atan(y_phi / x_phi);
200 }
201 let lam = atan2(y_div_a, x_div_a);
202 let z = if cosphi < 1e-6 {
203 let r = geocentric_radius(proj.a, b_div_a, cosphi, sinphi);
208 fabs(coords.z()) - r
209 } else {
210 let n = normal_radius_of_curvature(proj.a, proj.es, sinphi);
211 proj.a * p_div_a / cosphi - n
212 };
213
214 coords.set_phi(phi);
215 coords.set_lam(lam);
216 coords.set_z(z);
217}