Skip to content

Commit

Permalink
Merge georust#730
Browse files Browse the repository at this point in the history
730: Port Transform trait to `proj` crate. r=frewsxcv a=michaelkirk

- [x] I agree to follow the project's [code of conduct](https://github.com/georust/geo/blob/master/CODE_OF_CONDUCT.md).
- [x] I added an entry to `CHANGES.md` if knowledge of this change could be valuable to users.
---
~~Based on the georust#729, so please review that first:~~: Done! Merged and rebased.

Depends on proj pr:
- georust/proj#109

Co-authored-by: Michael Kirk <michael.code@endoftheworl.de>
  • Loading branch information
bors[bot] and michaelkirk authored Feb 20, 2022
2 parents 1d3b329 + faf0a96 commit d7df627
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 118 deletions.
2 changes: 1 addition & 1 deletion geo/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ geo-types = { version = "0.7.2", features = ["approx", "use-rstar"] }
geographiclib-rs = { version = "0.2" }
log = "0.4.11"
num-traits = "0.2"
proj = { version = "0.25", optional = true }
proj = { version = "0.25.2", optional = true }
robust = { version = "0.2.2" }
rstar = { version = "0.8" }
serde = { version = "1.0", optional = true, features = ["derive"] }
Expand Down
139 changes: 24 additions & 115 deletions geo/src/algorithm/transform.rs
Original file line number Diff line number Diff line change
@@ -1,117 +1,26 @@
use std::{convert::TryInto, error::Error, fmt};

/// Transform a geometry using PROJ.
pub trait Transform<T> {
type Output;

/// Transform a geometry.
///
/// # Examples
///
/// Transform a geometry using a PROJ string definition:
///
/// ```
/// use geo::{self, prelude::*};
///
/// let point = geo::point!(x: -36.508f32, y: -54.2815f32);
///
/// assert_eq!(
/// point.transform("+proj=axisswap +order=2,1,3,4").unwrap(),
/// geo::point!(x: -54.2815f32, y: -36.508f32)
/// );
/// ```
///
/// Transform a geometry from one CRS to another CRS:
///
/// ```
/// use geo::{self, prelude::*};
///
/// let point = geo::point!(x: -36.508f32, y: -54.2815f32);
///
/// assert_eq!(
/// point.transform(("EPSG:4326", "EPSG:3857")).unwrap(),
/// geo::point!(x: -4064052.0f32, y: -7223650.5f32)
/// );
/// ```
fn transform(
&self,
proj: impl TryInto<proj::Proj, Error = proj::ProjCreateError>,
) -> Result<Self::Output, TransformError>;

/// Transform a geometry from one CRS to another CRS.
///
/// # Examples
///
/// ```
/// use geo::{self, prelude::*};
///
/// let point: geo::Point<f32> = geo::point!(x: -36.508f32, y: -54.2815f32);
///
/// assert_eq!(
/// point.transform_crs_to_crs("EPSG:4326", "EPSG:3857").unwrap(),
/// geo::point!(x: -4064052.0f32, y: -7223650.5f32)
/// );
/// ```
fn transform_crs_to_crs(
&self,
source_crs: &str,
target_crs: &str,
) -> Result<Self::Output, TransformError>;
}

impl<T, G> Transform<T> for G
where
T: crate::CoordFloat,
G: crate::algorithm::map_coords::TryMapCoords<T, T, proj::ProjError>,
{
type Output = G::Output;

fn transform(
&self,
proj: impl TryInto<proj::Proj, Error = proj::ProjCreateError>,
) -> Result<Self::Output, TransformError> {
let transformer: proj::Proj = proj.try_into()?;
let result: Result<G::Output, proj::ProjError> =
self.try_map_coords(|&(x, y)| transformer.convert((x, y)));
Ok(result?)
}

fn transform_crs_to_crs(
&self,
source_crs: &str,
target_crs: &str,
) -> Result<Self::Output, TransformError> {
self.transform((source_crs, target_crs))
}
}

#[derive(Debug)]
pub enum TransformError {
UnknownCrs,
ProjCreateError(proj::ProjCreateError),
ProjError(proj::ProjError),
}

impl From<proj::ProjError> for TransformError {
fn from(e: proj::ProjError) -> Self {
TransformError::ProjError(e)
}
}

impl From<proj::ProjCreateError> for TransformError {
fn from(e: proj::ProjCreateError) -> Self {
TransformError::ProjCreateError(e)
pub use proj::{Area, Coord, Info, Proj, ProjBuilder, ProjError, Projinfo, Transform};

#[cfg(test)]
mod tests {
use super::*;
use geo_types::{point, Rect};

#[test]
fn test_transform() {
let mut subject = {
let point_a = point!(x: 4760096.421921f64, y: 3744293.729449f64);
let point_b = point!(x: 4760196.421921f64, y: 3744393.729449f64);
Rect::new(point_a, point_b)
};

subject
.transform_crs_to_crs("EPSG:2230", "EPSG:26946")
.unwrap();
let expected = {
let point_a = point!(x: 1450880.2910605022, y: 1141263.0111604782);
let point_b = point!(x: 1450910.771121464, y: 1141293.4912214363);
Rect::new(point_a, point_b)
};
assert_relative_eq!(subject, expected, epsilon = 0.2);
}
}

impl fmt::Display for TransformError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
TransformError::UnknownCrs => write!(f, "Unknown CRS"),
TransformError::ProjCreateError(err) => err.fmt(f),
TransformError::ProjError(err) => err.fmt(f),
}
}
}

impl Error for TransformError {}
4 changes: 2 additions & 2 deletions geo/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -252,11 +252,11 @@ pub mod prelude {
pub use crate::algorithm::rotate::{Rotate, RotatePoint};
pub use crate::algorithm::simplify::Simplify;
pub use crate::algorithm::simplifyvw::SimplifyVW;
#[cfg(feature = "use-proj")]
pub use crate::algorithm::transform::Transform;
pub use crate::algorithm::translate::Translate;
pub use crate::algorithm::vincenty_distance::VincentyDistance;
pub use crate::algorithm::vincenty_length::VincentyLength;
#[cfg(feature = "use-proj")]
pub use crate::proj::Transform;
}

/// A common numeric trait used for geo algorithms.
Expand Down

0 comments on commit d7df627

Please sign in to comment.