use oxiproj_geodesic::Geodesic;
#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
pub enum GeodesicError {
#[error("invalid geodesic input {field}: {reason}")]
InvalidInput {
field: &'static str,
reason: &'static str,
},
}
fn invalid_input(field: &'static str, reason: &'static str) -> GeodesicError {
GeodesicError::InvalidInput { field, reason }
}
fn validate_latitude(field: &'static str, value: f64) -> Result<(), GeodesicError> {
if !value.is_finite() {
return Err(invalid_input(field, "must be finite"));
}
if !(-90.0..=90.0).contains(&value) {
return Err(invalid_input(field, "must be in [-90, 90] degrees"));
}
Ok(())
}
fn validate_finite(field: &'static str, value: f64) -> Result<(), GeodesicError> {
if value.is_finite() {
Ok(())
} else {
Err(invalid_input(field, "must be finite"))
}
}
pub fn geodesic_inverse(
lat1_deg: f64,
lon1_deg: f64,
lat2_deg: f64,
lon2_deg: f64,
) -> Result<(f64, f64, f64), GeodesicError> {
validate_latitude("lat1_deg", lat1_deg)?;
validate_finite("lon1_deg", lon1_deg)?;
validate_latitude("lat2_deg", lat2_deg)?;
validate_finite("lon2_deg", lon2_deg)?;
let geodesic = Geodesic::wgs84();
let result = geodesic.inverse(lat1_deg, lon1_deg, lat2_deg, lon2_deg);
Ok((result.s12, result.azi1, result.azi2))
}
pub fn geodesic_direct(
lat1_deg: f64,
lon1_deg: f64,
azi1_deg: f64,
s12_m: f64,
) -> Result<(f64, f64, f64), GeodesicError> {
validate_latitude("lat1_deg", lat1_deg)?;
validate_finite("lon1_deg", lon1_deg)?;
validate_finite("azi1_deg", azi1_deg)?;
validate_finite("s12_m", s12_m)?;
let geodesic = Geodesic::wgs84();
let result = geodesic.direct(lat1_deg, lon1_deg, azi1_deg, s12_m);
Ok((result.lat2, result.lon2, result.azi2))
}