1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
#![doc(html_root_url = "https://urschrei.github.io/ostn02_phf/")] //! Look up OSTN02 adjustments for transforming ETRS89 Eastings and Northings //! to OSGB36 Eastings and Northings const MIN_X_SHIFT: f64 = 86.275; const MIN_Y_SHIFT: f64 = -81.603; const MIN_Z_SHIFT: f64 = 43.982; use std::f64; const NAN: f64 = f64::NAN; extern crate phf; include!("ostn02.rs"); extern crate libc; use libc::{c_double, int32_t}; /// Return a 3-tuple of adjustments which convert ETRS89 Eastings and Northings /// to OSGB36 Eastings, Northings, and Orthometric height fn get_shifts(tup: (i32, i32)) -> (f64, f64, f64) { // look up the shifts, or return NAN let key = format!("{:03x}{:03x}", tup.1, tup.0); match ostn02_lookup(&*key) { Some(res) => { (res.0 as f64 / 1000. + MIN_X_SHIFT, res.1 as f64 / 1000. + MIN_Y_SHIFT, res.2 as f64 / 1000. + MIN_Z_SHIFT) } None => (NAN, NAN, NAN), } } #[repr(C)] /// Incoming ETRS89 kilometer-grid references pub struct GridRefs { pub easting: int32_t, pub northing: int32_t, } #[repr(C)] /// Outgoing OSTN02 Easting, Northing, and height adjustments pub struct Adjustment { pub x_shift: c_double, pub y_shift: c_double, pub z_shift: c_double, } // From and Into traits for GridRefs impl From<(i32, i32)> for GridRefs { fn from(gr: (i32, i32)) -> GridRefs { GridRefs { easting: gr.0, northing: gr.1, } } } impl From<GridRefs> for (i32, i32) { fn from(gr: GridRefs) -> (i32, i32) { (gr.easting, gr.northing) } } // From and Into traits for Adjustment impl From<(f64, f64, f64)> for Adjustment { fn from(adj: (f64, f64, f64)) -> Adjustment { Adjustment { x_shift: adj.0, y_shift: adj.1, z_shift: adj.2, } } } impl From<Adjustment> for (f64, f64, f64) { fn from(adj: Adjustment) -> (f64, f64, f64) { (adj.x_shift, adj.y_shift, adj.z_shift) } } /// FFI function returning a 3-tuple of Easting, Northing, and height adjustments, for use in transforming /// ETRS89 Eastings and Northings to OSGB36 Eastings, Northings. /// The argument is a Struct containing kilometer-grid references of the ETRS89 Northings and Eastings you wish to convert /// /// # Examples /// /// ```python /// # Python example using ctypes /// import sys, ctypes /// from ctypes import c_int32, c_double, Structure /// /// /// class GridRefs(Structure): /// _fields_ = [("eastings", c_int32), /// ("northings", c_int32)] /// /// def __str__(self): /// return "({},{})".format(self.eastings, self.northings) /// /// /// class Shifts(Structure): /// _fields_ = [("x_shift", c_double), /// ("y_shift", c_double), /// ("z_shift", c_double)] /// /// def __str__(self): /// return "({}, {}, {})".format(self.x_shift, self.y_shift, self.z_shift) /// /// /// prefix = {'win32': ''}.get(sys.platform, 'lib') /// extension = {'darwin': '.dylib', 'win32': '.dll'}.get(sys.platform, '.so') /// lib = ctypes.cdll.LoadLibrary(prefix + "ostn02_phf" + extension) /// /// lib.get_shifts_ffi.argtypes = (GridRefs,) /// lib.get_shifts_ffi.restype = Shifts /// /// tup = GridRefs(651, 313) /// /// # Should return (102.775, -78.244, 44.252) /// print lib.get_shifts_ffi(tup) /// ``` #[no_mangle] pub extern "C" fn get_shifts_ffi(gr: GridRefs) -> Adjustment { get_shifts(gr.into()).into() } /// Return a 3-tuple of Easting, Northing, and height adjustments, for use in transforming /// ETRS89 Eastings and Northings to OSGB36 Eastings, Northings. /// The key is the combined hex-transformed (03x) kilometer-grid reference of the Northings and Eastings: /// /// # Examples /// /// ``` /// use ostn02_phf::ostn02_lookup; /// /// // Caister Tower Eastings and Northings: 651307.003, 313255.686 /// let e_grid = (651307.003 / 1000.) as i32; /// let n_grid = (313255.686 / 1000.) as i32; /// let key = format!("{:03x}{:03x}", n_grid, e_grid); /// // key is 13928b /// let result = ostn02_lookup(&*key).unwrap(); /// // result should be (16500, 3359, 270) /// assert_eq!(result, (16500, 3359, 270)); /// // remember that the actual adjustment for a coordinate is a bilinear transform, using a square /// // see ostn02_shifts in https://github.com/urschrei/lonlat_bng/blob/master/src/ostn02/mod.rs /// ``` pub fn ostn02_lookup(key: &str) -> Option<(i32, i32, i32)> { if key.is_empty() { return None; } OSTN02.get(&*key).cloned() } #[test] fn test_internal_ffi() { assert_eq!((102.775, -78.244, 44.252), get_shifts((651, 313))); } #[test] fn test_ffi() { let gr = GridRefs {easting: 651, northing: 313}; assert_eq!((102.775, -78.244, 44.252), get_shifts_ffi(gr).into()); }