wxy_domain/affine.rs
1//! Affine transforms in GDAL geotransform order.
2
3/// `[x0, dx, rx, y0, ry, dy]`, as GDAL and vaster order them:
4///
5/// ```text
6/// x = x0 + col * dx + row * rx
7/// y = y0 + col * ry + row * dy
8/// ```
9///
10/// `(col, row)` are continuous index coordinates (0 is the outer edge of
11/// the first cell). Rotation and shear terms (`rx`, `ry`) are allowed.
12#[derive(Debug, Clone, Copy, PartialEq)]
13pub struct Affine(pub [f64; 6]);
14
15impl Affine {
16 /// From an extent `[xmin, xmax, ymin, ymax]` and dimension
17 /// `[ncol, nrow]`, north up (row 0 at `ymax`).
18 pub fn from_extent(extent: [f64; 4], dim: [usize; 2]) -> Self {
19 Affine(vaster::extent_dim_to_gt(&extent, &dim))
20 }
21
22 /// The extent `[xmin, xmax, ymin, ymax]` covered by `dim` cells, for an
23 /// axis-aligned transform.
24 pub fn extent(&self, dim: [usize; 2]) -> [f64; 4] {
25 vaster::gt_to_extent(&self.0, &dim)
26 }
27
28 pub fn forward(&self, col: f64, row: f64) -> (f64, f64) {
29 let g = &self.0;
30 (
31 g[0] + col * g[1] + row * g[2],
32 g[3] + col * g[4] + row * g[5],
33 )
34 }
35
36 pub fn inverse(&self) -> Option<Affine> {
37 vaster::inv_geotransform(&self.0).map(Affine)
38 }
39
40 pub fn is_axis_aligned(&self) -> bool {
41 self.0[2] == 0.0 && self.0[4] == 0.0
42 }
43}