Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow to transform ogr geometries to other SRS #29

Merged
merged 5 commits into from
Jan 7, 2017
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions examples/spatial_reference.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
extern crate gdal;
use gdal::spatial_ref::{SpatialRef, CoordTransform};
use gdal::vector::Geometry;

fn main() {
let spatial_ref1 = SpatialRef::new_from_proj4("+proj=laea +lat_0=52 +lon_0=10 +x_0=4321000 +y_0=3210000 +ellps=GRS80 +units=m +no_defs").unwrap();
println!("Spatial ref from proj4 to wkt:\n{:?}\n", spatial_ref1.to_wkt().unwrap());
let spatial_ref2 = SpatialRef::new_from_wkt("GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",7030]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY[\"EPSG\",6326]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",8901]],UNIT[\"DMSH\",0.0174532925199433,AUTHORITY[\"EPSG\",9108]],AXIS[\"Lat\",NORTH],AXIS[\"Long\",EAST],AUTHORITY[\"EPSG\",4326]]").unwrap();
println!("Spatial ref from wkt to proj4:\n{:?}\n", spatial_ref2.to_proj4().unwrap());
let spatial_ref3 = SpatialRef::new("urn:ogc:def:crs:EPSG:6.3:26986").unwrap();
println!("Spatial ref from ogc naming to wkt:\n{:?}\n", spatial_ref3.to_wkt().unwrap());
let spatial_ref4 = SpatialRef::new_from_epsg(4326).unwrap();
println!("Spatial ref from epsg code to wkt:\n{:?}\n", spatial_ref4.to_wkt().unwrap());
println!("Spatial ref from epsg code to pretty wkt:\n{:?}\n", spatial_ref4.to_pretty_wkt().unwrap());
println!("Comparison between identical SRS : {:?}\n", spatial_ref2 == spatial_ref4);
let htransform = CoordTransform::new(&spatial_ref2, &spatial_ref1).unwrap();
let mut xs = &mut [23.43, 23.50];
let mut ys = &mut [37.58, 37.70];
println!("Before transformation :\n{:?} {:?}", xs, ys);
htransform.transform_coord(xs, ys, &mut [0.0, 0.0]);
println!("After transformation :\n{:?} {:?}\n", xs, ys);
let mut geom = Geometry::from_wkt("POLYGON((23.43 37.58, 23.43 40.0, 25.29 40.0, 25.29 37.58, 23.43 37.58))").unwrap();
println!("Polygon before transformation:\n{:?}\n", geom.wkt().unwrap());
geom.transform(&htransform).unwrap();
println!("Polygon after transformation:\n{:?}\n", geom.wkt().unwrap());
let spatial_ref5 = SpatialRef::new_from_epsg(4326).unwrap();
println!("To wkt: {:?}", spatial_ref5.to_wkt());
spatial_ref5.morph_to_esri().unwrap();
println!("To esri wkt: {:?}", spatial_ref5.to_wkt());
println!("To xml: {:?}", spatial_ref5.to_xml());
}
5 changes: 4 additions & 1 deletion gdal-sys/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,8 @@ pub mod gdal_enums;
pub mod ogr;
pub mod ogr_enums;

// OGR Spatial Reference module
pub mod osr;

