|  | 
|  | 1 | +use core::f64; | 
|  | 2 | +use std::{ | 
|  | 3 | +    cell::RefCell, | 
|  | 4 | +    ffi::{CStr, CString}, | 
|  | 5 | +}; | 
|  | 6 | + | 
|  | 7 | +use libc::c_void; | 
|  | 8 | + | 
|  | 9 | +use crate::{ | 
|  | 10 | +    errors::{GdalError, Result}, | 
|  | 11 | +    Dataset, | 
|  | 12 | +}; | 
|  | 13 | + | 
|  | 14 | +/// Ground Control Point (GCP) used to georeference a dataset. | 
|  | 15 | +#[derive(Debug, Clone)] | 
|  | 16 | +pub struct GCP { | 
|  | 17 | +    id: String, | 
|  | 18 | +    info: Option<String>, | 
|  | 19 | +    pixel_xy: (usize, usize), | 
|  | 20 | +    location_xyz: (f64, f64, f64), | 
|  | 21 | +} | 
|  | 22 | + | 
|  | 23 | +impl GCP { | 
|  | 24 | +    /// Returns a GCP constructed from its GDAL C equivalent. | 
|  | 25 | +    /// | 
|  | 26 | +    /// # Arguments | 
|  | 27 | +    /// | 
|  | 28 | +    /// * `c_gcp` - A valid pointer to a C GDAL GCP representation. | 
|  | 29 | +    pub fn with_c_gcp(c_gcp: *const gdal_sys::GDAL_GCP) -> GCP { | 
|  | 30 | +        unsafe { | 
|  | 31 | +            GCP { | 
|  | 32 | +                id: CStr::from_ptr((*c_gcp).pszId).to_str().unwrap().to_string(), | 
|  | 33 | +                info: CStr::from_ptr((*c_gcp).pszInfo) | 
|  | 34 | +                    .to_str() | 
|  | 35 | +                    .map_or(None, |str| Some(str.to_string())), | 
|  | 36 | +                pixel_xy: ((*c_gcp).dfGCPPixel as usize, (*c_gcp).dfGCPLine as usize), | 
|  | 37 | +                location_xyz: ((*c_gcp).dfGCPX, (*c_gcp).dfGCPY, (*c_gcp).dfGCPZ), | 
|  | 38 | +            } | 
|  | 39 | +        } | 
|  | 40 | +    } | 
|  | 41 | +} | 
|  | 42 | + | 
|  | 43 | +/// Polynomial order. | 
|  | 44 | +#[derive(Copy, Clone)] | 
|  | 45 | +pub enum Order { | 
|  | 46 | +    First = 1, | 
|  | 47 | +    Second = 2, | 
|  | 48 | +    Third = 3, | 
|  | 49 | +} | 
|  | 50 | +/// Transformer used to map between pixel/line/height to longitude/latitude/height. | 
|  | 51 | +pub struct Transformer { | 
|  | 52 | +    c_transformer_ref: RefCell<*mut c_void>, | 
|  | 53 | +} | 
|  | 54 | + | 
|  | 55 | +impl Transformer { | 
|  | 56 | +    /// Construct a ```Transformer``` from a valid C GDAL pointer to a transformer instance. | 
|  | 57 | +    pub(crate) unsafe fn with_c_transformer(c_transformer: *mut c_void) -> Transformer { | 
|  | 58 | +        Transformer { | 
|  | 59 | +            c_transformer_ref: RefCell::new(c_transformer), | 
|  | 60 | +        } | 
|  | 61 | +    } | 
|  | 62 | + | 
|  | 63 | +    /// Extracts the Ground Control Points from the specified dataset. | 
|  | 64 | +    /// | 
|  | 65 | +    /// # Arguments | 
|  | 66 | +    /// | 
|  | 67 | +    /// * `dataset` - The `Dataset` to extract Ground Control Points from. | 
|  | 68 | +    /// | 
|  | 69 | +    /// # Examples | 
|  | 70 | +    /// | 
|  | 71 | +    /// ```no_run | 
|  | 72 | +    /// use std::path::Path; | 
|  | 73 | +    /// use gdal::Dataset; | 
|  | 74 | +    /// use gdal::alg::transform::Transformer; | 
|  | 75 | +    /// | 
|  | 76 | +    /// let path = Path::new("/path/to/dataset"); | 
|  | 77 | +    /// let dataset = Dataset::open(&path)?; | 
|  | 78 | +    /// let gcps = Transformer::gcps(&dataset); | 
|  | 79 | +    /// ``` | 
|  | 80 | +    pub fn gcps(dataset: &Dataset) -> Vec<GCP> { | 
|  | 81 | +        unsafe { | 
|  | 82 | +            let count = gdal_sys::GDALGetGCPCount(dataset.c_dataset()) as usize; | 
|  | 83 | +            let gcps = gdal_sys::GDALGetGCPs(dataset.c_dataset()); | 
|  | 84 | +            let mut result = Vec::with_capacity(count); | 
|  | 85 | +            for i in 0..count { | 
|  | 86 | +                let gcp = GCP::with_c_gcp(gcps.add(i)); | 
|  | 87 | +                result.push(gcp); | 
|  | 88 | +            } | 
|  | 89 | +            result | 
|  | 90 | +        } | 
|  | 91 | +    } | 
|  | 92 | + | 
|  | 93 | +    /// Constructs a GCP based polynomial `Transformer`. | 
|  | 94 | +    /// | 
|  | 95 | +    /// # Arguments | 
|  | 96 | +    /// | 
|  | 97 | +    /// * `gcp`    - A vector of Ground Control Points to be used as input. | 
|  | 98 | +    /// * `order`  - The requested polynomial order. Using a third-order polynomial is not recommended due | 
|  | 99 | +    ///              to the potential for numeric instabilities. | 
|  | 100 | +    /// * `reversed` - Set it to `true` to compute the reversed transformation. | 
|  | 101 | +
 | 
|  | 102 | +    pub fn gcp(gcp: Vec<GCP>, order: Order, reversed: bool) -> Result<Transformer> { | 
|  | 103 | +        let pas_gcp_list = gcp | 
|  | 104 | +            .iter() | 
|  | 105 | +            .map(|p| { | 
|  | 106 | +                let psz_info = match &p.info { | 
|  | 107 | +                    Some(arg) => { | 
|  | 108 | +                        let tmp = CString::new(arg.clone()).unwrap(); | 
|  | 109 | +                        tmp.into_raw() | 
|  | 110 | +                    } | 
|  | 111 | +                    None => std::ptr::null_mut(), | 
|  | 112 | +                }; | 
|  | 113 | + | 
|  | 114 | +                gdal_sys::GDAL_GCP { | 
|  | 115 | +                    pszId: CString::new(p.id.clone()).unwrap().into_raw(), | 
|  | 116 | +                    pszInfo: psz_info, | 
|  | 117 | +                    dfGCPPixel: p.pixel_xy.0 as f64, | 
|  | 118 | +                    dfGCPLine: p.pixel_xy.1 as f64, | 
|  | 119 | +                    dfGCPX: p.location_xyz.0, | 
|  | 120 | +                    dfGCPY: p.location_xyz.1, | 
|  | 121 | +                    dfGCPZ: p.location_xyz.2, | 
|  | 122 | +                } | 
|  | 123 | +            }) | 
|  | 124 | +            .collect::<Vec<_>>(); | 
|  | 125 | + | 
|  | 126 | +        let c_transformer = unsafe { | 
|  | 127 | +            gdal_sys::GDALCreateGCPTransformer( | 
|  | 128 | +                gcp.len() as i32, | 
|  | 129 | +                pas_gcp_list.as_ptr(), | 
|  | 130 | +                order as i32, | 
|  | 131 | +                if reversed { 1 } else { 0 }, | 
|  | 132 | +            ) | 
|  | 133 | +        }; | 
|  | 134 | + | 
|  | 135 | +        if c_transformer.is_null() { | 
|  | 136 | +            return Err(GdalError::NullPointer { | 
|  | 137 | +                method_name: "GDALCreateGCPTransformer", | 
|  | 138 | +                msg: "Failed to create GCP Transformer".to_string(), | 
|  | 139 | +            }); | 
|  | 140 | +        } | 
|  | 141 | + | 
|  | 142 | +        Ok(unsafe { Transformer::with_c_transformer(c_transformer) }) | 
|  | 143 | +    } | 
|  | 144 | + | 
|  | 145 | +    /// Transform a 2D point based on GCP derived polynomial model. | 
|  | 146 | +    /// | 
|  | 147 | +    /// # Arguments | 
|  | 148 | +    /// | 
|  | 149 | +    /// * `x` - The x point (pixel or longitude). | 
|  | 150 | +    /// * `y` - The y point (pixel or longitude). | 
|  | 151 | +    pub fn transform(&self, x: f64, y: f64) -> Option<(f64, f64)> { | 
|  | 152 | +        let mut x: [f64; 1] = [x]; | 
|  | 153 | +        let mut y: [f64; 1] = [y]; | 
|  | 154 | +        let mut z: [f64; 1] = [0.]; | 
|  | 155 | +        let mut r: [i32; 1] = [0]; | 
|  | 156 | +        unsafe { | 
|  | 157 | +            gdal_sys::GDALGCPTransform( | 
|  | 158 | +                *self.c_transformer_ref.borrow(), | 
|  | 159 | +                0, | 
|  | 160 | +                1, | 
|  | 161 | +                x.as_mut_ptr(), | 
|  | 162 | +                y.as_mut_ptr(), | 
|  | 163 | +                z.as_mut_ptr(), | 
|  | 164 | +                r.as_mut_ptr(), | 
|  | 165 | +            ); | 
|  | 166 | +            if r[0] != 0 { | 
|  | 167 | +                Some((x[0], y[0])) | 
|  | 168 | +            } else { | 
|  | 169 | +                None | 
|  | 170 | +            } | 
|  | 171 | +        } | 
|  | 172 | +    } | 
|  | 173 | +} | 
|  | 174 | + | 
|  | 175 | +/// Cleanup the GCP transformer when it exits scope. | 
|  | 176 | +impl Drop for Transformer { | 
|  | 177 | +    fn drop(&mut self) { | 
|  | 178 | +        unsafe { | 
|  | 179 | +            let c_transformer = self.c_transformer_ref.borrow(); | 
|  | 180 | +            gdal_sys::GDALDestroyGCPTransformer(c_transformer.to_owned()); | 
|  | 181 | +        } | 
|  | 182 | +    } | 
|  | 183 | +} | 
0 commit comments