oxigdal_algorithms/raster/
reclassify.rs1use crate::error::{AlgorithmError, Result};
4use oxigdal_core::buffer::RasterBuffer;
5
6#[derive(Debug, Clone, Copy)]
8pub struct ReclassRule {
9 pub min: f64,
11 pub max: f64,
13 pub new_value: f64,
15}
16
17impl ReclassRule {
18 #[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 #[must_use]
30 pub fn matches(&self, value: f64) -> bool {
31 value >= self.min && value <= self.max
32 }
33}
34
35pub 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); 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}