// cpl modules
pub mod cpl_error;
pub mod cpl_error;
2 changes: 2 additions & 0 deletions gdal-sys/src/ogr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ extern {
pub fn OGR_G_GetGeometryCount(hGeom: *const c_void) -> c_int;
pub fn OGR_G_GetGeometryRef(hGeom: *const c_void, iSubGeom: c_int) -> *const c_void;
pub fn OGR_G_AddGeometryDirectly(hGeom: *const c_void, hNewSubGeom: *const c_void) -> OGRErr;
pub fn OGR_G_Transform(hGeom: *const c_void, hCT: *const c_void) -> OGRErr;
pub fn OGR_G_TransformTo(hGeom: *const c_void, hSRS: *const c_void) -> OGRErr;
pub fn OGR_G_DestroyGeometry(hGeom: *mut c_void);
pub fn OGR_Fld_GetNameRef(hDefn: *const c_void) -> *const c_char;
pub fn OGR_Fld_GetType(hDefn: *const c_void) -> c_int;
Expand Down
21 changes: 21 additions & 0 deletions gdal-sys/src/osr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
use libc::{c_int, c_char, c_double, c_void};
use ogr_enums::*;

#[link(name="gdal")]
extern {
pub fn OSRNewSpatialReference(pszWKT: *const c_char) -> *mut c_void;
pub fn OSRClone(hSRS: *const c_void) -> *mut c_void;
pub fn OSRDestroySpatialReference(hSRS: *mut c_void) -> c_void;
pub fn OSRSetFromUserInput(hSRS: *mut c_void, pszDefinition: *const c_char) -> OGRErr;
pub fn OSRImportFromEPSG(hSRS: *const c_void, nCode: c_int) -> OGRErr;
pub fn OSRImportFromProj4(hSRS: *mut c_void, proj4_string: *const c_char) -> OGRErr;
pub fn OSRExportToWkt(hSRS: *const c_void, ppszReturn: &mut *const c_char) -> OGRErr;
pub fn OSRExportToPrettyWkt(hSRS: *const c_void, ppszReturn: &mut *const c_char, bSimplify: c_int) -> OGRErr;
pub fn OSRExportToProj4(hSRS: *const c_void, ppszReturn: &mut *const c_char) -> OGRErr;
pub fn OSRExportToXML(hSRS: *const c_void, ppszRawXML: &mut *const c_char, pszDialect: *const c_char) -> OGRErr;
pub fn OSRMorphToESRI(hSRS: *mut c_void) -> OGRErr;
pub fn OSRIsSame(hSRS1: *const c_void, hSRS2: *const c_void) -> bool;
pub fn OCTNewCoordinateTransformation(hSourceSRS: *const c_void, hTargetSRS: *const c_void) -> *mut c_void;
pub fn OCTDestroyCoordinateTransformation(hCT: *mut c_void) -> c_void;
pub fn OCTTransform(hCT: *const c_void, nCount: c_int, x: *mut c_double, y: *mut c_double, z: *mut c_double) -> bool;
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,5 @@ pub mod metadata;
pub mod version;
pub mod raster;
pub mod vector;
pub mod spatial_ref;
pub mod errors;
7 changes: 7 additions & 0 deletions src/spatial_ref/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
pub use spatial_ref::srs::SpatialRef;
pub use spatial_ref::srs::CoordTransform;

mod srs;

#[cfg(test)]
mod tests;
163 changes: 163 additions & 0 deletions src/spatial_ref/srs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
use libc::{c_int, c_char, c_void};
use std::ffi::{CString};
use std::ptr;
use utils::_string;
use gdal_sys::{osr, ogr_enums};

use errors::*;

pub struct CoordTransform(*mut c_void);

impl Drop for CoordTransform {
fn drop(&mut self) {
unsafe { osr::OCTDestroyCoordinateTransformation(self.0) };
self.0 = ptr::null_mut();
}
}

impl CoordTransform {
pub fn new(sp_ref1: &SpatialRef, sp_ref2: &SpatialRef) -> Result<CoordTransform> {
let c_obj = unsafe { osr::OCTNewCoordinateTransformation(sp_ref1.0, sp_ref2.0) };
if c_obj.is_null() {
return Err(ErrorKind::NullPointer("OCTNewCoordinateTransformation").into());
}
Ok(CoordTransform(c_obj))
}

pub fn transform_coord(&self, x: &mut [f64], y: &mut [f64], z: &mut [f64]){
let nb_coords = x.len();
assert_eq!(nb_coords, y.len());
let ret_val = unsafe { osr::OCTTransform(
self.0,
nb_coords as c_int,
x.as_mut_ptr(),
y.as_mut_ptr(),
z.as_mut_ptr()
) };
assert_eq!(true, ret_val);
}

pub fn to_c_hct(&self) -> *const c_void {
self.0 as *const c_void
}
}

pub struct SpatialRef(*mut c_void);

impl Drop for SpatialRef {
fn drop(&mut self){
unsafe { osr::OSRDestroySpatialReference(self.0)};
self.0 = ptr::null_mut();
}
}

impl Clone for SpatialRef {
fn clone(&self) -> SpatialRef {
let n_obj = unsafe { osr::OSRClone(self.0 as *const c_void)};
SpatialRef(n_obj)
}
}

impl PartialEq for SpatialRef {
fn eq(&self, other: &SpatialRef) -> bool {
unsafe { osr::OSRIsSame(self.0, other.0)}
}
}

impl SpatialRef {
pub fn new(definition: &str) -> Result<SpatialRef> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there no use-case for new() without parameters? Maybe the wrapped OSRSetFromUserInput should be a separate method like new_from_definition(definition: &str) or something like this?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, there is use-cases for new() without parameters (I did this as a shortcut as currently the other exposed functions only allow to "import" a SRS, but it might be better to keep a method to create an empty Spatial Reference).

As you advised, it keeps now a new() method to create a spatial reference without parameters and a new_from_definition() using OSRSetFromUserInput.

let c_obj = unsafe { osr::OSRNewSpatialReference(ptr::null()) };
if c_obj.is_null() {
return Err(ErrorKind::NullPointer("OSRNewSpatialReference").into());
}
let rv = unsafe { osr::OSRSetFromUserInput(c_obj, CString::new(definition).unwrap().as_ptr()) };
if rv != ogr_enums::OGRErr::OGRERR_NONE {
return Err(ErrorKind::OgrError(rv, "OSRSetFromUserInput").into());
}
Ok(SpatialRef(c_obj))
}

pub fn new_from_wkt(wkt: &str) -> Result<SpatialRef> {
let c_str = CString::new(wkt).unwrap();
let c_obj = unsafe { osr::OSRNewSpatialReference(c_str.as_ptr()) };
if c_obj.is_null() {
return Err(ErrorKind::NullPointer("OSRNewSpatialReference").into());
}
Ok(SpatialRef(c_obj))
}

pub fn new_from_epsg(epsg_code: u32) -> Result<SpatialRef> {
let null_ptr = ptr::null_mut();
let c_obj = unsafe { osr::OSRNewSpatialReference(null_ptr) };
let rv = unsafe { osr::OSRImportFromEPSG(c_obj, epsg_code as c_int) };
if rv != ogr_enums::OGRErr::OGRERR_NONE {
Err(ErrorKind::OgrError(rv, "OSRImportFromEPSG").into())
} else {
Ok(SpatialRef(c_obj))
}
}

pub fn new_from_proj4(proj4_string: &str) -> Result<SpatialRef> {
let c_str = CString::new(proj4_string).unwrap();
let null_ptr = ptr::null_mut();
let c_obj = unsafe { osr::OSRNewSpatialReference(null_ptr) };
let rv = unsafe { osr::OSRImportFromProj4(c_obj, c_str.as_ptr()) };
if rv != ogr_enums::OGRErr::OGRERR_NONE {
Err(ErrorKind::OgrError(rv, "OSRImportFromProj4").into())
} else {
Ok(SpatialRef(c_obj))
}
}

pub fn to_wkt(&self) -> Result<String> {
let mut c_wkt: *const c_char = ptr::null_mut();
let _err = unsafe { osr::OSRExportToWkt(self.0, &mut c_wkt) };
if _err != ogr_enums::OGRErr::OGRERR_NONE {
Err(ErrorKind::OgrError(_err, "OSRExportToWkt").into())
} else {
Ok(_string(c_wkt))
}
}

pub fn morph_to_esri(&self) -> Result<()> {
let _err = unsafe { osr::OSRMorphToESRI(self.0) };
if _err != ogr_enums::OGRErr::OGRERR_NONE {
return Err(ErrorKind::OgrError(_err, "OSRMorphToESRI").into());
}
Ok(())
}

pub fn to_pretty_wkt(&self) -> Result<String> {
let mut c_wkt: *const c_char = ptr::null_mut();
let _err = unsafe { osr::OSRExportToPrettyWkt(self.0, &mut c_wkt, false as c_int) };
if _err != ogr_enums::OGRErr::OGRERR_NONE {
Err(ErrorKind::OgrError(_err, "OSRExportToPrettyWkt").into())
} else {
Ok(_string(c_wkt))
}
}

pub fn to_xml(&self) -> Result<String> {
let mut c_raw_xml: *const c_char = ptr::null_mut();
let _err = unsafe { osr::OSRExportToXML(self.0, &mut c_raw_xml, ptr::null() as *const c_char) };
if _err != ogr_enums::OGRErr::OGRERR_NONE {
Err(ErrorKind::OgrError(_err, "OSRExportToXML").into())
} else {
Ok(_string(c_raw_xml))
}
}

pub fn to_proj4(&self) -> Result<String> {
let mut c_proj4str: *const c_char = ptr::null_mut();
let _err = unsafe { osr::OSRExportToProj4(self.0, &mut c_proj4str) };
if _err != ogr_enums::OGRErr::OGRERR_NONE {
Err(ErrorKind::OgrError(_err, "OSRExportToProj4").into())
} else {
Ok(_string(c_proj4str))
}
}

pub fn to_c_hsrs(&self) -> *const c_void {
self.0 as *const c_void
}
}
60 changes: 60 additions & 0 deletions src/spatial_ref/tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
use super::srs::{SpatialRef, CoordTransform};
use vector::Geometry;

#[test]
fn from_wkt_to_proj4() {
let spatial_ref = SpatialRef::new_from_wkt("GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",7030]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY[\"EPSG\",6326]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",8901]],UNIT[\"DMSH\",0.0174532925199433,AUTHORITY[\"EPSG\",9108]],AXIS[\"Lat\",NORTH],AXIS[\"Long\",EAST],AUTHORITY[\"EPSG\",4326]]").unwrap();
assert_eq!("+proj=longlat +ellps=WGS84 +towgs84=0,0,0,0,0,0,0 +no_defs ", spatial_ref.to_proj4().unwrap());
let spatial_ref = SpatialRef::new("GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",7030]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY[\"EPSG\",6326]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",8901]],UNIT[\"DMSH\",0.0174532925199433,AUTHORITY[\"EPSG\",9108]],AXIS[\"Lat\",NORTH],AXIS[\"Long\",EAST],AUTHORITY[\"EPSG\",4326]]").unwrap();
assert_eq!("+proj=longlat +ellps=WGS84 +towgs84=0,0,0,0,0,0,0 +no_defs ", spatial_ref.to_proj4().unwrap());
}

