Skip to content

Commit eba90c9

Browse files
michaeljfazio-thalesmichaeljfazio
authored andcommitted
GCP Transformer and create_and_reproject warp.
1 parent f498f77 commit eba90c9

File tree

6 files changed

+227
-3
lines changed

6 files changed

+227
-3
lines changed

CHANGES.md

+2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# Changes
22

33
## Unreleased
4+
* Added wrapper for Transformer (currently only implementing GCP Transformer)
5+
* Added wrapper for GDALCreateAndReprojectImage
46
* **Breaking**: Upgrade to `ndarray 0.15`
57
* <https://github.com/georust/gdal/pull/175>
68
* Implement wrapper for `OGR_L_TestCapability`

src/alg/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pub mod transform;

src/alg/transform.rs

+183
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
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+
}

src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
pub use version::version_info;
2424

25+
pub mod alg;
2526
pub mod config;
2627
mod dataset;
2728
mod driver;

src/raster/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ mod warp;
66

77
pub use rasterband::{Buffer, ByteBuffer, ColorInterpretation, RasterBand};
88
pub use types::{GDALDataType, GdalType};
9-
pub use warp::reproject;
9+
pub use warp::{create_and_reproject, reproject};
1010

1111
#[cfg(test)]
1212
mod tests;

src/raster/warp.rs

+39-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
1-
use crate::dataset::Dataset;
21
use crate::utils::_last_cpl_err;
2+
use crate::{dataset::Dataset, spatial_ref::SpatialRef, Driver};
33
use gdal_sys::{self, CPLErr, GDALResampleAlg};
4-
use std::ptr::{null, null_mut};
4+
use std::{
5+
ffi::CString,
6+
path::Path,
7+
ptr::{null, null_mut},
8+
};
59

610
use crate::errors::*;
711

@@ -25,3 +29,36 @@ pub fn reproject(src: &Dataset, dst: &Dataset) -> Result<()> {
2529
}
2630
Ok(())
2731
}
32+
33+
pub fn create_and_reproject(
34+
src: &Dataset,
35+
dst_path: &Path,
36+
dst_srs: &SpatialRef,
37+
dst_driver: &Driver,
38+
) -> Result<()> {
39+
let psz_dst_filename = dst_path
40+
.to_str()
41+
.expect("Destination path must be supplied.");
42+
let psz_dst_wkt = dst_srs.to_wkt().expect("Failed to obtain WKT for SRS.");
43+
44+
let rv = unsafe {
45+
gdal_sys::GDALCreateAndReprojectImage(
46+
src.c_dataset(),
47+
null(),
48+
CString::new(psz_dst_filename)?.as_ptr(),
49+
CString::new(psz_dst_wkt)?.as_ptr(),
50+
dst_driver.c_driver(),
51+
null_mut(),
52+
GDALResampleAlg::GRA_Bilinear,
53+
0.0,
54+
0.0,
55+
None,
56+
null_mut(),
57+
null_mut(),
58+
)
59+
};
60+
if rv != CPLErr::CE_None {
61+
return Err(_last_cpl_err(rv));
62+
}
63+
Ok(())
64+
}

0 commit comments

Comments
 (0)