diff --git a/geozero/Cargo.toml b/geozero/Cargo.toml index 178a3ff1..0f5bf843 100644 --- a/geozero/Cargo.toml +++ b/geozero/Cargo.toml @@ -24,6 +24,7 @@ with-postgis-sqlx = ["with-wkb", "sqlx/postgres"] with-postgis-postgres = ["with-wkb", "postgres-types", "bytes"] with-mvt = ["prost"] with-tessellator = ["lyon"] +with-proj = ["proj", "delegate"] [dependencies] thiserror = "1.0" @@ -40,6 +41,8 @@ postgres-types = { version = "0.2", optional = true } bytes = { version = "1.1", optional = true } prost = { version = "0.9.0", optional = true } wkt = { version = "0.10.0", optional = true } +proj = { version = "0.25.2", optional = true } +delegate = { version = "0.6.2", optional = true } [dev-dependencies] seek_bufread = "1.2" diff --git a/geozero/src/lib.rs b/geozero/src/lib.rs index f6b12758..902fd754 100644 --- a/geozero/src/lib.rs +++ b/geozero/src/lib.rs @@ -33,6 +33,7 @@ pub mod error; mod feature_processor; mod geometry_processor; mod multiplex; +mod proj; mod property_processor; pub use api::*; @@ -72,6 +73,9 @@ pub mod svg; #[cfg(feature = "with-svg")] pub use crate::svg::conversion::*; +#[cfg(feature = "with-proj")] +pub use crate::proj::ToProjected; + #[cfg(feature = "with-tesselator")] pub mod tessellator; diff --git a/geozero/src/proj.rs b/geozero/src/proj.rs new file mode 100644 index 00000000..19b2cb17 --- /dev/null +++ b/geozero/src/proj.rs @@ -0,0 +1,139 @@ +use crate::error::Result; +use crate::{ + ColumnValue, CoordDimensions, FeatureProcessor, GeomProcessor, GeozeroDatasource, + PropertyProcessor, +}; + +use delegate::delegate; +use proj::Proj; + +use std::rc::Rc; + +/// ``` +/// use proj::Proj; +/// use geozero::{ToProjected, ProcessToJson, wkt::WktStr}; +/// +/// // Convert from [NAD 83 US Survey Feet](https://epsg.io/2230) to [NAD 83 Meters](https://epsg.io/26946) Using EPSG Codes +/// let from = "EPSG:2230"; +/// let to = "EPSG:26946"; +/// let proj = Proj::new_known_crs(from, to, None).unwrap(); +/// +/// let wkt = WktStr("POINT (4760096.421921 3744293.729449)"); +/// let expected = r#"{"type": "Point", "coordinates": [1450880.2910605022,1141263.0111604782]}"#; +/// assert_eq!(expected, &wkt.to_projected(proj).to_json().unwrap()); +/// ``` +pub trait ToProjected: GeozeroDatasource + Sized { + fn to_projected(self, proj: Proj) -> ProjWrappedDataSource; +} + +impl ToProjected for Input { + fn to_projected(self, proj: Proj) -> ProjWrappedDataSource { + ProjWrappedDataSource { + proj: Rc::new(proj), + input: self, + } + } +} + +pub struct ProjWrappedDataSource { + proj: Rc, + input: Input, +} + +struct ProjPreprocessor<'a> { + proj: Rc, + output: &'a mut dyn FeatureProcessor, +} + +impl GeozeroDatasource for ProjWrappedDataSource { + fn process(&mut self, processor: &mut P) -> Result<()> { + let mut wrapped = ProjPreprocessor { + proj: self.proj.clone(), + output: processor, + }; + self.input.process(&mut wrapped) + } +} + +impl FeatureProcessor for ProjPreprocessor<'_> { + delegate! { + to self.output { + fn dataset_begin(&mut self, name: Option<&str>) -> Result<()>; + fn dataset_end(&mut self) -> Result<()>; + fn feature_begin(&mut self, idx: u64) -> Result<()>; + fn feature_end(&mut self, idx: u64) -> Result<()>; + fn properties_begin(&mut self) -> Result<()>; + fn properties_end(&mut self) -> Result<()>; + fn geometry_begin(&mut self) -> Result<()>; + fn geometry_end(&mut self) -> Result<()>; + } + } +} + +impl PropertyProcessor for ProjPreprocessor<'_> { + delegate! { + to self.output { + fn property(&mut self, idx: usize, name: &str, value: &ColumnValue) -> Result; + } + } +} + +impl GeomProcessor for ProjPreprocessor<'_> { + fn coordinate( + &mut self, + x: f64, + y: f64, + z: Option, + m: Option, + t: Option, + tm: Option, + idx: usize, + ) -> Result<()> { + let (x, y) = self.proj.convert((x, y)).unwrap(); + self.output.coordinate(x, y, z, m, t, tm, idx) + } + + fn xy(&mut self, x: f64, y: f64, idx: usize) -> Result<()> { + let (x, y) = self.proj.convert((x, y)).unwrap(); + self.output.xy(x, y, idx) + } + + delegate! { + to self.output { + fn dimensions(&self) -> CoordDimensions; + fn multi_dim(&self) -> bool; + fn srid(&mut self, srid: Option) -> Result<()>; + fn empty_point(&mut self, idx: usize) -> Result<()>; + fn point_begin(&mut self, idx: usize) -> Result<()>; + fn point_end(&mut self, idx: usize) -> Result<()>; + fn multipoint_begin(&mut self, size: usize, idx: usize) -> Result<()>; + fn multipoint_end(&mut self, idx: usize) -> Result<()>; + fn linestring_begin(&mut self, tagged: bool, size: usize, idx: usize) -> Result<()>; + fn linestring_end(&mut self, tagged: bool, idx: usize) -> Result<()>; + fn multilinestring_begin(&mut self, size: usize, idx: usize) -> Result<()>; + fn multilinestring_end(&mut self, idx: usize) -> Result<()>; + fn polygon_begin(&mut self, tagged: bool, size: usize, idx: usize) -> Result<()>; + fn polygon_end(&mut self, tagged: bool, idx: usize) -> Result<()>; + fn multipolygon_begin(&mut self, size: usize, idx: usize) -> Result<()>; + fn multipolygon_end(&mut self, idx: usize) -> Result<()>; + fn geometrycollection_begin(&mut self, size: usize, idx: usize) -> Result<()>; + fn geometrycollection_end(&mut self, idx: usize) -> Result<()>; + fn circularstring_begin(&mut self, size: usize, idx: usize) -> Result<()>; + fn circularstring_end(&mut self, idx: usize) -> Result<()>; + fn compoundcurve_begin(&mut self, size: usize, idx: usize) -> Result<()>; + fn compoundcurve_end(&mut self, idx: usize) -> Result<()>; + fn curvepolygon_begin(&mut self, size: usize, idx: usize) -> Result<()>; + fn curvepolygon_end(&mut self, idx: usize) -> Result<()>; + fn multicurve_begin(&mut self, size: usize, idx: usize) -> Result<()>; + fn multicurve_end(&mut self, idx: usize) -> Result<()>; + fn multisurface_begin(&mut self, size: usize, idx: usize) -> Result<()>; + fn multisurface_end(&mut self, idx: usize) -> Result<()>; + fn triangle_begin(&mut self, tagged: bool, size: usize, idx: usize) -> Result<()>; + fn triangle_end(&mut self, tagged: bool, idx: usize) -> Result<()>; + fn polyhedralsurface_begin(&mut self, size: usize, idx: usize) -> Result<()>; + fn polyhedralsurface_end(&mut self, idx: usize) -> Result<()>; + fn tin_begin(&mut self, size: usize, idx: usize) -> Result<()>; + fn tin_end(&mut self, idx: usize) -> Result<()>; + } + } +}