oxigdal_algorithms/raster/calculator/
ops.rs1use super::{evaluator::Evaluator, lexer::Lexer, optimizer::Optimizer, parser::Parser};
4use crate::error::{AlgorithmError, Result};
5use oxigdal_core::buffer::RasterBuffer;
6
7#[cfg(feature = "parallel")]
8use rayon::prelude::*;
9
10pub struct RasterCalculator;
12
13impl RasterCalculator {
14 pub fn evaluate(expression: &str, bands: &[RasterBuffer]) -> Result<RasterBuffer> {
31 if bands.is_empty() {
32 return Err(AlgorithmError::EmptyInput {
33 operation: "evaluate",
34 });
35 }
36
37 let width = bands[0].width();
39 let height = bands[0].height();
40 for (_i, band) in bands.iter().enumerate().skip(1) {
41 if band.width() != width || band.height() != height {
42 return Err(AlgorithmError::InvalidDimensions {
43 message: "All bands must have same dimensions",
44 actual: band.width() as usize,
45 expected: width as usize,
46 });
47 }
48 }
49
50 let mut lexer = Lexer::new(expression);
52 let tokens = lexer.tokenize()?;
53
54 let mut parser = Parser::new(tokens);
56 let expr = parser.parse()?;
57
58 let expr = Optimizer::optimize(expr);
60
61 let evaluator = Evaluator::new(bands);
63 let mut result = RasterBuffer::zeros(width, height, bands[0].data_type());
64
65 for y in 0..height {
66 for x in 0..width {
67 let value = evaluator.eval_pixel(&expr, x, y)?;
68 result
69 .set_pixel(x, y, value)
70 .map_err(AlgorithmError::Core)?;
71 }
72 }
73
74 Ok(result)
75 }
76
77 #[cfg(feature = "parallel")]
97 pub fn evaluate_parallel(expression: &str, bands: &[RasterBuffer]) -> Result<RasterBuffer> {
98 if bands.is_empty() {
99 return Err(AlgorithmError::EmptyInput {
100 operation: "evaluate_parallel",
101 });
102 }
103
104 let width = bands[0].width();
106 let height = bands[0].height();
107 for band in bands.iter().skip(1) {
108 if band.width() != width || band.height() != height {
109 return Err(AlgorithmError::InvalidDimensions {
110 message: "All bands must have same dimensions",
111 actual: band.width() as usize,
112 expected: width as usize,
113 });
114 }
115 }
116
117 let mut lexer = Lexer::new(expression);
119 let tokens = lexer.tokenize()?;
120
121 let mut parser = Parser::new(tokens);
123 let expr = parser.parse()?;
124
125 let expr = Optimizer::optimize(expr);
127
128 let evaluator = Evaluator::new(bands);
130
131 let mut result = RasterBuffer::zeros(width, height, bands[0].data_type());
133
134 let row_data: Result<Vec<Vec<f64>>> = (0..height)
136 .into_par_iter()
137 .map(|y| {
138 let mut row = Vec::with_capacity(width as usize);
139 for x in 0..width {
140 let value = evaluator.eval_pixel(&expr, x, y)?;
141 row.push(value);
142 }
143 Ok(row)
144 })
145 .collect();
146
147 let row_data = row_data?;
148
149 for (y, row) in row_data.iter().enumerate() {
151 for (x, &value) in row.iter().enumerate() {
152 result
153 .set_pixel(x as u64, y as u64, value)
154 .map_err(AlgorithmError::Core)?;
155 }
156 }
157
158 Ok(result)
159 }
160
161 pub fn apply_binary(
163 a: &RasterBuffer,
164 b: &RasterBuffer,
165 op: RasterExpression,
166 ) -> Result<RasterBuffer> {
167 if a.width() != b.width() || a.height() != b.height() {
168 return Err(AlgorithmError::InvalidDimensions {
169 message: "Rasters must have same dimensions",
170 actual: a.width() as usize,
171 expected: b.width() as usize,
172 });
173 }
174
175 let mut result = RasterBuffer::zeros(a.width(), a.height(), a.data_type());
176
177 for y in 0..a.height() {
178 for x in 0..a.width() {
179 let val_a = a.get_pixel(x, y).map_err(AlgorithmError::Core)?;
180 let val_b = b.get_pixel(x, y).map_err(AlgorithmError::Core)?;
181
182 let val = match op {
183 RasterExpression::Add => val_a + val_b,
184 RasterExpression::Subtract => val_a - val_b,
185 RasterExpression::Multiply => val_a * val_b,
186 RasterExpression::Divide => {
187 if val_b.abs() < f64::EPSILON {
188 f64::NAN
189 } else {
190 val_a / val_b
191 }
192 }
193 RasterExpression::Max => val_a.max(val_b),
194 RasterExpression::Min => val_a.min(val_b),
195 };
196
197 result.set_pixel(x, y, val).map_err(AlgorithmError::Core)?;
198 }
199 }
200
201 Ok(result)
202 }
203
204 pub fn apply_unary<F>(src: &RasterBuffer, func: F) -> Result<RasterBuffer>
206 where
207 F: Fn(f64) -> f64,
208 {
209 let mut result = RasterBuffer::zeros(src.width(), src.height(), src.data_type());
210
211 for y in 0..src.height() {
212 for x in 0..src.width() {
213 let val = src.get_pixel(x, y).map_err(AlgorithmError::Core)?;
214 let new_val = func(val);
215 result
216 .set_pixel(x, y, new_val)
217 .map_err(AlgorithmError::Core)?;
218 }
219 }
220
221 Ok(result)
222 }
223}
224
225#[derive(Debug, Clone, Copy, PartialEq)]
227pub enum RasterExpression {
228 Add,
230 Subtract,
232 Multiply,
234 Divide,
236 Max,
238 Min,
240}