oxiproj-transformations 0.1.1

Datum transformations and coordinate conversions for OxiProj.
Documentation
//! `geocent` conversion. Ported from PROJ `src/conversions/geocent.cpp`.
//!
//! This is the PROJ 2D stub; the actual geocentric Cartesian conversion is
//! performed by `cart`. In historical PROJ, the `geocent` operation is a
//! pass-through 2D stub: the forward operation copies the geodetic input
//! `lp` straight into the output `xy` (`xy.x = lp.lam`, `xy.y = lp.phi`) and
//! the inverse copies it back unchanged. The declared I/O units are
//! `left = RADIANS` and `right = CARTESIAN`. The real 3D geodetic <-> geocentric
//! Cartesian transformation is carried out by the `cart` conversion in modern
//! PROJ; this operation is preserved as a faithful port of that legacy 2D stub.

use crate::{TransBuild, TransParams};
use oxiproj_core::{IoUnits, Lp, Operation, ProjResult, Xy};

/// The `geocent` 2D stub conversion.
///
/// Faithful port of the historical PROJ `src/conversions/geocent.cpp` stub.
/// Both directions are pure pass-through copies between geodetic longitude/
/// latitude and the stub's Cartesian slots; the genuine geocentric Cartesian
/// work lives in the `cart` conversion.
#[derive(Debug)]
struct Geocent;

impl Operation for Geocent {
    /// Forward 2D stub: copy `lp` into `xy` unchanged.
    ///
    /// Ported from `src/conversions/geocent.cpp` (`forward` callback).
    fn forward_2d(&self, lp: Lp) -> ProjResult<Xy> {
        Ok(Xy::new(lp.lam, lp.phi))
    }

    /// Inverse 2D stub: copy `xy` back into `lp` unchanged.
    ///
    /// Ported from `src/conversions/geocent.cpp` (`inverse` callback).
    fn inverse_2d(&self, xy: Xy) -> ProjResult<Lp> {
        Ok(Lp::new(xy.x, xy.y))
    }

    fn has_inverse(&self) -> bool {
        true
    }
}

/// Construct the `geocent` conversion.
///
/// Ported from `src/conversions/geocent.cpp` (`PJ_CONVERSION(geocent)`). The
/// operation takes no parameters; the geodetic input is in radians and the
/// (stub) output is Cartesian.
pub fn new(_p: &TransParams) -> ProjResult<TransBuild> {
    Ok(TransBuild::new(
        Box::new(Geocent),
        IoUnits::Radians,
        IoUnits::Cartesian,
    ))
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::TransParamLookup;
    use oxiproj_core::Ellipsoid;

    struct NoParams;

    impl TransParamLookup for NoParams {
        fn get_dms(&self, _key: &str) -> Option<f64> {
            None
        }
        fn get_f64(&self, _key: &str) -> Option<f64> {
            None
        }
        fn get_int(&self, _key: &str) -> Option<i64> {
            None
        }
        fn get_str(&self, _key: &str) -> Option<&str> {
            None
        }
        fn get_bool(&self, _key: &str) -> bool {
            false
        }
        fn exists(&self, _key: &str) -> bool {
            false
        }
    }

    #[test]
    fn geocent_stub_round_trip() {
        let op = Geocent;
        let xy = op.forward_2d(Lp::new(0.2, 0.3)).unwrap();
        assert!(xy.x == 0.2 && xy.y == 0.3);
        let lp = op.inverse_2d(xy).unwrap();
        assert!(lp.lam == 0.2 && lp.phi == 0.3);
    }

    #[test]
    fn geocent_build_units() {
        let ell = Ellipsoid::named("WGS84").unwrap();
        let np = NoParams;
        let tp = TransParams {
            ellipsoid: &ell,
            params: &np,
            registry: None,
        };
        let b = new(&tp);
        assert!(b.is_ok());
    }
}