use std::ops::Mul;
use geo_types::{Coord, Rect};
use crate::error::Error;
#[derive(Clone, PartialEq, Debug)]
pub struct Transform {
a: f64,
b: f64,
c: f64,
d: f64,
e: f64,
f: f64,
}
impl Transform {
#![allow(clippy::many_single_char_names)]
pub const fn new(a: f64, b: f64, c: f64, d: f64, e: f64, f: f64) -> Self {
Self { a, b, c, d, e, f }
}
pub const fn from_rasterio(transform: &[f64; 6]) -> Self {
Self::new(
transform[0],
transform[1],
transform[2],
transform[3],
transform[4],
transform[5],
)
}
pub const fn from_gdal(transform: &[f64; 6]) -> Self {
Self::new(
transform[1],
transform[2],
transform[0],
transform[4],
transform[5],
transform[3],
)
}
pub fn determinant(&self) -> f64 {
self.a * self.e - self.b * self.d
}
pub fn is_degenerate(&self) -> bool {
self.determinant() == 0.0
}
pub fn invert(&self) -> Result<Self, Error> {
if self.is_degenerate() {
return Err(Error::TransformNotInvertible);
}
let idet = 1.0 / self.determinant();
let ra = self.e * idet;
let rb = -self.b * idet;
let rd = -self.d * idet;
let re = self.a * idet;
Ok(Self::new(
ra,
rb,
-self.c * ra - self.f * rb,
rd,
re,
-self.c * rd - self.f * re,
))
}
pub fn transform_coordinate(&self, coordinate: &Coord<f64>) -> Coord<f64> {
Coord {
x: coordinate.x.mul_add(self.a, coordinate.y * self.b) + self.c,
y: coordinate.x.mul_add(self.d, coordinate.y * self.e) + self.f,
}
}
}
impl Mul<&Coord<f64>> for &Transform {
type Output = Coord<f64>;
fn mul(self, rhs: &Coord<f64>) -> Self::Output {
self.transform_coordinate(rhs)
}
}
impl Mul<Coord<f64>> for &Transform {
type Output = Coord<f64>;
fn mul(self, rhs: Coord<f64>) -> Self::Output {
self.transform_coordinate(&rhs)
}
}
impl Mul<&Rect<f64>> for &Transform {
type Output = Rect<f64>;
fn mul(self, rhs: &Rect<f64>) -> Self::Output {
Rect::new(self * rhs.min(), self * rhs.max())
}
}
#[cfg(test)]
mod tests {
use geo_types::Coord;
use crate::transform::Transform;
fn r_tiff_test_helper(gt: &Transform) {
let px_ul = Coord { x: 0., y: 0. };
let coord_ul = gt * px_ul;
assert_relative_eq!(coord_ul.x, 8.11377);
assert_relative_eq!(coord_ul.y, 49.40792);
let gt_inv = gt.invert().unwrap();
let px_ul_back = >_inv * coord_ul;
assert_relative_eq!(px_ul_back.x, 0.0);
assert_relative_eq!(px_ul_back.y, 0.0);
}
#[test]
fn test_r_tiff_from_gdal() {
let gt = Transform::from_gdal(&[
8.11377,
0.0011965049999999992,
0.0,
49.40792,
0.0,
-0.001215135,
]);
r_tiff_test_helper(>);
}
#[test]
fn test_r_tiff_from_rasterio() {
let gt = Transform::from_rasterio(&[
0.0011965049999999992,
0.0,
8.11377,
0.0,
-0.001215135,
49.40792,
]);
r_tiff_test_helper(>);
}
}