miniproj_ops/ops/
ellipsoid.rs1use crate::PseudoSerialize;
4
5#[derive(Copy, Clone, Debug)]
7pub struct Ellipsoid {
8 pub a: f64,
10 pub b: f64,
12 pub f: f64,
14 pub e: f64,
16 pub e_squared: f64,
18}
19impl Ellipsoid {
20 #[must_use]
22 pub fn from_a_b(a: f64, b: f64) -> Self {
23 let f = (a - b) / a;
24 let e_squared = (2f64 * f) - f.powi(2);
25 Self {
26 a,
27 b,
28 f,
29 e_squared,
30 e: e_squared.sqrt(),
31 }
32 }
33
34 #[must_use]
36 pub fn from_a_f_inv(a: f64, f_inv: f64) -> Self {
37 let f = 1.0 / f_inv;
38 let e_squared = (2f64 / f_inv) - f_inv.powi(-2);
39 Self {
40 a,
41 b: a - a / f_inv,
42 f,
43 e_squared,
44 e: e_squared.sqrt(),
45 }
46 }
47
48 pub fn a(&self) -> f64 {
50 self.a
51 }
52
53 pub fn b(&self) -> f64 {
55 self.b
56 }
57
58 #[deprecated(since = "0.8.0")]
60 pub fn f_inv(&self) -> f64 {
61 1f64 / self.f
62 }
63
64 pub fn f(&self) -> f64 {
66 self.f
67 }
68
69 pub fn e(&self) -> f64 {
71 self.e
72 }
73
74 pub fn e_squared(&self) -> f64 {
76 self.e_squared
77 }
78
79 pub fn e_2(&self) -> f64 {
81 f64::sqrt(self.e_squared() / (1.0 - self.e_squared()))
82 }
83
84 pub fn rho(&self, lat: f64) -> f64 {
86 self.a * (1.0 - self.e_squared()) / (1.0 - self.e_squared() * lat.sin().powi(2)).powf(1.5)
87 }
88
89 pub fn ny(&self, lat: f64) -> f64 {
91 self.a / (1.0 - self.e_squared() * lat.sin().powi(2)).sqrt()
92 }
93
94 pub fn rad_auth(&self) -> f64 {
96 self.a
97 * ((1.0
98 - ((1.0 - self.e_squared()) / (2.0 * self.e()))
99 * f64::ln((1.0 - self.e()) / (1.0 + self.e())))
100 * 0.5)
101 .sqrt()
102 }
103
104 pub fn rad_conformal(&self, lat: f64) -> f64 {
106 f64::sqrt(self.rho(lat) * self.ny(lat))
107 }
108
109 pub fn geocentric_to_deg(&self, x: f64, y: f64, z: f64) -> (f64, f64, f64) {
111 let (lon, lat, h) = self.geocentric_to_rad(x, y, z);
112 (lon.to_degrees(), lat.to_degrees(), h)
113 }
114
115 pub fn deg_to_geocentric(&self, lon: f64, lat: f64, height: f64) -> (f64, f64, f64) {
117 self.rad_to_geocentric(lon.to_radians(), lat.to_radians(), height)
118 }
119
120 pub fn geocentric_to_rad(&self, x: f64, y: f64, z: f64) -> (f64, f64, f64) {
122 let lon = y.atan2(x);
123 let epsilon = self.e_squared() / (1f64 - self.e_squared());
124 let p = (x.powi(2) + y.powi(2)).sqrt();
125 let q = (z * self.a).atan2(p * self.b);
126 let lat = (z + epsilon * self.b * q.sin().powi(3))
127 .atan2(p - self.e_squared() * self.a * q.cos().powi(3));
128 let h = (p / lat.cos()) - self.ny(lat);
129 (lon, lat, h)
130 }
131
132 pub fn rad_to_geocentric(&self, lon: f64, lat: f64, height: f64) -> (f64, f64, f64) {
134 let ny = self.ny(lat);
135 let r = ny + height;
136 (
137 r * lat.cos() * lon.cos(),
138 r * lat.cos() * lon.sin(),
139 ((1f64 - self.e_squared()) * ny + height) * lat.sin(),
140 )
141 }
142}
143
144impl PseudoSerialize for Ellipsoid {
145 fn to_constructed(&self) -> String {
146 format! {
147r"Ellipsoid{{
148 a: {}f64,
149 b: {}f64,
150 e: {}f64,
151 e_squared: {}f64,
152 f: {}f64,
153}}",
154 self.a,
155 self.b,
156 self.e,
157 self.e_squared,
158 self.f,
159 }
160 }
161}
162
163#[cfg(test)]
164mod tests {
165 use super::Ellipsoid;
166
167 #[test]
168 fn geocentric_roundtrip() {
169 let ell = Ellipsoid::from_a_f_inv(6378137.000, 298.2572236);
170 let expected_lat = 53f64 + 48f64 / 60f64 + 33.820 / 3600f64;
171 let expected_lon = 2f64 + 7f64 / 60f64 + 46.380 / 3600f64;
172 let expected_eh = 73.0;
173
174 let expected_x = 3771793.968;
175 let expected_y = 140253.342;
176 let expected_z = 5124304.349;
177
178 let (lon, lat, eh) = ell.geocentric_to_deg(expected_x, expected_y, expected_z);
179
180 let (x, y, z) = ell.deg_to_geocentric(lon, lat, eh);
181 eprintln!("lon: {expected_lon} - {lon}");
182 eprintln!("lat: {expected_lat} - {lat}");
183 eprintln!("eh: {expected_eh} - {eh}");
184
185 eprintln!("X: {expected_x} - {x}");
186 eprintln!("Y: {expected_y} - {y}");
187 eprintln!("Z: {expected_z} - {z}");
188 assert!((expected_lon - lon).abs() < 0.01 / 3600.0);
189 assert!((expected_lat - lat).abs() < 0.01 / 3600.0);
190 assert!((expected_eh - eh).abs() < 0.01);
191 assert!((expected_x - x).abs() < 0.01);
192 assert!((expected_y - y).abs() < 0.01);
193 assert!((expected_z - z).abs() < 0.01);
194 }
195}