#[cfg(feature = "no_std")]
use alloc::vec::Vec;
use crate::error::{ProjError, ProjResult};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(i32)]
#[non_exhaustive]
pub enum DatumType {
Unknown = 0,
ThreeParam = 1,
SevenParam = 2,
GridShift = 3,
Wgs84 = 4,
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct DatumDef {
pub id: &'static str,
pub defn: &'static str,
pub ellipse_id: &'static str,
pub comments: &'static str,
}
pub static DATUMS: &[DatumDef] = &[
DatumDef {
id: "WGS84",
defn: "towgs84=0,0,0",
ellipse_id: "WGS84",
comments: "",
},
DatumDef {
id: "GGRS87",
defn: "towgs84=-199.87,74.79,246.62",
ellipse_id: "GRS80",
comments: "Greek_Geodetic_Reference_System_1987",
},
DatumDef {
id: "NAD83",
defn: "towgs84=0,0,0",
ellipse_id: "GRS80",
comments: "North_American_Datum_1983",
},
DatumDef {
id: "NAD27",
defn: "nadgrids=@conus,@alaska,@ntv2_0.gsb,@ntv1_can.dat",
ellipse_id: "clrk66",
comments: "North_American_Datum_1927",
},
DatumDef {
id: "potsdam",
defn: "nadgrids=@BETA2007.gsb",
ellipse_id: "bessel",
comments: "Potsdam Rauenberg 1950 DHDN",
},
DatumDef {
id: "carthage",
defn: "towgs84=-263.0,6.0,431.0",
ellipse_id: "clrk80ign",
comments: "Carthage 1934 Tunisia",
},
DatumDef {
id: "hermannskogel",
defn: "towgs84=577.326,90.129,463.919,5.137,1.474,5.297,2.4232",
ellipse_id: "bessel",
comments: "Hermannskogel",
},
DatumDef {
id: "ire65",
defn: "towgs84=482.530,-130.596,564.557,-1.042,-0.214,-0.631,8.15",
ellipse_id: "mod_airy",
comments: "Ireland 1965",
},
DatumDef {
id: "nzgd49",
defn: "towgs84=59.47,-5.04,187.44,0.47,-0.1,1.024,-4.5993",
ellipse_id: "intl",
comments: "New Zealand Geodetic Datum 1949",
},
DatumDef {
id: "OSGB36",
defn: "towgs84=446.448,-125.157,542.060,0.1502,0.2470,0.8421,-20.4894",
ellipse_id: "airy",
comments: "Airy 1830",
},
];
pub fn find_datum(id: &str) -> Option<&'static DatumDef> {
DATUMS.iter().find(|d| d.id == id)
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct DatumParams {
pub dx: f64,
pub dy: f64,
pub dz: f64,
pub rx: f64,
pub ry: f64,
pub rz: f64,
pub s: f64,
pub datum_type: DatumType,
}
pub fn datum_params(id: &str) -> ProjResult<Option<DatumParams>> {
let def = match find_datum(id) {
Some(d) => d,
None => return Err(ProjError::IllegalArgValue),
};
let defn = def.defn;
if let Some(rest) = defn.strip_prefix("towgs84=") {
let mut nums: Vec<f64> = Vec::new();
for part in rest.split(',') {
let part = part.trim();
match part.parse::<f64>() {
Ok(v) => nums.push(v),
Err(_) => return Err(ProjError::IllegalArgValue),
}
}
let datum_type = if nums.len() == 7 {
DatumType::SevenParam
} else if nums.len() == 3 {
if (id == "WGS84" || id == "NAD83") && nums.iter().all(|&v| v == 0.0) {
DatumType::Wgs84
} else {
DatumType::ThreeParam
}
} else {
return Err(ProjError::IllegalArgValue);
};
let g = |i: usize| -> f64 { nums.get(i).copied().unwrap_or(0.0) };
Ok(Some(DatumParams {
dx: g(0),
dy: g(1),
dz: g(2),
rx: g(3),
ry: g(4),
rz: g(5),
s: g(6),
datum_type,
}))
} else if defn.starts_with("nadgrids=") {
Ok(None)
} else {
Err(ProjError::IllegalArgValue)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn osgb36_ellipse_id() {
assert_eq!(find_datum("OSGB36").map(|d| d.ellipse_id), Some("airy"));
}
#[test]
fn carthage_three_param() {
let p = datum_params("carthage").expect("ok").expect("some");
assert_eq!(p.datum_type, DatumType::ThreeParam);
assert_eq!(p.dx, -263.0);
assert_eq!(p.dy, 6.0);
assert_eq!(p.dz, 431.0);
assert_eq!(p.s, 0.0);
}
#[test]
fn osgb36_seven_param() {
let p = datum_params("OSGB36").expect("ok").expect("some");
assert_eq!(p.datum_type, DatumType::SevenParam);
assert_eq!(p.s, -20.4894);
assert_eq!(p.rx, 0.1502);
assert_eq!(p.dx, 446.448);
}
#[test]
fn nad27_is_nadgrids() {
assert_eq!(datum_params("NAD27").expect("ok"), None);
}
#[test]
fn wgs84_type() {
let p = datum_params("WGS84").expect("ok").expect("some");
assert_eq!(p.datum_type, DatumType::Wgs84);
assert_eq!((p.dx, p.dy, p.dz), (0.0, 0.0, 0.0));
}
#[test]
fn nad83_type() {
assert_eq!(
datum_params("NAD83").expect("ok").expect("some").datum_type,
DatumType::Wgs84
);
}
#[test]
fn ggrs87_three_param() {
assert_eq!(
datum_params("GGRS87")
.expect("ok")
.expect("some")
.datum_type,
DatumType::ThreeParam
);
}
#[test]
fn unknown_datum_is_err() {
assert!(datum_params("unknown_datum").is_err());
}
#[test]
fn datums_len() {
assert_eq!(DATUMS.len(), 10);
}
#[test]
fn datum_type_repr() {
assert_eq!(DatumType::Wgs84 as i32, 4);
assert_eq!(DatumType::GridShift as i32, 3);
}
}