use crate::utils::{_last_cpl_err, _last_null_pointer_err, _string};
use gdal_sys::{self, CPLErr, OGRCoordinateTransformationH, OGRErr, OGRSpatialReferenceH};
use libc::{c_char, c_int};
use std::ffi::{CStr, CString};
use std::ptr::{self, null_mut};
use std::str::FromStr;
use crate::errors::*;
pub struct CoordTransform {
inner: OGRCoordinateTransformationH,
from: String,
to: String,
}
impl Drop for CoordTransform {
fn drop(&mut self) {
unsafe { gdal_sys::OCTDestroyCoordinateTransformation(self.inner) };
self.inner = ptr::null_mut();
}
}
impl CoordTransform {
pub fn new(sp_ref1: &SpatialRef, sp_ref2: &SpatialRef) -> Result<CoordTransform> {
let c_obj = unsafe { gdal_sys::OCTNewCoordinateTransformation(sp_ref1.0, sp_ref2.0) };
if c_obj.is_null() {
return Err(_last_null_pointer_err("OCTNewCoordinateTransformation"));
}
Ok(CoordTransform {
inner: c_obj,
from: sp_ref1.authority().or_else(|_| sp_ref1.to_proj4())?,
to: sp_ref2.authority().or_else(|_| sp_ref2.to_proj4())?,
})
}
#[cfg(all(major_ge_3, minor_ge_4))]
pub fn transform_bounds(&self, bounds: &[f64; 4], densify_pts: i32) -> Result<([f64; 4])> {
let mut out_xmin: f64 = 0.;
let mut out_ymin: f64 = 0.;
let mut out_xmax: f64 = 0.;
let mut out_ymax: f64 = 0.;
let ret_val = unsafe {
gdal_sys::OCTTransformBounds(
self.inner,
bounds[0],
bounds[1],
bounds[2],
bounds[3],
&mut out_xmin,
&mut out_ymin,
&mut out_xmax,
&mut out_ymax,
densify_pts as c_int,
) == 1
};
if !ret_val {
let msg = match _last_cpl_err(CPLErr::CE_Failure) {
GdalError::CplError { msg, .. } => match msg.is_empty() {
false => Some(msg),
_ => None,
},
err => return Err(err),
};
return Err(GdalError::InvalidCoordinateRange {
from: self.from.clone(),
to: self.to.clone(),
msg,
});
}
Ok([out_xmin, out_ymin, out_xmax, out_ymax])
}
pub fn transform_coords(&self, x: &mut [f64], y: &mut [f64], z: &mut [f64]) -> Result<()> {
let nb_coords = x.len();
assert_eq!(
nb_coords,
y.len(),
"transform coordinate slices have different lengths: {} != {}",
nb_coords,
y.len()
);
let ret_val = unsafe {
gdal_sys::OCTTransform(
self.inner,
nb_coords as c_int,
x.as_mut_ptr(),
y.as_mut_ptr(),
if z.is_empty() {
null_mut()
} else {
assert_eq!(
nb_coords,
z.len(),
"transform coordinate slices have different lengths: {} != {}",
nb_coords,
z.len()
);
z.as_mut_ptr()
},
) == 1
};
if ret_val {
Ok(())
} else {
let err = _last_cpl_err(CPLErr::CE_Failure);
let msg = if let GdalError::CplError { msg, .. } = err {
if msg.trim().is_empty() {
None
} else {
Some(msg)
}
} else {
return Err(err);
};
Err(GdalError::InvalidCoordinateRange {
from: self.from.clone(),
to: self.to.clone(),
msg,
})
}
}
#[deprecated(since = "0.3.1", note = "use `transform_coords` instead")]
pub fn transform_coord(&self, x: &mut [f64], y: &mut [f64], z: &mut [f64]) {
self.transform_coords(x, y, z)
.expect("Coordinate transform failed")
}
pub fn to_c_hct(&self) -> OGRCoordinateTransformationH {
self.inner
}
}
#[derive(Debug, Clone)]
pub struct AreaOfUse {
pub west_lon_degree: f64,
pub south_lat_degree: f64,
pub east_lon_degree: f64,
pub north_lat_degree: f64,
pub name: String,
}
pub type AxisOrientationType = gdal_sys::OGRAxisOrientation::Type;
#[derive(Debug)]
pub struct SpatialRef(OGRSpatialReferenceH);
impl Drop for SpatialRef {
fn drop(&mut self) {
unsafe { gdal_sys::OSRRelease(self.0) };
self.0 = ptr::null_mut();
}
}
impl Clone for SpatialRef {
fn clone(&self) -> SpatialRef {
let n_obj = unsafe { gdal_sys::OSRClone(self.0) };
SpatialRef(n_obj)
}
}
impl PartialEq for SpatialRef {
fn eq(&self, other: &SpatialRef) -> bool {
unsafe { gdal_sys::OSRIsSame(self.0, other.0) == 1 }
}
}
impl SpatialRef {
pub fn new() -> Result<SpatialRef> {
let c_obj = unsafe { gdal_sys::OSRNewSpatialReference(ptr::null()) };
if c_obj.is_null() {
return Err(_last_null_pointer_err("OSRNewSpatialReference"));
}
Ok(SpatialRef(c_obj))
}
pub fn from_definition(definition: &str) -> Result<SpatialRef> {
let c_obj = unsafe { gdal_sys::OSRNewSpatialReference(ptr::null()) };
if c_obj.is_null() {
return Err(_last_null_pointer_err("OSRNewSpatialReference"));
}
let rv =
unsafe { gdal_sys::OSRSetFromUserInput(c_obj, CString::new(definition)?.as_ptr()) };
if rv != OGRErr::OGRERR_NONE {
return Err(GdalError::OgrError {
err: rv,
method_name: "OSRSetFromUserInput",
});
}
Ok(SpatialRef(c_obj))
}
pub fn from_wkt(wkt: &str) -> Result<SpatialRef> {
let c_str = CString::new(wkt)?;
let c_obj = unsafe { gdal_sys::OSRNewSpatialReference(c_str.as_ptr()) };
if c_obj.is_null() {
return Err(_last_null_pointer_err("OSRNewSpatialReference"));
}
Ok(SpatialRef(c_obj))
}
pub fn from_epsg(epsg_code: u32) -> Result<SpatialRef> {
let null_ptr = ptr::null_mut();
let c_obj = unsafe { gdal_sys::OSRNewSpatialReference(null_ptr) };
let rv = unsafe { gdal_sys::OSRImportFromEPSG(c_obj, epsg_code as c_int) };
if rv != OGRErr::OGRERR_NONE {
Err(GdalError::OgrError {
err: rv,
method_name: "OSRImportFromEPSG",
})
} else {
Ok(SpatialRef(c_obj))
}
}
pub fn from_proj4(proj4_string: &str) -> Result<SpatialRef> {
let c_str = CString::new(proj4_string)?;
let null_ptr = ptr::null_mut();
let c_obj = unsafe { gdal_sys::OSRNewSpatialReference(null_ptr) };
let rv = unsafe { gdal_sys::OSRImportFromProj4(c_obj, c_str.as_ptr()) };
if rv != OGRErr::OGRERR_NONE {
Err(GdalError::OgrError {
err: rv,
method_name: "OSRImportFromProj4",
})
} else {
Ok(SpatialRef(c_obj))
}
}
pub fn from_esri(esri_wkt: &str) -> Result<SpatialRef> {
let c_str = CString::new(esri_wkt)?;
let mut ptrs = vec![c_str.as_ptr() as *mut c_char, ptr::null_mut()];
let null_ptr = ptr::null_mut();
let c_obj = unsafe { gdal_sys::OSRNewSpatialReference(null_ptr) };
let rv = unsafe { gdal_sys::OSRImportFromESRI(c_obj, ptrs.as_mut_ptr()) };
if rv != OGRErr::OGRERR_NONE {
Err(GdalError::OgrError {
err: rv,
method_name: "OSRImportFromESRI",
})
} else {
Ok(SpatialRef(c_obj))
}
}
pub unsafe fn from_c_obj(c_obj: OGRSpatialReferenceH) -> Result<SpatialRef> {
let mut_c_obj = gdal_sys::OSRClone(c_obj);
if mut_c_obj.is_null() {
Err(_last_null_pointer_err("OSRClone"))
} else {
Ok(SpatialRef(mut_c_obj))
}
}
pub fn to_wkt(&self) -> Result<String> {
let mut c_wkt = ptr::null_mut();
let rv = unsafe { gdal_sys::OSRExportToWkt(self.0, &mut c_wkt) };
let res = if rv != OGRErr::OGRERR_NONE {
Err(GdalError::OgrError {
err: rv,
method_name: "OSRExportToWkt",
})
} else {
Ok(_string(c_wkt))
};
unsafe { gdal_sys::VSIFree(c_wkt.cast::<std::ffi::c_void>()) };
res
}
pub fn morph_to_esri(&self) -> Result<()> {
let rv = unsafe { gdal_sys::OSRMorphToESRI(self.0) };
if rv != OGRErr::OGRERR_NONE {
return Err(GdalError::OgrError {
err: rv,
method_name: "OSRMorphToESRI",
});
}
Ok(())
}
pub fn to_pretty_wkt(&self) -> Result<String> {
let mut c_wkt = ptr::null_mut();
let rv = unsafe { gdal_sys::OSRExportToPrettyWkt(self.0, &mut c_wkt, false as c_int) };
let res = if rv != OGRErr::OGRERR_NONE {
Err(GdalError::OgrError {
err: rv,
method_name: "OSRExportToPrettyWkt",
})
} else {
Ok(_string(c_wkt))
};
unsafe { gdal_sys::VSIFree(c_wkt.cast::<std::ffi::c_void>()) };
res
}
pub fn to_xml(&self) -> Result<String> {
let mut c_raw_xml = ptr::null_mut();
let rv = unsafe { gdal_sys::OSRExportToXML(self.0, &mut c_raw_xml, ptr::null()) };
let res = if rv != OGRErr::OGRERR_NONE {
Err(GdalError::OgrError {
err: rv,
method_name: "OSRExportToXML",
})
} else {
Ok(_string(c_raw_xml))
};
unsafe { gdal_sys::VSIFree(c_raw_xml.cast::<std::ffi::c_void>()) };
res
}
pub fn to_proj4(&self) -> Result<String> {
let mut c_proj4str = ptr::null_mut();
let rv = unsafe { gdal_sys::OSRExportToProj4(self.0, &mut c_proj4str) };
let res = if rv != OGRErr::OGRERR_NONE {
Err(GdalError::OgrError {
err: rv,
method_name: "OSRExportToProj4",
})
} else {
Ok(_string(c_proj4str))
};
unsafe { gdal_sys::VSIFree(c_proj4str.cast::<std::ffi::c_void>()) };
res
}
pub fn auth_name(&self) -> Result<String> {
let c_ptr = unsafe { gdal_sys::OSRGetAuthorityName(self.0, ptr::null()) };
if c_ptr.is_null() {
Err(_last_null_pointer_err("SRGetAuthorityName"))
} else {
Ok(_string(c_ptr))
}
}
pub fn auth_code(&self) -> Result<i32> {
let c_ptr = unsafe { gdal_sys::OSRGetAuthorityCode(self.0, ptr::null()) };
if c_ptr.is_null() {
return Err(_last_null_pointer_err("OSRGetAuthorityCode"));
}
let c_str = unsafe { CStr::from_ptr(c_ptr) };
let epsg = i32::from_str(c_str.to_str()?);
match epsg {
Ok(n) => Ok(n),
Err(_) => Err(GdalError::OgrError {
err: OGRErr::OGRERR_UNSUPPORTED_SRS,
method_name: "OSRGetAuthorityCode",
}),
}
}
pub fn authority(&self) -> Result<String> {
let c_ptr = unsafe { gdal_sys::OSRGetAuthorityName(self.0, ptr::null()) };
if c_ptr.is_null() {
return Err(_last_null_pointer_err("SRGetAuthorityName"));
}
let name = unsafe { CStr::from_ptr(c_ptr) }.to_str()?;
let c_ptr = unsafe { gdal_sys::OSRGetAuthorityCode(self.0, ptr::null()) };
if c_ptr.is_null() {
return Err(_last_null_pointer_err("OSRGetAuthorityCode"));
}
let code = unsafe { CStr::from_ptr(c_ptr) }.to_str()?;
Ok(format!("{}:{}", name, code))
}
pub fn auto_identify_epsg(&mut self) -> Result<()> {
let rv = unsafe { gdal_sys::OSRAutoIdentifyEPSG(self.0) };
if rv != OGRErr::OGRERR_NONE {
Err(GdalError::OgrError {
err: rv,
method_name: "OSRAutoIdentifyEPSG",
})
} else {
Ok(())
}
}
#[cfg(major_ge_3)]
pub fn name(&self) -> Result<String> {
let c_ptr = unsafe { gdal_sys::OSRGetName(self.0) };
if c_ptr.is_null() {
return Err(_last_null_pointer_err("OSRGetName"));
}
Ok(_string(c_ptr))
}
pub fn angular_units_name(&self) -> Result<String> {
let mut c_ptr = ptr::null_mut();
unsafe { gdal_sys::OSRGetAngularUnits(self.0, &mut c_ptr) };
if c_ptr.is_null() {
return Err(_last_null_pointer_err("OSRGetAngularUnits"));
}
Ok(_string(c_ptr))
}
pub fn angular_units(&self) -> f64 {
unsafe { gdal_sys::OSRGetAngularUnits(self.0, ptr::null_mut()) }
}
pub fn linear_units_name(&self) -> Result<String> {
let mut c_ptr = ptr::null_mut();
unsafe { gdal_sys::OSRGetLinearUnits(self.0, &mut c_ptr) };
if c_ptr.is_null() {
return Err(_last_null_pointer_err("OSRGetLinearUnits"));
}
Ok(_string(c_ptr))
}
pub fn linear_units(&self) -> f64 {
unsafe { gdal_sys::OSRGetLinearUnits(self.0, ptr::null_mut()) }
}
#[inline]
pub fn is_geographic(&self) -> bool {
unsafe { gdal_sys::OSRIsGeographic(self.0) == 1 }
}
#[inline]
#[cfg(all(major_ge_3, minor_ge_1))]
pub fn is_derived_geographic(&self) -> bool {
unsafe { gdal_sys::OSRIsDerivedGeographic(self.0) == 1 }
}
#[inline]
pub fn is_local(&self) -> bool {
unsafe { gdal_sys::OSRIsLocal(self.0) == 1 }
}
#[inline]
pub fn is_projected(&self) -> bool {
unsafe { gdal_sys::OSRIsProjected(self.0) == 1 }
}
#[inline]
pub fn is_compound(&self) -> bool {
unsafe { gdal_sys::OSRIsCompound(self.0) == 1 }
}
#[inline]
pub fn is_geocentric(&self) -> bool {
unsafe { gdal_sys::OSRIsGeocentric(self.0) == 1 }
}
#[inline]
pub fn is_vertical(&self) -> bool {
unsafe { gdal_sys::OSRIsVertical(self.0) == 1 }
}
pub fn axis_orientation(&self, target_key: &str, axis: i32) -> Result<AxisOrientationType> {
let mut orientation = gdal_sys::OGRAxisOrientation::OAO_Other;
let c_ptr = unsafe {
gdal_sys::OSRGetAxis(
self.0,
CString::new(target_key)?.as_ptr(),
axis as c_int,
&mut orientation,
)
};
if c_ptr.is_null() {
Err(GdalError::AxisNotFoundError {
key: target_key.into(),
method_name: "OSRGetAxis",
})
} else {
Ok(orientation)
}
}
pub fn axis_name(&self, target_key: &str, axis: i32) -> Result<String> {
let c_ptr = unsafe {
gdal_sys::OSRGetAxis(
self.0,
CString::new(target_key)?.as_ptr(),
axis as c_int,
ptr::null_mut(),
)
};
if c_ptr.is_null() {
Err(GdalError::AxisNotFoundError {
key: target_key.into(),
method_name: "OSRGetAxis",
})
} else {
Ok(_string(c_ptr))
}
}
#[cfg(all(major_ge_3, minor_ge_1))]
pub fn axes_count(&self) -> i32 {
unsafe { gdal_sys::OSRGetAxesCount(self.0) }
}
#[cfg(major_ge_3)]
pub fn set_axis_mapping_strategy(&self, strategy: gdal_sys::OSRAxisMappingStrategy::Type) {
unsafe {
gdal_sys::OSRSetAxisMappingStrategy(self.0, strategy);
}
}
#[cfg(major_ge_3)]
#[deprecated(note = "use `axis_mapping_strategy` instead")]
pub fn get_axis_mapping_strategy(&self) -> gdal_sys::OSRAxisMappingStrategy::Type {
self.axis_mapping_strategy()
}
#[cfg(major_ge_3)]
pub fn axis_mapping_strategy(&self) -> gdal_sys::OSRAxisMappingStrategy::Type {
unsafe { gdal_sys::OSRGetAxisMappingStrategy(self.0) }
}
#[cfg(major_ge_3)]
pub fn area_of_use(&self) -> Option<AreaOfUse> {
let mut c_area_name: *const libc::c_char = ptr::null_mut();
let (mut w_long, mut s_lat, mut e_long, mut n_lat): (f64, f64, f64, f64) =
(0.0, 0.0, 0.0, 0.0);
let ret_val = unsafe {
gdal_sys::OSRGetAreaOfUse(
self.0,
&mut w_long,
&mut s_lat,
&mut e_long,
&mut n_lat,
&mut c_area_name,
) == 1
};
if ret_val {
Some(AreaOfUse {
west_lon_degree: w_long,
south_lat_degree: s_lat,
east_lon_degree: e_long,
north_lat_degree: n_lat,
name: _string(c_area_name),
})
} else {
None
}
}
pub fn to_c_hsrs(&self) -> OGRSpatialReferenceH {
self.0
}
}