use crate::{geotransform, Dimension, Extent};
pub fn ncell(dim: &Dimension) -> usize {
dim[0] * dim[1]
}
pub fn cell_from_row_col(dim: &Dimension, row: usize, col: usize) -> Option<usize> {
if col >= dim[0] || row >= dim[1] {
return None;
}
Some(row * dim[0] + col)
}
pub fn row_from_cell(dim: &Dimension, cell: usize) -> usize {
cell / dim[0]
}
pub fn col_from_cell(dim: &Dimension, cell: usize) -> usize {
cell % dim[0]
}
pub fn row_col_from_cell(dim: &Dimension, cell: usize) -> (usize, usize) {
(row_from_cell(dim, cell), col_from_cell(dim, cell))
}
pub fn xy_from_cell(dim: &Dimension, extent: &Extent, cell: usize) -> (f64, f64) {
let gt = geotransform::extent_dim_to_gt(extent, dim);
let col = col_from_cell(dim, cell);
let row = row_from_cell(dim, cell);
geotransform::xy_from_col_row(>, col as f64, row as f64)
}
pub fn cell_from_xy(dim: &Dimension, extent: &Extent, x: f64, y: f64) -> Option<usize> {
if x < extent[0] || x > extent[1] || y < extent[2] || y > extent[3] {
return None;
}
let x_res = (extent[1] - extent[0]) / dim[0] as f64;
let y_res = (extent[3] - extent[2]) / dim[1] as f64;
let col = ((x - extent[0]) / x_res) as usize;
let row = ((extent[3] - y) / y_res) as usize;
let col = col.min(dim[0] - 1);
let row = row.min(dim[1] - 1);
Some(row * dim[0] + col)
}
pub fn x_centre(dim: &Dimension, extent: &Extent) -> Vec<f64> {
let gt = geotransform::extent_dim_to_gt(extent, dim);
(0..dim[0])
.map(|col| geotransform::x_from_col(>, col as f64))
.collect()
}
pub fn y_centre(dim: &Dimension, extent: &Extent) -> Vec<f64> {
let gt = geotransform::extent_dim_to_gt(extent, dim);
(0..dim[1])
.map(|row| geotransform::y_from_row(>, row as f64))
.collect()
}
pub fn x_corner(dim: &Dimension, extent: &Extent) -> Vec<f64> {
let res = geotransform::x_res(dim, extent);
(0..=dim[0])
.map(|i| extent[0] + i as f64 * res)
.collect()
}
pub fn y_corner(dim: &Dimension, extent: &Extent) -> Vec<f64> {
let res = geotransform::y_res(dim, extent);
(0..=dim[1])
.map(|i| extent[3] - i as f64 * res)
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn cell_row_col_roundtrip() {
let dim = [10, 8];
for cell in 0..ncell(&dim) {
let (row, col) = row_col_from_cell(&dim, cell);
assert_eq!(cell_from_row_col(&dim, row, col), Some(cell));
}
}
#[test]
fn xy_cell_roundtrip() {
let dim = [20, 15];
let extent = [-180.0, 180.0, -90.0, 90.0];
for cell in 0..ncell(&dim) {
let (x, y) = xy_from_cell(&dim, &extent, cell);
let cell2 = cell_from_xy(&dim, &extent, x, y);
assert_eq!(cell2, Some(cell), "failed for cell {cell}");
}
}
#[test]
fn cell_from_xy_outside() {
let dim = [10, 10];
let extent = [0.0, 10.0, 0.0, 10.0];
assert_eq!(cell_from_xy(&dim, &extent, -0.1, 5.0), None);
assert_eq!(cell_from_xy(&dim, &extent, 10.1, 5.0), None);
assert_eq!(cell_from_xy(&dim, &extent, 5.0, -0.1), None);
assert_eq!(cell_from_xy(&dim, &extent, 5.0, 10.1), None);
assert!(cell_from_xy(&dim, &extent, 0.0, 5.0).is_some()); assert!(cell_from_xy(&dim, &extent, 10.0, 5.0).is_some()); assert!(cell_from_xy(&dim, &extent, 5.0, 0.0).is_some()); assert!(cell_from_xy(&dim, &extent, 5.0, 10.0).is_some()); }
#[test]
fn corners_and_centres_consistent() {
let dim = [5, 3];
let extent = [0.0, 10.0, 0.0, 6.0];
let xc = x_corner(&dim, &extent);
let xm = x_centre(&dim, &extent);
for i in 0..dim[0] {
let mid = (xc[i] + xc[i + 1]) / 2.0;
assert!((xm[i] - mid).abs() < 1e-10);
}
let yc = y_corner(&dim, &extent);
let ym = y_centre(&dim, &extent);
for i in 0..dim[1] {
let mid = (yc[i] + yc[i + 1]) / 2.0;
assert!((ym[i] - mid).abs() < 1e-10);
}
}
}