#[test]
fn from_proj4_to_wkt(){
let spatial_ref = SpatialRef::new_from_proj4("+proj=laea +lat_0=52 +lon_0=10 +x_0=4321000 +y_0=3210000 +ellps=GRS80 +units=m +no_defs").unwrap();
assert_eq!(spatial_ref.to_wkt().unwrap(), "PROJCS[\"unnamed\",GEOGCS[\"GRS 1980(IUGG, 1980)\",DATUM[\"unknown\",SPHEROID[\"GRS80\",6378137,298.257222101]],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],PROJECTION[\"Lambert_Azimuthal_Equal_Area\"],PARAMETER[\"latitude_of_center\",52],PARAMETER[\"longitude_of_center\",10],PARAMETER[\"false_easting\",4321000],PARAMETER[\"false_northing\",3210000],UNIT[\"Meter\",1]]");
}

#[test]
fn from_epsg_to_wkt_proj4(){
let spatial_ref = SpatialRef::new_from_epsg(4326).unwrap();
let wkt = spatial_ref.to_wkt().unwrap();
assert_eq!("GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]],AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4326\"]]", wkt);
let proj4string = spatial_ref.to_proj4().unwrap();
assert_eq!("+proj=longlat +datum=WGS84 +no_defs ", proj4string);
}

