map_engine/
windows.rs

1//! Types and helpers to work with data windows.
2use crate::affine::GeoTransform;
3use serde::{Deserialize, Serialize};
4use std::cmp;
5use std::ops::Mul;
6
7/// A data window representing a section of a raster image
8#[derive(Debug, Default, PartialEq, Copy, Clone, Serialize, Deserialize)]
9pub struct Window {
10    pub col_off: isize,
11    pub row_off: isize,
12    pub width: usize,
13    pub height: usize,
14}
15
16impl Window {
17    /// Create a new window.
18    ///
19    /// # Argument
20    ///
21    /// * `col_off` - Column pixel index of the upper-left corner
22    /// * `row_off` - Row pixel index of the upper-left corner
23    /// * `width` - Window width in pixels
24    /// * `height` - Window height in pixels
25    pub fn new(col_off: isize, row_off: isize, width: usize, height: usize) -> Self {
26        Window {
27            col_off,
28            row_off,
29            width,
30            height,
31        }
32    }
33
34    /// Get the start and end pixel indices in the row and column space
35    pub fn toranges(self) -> ((isize, isize), (isize, isize)) {
36        (
37            (self.row_off, self.row_off + self.height as isize),
38            (self.col_off, self.col_off + self.width as isize),
39        )
40    }
41
42    /// Check if the window covers 0 pixels
43    pub fn is_zero(self) -> bool {
44        self.height == 0 || self.width == 0
45    }
46
47    /// Check if both windows intersect
48    pub fn intersects(&self, other: &Window) -> bool {
49        intersection(&[*self, *other]).is_some()
50    }
51
52    /// Get a GeoTransform relative to this window
53    pub fn geotransform(&self, geo: &GeoTransform) -> GeoTransform {
54        let (x, y) = geo * (self.col_off, self.row_off);
55        let c = geo.geo[2];
56        let f = geo.geo[5];
57        &GeoTransform::translation(x - c, y - f) * geo
58    }
59
60    /// Get the spatial bounds of the window
61    pub fn bounds(&self, geo: &GeoTransform) -> (f64, f64, f64, f64) {
62        let row_min = self.row_off;
63        let row_max = row_min + self.height as isize;
64        let col_min = self.col_off;
65        let col_max = col_min + self.width as isize;
66
67        let (left, bottom) = geo * (col_min, row_max);
68        let (right, top) = geo * (col_max, row_min);
69        (left, top, right, bottom)
70    }
71
72    pub fn bounds_lat_long(
73        &self,
74        spatial_ref_code: i32,
75        geo: &GeoTransform,
76    ) -> (f64, f64, f64, f64) {
77        let spatial_ref =
78            gdal::spatial_ref::SpatialRef::from_epsg(spatial_ref_code as u32).unwrap();
79        let wgs84_crs = gdal::spatial_ref::SpatialRef::from_epsg(4326).unwrap();
80        let vertex_trans =
81            gdal::spatial_ref::CoordTransform::new(&spatial_ref, &wgs84_crs).unwrap();
82        let (left, top, right, bottom) = self.bounds(geo);
83        let mut xs = [left, right];
84        let mut ys = [top, bottom];
85        let mut zs = [0.0f64; 2];
86        vertex_trans
87            .transform_coords(&mut xs, &mut ys, &mut zs)
88            .unwrap();
89        (xs[0], ys[0], xs[1], ys[1])
90    }
91
92    // pub fn from_slices(self, rows: (i32, i32), cols: (i32, i32), boudless: bool) -> Self {}
93    // pub fn from_bounds(
94    //     self,
95    //     left: f64,
96    //     bottom: f64,
97    //     right: f64,
98    //     top: f64,
99    //     transform: GeoTransform,
100    // ) -> Self {
101    //     let (row_start, col_start) = transform.rowcol(left, top);
102    //     let (row_stop, col_stop) = transform.rowcol(right, bottom);
103    //     Self::new(
104    //         row_start,
105    //         col_start,
106    //         col_stop - col_start,
107    //         row_stop - row_start,
108    //     )
109    // }
110}
111
112impl Mul<f64> for Window {
113    type Output = Window;
114
115    fn mul(self, rhs: f64) -> Window {
116        let extended_width = (self.width as f64 * rhs).ceil() as isize;
117        let extended_height = (self.height as f64 * rhs).ceil() as isize;
118        let col_shift = (extended_width - self.width as isize) / 2;
119        let row_shift = (extended_width - self.width as isize) / 2;
120        Window::new(
121            self.col_off - col_shift,
122            self.row_off - row_shift,
123            extended_width as usize,
124            extended_height as usize,
125        )
126    }
127}
128
129// Finds the intersection of 2 windows.
130//
131// This is a private function used as the inner block of the
132// reduce part of `intersection`.
133// Users of this API should use `intersection` function directly.
134fn intersection2(w0: Window, w1: Window) -> Window {
135    if w0.is_zero() || w1.is_zero() {
136        return Window::default();
137    }
138
139    let v0 = cmp::max(w0.col_off, w1.col_off);
140    let v1 = cmp::min(
141        w0.col_off + w0.width as isize,
142        w1.col_off + w1.width as isize,
143    );
144    if v0 >= v1 {
145        return Window::default();
146    }
147
148    let h0 = cmp::max(w0.row_off, w1.row_off);
149    let h1 = cmp::min(
150        w0.row_off + w0.height as isize,
151        w1.row_off + w1.height as isize,
152    );
153    if h0 >= h1 {
154        return Window::default();
155    }
156
157    Window {
158        col_off: v0,
159        row_off: h0,
160        width: (v1 - v0) as usize,
161        height: (h1 - h0) as usize,
162    }
163}
164
165/// Find the intersect between multiple [`Window`]s.
166///
167/// # Arguments
168///
169/// * `windows` - Windows to intersect.
170pub fn intersection(windows: &[Window]) -> Option<Window> {
171    match windows.iter().copied().reduce(intersection2) {
172        None => None,
173        Some(w) => {
174            if w == Window::default() {
175                None
176            } else {
177                Some(w)
178            }
179        }
180    }
181}
182
183#[test]
184fn test_intersection2() {
185    let w0 = Window::new(0, 0, 10, 10);
186    let w1 = Window::new(11, 11, 10, 10);
187    assert_eq!(intersection2(w0, w1), Window::new(0, 0, 0, 0));
188
189    let w1 = Window::new(11, 11, 10, 10);
190    let w0 = Window::new(0, 0, 10, 10);
191    assert_eq!(intersection2(w0, w1), Window::new(0, 0, 0, 0));
192
193    let w0 = Window::new(0, 0, 10, 10);
194    let w1 = Window::new(5, 5, 10, 10);
195    assert_eq!(intersection2(w0, w1), Window::new(5, 5, 5, 5));
196
197    let w0 = Window::new(5, 5, 10, 10);
198    let w1 = Window::new(5, 5, 10, 10);
199    assert_eq!(intersection2(w0, w1), Window::new(5, 5, 10, 10));
200
201    let w0 = Window::new(5, 5, 10, 10);
202    let w1 = Window::new(5, 5, 10, 10);
203    assert_eq!(intersection2(w0, w1), Window::new(5, 5, 10, 10));
204
205    let w0 = Window::new(0, 0, 0, 0);
206    let w1 = Window::new(0, 0, 0, 0);
207    assert_eq!(intersection2(w0, w1), Window::new(0, 0, 0, 0));
208}