use crate::Projection;
use crate::ellipsoids::Ellipsoid;
use crate::errors::{
ProjectionError, ensure_finite, ensure_within_range, unpack_required_parameter,
};
#[cfg(feature = "tracing")]
use tracing::instrument;
#[derive(Copy, Clone, PartialEq, PartialOrd, Debug)]
pub struct EquidistantCylindrical {
ref_lat: f64,
ref_lon: f64,
std_par: f64,
r: f64,
r_time_par_cos: f64,
}
impl EquidistantCylindrical {
#[must_use]
pub fn builder() -> EquidistantCylindricalBuilder {
EquidistantCylindricalBuilder::default()
}
}
#[derive(Copy, Clone, PartialEq, PartialOrd, Debug)]
pub struct EquidistantCylindricalBuilder {
ref_lon: Option<f64>,
ref_lat: Option<f64>,
std_par: f64,
}
impl Default for EquidistantCylindricalBuilder {
fn default() -> Self {
Self {
ref_lon: None,
ref_lat: None,
std_par: 0.0,
}
}
}
impl EquidistantCylindricalBuilder {
pub const fn ref_lonlat(&mut self, lon: f64, lat: f64) -> &mut Self {
self.ref_lon = Some(lon);
self.ref_lat = Some(lat);
self
}
pub const fn standard_parallel(&mut self, std_par: f64) -> &mut Self {
self.std_par = std_par;
self
}
pub fn initialize_projection(&self) -> Result<EquidistantCylindrical, ProjectionError> {
let ref_lon = unpack_required_parameter!(self, ref_lon);
let ref_lat = unpack_required_parameter!(self, ref_lat);
let std_par = self.std_par;
ensure_finite!(ref_lon, ref_lat, std_par);
ensure_within_range!(ref_lon, -180.0..180.0);
ensure_within_range!(ref_lat, -90.0..90.0);
ensure_within_range!(std_par, -90.0..90.0);
let r = Ellipsoid::SPHERE.A;
let r_time_par_cos = r * std_par.to_radians().cos();
Ok(EquidistantCylindrical {
ref_lat: ref_lat.to_radians(),
ref_lon: ref_lon.to_radians(),
std_par: std_par.to_radians(),
r,
r_time_par_cos,
})
}
}
impl Projection for EquidistantCylindrical {
#[inline]
#[cfg_attr(feature = "tracing", instrument(level = "trace"))]
fn project_unchecked(&self, lon: f64, lat: f64) -> (f64, f64) {
let lon = lon.to_radians();
let lat = lat.to_radians();
let x = self.r_time_par_cos * (lon - self.ref_lon);
let y = self.r * (lat - self.ref_lat);
(x, y)
}
#[inline]
#[cfg_attr(feature = "tracing", instrument(level = "trace"))]
fn inverse_project_unchecked(&self, x: f64, y: f64) -> (f64, f64) {
let lon = (x / self.r_time_par_cos) + self.ref_lon;
let lat = (y / self.r) + self.ref_lat;
(lon.to_degrees(), lat.to_degrees())
}
}