use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CRS {
wkt: Option<String>,
epsg: Option<u32>,
proj: Option<String>,
}
impl CRS {
pub fn from_epsg(code: u32) -> Self {
Self {
wkt: None,
epsg: Some(code),
proj: None,
}
}
pub fn from_wkt(wkt: impl Into<String>) -> Self {
Self {
wkt: Some(wkt.into()),
epsg: None,
proj: None,
}
}
pub fn from_proj(proj: impl Into<String>) -> Self {
Self {
wkt: None,
epsg: None,
proj: Some(proj.into()),
}
}
pub fn wgs84() -> Self {
Self::from_epsg(4326)
}
pub fn web_mercator() -> Self {
Self::from_epsg(3857)
}
pub fn epsg(&self) -> Option<u32> {
self.epsg
}
pub fn wkt(&self) -> Option<&str> {
self.wkt.as_deref()
}
pub fn proj(&self) -> Option<&str> {
self.proj.as_deref()
}
pub fn is_equivalent(&self, other: &CRS) -> bool {
if let (Some(a), Some(b)) = (self.epsg, other.epsg) {
return a == b;
}
if let (Some(a), Some(b)) = (&self.wkt, &other.wkt) {
return a == b;
}
if let (Some(a), Some(b)) = (&self.proj, &other.proj) {
return a == b;
}
false
}
pub fn identifier(&self) -> String {
if let Some(code) = self.epsg {
return format!("EPSG:{}", code);
}
if let Some(proj) = &self.proj {
return proj.clone();
}
if let Some(wkt) = &self.wkt {
return format!("WKT:{}", &wkt[..wkt.len().min(50)]);
}
"Unknown".to_string()
}
}
impl fmt::Display for CRS {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.identifier())
}
}
impl Default for CRS {
fn default() -> Self {
Self::wgs84()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_crs_epsg() {
let crs = CRS::from_epsg(4326);
assert_eq!(crs.epsg(), Some(4326));
assert_eq!(crs.identifier(), "EPSG:4326");
}
#[test]
fn test_crs_equivalence() {
let a = CRS::from_epsg(4326);
let b = CRS::wgs84();
assert!(a.is_equivalent(&b));
}
}