diff --git a/Cargo.toml b/Cargo.toml index c9761da4..7268202c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,6 +31,7 @@ network = ["reqwest", "proj-sys/network"] [dev-dependencies] approx = "0.3" +insta = "1.12.0" [package.metadata.docs.rs] features = [ "proj-sys/nobuild", "network", "geo-types" ] diff --git a/proj-sys/build.rs b/proj-sys/build.rs index d8ac4df1..076f2e47 100644 --- a/proj-sys/build.rs +++ b/proj-sys/build.rs @@ -99,7 +99,7 @@ fn build_from_source() -> Result> config.define("BUILD_PROJSYNC", "OFF"); config.define("ENABLE_CURL", "OFF"); - let enable_tiff = cfg!(feature="network"); + let enable_tiff = cfg!(feature = "network"); if enable_tiff { eprintln!("enabling tiff support"); config.define("ENABLE_TIFF", "ON"); diff --git a/src/geo_types.rs b/src/geo_types.rs index 92570437..cab08e25 100644 --- a/src/geo_types.rs +++ b/src/geo_types.rs @@ -1,3 +1,6 @@ +use crate::{Proj, ProjError}; +use geo_types; + ///```rust /// # use approx::assert_relative_eq; /// extern crate proj; @@ -51,3 +54,303 @@ impl crate::Coord for geo_types::Point { Self::new(x, y) } } + +impl Proj { + /// + /// Convert a [`geo_types::LineString`], using [`Self::convert_array()`] internally + /// + pub fn convert_line_string<'a, T>( + &self, + line_string: &'a mut geo_types::LineString, + ) -> Result<&'a mut geo_types::LineString, ProjError> + where + T: crate::proj::CoordinateType, + { + self.convert_array(&mut line_string.0)?; + Ok(line_string) + } + + /// + /// Convert a [`geo_types::Line`]'s points using [`Self::convert()`] + /// + pub fn convert_line( + &self, + line: &geo_types::Line, + ) -> Result, ProjError> + where + T: crate::proj::CoordinateType, + { + Ok(geo_types::Line::new( + self.convert(line.start)?, + self.convert(line.end)?, + )) + } + + /// + /// Convert a [`geo_types::Rect`]'s point using [`Self::convert()`] + /// + pub fn convert_rect( + &self, + rect: &geo_types::Rect, + ) -> Result, ProjError> + where + T: crate::proj::CoordinateType, + { + Ok(geo_types::Rect::new( + self.convert(rect.min())?, + self.convert(rect.max())?, + )) + } + + /// + /// Convert a [`geo_types::Triangle`]'s point using [`Self::convert()`] + /// + pub fn convert_triangle( + &self, + triangle: &geo_types::Triangle, + ) -> Result, ProjError> + where + T: crate::proj::CoordinateType, + { + Ok(geo_types::Triangle( + self.convert(triangle.0)?, + self.convert(triangle.1)?, + self.convert(triangle.2)?, + )) + } + + /// + /// Convert a [`geo_types::Polygon`] exterior and interior using [`Self::convert_line_string()`] + /// + pub fn convert_polygon( + &self, + polygon: geo_types::Polygon, + ) -> Result, ProjError> + where + T: crate::proj::CoordinateType, + { + let (mut exterior, mut interiors) = polygon.into_inner(); + self.convert_line_string(&mut exterior)?; + for mut interior in interiors.iter_mut() { + self.convert_line_string(&mut interior)?; + } + Ok(geo_types::Polygon::new(exterior, interiors)) + } + + /// + /// Convert a [`geo_types::MultiPolygon`] using [`Self::convert_polygon()`] + /// + pub fn convert_multi_polygon( + &self, + multi_polygon: geo_types::MultiPolygon, + ) -> Result, ProjError> + where + T: crate::proj::CoordinateType, + { + Ok(geo_types::MultiPolygon( + multi_polygon + .into_iter() + .map(|p| self.convert_polygon(p)) + .collect::, _>>()?, + )) + } + + /// + /// Convert [`geo_types::MultiPoint`] using [`Self::convert_array()`] + /// + pub fn convert_multi_point<'a, T>( + &self, + multi_point: &'a mut geo_types::MultiPoint, + ) -> Result<&'a mut geo_types::MultiPoint, ProjError> + where + T: crate::proj::CoordinateType, + { + self.convert_array(&mut multi_point.0)?; + Ok(multi_point) + } + + /// + /// Convert a [`geo_types::MultiLineString`] using [`Self::convert_line_string()`] + /// + pub fn convert_multi_line_string<'a, T>( + &self, + multi_line_string: &'a mut geo_types::MultiLineString, + ) -> Result<&'a mut geo_types::MultiLineString, ProjError> + where + T: crate::proj::CoordinateType, + { + for ls in multi_line_string.into_iter() { + self.convert_line_string(ls)?; + } + Ok(multi_line_string) + } + + /// + /// Convert a [`geo_types::Geometry`] + /// + pub fn convert_geometry( + &self, + geometry: geo_types::Geometry, + ) -> Result, ProjError> + where + T: crate::proj::CoordinateType, + { + match geometry { + geo_types::Geometry::Point(p) => Ok(self.convert(p)?.into()), + geo_types::Geometry::Line(mut line) => { + let _ = self.convert_line(&mut line)?; + Ok(line.into()) + } + geo_types::Geometry::LineString(mut ls) => { + self.convert_line_string(&mut ls)?; + Ok(ls.into()) + } + geo_types::Geometry::Polygon(p) => Ok(self.convert_polygon(p)?.into()), + geo_types::Geometry::MultiPoint(mut multi_point) => { + self.convert_multi_point(&mut multi_point)?; + Ok(multi_point.into()) + } + geo_types::Geometry::MultiLineString(mut multi_line_string) => { + self.convert_multi_line_string(&mut multi_line_string)?; + Ok(multi_line_string.into()) + } + geo_types::Geometry::MultiPolygon(multi_polygon) => { + Ok(self.convert_multi_polygon(multi_polygon)?.into()) + } + geo_types::Geometry::GeometryCollection(geometry_collection) => { + Ok(geo_types::Geometry::GeometryCollection( + self.convert_geometry_collection(geometry_collection)?, + )) + } + geo_types::Geometry::Rect(rect) => Ok(self.convert_rect(&rect)?.into()), + geo_types::Geometry::Triangle(triangle) => Ok(self.convert_triangle(&triangle)?.into()), + } + } + + /// + /// Convert a [`geo_types::GeometryCollection`] + /// + pub fn convert_geometry_collection( + &self, + geometry_collection: geo_types::GeometryCollection, + ) -> Result, ProjError> + where + T: crate::proj::CoordinateType, + { + Ok(geo_types::GeometryCollection( + geometry_collection + .into_iter() + .map(|g| self.convert_geometry(g)) + .collect::, _>>()?, + )) + } +} + +#[cfg(test)] +mod test { + use super::*; + use geo_types::{ + line_string, point, polygon, GeometryCollection, LineString, MultiPolygon, Polygon, Rect, + Triangle, + }; + + fn proj() -> Proj { + let from = "+proj=lcc +lat_1=49 +lat_2=44 +lat_0=46.5 +lon_0=3 +x_0=700000 +y_0=6600000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs +type=crs"; + let to = "proj=longlat +datum=WGS84 +no_defs +type=crs"; + Proj::new_known_crs(&from, &to, None).unwrap() + } + + fn triangle() -> Triangle { + Triangle( + point!(x: 493903.77, y: 6771154.06).into(), + point!(x: 648773.74, y: 6863725.64).into(), + point!(x: 740697.71, y: 6745951.11).into(), + ) + } + + fn rect() -> Rect { + Rect::new( + point!(x: -378305.81, y: 6093283.21), + point!(x: 1212610.74, y: 7186901.68), + ) + } + + fn multi_polygon() -> MultiPolygon { + MultiPolygon(vec![ + polygon(), + rect().to_polygon(), + triangle().to_polygon(), + ]) + } + + fn line_string() -> LineString { + line_string![ + (x: 617466.55, y: 6471839.66), + (x: 724549.17, y: 6557378.31), + (x: 806203.24, y: 6497115.20), + ] + } + + fn polygon() -> Polygon { + polygon!( + exterior: [ + (x: 459684.42, y: 6902803.84), + (x: 1008457.04, y: 6842618.16), + (x: 1066542.47, y: 6313538.74), + (x: 349688.67, y: 6268474.77), + ], + interiors: [ + [ + (x: 617466.55, y: 6471839.66), + (x: 724549.17, y: 6557378.31), + (x: 806203.24, y: 6497115.20), + ], + ], + ) + } + + #[test] + fn test_convert_line_string() { + let mut input = line_string(); + let output = proj().convert_line_string(&mut input).unwrap(); + insta::assert_debug_snapshot!(output); + } + + #[test] + fn test_convert_polygon() { + let output = proj().convert_polygon(polygon()).unwrap(); + insta::assert_debug_snapshot!(output); + } + + #[test] + fn test_convert_rect() { + let output = proj().convert_rect(&rect()).unwrap(); + insta::assert_debug_snapshot!(output); + } + + #[test] + fn test_convert_triangle() { + let output = proj().convert_triangle(&triangle()).unwrap(); + insta::assert_debug_snapshot!(output); + } + + #[test] + fn test_convert_multi_polygon() { + let output = proj().convert_multi_polygon(multi_polygon()).unwrap(); + insta::assert_debug_snapshot!(output); + } + + #[test] + fn test_convert_geometry_collection() { + let multi_geometry = GeometryCollection(vec![ + triangle().into(), + line_string().into(), + rect().into(), + polygon().into(), + multi_polygon().into(), + ]); + + let output = proj().convert_geometry_collection(multi_geometry).unwrap(); + insta::assert_debug_snapshot!(output); + } +} diff --git a/src/snapshots/proj__geo_types__test__convert_geometry_collection.snap b/src/snapshots/proj__geo_types__test__convert_geometry_collection.snap new file mode 100644 index 00000000..55fe8ae5 --- /dev/null +++ b/src/snapshots/proj__geo_types__test__convert_geometry_collection.snap @@ -0,0 +1,210 @@ +--- +source: src/geo_types.rs +assertion_line: 327 +expression: output + +--- +GeometryCollection( + [ + Triangle( + Triangle( + Coordinate { + x: 0.23556229698665834, + y: 48.00830018706168, + }, + Coordinate { + x: 2.3016357481228447, + y: 48.871518021466834, + }, + Coordinate { + x: 3.543777524968399, + y: 47.81269343169002, + }, + ), + ), + LineString( + LineString( + [ + Coordinate { + x: 1.9461678869815624, + y: 45.34098542962509, + }, + Coordinate { + x: 3.3178711489185697, + y: 46.11575845137438, + }, + Coordinate { + x: 4.361572299428162, + y: 45.56529578393842, + }, + ], + ), + ), + Rect( + Rect { + min: Coordinate { + x: -9.859999997843591, + y: 41.14999998828262, + }, + max: Coordinate { + x: 10.379999964442588, + y: 51.55999998529196, + }, + }, + ), + Polygon( + Polygon { + exterior: LineString( + [ + Coordinate { + x: -0.2966308165091663, + y: 49.1798238791979, + }, + Coordinate { + x: 7.186088542417848, + y: 48.610157172487696, + }, + Coordinate { + x: 7.558593793164613, + y: 43.82561056077398, + }, + Coordinate { + x: -1.3264704302504164, + y: 43.42898790522051, + }, + Coordinate { + x: -0.2966308165091663, + y: 49.1798238791979, + }, + ], + ), + interiors: [ + LineString( + [ + Coordinate { + x: 1.9461678869815624, + y: 45.34098542962509, + }, + Coordinate { + x: 3.3178711489185697, + y: 46.11575845137438, + }, + Coordinate { + x: 4.361572299428162, + y: 45.56529578393842, + }, + Coordinate { + x: 1.9461678869815624, + y: 45.34098542962509, + }, + ], + ), + ], + }, + ), + MultiPolygon( + MultiPolygon( + [ + Polygon { + exterior: LineString( + [ + Coordinate { + x: -0.2966308165091663, + y: 49.1798238791979, + }, + Coordinate { + x: 7.186088542417848, + y: 48.610157172487696, + }, + Coordinate { + x: 7.558593793164613, + y: 43.82561056077398, + }, + Coordinate { + x: -1.3264704302504164, + y: 43.42898790522051, + }, + Coordinate { + x: -0.2966308165091663, + y: 49.1798238791979, + }, + ], + ), + interiors: [ + LineString( + [ + Coordinate { + x: 1.9461678869815624, + y: 45.34098542962509, + }, + Coordinate { + x: 3.3178711489185697, + y: 46.11575845137438, + }, + Coordinate { + x: 4.361572299428162, + y: 45.56529578393842, + }, + Coordinate { + x: 1.9461678869815624, + y: 45.34098542962509, + }, + ], + ), + ], + }, + Polygon { + exterior: LineString( + [ + Coordinate { + x: -9.859999997843591, + y: 41.14999998828262, + }, + Coordinate { + x: -12.372413507046177, + y: 50.83083621340264, + }, + Coordinate { + x: 10.379999964442588, + y: 51.55999998529196, + }, + Coordinate { + x: 9.155586246082937, + y: 41.76063524340224, + }, + Coordinate { + x: -9.859999997843591, + y: 41.14999998828262, + }, + ], + ), + interiors: [], + }, + Polygon { + exterior: LineString( + [ + Coordinate { + x: 0.23556229698665834, + y: 48.00830018706168, + }, + Coordinate { + x: 2.3016357481228447, + y: 48.871518021466834, + }, + Coordinate { + x: 3.543777524968399, + y: 47.81269343169002, + }, + Coordinate { + x: 0.23556229698665834, + y: 48.00830018706168, + }, + ], + ), + interiors: [], + }, + ], + ), + ), + ], +) diff --git a/src/snapshots/proj__geo_types__test__convert_line_string.snap b/src/snapshots/proj__geo_types__test__convert_line_string.snap new file mode 100644 index 00000000..80699886 --- /dev/null +++ b/src/snapshots/proj__geo_types__test__convert_line_string.snap @@ -0,0 +1,22 @@ +--- +source: src/geo_types.rs +assertion_line: 289 +expression: output + +--- +LineString( + [ + Coordinate { + x: 1.9461678869815624, + y: 45.34098542962509, + }, + Coordinate { + x: 3.3178711489185697, + y: 46.11575845137438, + }, + Coordinate { + x: 4.361572299428162, + y: 45.56529578393842, + }, + ], +) diff --git a/src/snapshots/proj__geo_types__test__convert_multi_polygon.snap b/src/snapshots/proj__geo_types__test__convert_multi_polygon.snap new file mode 100644 index 00000000..08b01c84 --- /dev/null +++ b/src/snapshots/proj__geo_types__test__convert_multi_polygon.snap @@ -0,0 +1,108 @@ +--- +source: src/geo_types.rs +assertion_line: 312 +expression: output + +--- +MultiPolygon( + [ + Polygon { + exterior: LineString( + [ + Coordinate { + x: -0.2966308165091663, + y: 49.1798238791979, + }, + Coordinate { + x: 7.186088542417848, + y: 48.610157172487696, + }, + Coordinate { + x: 7.558593793164613, + y: 43.82561056077398, + }, + Coordinate { + x: -1.3264704302504164, + y: 43.42898790522051, + }, + Coordinate { + x: -0.2966308165091663, + y: 49.1798238791979, + }, + ], + ), + interiors: [ + LineString( + [ + Coordinate { + x: 1.9461678869815624, + y: 45.34098542962509, + }, + Coordinate { + x: 3.3178711489185697, + y: 46.11575845137438, + }, + Coordinate { + x: 4.361572299428162, + y: 45.56529578393842, + }, + Coordinate { + x: 1.9461678869815624, + y: 45.34098542962509, + }, + ], + ), + ], + }, + Polygon { + exterior: LineString( + [ + Coordinate { + x: -9.859999997843591, + y: 41.14999998828262, + }, + Coordinate { + x: -12.372413507046177, + y: 50.83083621340264, + }, + Coordinate { + x: 10.379999964442588, + y: 51.55999998529196, + }, + Coordinate { + x: 9.155586246082937, + y: 41.76063524340224, + }, + Coordinate { + x: -9.859999997843591, + y: 41.14999998828262, + }, + ], + ), + interiors: [], + }, + Polygon { + exterior: LineString( + [ + Coordinate { + x: 0.23556229698665834, + y: 48.00830018706168, + }, + Coordinate { + x: 2.3016357481228447, + y: 48.871518021466834, + }, + Coordinate { + x: 3.543777524968399, + y: 47.81269343169002, + }, + Coordinate { + x: 0.23556229698665834, + y: 48.00830018706168, + }, + ], + ), + interiors: [], + }, + ], +) diff --git a/src/snapshots/proj__geo_types__test__convert_polygon.snap b/src/snapshots/proj__geo_types__test__convert_polygon.snap new file mode 100644 index 00000000..c5c4abec --- /dev/null +++ b/src/snapshots/proj__geo_types__test__convert_polygon.snap @@ -0,0 +1,54 @@ +--- +source: src/geo_types.rs +assertion_line: 295 +expression: output + +--- +Polygon { + exterior: LineString( + [ + Coordinate { + x: -0.2966308165091663, + y: 49.1798238791979, + }, + Coordinate { + x: 7.186088542417848, + y: 48.610157172487696, + }, + Coordinate { + x: 7.558593793164613, + y: 43.82561056077398, + }, + Coordinate { + x: -1.3264704302504164, + y: 43.42898790522051, + }, + Coordinate { + x: -0.2966308165091663, + y: 49.1798238791979, + }, + ], + ), + interiors: [ + LineString( + [ + Coordinate { + x: 1.9461678869815624, + y: 45.34098542962509, + }, + Coordinate { + x: 3.3178711489185697, + y: 46.11575845137438, + }, + Coordinate { + x: 4.361572299428162, + y: 45.56529578393842, + }, + Coordinate { + x: 1.9461678869815624, + y: 45.34098542962509, + }, + ], + ), + ], +} diff --git a/src/snapshots/proj__geo_types__test__convert_rect.snap b/src/snapshots/proj__geo_types__test__convert_rect.snap new file mode 100644 index 00000000..f9b76abd --- /dev/null +++ b/src/snapshots/proj__geo_types__test__convert_rect.snap @@ -0,0 +1,16 @@ +--- +source: src/geo_types.rs +assertion_line: 288 +expression: output + +--- +Rect { + min: Coordinate { + x: -9.859999997843591, + y: 41.14999998828262, + }, + max: Coordinate { + x: 10.379999964442588, + y: 51.55999998529196, + }, +} diff --git a/src/snapshots/proj__geo_types__test__convert_triangle.snap b/src/snapshots/proj__geo_types__test__convert_triangle.snap new file mode 100644 index 00000000..e5b86062 --- /dev/null +++ b/src/snapshots/proj__geo_types__test__convert_triangle.snap @@ -0,0 +1,20 @@ +--- +source: src/geo_types.rs +assertion_line: 307 +expression: output + +--- +Triangle( + Coordinate { + x: 0.23556229698665834, + y: 48.00830018706168, + }, + Coordinate { + x: 2.3016357481228447, + y: 48.871518021466834, + }, + Coordinate { + x: 3.543777524968399, + y: 47.81269343169002, + }, +)