Skip to content

Commit 5561935

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

File tree

6 files changed

+228
-3
lines changed

6 files changed

+228
-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

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

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)