use proj4rs::Proj;
pub struct Crs {
proj: Proj,
}
#[derive(Debug)]
pub enum CrsError {
Proj(proj4rs::errors::Error),
Wkt(alloc::string::String),
}
impl core::fmt::Display for CrsError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
CrsError::Proj(e) => write!(f, "invalid CRS definition: {e}"),
CrsError::Wkt(m) => write!(f, "invalid WKT: {m}"),
}
}
}
impl std::error::Error for CrsError {}
impl Crs {
pub fn from_proj_string(s: &str) -> Result<Self, CrsError> {
Proj::from_proj_string(s)
.map(|proj| Self { proj })
.map_err(CrsError::Proj)
}
pub fn from_epsg(code: u16) -> Result<Self, CrsError> {
Proj::from_epsg_code(code)
.map(|proj| Self { proj })
.map_err(CrsError::Proj)
}
pub fn from_wkt(wkt: &str) -> Result<Self, CrsError> {
let projstr =
proj4wkt::wkt_to_projstring(wkt).map_err(|e| CrsError::Wkt(alloc::format!("{e:?}")))?;
Self::from_proj_string(&projstr)
}
#[must_use]
pub(crate) fn proj(&self) -> &Proj {
&self.proj
}
#[must_use]
pub fn is_geographic(&self) -> bool {
self.proj.is_latlong()
}
}
#[cfg(test)]
mod tests {
use super::Crs;
#[test]
fn build_from_epsg() {
assert!(Crs::from_epsg(4326).is_ok());
assert!(Crs::from_epsg(3857).is_ok());
}
#[test]
fn build_from_proj_string() {
assert!(Crs::from_proj_string("+proj=longlat +datum=WGS84 +no_defs").is_ok());
}
#[test]
fn wgs84_is_geographic() {
let wgs84 = Crs::from_epsg(4326).unwrap();
assert!(wgs84.is_geographic());
let mercator = Crs::from_epsg(3857).unwrap();
assert!(!mercator.is_geographic());
}
#[test]
fn bad_epsg_errors() {
assert!(Crs::from_epsg(1).is_err());
}
}