use crate::crs::{Crs, CrsTransformPolicy};
use crate::error::{ProjectionError, Result};
use crate::projections::ProjectionKind;
#[derive(Debug)]
pub struct CompoundCrs {
pub name: String,
pub horizontal: Crs,
pub vertical: Crs,
pub epsg_code: Option<u32>,
}
impl CompoundCrs {
pub fn new(name: impl Into<String>, horizontal: Crs, vertical: Crs) -> Result<Self> {
if matches!(horizontal.projection.params().kind, ProjectionKind::Vertical) {
return Err(ProjectionError::UnsupportedProjection(
"horizontal component cannot be vertical".to_string(),
));
}
if !matches!(vertical.projection.params().kind, ProjectionKind::Vertical) {
return Err(ProjectionError::UnsupportedProjection(
"vertical component must be a vertical CRS".to_string(),
));
}
Ok(Self {
name: name.into(),
horizontal,
vertical,
epsg_code: None,
})
}
pub fn from_epsg(code: u32) -> Result<Self> {
match code {
7405 => {
let horizontal = Crs::from_epsg(27700)?;
let vertical = Crs::from_epsg(5701)?;
Ok(Self {
name: "OSGB36 / British National Grid + ODN height (EPSG:7405)".to_string(),
horizontal,
vertical,
epsg_code: Some(code),
})
}
_ => Err(ProjectionError::UnsupportedProjection(format!(
"compound EPSG:{code} is not currently supported"
))),
}
}
pub fn transform_to(&self, x: f64, y: f64, z: f64, target: &CompoundCrs) -> Result<(f64, f64, f64)> {
self.transform_to_with_policy(x, y, z, target, CrsTransformPolicy::Strict)
}
pub fn transform_to_with_policy(
&self,
x: f64,
y: f64,
z: f64,
target: &CompoundCrs,
policy: CrsTransformPolicy,
) -> Result<(f64, f64, f64)> {
let (x_out, y_out) = self
.horizontal
.transform_to_with_policy(x, y, &target.horizontal, policy)?;
let (lon_deg, lat_deg) = self.horizontal.inverse(x, y)?;
let (_, _, z_out) = self
.vertical
.transform_to_3d_with_policy(lon_deg, lat_deg, z, &target.vertical, policy)?;
Ok((x_out, y_out, z_out))
}
}