Skip to main content

oxigdal_algorithms/raster/
reclassify.rs

1//! Raster reclassification
2
3use crate::error::{AlgorithmError, Result};
4use oxigdal_core::buffer::RasterBuffer;
5
6/// Reclassification rule
7#[derive(Debug, Clone, Copy)]
8pub struct ReclassRule {
9    /// Minimum value (inclusive)
10    pub min: f64,
11    /// Maximum value (inclusive)
12    pub max: f64,
13    /// New value for this range
14    pub new_value: f64,
15}
16
17impl ReclassRule {
18    /// Creates a new reclassification rule
19    #[must_use]
20    pub const fn new(min: f64, max: f64, new_value: f64) -> Self {
21        Self {
22            min,
23            max,
24            new_value,
25        }
26    }
27
28    /// Checks if a value falls within this rule's range
29    #[must_use]
30    pub fn matches(&self, value: f64) -> bool {
31        value >= self.min && value <= self.max
32    }
33}
34
35/// Reclassifies a raster based on rules
36pub fn reclassify(src: &RasterBuffer, rules: &[ReclassRule]) -> Result<RasterBuffer> {
37    if rules.is_empty() {
38        return Err(AlgorithmError::InvalidParameter {
39            parameter: "rules",
40            message: "At least one rule required".to_string(),
41        });
42    }
43
44    let mut result = RasterBuffer::zeros(src.width(), src.height(), src.data_type());
45
46    for y in 0..src.height() {
47        for x in 0..src.width() {
48            let val = src.get_pixel(x, y).map_err(AlgorithmError::Core)?;
49
50            let new_val = rules
51                .iter()
52                .find(|rule| rule.matches(val))
53                .map(|rule| rule.new_value)
54                .unwrap_or(val); // Keep original if no match
55
56            result
57                .set_pixel(x, y, new_val)
58                .map_err(AlgorithmError::Core)?;
59        }
60    }
61
62    Ok(result)
63}
64
65#[cfg(test)]
66mod tests {
67    use super::*;
68    use oxigdal_core::types::RasterDataType;
69
70    #[test]
71    fn test_reclassify() {
72        let mut src = RasterBuffer::zeros(5, 5, RasterDataType::Float32);
73        src.set_pixel(0, 0, 5.0).ok();
74        src.set_pixel(1, 1, 15.0).ok();
75
76        let rules = [
77            ReclassRule::new(0.0, 10.0, 1.0),
78            ReclassRule::new(10.0, 20.0, 2.0),
79        ];
80
81        let result = reclassify(&src, &rules);
82        assert!(result.is_ok());
83    }
84}