use crate::affine::GeoTransform;
use serde::{Deserialize, Serialize};
use std::cmp;
use std::ops::Mul;
#[derive(Debug, Default, PartialEq, Copy, Clone, Serialize, Deserialize)]
pub struct Window {
pub col_off: isize,
pub row_off: isize,
pub width: usize,
pub height: usize,
}
impl Window {
pub fn new(col_off: isize, row_off: isize, width: usize, height: usize) -> Self {
Window {
col_off,
row_off,
width,
height,
}
}
pub fn toranges(self) -> ((isize, isize), (isize, isize)) {
(
(self.row_off, self.row_off + self.height as isize),
(self.col_off, self.col_off + self.width as isize),
)
}
pub fn is_zero(self) -> bool {
self.height == 0 || self.width == 0
}
pub fn intersects(&self, other: &Window) -> bool {
intersection(&[*self, *other]).is_some()
}
pub fn geotransform(&self, geo: &GeoTransform) -> GeoTransform {
let (x, y) = geo * (self.col_off, self.row_off);
let c = geo.geo[2];
let f = geo.geo[5];
&GeoTransform::translation(x - c, y - f) * geo
}
pub fn bounds(&self, geo: &GeoTransform) -> (f64, f64, f64, f64) {
let row_min = self.row_off;
let row_max = row_min + self.height as isize;
let col_min = self.col_off;
let col_max = col_min + self.width as isize;
let (left, bottom) = geo * (col_min, row_max);
let (right, top) = geo * (col_max, row_min);
(left, top, right, bottom)
}
pub fn bounds_lat_long(
&self,
spatial_ref_code: i32,
geo: &GeoTransform,
) -> (f64, f64, f64, f64) {
let spatial_ref =
gdal::spatial_ref::SpatialRef::from_epsg(spatial_ref_code as u32).unwrap();
let wgs84_crs = gdal::spatial_ref::SpatialRef::from_epsg(4326).unwrap();
let vertex_trans =
gdal::spatial_ref::CoordTransform::new(&spatial_ref, &wgs84_crs).unwrap();
let (left, top, right, bottom) = self.bounds(geo);
let mut xs = [left, right];
let mut ys = [top, bottom];
let mut zs = [0.0f64; 2];
vertex_trans
.transform_coords(&mut xs, &mut ys, &mut zs)
.unwrap();
(xs[0], ys[0], xs[1], ys[1])
}
}
impl Mul<f64> for Window {
type Output = Window;
fn mul(self, rhs: f64) -> Window {
let extended_width = (self.width as f64 * rhs).ceil() as isize;
let extended_height = (self.height as f64 * rhs).ceil() as isize;
let col_shift = (extended_width - self.width as isize) / 2;
let row_shift = (extended_width - self.width as isize) / 2;
Window::new(
self.col_off - col_shift,
self.row_off - row_shift,
extended_width as usize,
extended_height as usize,
)
}
}
fn intersection2(w0: Window, w1: Window) -> Window {
if w0.is_zero() || w1.is_zero() {
return Window::default();
}
let v0 = cmp::max(w0.col_off, w1.col_off);
let v1 = cmp::min(
w0.col_off + w0.width as isize,
w1.col_off + w1.width as isize,
);
if v0 >= v1 {
return Window::default();
}
let h0 = cmp::max(w0.row_off, w1.row_off);
let h1 = cmp::min(
w0.row_off + w0.height as isize,
w1.row_off + w1.height as isize,
);
if h0 >= h1 {
return Window::default();
}
Window {
col_off: v0,
row_off: h0,
width: (v1 - v0) as usize,
height: (h1 - h0) as usize,
}
}
pub fn intersection(windows: &[Window]) -> Option<Window> {
match windows.iter().copied().reduce(intersection2) {
None => None,
Some(w) => {
if w == Window::default() {
None
} else {
Some(w)
}
}
}
}
#[test]
fn test_intersection2() {
let w0 = Window::new(0, 0, 10, 10);
let w1 = Window::new(11, 11, 10, 10);
assert_eq!(intersection2(w0, w1), Window::new(0, 0, 0, 0));
let w1 = Window::new(11, 11, 10, 10);
let w0 = Window::new(0, 0, 10, 10);
assert_eq!(intersection2(w0, w1), Window::new(0, 0, 0, 0));
let w0 = Window::new(0, 0, 10, 10);
let w1 = Window::new(5, 5, 10, 10);
assert_eq!(intersection2(w0, w1), Window::new(5, 5, 5, 5));
let w0 = Window::new(5, 5, 10, 10);
let w1 = Window::new(5, 5, 10, 10);
assert_eq!(intersection2(w0, w1), Window::new(5, 5, 10, 10));
let w0 = Window::new(5, 5, 10, 10);
let w1 = Window::new(5, 5, 10, 10);
assert_eq!(intersection2(w0, w1), Window::new(5, 5, 10, 10));
let w0 = Window::new(0, 0, 0, 0);
let w1 = Window::new(0, 0, 0, 0);
assert_eq!(intersection2(w0, w1), Window::new(0, 0, 0, 0));
}