eorst 1.0.1

Earth Observation and Remote Sensing Toolkit - library for raster processing pipelines
//! Geospatial and array shape types.
//!
//! This module contains all the fundamental data structures for describing
//! raster dimensions, coordinates, geotransforms, and block configurations.

use clap::ValueEnum;
use serde::{Deserialize, Serialize};

/// Shape of a 4D raster array (times, layers, rows, cols).
#[derive(PartialEq, Debug, Clone, Copy)]
pub struct RasterDataShape {
    /// Number of time steps.
    pub times: usize,
    /// Number of layers/bands.
    pub layers: usize,
    /// Number of rows.
    pub rows: usize,
    /// Number of columns.
    pub cols: usize,
}

/// Dimension along which to stack or layer data.
#[derive(Debug, std::cmp::PartialEq, Clone, Copy, Default)]
pub enum Dimension {
    /// Stack along the layer dimension.
    #[default]
    Layer,
    /// Stack along the time dimension.
    Time,
}

/// Image resolution (pixel size in georeferenced units).
#[derive(Debug, Default, PartialEq, Clone, Copy)]
pub struct ImageResolution {
    /// X resolution (pixel width).
    pub x: f64,
    /// Y resolution (pixel height, typically negative for north-up).
    pub y: f64,
}

/// Image dimensions in pixels.
#[derive(Default, Debug, PartialEq, Clone, Copy)]
pub struct ImageSize {
    /// Number of rows (height).
    pub rows: usize,
    /// Number of columns (width).
    pub cols: usize,
}

/// Offset in array coordinates.
#[derive(PartialEq, Debug, Clone, Copy)]
pub struct Offset {
    /// Row offset.
    pub rows: isize,
    /// Column offset.
    pub cols: isize,
}

/// Size in array coordinates.
#[derive(PartialEq, Debug, Clone, Copy)]
pub struct Size {
    /// Number of rows.
    pub rows: isize,
    /// Number of columns.
    pub cols: isize,
}

/// Block size for chunked raster operations.
#[derive(Debug, PartialEq, Clone, Copy)]
pub struct BlockSize {
    /// Number of rows per block.
    pub rows: usize,
    /// Number of columns per block.
    pub cols: usize,
}

impl Default for BlockSize {
    fn default() -> Self {
        BlockSize {
            rows: 1024,
            cols: 1024,
        }
    }
}

/// Geotransform parameters defining the affine transformation from pixel to georeferenced coordinates.
#[derive(Default, Debug, PartialEq, Clone, Copy)]
pub struct GeoTransform {
    /// X coordinate of the upper-left pixel center.
    pub x_ul: f64,
    /// X resolution (pixel width).
    pub x_res: f64,
    /// X rotation (typically 0).
    pub x_rot: f64,
    /// Y coordinate of the upper-left pixel center.
    pub y_ul: f64,
    /// Y rotation (typically 0).
    pub y_rot: f64,
    /// Y resolution (pixel height, typically negative).
    pub y_res: f64,
}

impl GeoTransform {
    /// Converts the GeoTransform to a 6-element array.
    /// Array format: [x_ul, x_res, x_rot, y_ul, y_rot, y_res].
    pub fn to_array(&self) -> [f64; 6] {
        let array: [f64; 6] = [
            self.x_ul, self.x_res, self.x_rot, self.y_ul, self.y_rot, self.y_res,
        ];
        array
    }
}

/// Window for reading raster data.
#[derive(PartialEq, Debug, Clone, Copy)]
pub struct ReadWindow {
    /// Offset from which to start reading.
    pub offset: Offset,
    /// Size of the window to read.
    pub size: Size,
}

/// Overlap size for block-based operations (padding around each block).
#[derive(Debug, PartialEq, Clone, Copy, Default)]
pub struct Overlap {
    /// Number of pixels to pad on the left side.
    pub left: usize,
    /// Number of pixels to pad on the top side.
    pub top: usize,
    /// Number of pixels to pad on the right side.
    pub right: usize,
    /// Number of pixels to pad on the bottom side.
    pub bottom: usize,
}

/// Sampling method for aggregating raster values.
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize, ValueEnum)]
pub enum SamplingMethod {
    /// Use the actual value.
    Value,
    /// Use the most common value (mode).
    Mode,
    /// Use the minimum value.
    Min,
    /// Use the standard deviation.
    StdDev,
    /// Use the average value.
    Avg,
}

/// 2D index into an array (column, row).
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct Index2d {
    /// Column index.
    pub col: usize,
    /// Row index.
    pub row: usize,
}

/// x and y coordinates of a location.
#[derive(Default, Debug, PartialEq, Clone, Copy)]
pub struct Coordinates {
    pub(crate) x: f64,
    pub(crate) y: f64,
}

/// Rectangle for array indexing (distances from a center point).
#[derive(Debug, Copy, Clone)]
pub struct Rectangle {
    /// Number of columns to the left of the center.
    pub left: usize,
    /// Number of rows above the center.
    pub top: usize,
    /// Number of columns to the right of the center.
    pub right: usize,
    /// Number of rows below the center.
    pub bottom: usize,
}

/// Coordinate value type for spatial coordinates.
#[derive(Debug, PartialEq)]
pub enum CoordType {
    /// Integer coordinate value.
    EInt(i64),
    /// Floating-point coordinate value.
    EFloat(f32),
    /// String coordinate value.
    EString(String),
}

/// Output format for raster write operations.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, strum::EnumString)]
#[strum(ascii_case_insensitive)]
pub enum OutputFormat {
    /// Simple tiled GeoTIFF (fast, single-pass, no overviews).
    #[default]
    #[strum(serialize = "geotiff")]
    GeoTiff,
    /// Tiled GeoTIFF with overviews built in-place after writing.
    #[strum(serialize = "geotiff-overviews", serialize = "geotiff_overviews")]
    GeoTiffOverviews,
    /// Cloud Optimized GeoTIFF (overviews + IFD reordering via gdal_translate).
    #[strum(serialize = "cog")]
    COG,
}

/// Configuration for output file generation.
#[derive(Debug, Clone)]
pub struct OutputConfig {
    /// Output format controlling overviews and COG compliance.
    pub format: OutputFormat,
    /// Compression for the final output (e.g., "LZW", "DEFLATE", "ZSTD").
    pub compression: String,
    /// Resampling method for overviews (e.g., "CUBIC", "NEAREST", "AVERAGE").
    pub overview_resampling: String,
    /// Overview decimation levels (e.g., \[2, 4, 8, 16, 32\]).
    pub overview_levels: Vec<i32>,
}

impl Default for OutputConfig {
    fn default() -> Self {
        Self {
            format: OutputFormat::GeoTiff,
            compression: "LZW".to_string(),
            overview_resampling: "CUBIC".to_string(),
            overview_levels: vec![2, 4, 8, 16, 32],
        }
    }
}