Skip to content

Commit

Permalink
Use transform trait from proj.
Browse files Browse the repository at this point in the history
The trait was mostly lifted from geo and ported to proj.

To it, I added:

- a mutable (in place) version
- versions that take a &Proj rather than an Into<Proj> so that we can
  re-use a proj instance and take advantage of proj_array in some
  contexts.
  • Loading branch information
michaelkirk authored and frewsxcv committed Feb 20, 2022
1 parent 1d3b329 commit faf0a96
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 faf0a96

Please sign in to comment.