use std::fmt::Debug;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use crate::error::{GeoArrowError, GeoArrowResult};
#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub struct Crs {
crs: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
crs_type: Option<CrsType>,
}
impl Crs {
pub fn from_projjson(value: Value) -> Self {
Self {
crs: Some(value),
crs_type: Some(CrsType::Projjson),
}
}
pub fn from_wkt2_2019(value: String) -> Self {
Self {
crs: Some(Value::String(value)),
crs_type: Some(CrsType::Wkt2_2019),
}
}
pub fn from_unknown_crs_type(value: String) -> Self {
Self {
crs: Some(Value::String(value)),
crs_type: None,
}
}
pub fn from_authority_code(value: String) -> Self {
assert!(value.contains(':'), "':' should be authority:code CRS");
Self {
crs: Some(Value::String(value)),
crs_type: Some(CrsType::AuthorityCode),
}
}
pub fn from_srid(value: String) -> Self {
Self {
crs: Some(Value::String(value)),
crs_type: Some(CrsType::Srid),
}
}
pub fn crs_type(&self) -> Option<CrsType> {
self.crs_type
}
pub fn crs_value(&self) -> Option<&Value> {
self.crs.as_ref()
}
pub(crate) fn should_serialize(&self) -> bool {
self.crs.is_some()
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub enum CrsType {
#[serde(rename = "projjson")]
Projjson,
#[serde(rename = "wkt2:2019")]
Wkt2_2019,
#[serde(rename = "authority_code")]
AuthorityCode,
#[serde(rename = "srid")]
Srid,
}
pub trait CrsTransform: Debug {
fn _convert_to_projjson(&self, crs: &Crs) -> GeoArrowResult<Option<Value>>;
fn _convert_to_wkt(&self, crs: &Crs) -> GeoArrowResult<Option<String>>;
fn extract_projjson(&self, crs: &Crs) -> GeoArrowResult<Option<Value>> {
match crs.crs_type() {
Some(CrsType::Projjson) => Ok(crs.crs_value().cloned()),
_ => self._convert_to_projjson(crs),
}
}
fn extract_wkt(&self, crs: &Crs) -> GeoArrowResult<Option<String>> {
if let (Some(crs), Some(crs_type)) = (crs.crs_value(), crs.crs_type()) {
if crs_type == CrsType::Wkt2_2019 {
if let Value::String(inner) = crs {
return Ok::<_, GeoArrowError>(Some(inner.clone()));
}
}
}
self._convert_to_wkt(crs)
}
}
#[derive(Debug, Clone, Default)]
pub struct DefaultCrsTransform {}
impl CrsTransform for DefaultCrsTransform {
fn _convert_to_projjson(&self, _crs: &Crs) -> GeoArrowResult<Option<Value>> {
Ok(None)
}
fn _convert_to_wkt(&self, _crs: &Crs) -> GeoArrowResult<Option<String>> {
Ok(None)
}
}
#[cfg(test)]
mod test {
use serde_json::json;
use super::*;
#[test]
fn crs_omitted() {
let crs = Crs::default();
assert!(crs.crs_value().is_none());
assert!(crs.crs_type().is_none());
assert!(!crs.should_serialize());
}
#[test]
fn crs_projjson() {
let crs = Crs::from_projjson(json!({}));
assert!(crs.crs_value().is_some_and(|x| x.is_object()));
assert!(
crs.crs_type()
.is_some_and(|x| matches!(x, CrsType::Projjson))
);
assert!(crs.should_serialize());
assert_eq!(
serde_json::to_string(&crs).unwrap(),
r#"{"crs":{},"crs_type":"projjson"}"#
);
}
#[test]
fn crs_wkt2() {
let crs = Crs::from_wkt2_2019("TESTCRS[]".to_string());
assert_eq!(
crs.crs_value(),
Some(&Value::String("TESTCRS[]".to_string()))
);
assert!(matches!(crs.crs_type(), Some(CrsType::Wkt2_2019)));
assert!(crs.should_serialize());
assert_eq!(
serde_json::to_string(&crs).unwrap(),
r#"{"crs":"TESTCRS[]","crs_type":"wkt2:2019"}"#
);
}
#[test]
fn crs_authority_code() {
let crs = Crs::from_authority_code("GEOARROW:1234".to_string());
assert_eq!(
crs.crs_value(),
Some(&Value::String("GEOARROW:1234".to_string()))
);
assert!(matches!(crs.crs_type(), Some(CrsType::AuthorityCode)));
assert!(crs.should_serialize());
assert_eq!(
serde_json::to_string(&crs).unwrap(),
r#"{"crs":"GEOARROW:1234","crs_type":"authority_code"}"#
);
}
#[test]
fn crs_srid() {
let crs = Crs::from_srid("1234".to_string());
assert_eq!(crs.crs_value(), Some(&Value::String("1234".to_string())),);
assert!(matches!(crs.crs_type(), Some(CrsType::Srid)));
assert!(crs.should_serialize());
assert_eq!(
serde_json::to_string(&crs).unwrap(),
r#"{"crs":"1234","crs_type":"srid"}"#
);
}
#[test]
fn crs_unknown() {
let crs = Crs::from_unknown_crs_type("1234".to_string());
assert_eq!(crs.crs_value(), Some(&Value::String("1234".to_string())),);
assert!(crs.crs_type().is_none());
assert!(crs.should_serialize());
assert_eq!(serde_json::to_string(&crs).unwrap(), r#"{"crs":"1234"}"#);
}
}