pub fn llz2xyz(ll: (f64, f64), altitude: f64) -> (f64, f64, f64) {
let (long, lat) = ll;
const A: f64 = 6378137.; const F: f64 = 1. / 298.257223563; const E2: f64 = F * (2. - F);
let n = A / (1. - E2 * lat.sin().powf(2.)).sqrt();
let x = (n + altitude) * lat.cos() * long.cos();
let y = (n + altitude) * lat.cos() * long.sin();
let z = (n * (1. - E2) + altitude) * lat.sin();
(x, y, z)
}
pub fn xyz2llz(xyz: (f64, f64, f64)) -> ((f64, f64), f64) {
let (x, y, z) = xyz;
const A: f64 = 6378137.; const F: f64 = 1. / 298.257223563; const E2: f64 = F * (2. - F);
let p = (x.powf(2.) + y.powf(2.)).sqrt();
let mut lat = (z / (p * (1. - E2))).atan();
loop {
let n = A / (1. - E2 * lat.sin().powf(2.)).sqrt();
let next_lat = (z / (p - E2 * n * lat.cos())).atan();
if (lat - next_lat).abs() < 1e-12 {
lat = next_lat;
break;
}
lat = next_lat;
}
let long = y.atan2(x);
let h = p / lat.cos() - A / (1. - E2 * lat.sin().powf(2.)).sqrt();
((long, lat), h)
}
#[cfg(test)]
mod tests {
use close_to::assert_close_to;
use super::*;
#[test]
fn llz2xyz_works() {
let (long, lat) = (140_f64.to_radians(), 36_f64.to_radians());
let altitude = 100_f64;
let (x, y, z) = llz2xyz((long, lat), altitude);
assert_close_to(x, -3957446.631, 3);
assert_close_to(y, 3320692.008, 3);
assert_close_to(z, 3728250.454, 3);
}
#[test]
fn xyz2llz_works() {
let xyz = (-3957446.631, 3320692.008, 3728250.454);
let ((long, lat), altitude) = xyz2llz(xyz);
assert_close_to(long.to_degrees(), 140., 3);
assert_close_to(lat.to_degrees(), 36., 3);
assert_close_to(altitude, 100., 3);
}
}