1use crate::errors::MapEngineError;
32use num_traits::AsPrimitive;
33use serde::{Deserialize, Serialize};
34use std::ops::Mul;
35
36#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
37pub struct GeoTransform {
38 pub geo: [f64; 6],
39}
40
41impl GeoTransform {
42 pub fn new(geo: &[f64; 6]) -> Self {
43 Self { geo: *geo }
44 }
45 #[allow(clippy::many_single_char_names)]
46 pub fn from_gdal(geo: &[f64; 6]) -> Self {
47 let c = geo[0];
48 let a = geo[1];
49 let b = geo[2];
50 let f = geo[3];
51 let d = geo[4];
52 let e = geo[5];
53 Self::new(&[a, b, c, d, e, f])
54 }
55
56 pub fn translation(tx: f64, ty: f64) -> Self {
57 let geo = [1.0, 0.0, tx, 0.0, 1.0, ty];
58 Self::new(&geo)
59 }
60
61 pub fn scale(sx: f64, sy: f64) -> Self {
62 let geo = [sx, 0.0, 0.0, 0.0, sy, 0.0];
63 Self::new(&geo)
64 }
65
66 pub fn shear(sx: f64, sy: f64) -> Self {
67 let geo = [
68 1.0,
69 sx.to_radians().tan(),
70 0.0,
71 sy.to_radians().tan(),
72 1.0,
73 0.0,
74 ];
75 Self::new(&geo)
76 }
77
78 #[allow(clippy::many_single_char_names)]
79 pub fn to_tuple(&self) -> (f64, f64, f64, f64, f64, f64) {
80 let a = self.geo[0];
81 let b = self.geo[1];
82 let c = self.geo[2];
83 let d = self.geo[3];
84 let e = self.geo[4];
85 let f = self.geo[5];
86 (a, b, c, d, e, f)
87 }
88
89 pub fn to_array(&self) -> [f64; 6] {
90 self.geo
91 }
92
93 pub fn inv(&self) -> Result<Self, MapEngineError> {
94 let (sa, sb, sc, sd, se, sf) = self.to_tuple();
95
96 let determinant = self.determinant();
97 if determinant == 0.0 {
98 return Err(MapEngineError::AffineError("Determinant is zero".into()));
99 }
100
101 let idet = 1.0 / determinant;
102
103 let ra = se * idet;
104 let rb = -sb * idet;
105 let rd = -sd * idet;
106 let re = sa * idet;
107
108 let geo = [ra, rb, -sc * ra - sf * rb, rd, re, -sc * rd - sf * re];
109 Ok(Self::new(&geo))
110 }
111
112 pub fn determinant(&self) -> f64 {
113 let (a, b, _, d, e, _) = self.to_tuple();
114 a * e - b * d
115 }
116
117 pub fn xy(&self, row: u32, col: u32) -> (f64, f64) {
118 let (coff, roff) = (0.5, 0.5);
119 let trans = Self::translation(coff, roff);
120 let tmp = self * &trans;
121 &tmp * (col as f64, row as f64)
122 }
123
124 pub fn rowcol(&self, x: f64, y: f64) -> Result<(i32, i32), MapEngineError> {
125 let eps = 2.220446049250313e-16;
126
127 let inv = self.inv()?;
128 let (fcol, frow) = &inv * (x + eps, y + eps);
129
130 Ok((frow.floor() as i32, fcol.floor() as i32))
131 }
132
133 #[allow(clippy::many_single_char_names)]
134 pub fn to_gdal(&self) -> Self {
135 let (a, b, c, d, e, f) = self.to_tuple();
136 let geo = [c, a, b, f, d, e];
137 Self::new(&geo)
138 }
139
140 pub fn xoff(&self) -> f64 {
141 self.geo[2]
142 }
143 pub fn yoff(&self) -> f64 {
144 self.geo[5]
145 }
146}
147
148impl Mul<&GeoTransform> for &GeoTransform {
149 type Output = GeoTransform;
150
151 fn mul(self, rhs: &GeoTransform) -> GeoTransform {
152 let (sa, sb, sc, sd, se, sf) = self.to_tuple();
153 let (oa, ob, oc, od, oe, of) = rhs.to_tuple();
154 let geo = [
155 sa * oa + sb * od,
156 sa * ob + sb * oe,
157 sa * oc + sb * of + sc,
158 sd * oa + se * od,
159 sd * ob + se * oe,
160 sd * oc + se * of + sf,
161 ];
162 GeoTransform::new(&geo)
163 }
164}
165
166impl<T> Mul<(T, T)> for &GeoTransform
167where
168 T: AsPrimitive<f64>,
169{
170 type Output = (f64, f64);
171
172 fn mul(self, rhs: (T, T)) -> Self::Output {
173 let (sa, sb, sc, sd, se, sf) = self.to_tuple();
174 let (vx, vy) = rhs;
175 let vx: f64 = vx.as_();
176 let vy: f64 = vy.as_();
177 (vx * sa + vy * sb + sc, vx * sd + vy * se + sf)
178 }
179}
180
181impl From<gdal::GeoTransform> for GeoTransform {
182 #[allow(clippy::many_single_char_names)]
183 fn from(geo: gdal::GeoTransform) -> Self {
184 let c = geo[0];
185 let a = geo[1];
186 let b = geo[2];
187 let f = geo[3];
188 let d = geo[4];
189 let e = geo[5];
190 Self::new(&[a, b, c, d, e, f])
191 }
192}