-
Notifications
You must be signed in to change notification settings - Fork 96
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
Extend the existing possibilities of writing ogr datasets. #31
Changes from 7 commits
505996c
e00c7a3
2614188
0304f14
aec9987
9353476
7cba326
ca9abcd
8963266
63c97a8
a4361e1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
extern crate gdal; | ||
|
||
use std::fs; | ||
use std::path::Path; | ||
use gdal::vector::*; | ||
use gdal::spatial_ref::{SpatialRef, CoordTransform}; | ||
|
||
fn main() { | ||
let mut dataset_a = Dataset::open(Path::new("fixtures/roads.geojson")).unwrap(); | ||
let layer_a = dataset_a.layer(0).unwrap(); | ||
let fields_defn = layer_a.defn().fields() | ||
.map(|field| (field.name(), field.get_type(), field.get_width())) | ||
.collect::<Vec<_>>(); | ||
|
||
// Create a new dataset : | ||
fs::remove_file("/tmp/abcde.shp"); | ||
let drv = Driver::get("ESRI Shapefile").unwrap(); | ||
let mut ds = drv.create(Path::new("/tmp/abcde.shp")).unwrap(); | ||
let lyr = ds.create_layer().unwrap(); | ||
|
||
// Copy the origin layer shema to the destination layer : | ||
for fd in &fields_defn { | ||
let field_defn = FieldDefn::new(&fd.0, fd.1); | ||
field_defn.set_width(fd.2); | ||
field_defn.add_to_layer(&lyr); | ||
} | ||
|
||
// Prepare the origin and destination spatial references objects : | ||
let spatial_ref_src = SpatialRef::from_epsg(4326).unwrap(); | ||
let spatial_ref_dst = SpatialRef::from_epsg(3025).unwrap(); | ||
|
||
// And the feature used to actually transform the geometries : | ||
let htransform = CoordTransform::new(&spatial_ref_src, &spatial_ref_dst).unwrap(); | ||
|
||
// Get the definition to use on each feature : | ||
let defn = Defn::new_from_layer(&lyr); | ||
|
||
for feature_a in layer_a.features() { | ||
// Get the original geometry : | ||
let geom = feature_a.geometry(); | ||
// Get a new transformed geometry : | ||
let new_geom = geom.transform(&htransform).unwrap(); | ||
// Create the new feature, set its geometry : | ||
let mut ft = Feature::new(&defn); | ||
ft.set_geometry(new_geom); | ||
// copy each field value of the feature : | ||
for fd in &fields_defn { | ||
ft.set_field(&fd.0, fd.1, feature_a.field(&fd.0).unwrap()) | ||
} | ||
// Add the feature to the layer : | ||
ft.create(&lyr); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
extern crate gdal; | ||
|
||
use std::path::Path; | ||
use std::fs; | ||
use gdal::vector::{Defn, Driver, Feature, FieldDefn, Geometry, OFT_INTEGER, OFT_REAL, OFT_STRING, FieldValue}; | ||
|
||
fn main(){ | ||
/// Example 1, the detailed way : | ||
{ | ||
fs::remove_file("/tmp/output1.geojson"); | ||
let drv = Driver::get("GeoJSON").unwrap(); | ||
let mut ds = drv.create(Path::new("/tmp/output1.geojson")).unwrap(); | ||
|
||
let lyr = ds.create_layer().unwrap(); | ||
|
||
let field_defn = FieldDefn::new("Name", OFT_STRING); | ||
field_defn.set_width(80); | ||
field_defn.add_to_layer(&lyr); | ||
|
||
let field_defn = FieldDefn::new("Value", OFT_REAL); | ||
field_defn.add_to_layer(&lyr); | ||
|
||
let defn = Defn::new_from_layer(&lyr); | ||
|
||
// 1st feature : | ||
let mut ft = Feature::new(&defn); | ||
ft.set_geometry(Geometry::from_wkt("POINT (45.21 21.76)").unwrap()); | ||
ft.set_field_string("Name", "Feature 1"); | ||
ft.set_field_double("Value", 45.78); | ||
ft.create(&lyr); | ||
|
||
// 2nd feature : | ||
let mut ft = Feature::new(&defn); | ||
ft.set_geometry(Geometry::from_wkt("POINT (46.50 22.50)").unwrap()); | ||
ft.set_field_string("Name", "Feature 2"); | ||
ft.set_field_double("Value", 0.789); | ||
ft.create(&lyr); | ||
} | ||
|
||
/// Example 2, same output, shortened way : | ||
{ | ||
fs::remove_file("/tmp/output2.geojson"); | ||
let driver = Driver::get("GeoJSON").unwrap(); | ||
let mut ds = driver.create(Path::new("/tmp/output2.geojson")).unwrap(); | ||
let mut layer = ds.create_layer().unwrap(); | ||
|
||
layer.create_defn_fields(&[("Name", OFT_STRING), ("Value", OFT_REAL)]); | ||
// Shortcut for : | ||
// let field_defn = FieldDefn::new("Name", OFT_STRING); | ||
// field_defn.add_to_layer(&layer); | ||
// let field_defn = FieldDefn::new("Value", OFT_REAL); | ||
// field_defn.add_to_layer(&layer); | ||
|
||
layer.create_feature_fields( | ||
Geometry::from_wkt("POINT (45.21 21.76)").unwrap(), | ||
&["Name", "Value"], | ||
&[FieldValue::StringValue("Feature 1".to_string()), FieldValue::RealValue(45.78)] | ||
); | ||
layer.create_feature_fields( | ||
Geometry::from_wkt("POINT (46.50 22.50)").unwrap(), | ||
&["Name", "Value"], | ||
&[FieldValue::StringValue("Feature 2".to_string()), FieldValue::RealValue(0.789)] | ||
); | ||
// Shortcuts for : | ||
// let defn = Defn::new_from_layer(&layer); | ||
// | ||
// let mut ft = Feature::new(&defn); | ||
// ft.set_geometry(Geometry::from_wkt("POINT (45.21 21.76)").unwrap()); | ||
// ft.set_field("Name", OFT_STRING, "Feature 1"); | ||
// ft.set_field("Value", OFT_REAL, 45.78); | ||
// ft.create(&lyr); | ||
// | ||
// let mut ft = Feature::new(&defn); | ||
// ft.set_geometry(Geometry::from_wkt("POINT (46.50 22.50)").unwrap()); | ||
// ft.set_field("Name", OFT_STRING, "Feature 2"); | ||
// ft.set_field("Value", OFT_REAL, 0.789); | ||
// ft.create(&lyr); | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,9 @@ | ||
use libc::{c_int, c_void}; | ||
use utils::_string; | ||
use gdal_sys::ogr; | ||
use vector::layer::Layer; | ||
use gdal_major_object::MajorObject; | ||
|
||
|
||
/// Layer definition | ||
/// | ||
|
@@ -26,6 +29,11 @@ impl Defn { | |
total: total | ||
}; | ||
} | ||
|
||
pub fn new_from_layer(lyr: &Layer) -> Defn { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should be just There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure, I will change it. |
||
let c_defn = unsafe { ogr::OGR_L_GetLayerDefn(lyr.gdal_object_ptr())}; | ||
Defn {c_defn: c_defn} | ||
} | ||
} | ||
|
||
pub struct FieldIterator<'a> { | ||
|
@@ -66,4 +74,16 @@ impl<'a> Field<'a> { | |
let rv = unsafe { ogr::OGR_Fld_GetNameRef(self.c_field_defn) }; | ||
return _string(rv); | ||
} | ||
|
||
pub fn get_type(&'a self) -> i32 { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should be just There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess I can't use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As this will return a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok! |
||
unsafe { ogr::OGR_Fld_GetType(self.c_field_defn) } | ||
} | ||
|
||
pub fn get_width(&'a self) -> i32 { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should be just |
||
unsafe { ogr::OGR_Fld_GetWidth(self.c_field_defn) } | ||
} | ||
|
||
pub fn get_precision(&'a self) -> i32 { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should be just |
||
unsafe { ogr::OGR_Fld_GetPrecision(self.c_field_defn) } | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,13 @@ | ||
use std::ffi::CString; | ||
use libc::{c_void}; | ||
use libc::{c_void, c_double, c_int}; | ||
use vector::Defn; | ||
use utils::_string; | ||
use gdal_sys::ogr; | ||
use gdal_sys::{ogr, ogr_enums}; | ||
use vector::geometry::Geometry; | ||
use vector::layer::Layer; | ||
use gdal_major_object::MajorObject; | ||
|
||
use errors::*; | ||
|
||
/// OGR Feature | ||
pub struct Feature<'a> { | ||
|
@@ -15,6 +18,15 @@ pub struct Feature<'a> { | |
|
||
|
||
impl<'a> Feature<'a> { | ||
pub fn new(defn: &'a Defn) -> Feature { | ||
let c_feature = unsafe { ogr::OGR_F_Create(defn.c_defn()) }; | ||
unsafe { Feature { | ||
_defn: defn, | ||
c_feature: c_feature, | ||
geometry: Geometry::lazy_feature_geometry(), | ||
} } | ||
} | ||
|
||
pub unsafe fn _with_c_feature(defn: &'a Defn, c_feature: *const c_void) -> Feature { | ||
return Feature{ | ||
_defn: defn, | ||
|
@@ -43,6 +55,10 @@ impl<'a> Feature<'a> { | |
let rv = unsafe { ogr::OGR_F_GetFieldAsDouble(self.c_feature, field_id) }; | ||
return Some(FieldValue::RealValue(rv as f64)); | ||
}, | ||
ogr::OFT_INTEGER => { | ||
let rv = unsafe { ogr::OGR_F_GetFieldAsInteger(self.c_feature, field_id) }; | ||
return Some(FieldValue::IntegerValue(rv as i32)); | ||
}, | ||
_ => panic!("Unknown field type {}", field_type) | ||
} | ||
} | ||
|
@@ -55,6 +71,52 @@ impl<'a> Feature<'a> { | |
} | ||
return &self.geometry; | ||
} | ||
|
||
pub fn create(&self, lyr: &Layer) -> Result<()> { | ||
let rv = unsafe { ogr::OGR_L_CreateFeature(lyr.gdal_object_ptr(), self.c_feature) }; | ||
if rv != ogr_enums::OGRErr::OGRERR_NONE { | ||
return Err(ErrorKind::OgrError(rv, "OGR_L_CreateFeature").into()); | ||
} | ||
Ok(()) | ||
} | ||
|
||
pub fn set_field_string(&self, field_name: &str, value: &str){ | ||
let c_str_field_name = CString::new(field_name).unwrap(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If it is possible please try not to use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Alright for returning There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Error handling in rust is a bit difficult. Where is a lot about it in the book. To make this easier rust-gdal uses the error-chain crate. We already have some wrapping errors in errors.rs:
To handle the |
||
let c_str_value = CString::new(value).unwrap(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
let idx = unsafe { ogr::OGR_F_GetFieldIndex(self.c_feature, c_str_field_name.as_ptr())}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
unsafe { ogr::OGR_F_SetFieldString(self.c_feature, idx, c_str_value.as_ptr()) }; | ||
} | ||
|
||
pub fn set_field_double(&self, field_name: &str, value: f64){ | ||
let c_str_field_name = CString::new(field_name).unwrap(); | ||
let idx = unsafe { ogr::OGR_F_GetFieldIndex(self.c_feature, c_str_field_name.as_ptr())}; | ||
unsafe { ogr::OGR_F_SetFieldDouble(self.c_feature, idx, value as c_double) }; | ||
} | ||
|
||
pub fn set_field_integer(&self, field_name: &str, value: i32){ | ||
let c_str_field_name = CString::new(field_name).unwrap(); | ||
let idx = unsafe { ogr::OGR_F_GetFieldIndex(self.c_feature, c_str_field_name.as_ptr())}; | ||
unsafe { ogr::OGR_F_SetFieldInteger(self.c_feature, idx, value as c_int) }; | ||
} | ||
|
||
pub fn set_field(&self, field_name: &str, type_value: i32, value: FieldValue){ | ||
if type_value == 2 { | ||
self.set_field_double(field_name, value.as_real()); | ||
} else if type_value == 4 { | ||
self.set_field_string(field_name, value.as_string().as_str()); | ||
} else if type_value == 0 { | ||
self.set_field_integer(field_name, value.as_int()); | ||
} | ||
} | ||
|
||
pub fn set_geometry(&mut self, geom: Geometry) -> Result<()> { | ||
self.geometry = geom; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What happens if the following method call fails? Wouldn't it be more safe if this is done after the GDAL call and before |
||
let rv = unsafe { ogr::OGR_F_SetGeometry(self.c_feature, self.geometry.c_geometry()) }; | ||
if rv != ogr_enums::OGRErr::OGRERR_NONE { | ||
return Err(ErrorKind::OgrError(rv, "OGR_G_SetGeometry").into()); | ||
} | ||
Ok(()) | ||
} | ||
} | ||
|
||
|
||
|
@@ -66,6 +128,7 @@ impl<'a> Drop for Feature<'a> { | |
|
||
|
||
pub enum FieldValue { | ||
IntegerValue(i32), | ||
StringValue(String), | ||
RealValue(f64), | ||
} | ||
|
@@ -87,4 +150,12 @@ impl FieldValue { | |
_ => panic!("not a RealValue") | ||
} | ||
} | ||
|
||
/// Interpret the value as `i32`. Panics if the value is something else. | ||
pub fn as_int(self) -> i32 { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Methods that consume There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should I also change the name of the two previous methods ( There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i guess this would be the right thing to do. Also i think these methods should return an Option instead of panicking... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I made the other changes but not this last one (returning an pub fn set_field(&self, field_name: &str, type_value: OGRFieldType, value: FieldValue) -> Result<()> {
match type_value {
OGRFieldType::OFTReal => self.set_field_double(field_name, value.to_real()), like here, when using |
||
match self { | ||
FieldValue::IntegerValue(rv) => rv, | ||
_ => panic!("not an IntegerValue") | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be nice to change the OFT types into an enum and move it into ogr_enums.rs. This way all the FFI methods could take OgrFieldType instead of c_int as parameter. (link to the OGR enum)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was thinking too about moving OFT types into an enum, so no problem, I will do it.