#[test]
fn comparison(){
let spatial_ref1 = SpatialRef::new_from_wkt("GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",7030]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY[\"EPSG\",6326]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",8901]],UNIT[\"DMSH\",0.0174532925199433,AUTHORITY[\"EPSG\",9108]],AXIS[\"Lat\",NORTH],AXIS[\"Long\",EAST],AUTHORITY[\"EPSG\",4326]]").unwrap();
let spatial_ref2 = SpatialRef::new_from_epsg(4326).unwrap();
let spatial_ref3 = SpatialRef::new_from_epsg(3025).unwrap();
let spatial_ref4 = SpatialRef::new_from_proj4("+proj=longlat +datum=WGS84 +no_defs ").unwrap();
assert_eq!(true, spatial_ref1 == spatial_ref2);
assert_eq!(false, spatial_ref2 == spatial_ref3);
assert_eq!(true, spatial_ref4 == spatial_ref2);
}

#[test]
fn transform_coordinates(){
let spatial_ref1 = SpatialRef::new_from_wkt("GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",7030]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY[\"EPSG\",6326]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",8901]],UNIT[\"DMSH\",0.0174532925199433,AUTHORITY[\"EPSG\",9108]],AXIS[\"Lat\",NORTH],AXIS[\"Long\",EAST],AUTHORITY[\"EPSG\",4326]]").unwrap();
let spatial_ref2 = SpatialRef::new_from_epsg(3035).unwrap();
let transform = CoordTransform::new(&spatial_ref1, &spatial_ref2).unwrap();
let mut xs = &mut [23.43, 23.50];
let mut ys = &mut [37.58, 37.70];
transform.transform_coord(xs, ys, &mut [0.0, 0.0]);
assert_eq!(xs.get(0), Some(&5509543.1508097));
assert_eq!(ys.get(0), Some(&1716062.1916192223));
}

