#![doc = include_str!("../README.md")]
use geo::{Coord, Geometry, MapCoords, Point, coord};
use geodesy::Direction::{Fwd, Inv};
use geodesy::Error as GeodesyError;
use geodesy::coord::{Coor2D, CoordinateTuple};
use geodesy::ctx::{Context, Minimal, OpHandle};
#[derive(Debug)]
pub struct OMercError;
impl From<GeodesyError> for OMercError {
fn from(_: GeodesyError) -> Self {
OMercError
}
}
pub struct OMercTransformer {
context: Minimal,
operation: OpHandle,
}
impl OMercTransformer {
pub fn new(anchor_point: &Point) -> Result<Self, OMercError> {
let mut context = Minimal::new();
let operation = context.op(&format!(
"omerc ellps=WGS84 latc={} lonc={} k_0=1.0 alpha=1e-06",
anchor_point.y(),
anchor_point.x()
))?;
Ok(Self { context, operation })
}
pub fn to_cartesian(&self, geometry: &Geometry) -> Result<Geometry, OMercError> {
Ok(
geometry.try_map_coords(|c: Coord<f64>| -> Result<_, GeodesyError> {
let mut data = [Coor2D::geo(c.y, c.x)];
self.context.apply(self.operation, Fwd, &mut data)?;
Ok(coord! {x: data[0].x(), y: data[0].y()})
})?,
)
}
pub fn to_geodesic(&self, geometry: &Geometry) -> Result<Geometry, OMercError> {
Ok(
geometry.try_map_coords(|c: Coord<f64>| -> Result<_, GeodesyError> {
let mut data = [Coor2D::raw(c.x, c.y)];
self.context.apply(self.operation, Inv, &mut data)?;
let xy = data[0].xy_to_degrees();
Ok(coord! {x: xy.0, y: xy.1})
})?,
)
}
}
#[cfg(test)]
mod tests {
use super::*;
use assert_approx_eq::assert_approx_eq;
use geo::{Area, Centroid, Distance, Euclidean, polygon};
#[test]
fn roundtrip() {
let praca_se = Point::new(-46.632913, -23.550617);
let masp = Point::new(-46.655945, -23.561442);
let transformer = OMercTransformer::new(&praca_se).unwrap();
let c_masp = transformer.to_cartesian(&masp.into()).unwrap();
let g_masp: Point = transformer
.to_geodesic(&c_masp.into())
.unwrap()
.try_into()
.unwrap();
assert_approx_eq!(g_masp.x(), masp.x());
assert_approx_eq!(g_masp.y(), masp.y());
}
#[test]
fn distance_in_meters() {
let center_of_berlin = Point::new(13.409408, 52.520842);
let brandenburg_gate = Point::new(13.377701, 52.516262);
let transformer = OMercTransformer::new(¢er_of_berlin).unwrap();
let c_center_of_berlin = transformer.to_cartesian(¢er_of_berlin.into()).unwrap();
let c_brandenburg_gate = transformer.to_cartesian(&brandenburg_gate.into()).unwrap();
let distance = Euclidean.distance(&c_center_of_berlin, &c_brandenburg_gate);
assert_approx_eq!(distance, 2211.840487); }
#[test]
fn area_of_polygon() {
let museum_island = polygon![
(x: 13.393639, y: 52.522081),
(x: 13.397824, y: 52.519285),
(x: 13.403055, y: 52.512100),
(x: 13.405072, y: 52.511486),
(x: 13.411649, y: 52.513873),
(x: 13.406418, y: 52.514692),
(x: 13.401411, y: 52.519580),
(x: 13.398160, y: 52.521513),
(x: 13.393639, y: 52.522081),
];
let transformer = OMercTransformer::new(&museum_island.centroid().unwrap()).unwrap();
let c_museum_island = transformer.to_cartesian(&museum_island.into()).unwrap();
assert_approx_eq!(c_museum_island.unsigned_area(), 377902.962989); }
}