use crate::datum_params::DatumParams;
use crate::ellps::Ellipsoid;
use crate::errors::Result;
use crate::geocent::{geocentric_to_geodetic, geodetic_to_geocentric};
use crate::transform::Direction;
use DatumParams::*;
const SRS_WGS84_SEMIMAJOR: f64 = 6378137.0;
const SRS_WGS84_SEMIMINOR: f64 = 6356752.314;
const SRS_WGS84_ES: f64 = 0.0066943799901413165;
#[derive(Debug, Clone)]
pub(crate) struct Datum {
params: DatumParams,
pub a: f64,
pub b: f64,
pub es: f64,
}
impl Datum {
pub fn new(ellps: &Ellipsoid, params: DatumParams) -> Self {
let (a, b, es) = if params.use_nadgrids() {
(SRS_WGS84_SEMIMAJOR, SRS_WGS84_SEMIMINOR, SRS_WGS84_ES)
} else {
(ellps.a, ellps.b, ellps.es)
};
Self {
params: if params == ToWGS84_3(0., 0., 0.)
&& ellps.a == SRS_WGS84_SEMIMAJOR
&& (ellps.es - SRS_WGS84_ES).abs() < 0.000000000050
{
ToWGS84_0
} else {
params
},
a,
b,
es,
}
}
fn towgs84(&self, x: f64, y: f64, z: f64) -> Result<(f64, f64, f64)> {
match &self.params {
ToWGS84_0 => geodetic_to_geocentric(x, y, z, self.a, self.es),
ToWGS84_3(dx, dy, dz) => geodetic_to_geocentric(x, y, z, self.a, self.es)
.map(|(x, y, z)| (x + dx, y + dy, z + dz)),
ToWGS84_7(dx, dy, dz, rx, ry, rz, s) => {
geodetic_to_geocentric(x, y, z, self.a, self.es).map(|(x, y, z)| {
(
dx + s * (x - rz * y + ry * z),
dy + s * (rz * x + y - rx * z),
dz + s * (-ry * x + rx * y + z),
)
})
}
NadGrids(grids) => grids
.apply_shift(Direction::Forward, x, y, z)
.and_then(|(x, y, z)| geodetic_to_geocentric(x, y, z, self.a, self.es)),
NoDatum => Ok((x, y, z)),
}
}
fn fromwgs84(&self, x: f64, y: f64, z: f64) -> Result<(f64, f64, f64)> {
match &self.params {
ToWGS84_0 => geocentric_to_geodetic(x, y, z, self.a, self.es, self.b),
ToWGS84_3(dx, dy, dz) => {
geocentric_to_geodetic(x - dx, y - dy, z - dz, self.a, self.es, self.b)
}
ToWGS84_7(dx, dy, dz, rx, ry, rz, s) => {
let (x, y, z) = ((x - dx) / s, (y - dy) / s, (z - dz) / s);
geocentric_to_geodetic(
x + rz * y - ry * z,
-rz * x + y + rx * z,
ry * x - rx * y + z,
self.a,
self.es,
self.b,
)
}
NadGrids(grids) => geocentric_to_geodetic(x, y, z, self.a, self.es, self.b)
.and_then(|(x, y, z)| grids.apply_shift(Direction::Inverse, x, y, z)),
NoDatum => Ok((x, y, z)),
}
}
#[inline]
pub fn no_datum(&self) -> bool {
self.params.no_datum()
}
pub fn is_identical_to(&self, other: &Self) -> bool {
(self.params == other.params)
&& self.a == other.a
&& (self.es - other.es).abs() < 0.000000000050
}
#[inline]
pub fn transform(src: &Self, dst: &Self, x: f64, y: f64, z: f64) -> Result<(f64, f64, f64)> {
src.towgs84(x, y, z)
.and_then(|(x, y, z)| dst.fromwgs84(x, y, z))
}
}