oxiproj-core 0.1.2

Foundation types for OxiProj: coordinates, errors, ellipsoids, datums, and units.
Documentation
//! Linear and angular unit definitions ported from PROJ `src/units.cpp`.

use crate::consts::{DEG_TO_RAD, M_PI};

/// One unit-of-measure row. Ported from src/units.cpp.
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct UnitDef {
    /// Short unit identifier, e.g. `"km"`, `"us-ft"`.
    pub id: &'static str,
    /// Original PROJ `to_meter`/`to_radian` conversion-factor literal
    /// (kept as text for exact provenance, e.g. `"1000"` or a fraction).
    pub to_meter: &'static str,
    /// Free-text description of the unit.
    pub name: &'static str,
    /// Numeric conversion factor to the base unit (meters for linear
    /// units, radians for angular units), parsed from [`Self::to_meter`].
    pub factor: f64,
}

/// Linear units (id, to_meter literal, name, numeric factor). Ported from src/units.cpp.
pub static LINEAR_UNITS: &[UnitDef] = &[
    UnitDef {
        id: "km",
        to_meter: "1000",
        name: "Kilometer",
        factor: 1000.0,
    },
    UnitDef {
        id: "m",
        to_meter: "1",
        name: "Meter",
        factor: 1.0,
    },
    UnitDef {
        id: "dm",
        to_meter: "1/10",
        name: "Decimeter",
        factor: 0.1,
    },
    UnitDef {
        id: "cm",
        to_meter: "1/100",
        name: "Centimeter",
        factor: 0.01,
    },
    UnitDef {
        id: "mm",
        to_meter: "1/1000",
        name: "Millimeter",
        factor: 0.001,
    },
    UnitDef {
        id: "kmi",
        to_meter: "1852",
        name: "International Nautical Mile",
        factor: 1852.0,
    },
    UnitDef {
        id: "in",
        to_meter: "0.0254",
        name: "International Inch",
        factor: 0.0254,
    },
    UnitDef {
        id: "ft",
        to_meter: "0.3048",
        name: "International Foot",
        factor: 0.3048,
    },
    UnitDef {
        id: "yd",
        to_meter: "0.9144",
        name: "International Yard",
        factor: 0.9144,
    },
    UnitDef {
        id: "mi",
        to_meter: "1609.344",
        name: "International Statute Mile",
        factor: 1609.344,
    },
    UnitDef {
        id: "fath",
        to_meter: "1.8288",
        name: "International Fathom",
        factor: 1.8288,
    },
    UnitDef {
        id: "ch",
        to_meter: "20.1168",
        name: "International Chain",
        factor: 20.1168,
    },
    UnitDef {
        id: "link",
        to_meter: "0.201168",
        name: "International Link",
        factor: 0.201168,
    },
    UnitDef {
        id: "us-in",
        to_meter: "1/39.37",
        name: "U.S. Surveyor's Inch",
        factor: 100.0 / 3937.0,
    },
    UnitDef {
        id: "us-ft",
        to_meter: "0.304800609601219",
        name: "U.S. Surveyor's Foot",
        factor: 1200.0 / 3937.0,
    },
    UnitDef {
        id: "us-yd",
        to_meter: "0.914401828803658",
        name: "U.S. Surveyor's Yard",
        factor: 3600.0 / 3937.0,
    },
    UnitDef {
        id: "us-ch",
        to_meter: "20.11684023368047",
        name: "U.S. Surveyor's Chain",
        factor: 79200.0 / 3937.0,
    },
    UnitDef {
        id: "us-mi",
        to_meter: "1609.347218694437",
        name: "U.S. Surveyor's Statute Mile",
        factor: 6336000.0 / 3937.0,
    },
    UnitDef {
        id: "ind-yd",
        to_meter: "0.91439523",
        name: "Indian Yard",
        factor: 0.91439523,
    },
    UnitDef {
        id: "ind-ft",
        to_meter: "0.30479841",
        name: "Indian Foot",
        factor: 0.30479841,
    },
    UnitDef {
        id: "ind-ch",
        to_meter: "20.11669506",
        name: "Indian Chain",
        factor: 20.11669506,
    },
];

/// Angular units. Ported from src/units.cpp.
pub static ANGULAR_UNITS: &[UnitDef] = &[
    UnitDef {
        id: "rad",
        to_meter: "1.0",
        name: "Radian",
        factor: 1.0,
    },
    UnitDef {
        id: "deg",
        to_meter: "0.017453292519943296",
        name: "Degree",
        factor: DEG_TO_RAD,
    },
    UnitDef {
        id: "grad",
        to_meter: "0.015707963267948967",
        name: "Grad",
        factor: M_PI / 200.0,
    },
];

/// Look up a linear unit by exact id. Ported from src/units.cpp.
pub fn find_linear_unit(id: &str) -> Option<&'static UnitDef> {
    LINEAR_UNITS.iter().find(|u| u.id == id)
}

/// Look up an angular unit by exact id. Ported from src/units.cpp.
pub fn find_angular_unit(id: &str) -> Option<&'static UnitDef> {
    ANGULAR_UNITS.iter().find(|u| u.id == id)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn linear_unit_ft_factor() {
        assert_eq!(find_linear_unit("ft").map(|u| u.factor), Some(0.3048));
    }

    #[test]
    fn linear_unit_us_ft_factor() {
        assert!((find_linear_unit("us-ft").expect("us-ft").factor - 0.3048006096).abs() < 1e-9);
    }

    #[test]
    fn linear_unit_meter_factor() {
        assert_eq!(find_linear_unit("m").map(|u| u.factor), Some(1.0));
    }

    #[test]
    fn angular_unit_deg_factor() {
        assert_eq!(find_angular_unit("deg").map(|u| u.factor), Some(DEG_TO_RAD));
    }

    #[test]
    fn angular_unit_rad_factor() {
        assert_eq!(find_angular_unit("rad").map(|u| u.factor), Some(1.0));
    }

    #[test]
    fn linear_unit_unknown_is_none() {
        assert!(find_linear_unit("nope").is_none());
    }

    #[test]
    fn table_lengths() {
        assert_eq!(LINEAR_UNITS.len(), 21);
        assert_eq!(ANGULAR_UNITS.len(), 3);
    }

    #[test]
    fn angular_unit_grad_factor() {
        assert!(
            (find_angular_unit("grad").expect("grad").factor - 0.015707963267948967).abs() < 1e-15
        );
    }
}