use crate::physics::orbit::MERCURY_RADIUS;
#[derive(Debug, Clone, Copy)]
pub struct LatLon {
pub lat_deg: f64,
pub lon_deg: f64,
}
impl LatLon {
pub fn new(lat_deg: f64, lon_deg: f64) -> Self {
Self { lat_deg, lon_deg }
}
pub fn lat_rad(&self) -> f64 {
self.lat_deg.to_radians()
}
pub fn lon_rad(&self) -> f64 {
self.lon_deg.to_radians()
}
pub fn to_cartesian(&self) -> (f64, f64, f64) {
latlon_to_cartesian(self.lat_deg, self.lon_deg)
}
pub fn to_ecef(&self, altitude_m: f64) -> Ecef {
let r = MERCURY_RADIUS + altitude_m;
let lat = self.lat_rad();
let lon = self.lon_rad();
Ecef {
x: r * lat.cos() * lon.cos(),
y: r * lat.cos() * lon.sin(),
z: r * lat.sin(),
}
}
pub fn great_circle_distance(&self, other: &LatLon) -> f64 {
let d_lat = (other.lat_deg - self.lat_deg).to_radians();
let d_lon = (other.lon_deg - self.lon_deg).to_radians();
let a = (d_lat / 2.0).sin().powi(2)
+ self.lat_rad().cos() * other.lat_rad().cos() * (d_lon / 2.0).sin().powi(2);
2.0 * MERCURY_RADIUS * a.sqrt().asin()
}
}
#[derive(Debug, Clone, Copy)]
pub struct Ecef {
pub x: f64,
pub y: f64,
pub z: f64,
}
impl Ecef {
pub fn to_latlon(&self) -> LatLon {
let r = (self.x * self.x + self.y * self.y + self.z * self.z).sqrt();
let lat = (self.z / r).asin().to_degrees();
let lon = self.y.atan2(self.x).to_degrees();
LatLon::new(lat, lon)
}
pub fn altitude(&self) -> f64 {
let r = (self.x * self.x + self.y * self.y + self.z * self.z).sqrt();
r - MERCURY_RADIUS
}
pub fn distance(&self, other: &Ecef) -> f64 {
let dx = other.x - self.x;
let dy = other.y - self.y;
let dz = other.z - self.z;
(dx * dx + dy * dy + dz * dz).sqrt()
}
}
pub fn latlon_to_cartesian(lat: f64, lon: f64) -> (f64, f64, f64) {
let r = MERCURY_RADIUS;
let lat_rad = lat.to_radians();
let lon_rad = lon.to_radians();
let x = r * lat_rad.cos() * lon_rad.cos();
let y = r * lat_rad.cos() * lon_rad.sin();
let z = r * lat_rad.sin();
(x, y, z)
}
pub fn cartesian_to_latlon(x: f64, y: f64, z: f64) -> (f64, f64) {
let r = (x * x + y * y + z * z).sqrt();
let lat = (z / r).asin().to_degrees();
let lon = y.atan2(x).to_degrees();
(lat, lon)
}
pub fn surface_area_band(lat_min_deg: f64, lat_max_deg: f64) -> f64 {
let r = MERCURY_RADIUS;
let sin_min = lat_min_deg.to_radians().sin();
let sin_max = lat_max_deg.to_radians().sin();
2.0 * std::f64::consts::PI * r * r * (sin_max - sin_min).abs()
}