#[test]
fn transform_ogr_geometry(){
//let expected_value = "POLYGON ((5509543.150809700600803 1716062.191619219258428,5467122.000330002978444 1980151.204280239529908,5623571.028492723591626 2010213.310253676958382,5671834.921544363722205 1746968.078280254499987,5509543.150809700600803 1716062.191619219258428))";
let expected_value = "POLYGON ((5509543.15080969966948 1716062.191619222285226,5467122.000330002047122 1980151.204280242323875,5623571.028492721728981 2010213.31025367998518,5671834.921544362790883 1746968.078280256595463,5509543.15080969966948 1716062.191619222285226))";
let mut geom = Geometry::from_wkt("POLYGON((23.43 37.58, 23.43 40.0, 25.29 40.0, 25.29 37.58, 23.43 37.58))").unwrap();
let spatial_ref1 = SpatialRef::new_from_proj4("+proj=laea +lat_0=52 +lon_0=10 +x_0=4321000 +y_0=3210000 +ellps=GRS80 +units=m +no_defs").unwrap();
let spatial_ref2 = SpatialRef::new_from_wkt("GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",7030]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY[\"EPSG\",6326]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",8901]],UNIT[\"DMSH\",0.0174532925199433,AUTHORITY[\"EPSG\",9108]],AXIS[\"Lat\",NORTH],AXIS[\"Long\",EAST],AUTHORITY[\"EPSG\",4326]]").unwrap();
let htransform = CoordTransform::new(&spatial_ref2, &spatial_ref1).unwrap();
geom.transform(&htransform).unwrap();
assert_eq!(expected_value, geom.wkt().unwrap());
}
23 changes: 23 additions & 0 deletions src/vector/geometry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use std::ffi::CString;
use std::cell::RefCell;
use utils::_string;
use gdal_sys::{ogr, ogr_enums};
use spatial_ref::{SpatialRef, CoordTransform};

use errors::*;

Expand Down Expand Up @@ -164,6 +165,28 @@ impl Geometry {
}
Ok(())
}

pub fn transform(&mut self, htransform: &CoordTransform) -> Result<()> {
let rv = unsafe { ogr::OGR_G_Transform(
self.c_geometry(),
htransform.to_c_hct()
) };
if rv != ogr_enums::OGRErr::OGRERR_NONE {
return Err(ErrorKind::OgrError(rv, "OGR_G_Transform").into());
}
Ok(())
}

pub fn transform_to(&mut self, spatial_ref: &SpatialRef) -> Result<()> {
let rv = unsafe { ogr::OGR_G_TransformTo(
self.c_geometry(),
spatial_ref.to_c_hsrs()
) };
if rv != ogr_enums::OGRErr::OGRERR_NONE {
return Err(ErrorKind::OgrError(rv, "OGR_G_TransformTo").into());
}
Ok(())
}
}

impl Drop for Geometry {
Expand Down