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 raw_expr = parser.parse()?;
57
58 let (expr, cache_slots) = Optimizer::optimize(raw_expr);
60
61 let evaluator = Evaluator::new(bands, &cache_slots);
63 let slot_count = cache_slots.len();
64
65 let mut result = RasterBuffer::zeros(width, height, bands[0].data_type());
66
67 for y in 0..height {
68 for x in 0..width {
69 let mut pixel_cache: Vec<Option<f64>> = vec![None; slot_count];
71 let value = evaluator.eval_pixel(&expr, x, y, &mut pixel_cache)?;
72 result
73 .set_pixel(x, y, value)
74 .map_err(AlgorithmError::Core)?;
75 }
76 }
77
78 Ok(result)
79 }
80
81 #[cfg(feature = "parallel")]
101 pub fn evaluate_parallel(expression: &str, bands: &[RasterBuffer]) -> Result<RasterBuffer> {
102 if bands.is_empty() {
103 return Err(AlgorithmError::EmptyInput {
104 operation: "evaluate_parallel",
105 });
106 }
107
108 let width = bands[0].width();
110 let height = bands[0].height();
111 for band in bands.iter().skip(1) {
112 if band.width() != width || band.height() != height {
113 return Err(AlgorithmError::InvalidDimensions {
114 message: "All bands must have same dimensions",
115 actual: band.width() as usize,
116 expected: width as usize,
117 });
118 }
119 }
120
121 let mut lexer = Lexer::new(expression);
123 let tokens = lexer.tokenize()?;
124
125 let mut parser = Parser::new(tokens);
127 let raw_expr = parser.parse()?;
128
129 let (expr, cache_slots) = Optimizer::optimize(raw_expr);
131
132 let evaluator = Evaluator::new(bands, &cache_slots);
134 let slot_count = cache_slots.len();
135
136 let mut result = RasterBuffer::zeros(width, height, bands[0].data_type());
138
139 let row_data: Result<Vec<Vec<f64>>> = (0..height)
141 .into_par_iter()
142 .map(|y| {
143 let mut row = Vec::with_capacity(width as usize);
144 for x in 0..width {
145 let mut pixel_cache: Vec<Option<f64>> = vec![None; slot_count];
146 let value = evaluator.eval_pixel(&expr, x, y, &mut pixel_cache)?;
147 row.push(value);
148 }
149 Ok(row)
150 })
151 .collect();
152
153 let row_data = row_data?;
154
155 for (y, row) in row_data.iter().enumerate() {
157 for (x, &value) in row.iter().enumerate() {
158 result
159 .set_pixel(x as u64, y as u64, value)
160 .map_err(AlgorithmError::Core)?;
161 }
162 }
163
164 Ok(result)
165 }
166
167 pub fn apply_binary(
169 a: &RasterBuffer,
170 b: &RasterBuffer,
171 op: RasterExpression,
172 ) -> Result<RasterBuffer> {
173 if a.width() != b.width() || a.height() != b.height() {
174 return Err(AlgorithmError::InvalidDimensions {
175 message: "Rasters must have same dimensions",
176 actual: a.width() as usize,
177 expected: b.width() as usize,
178 });
179 }
180
181 let mut result = RasterBuffer::zeros(a.width(), a.height(), a.data_type());
182
183 for y in 0..a.height() {
184 for x in 0..a.width() {
185 let val_a = a.get_pixel(x, y).map_err(AlgorithmError::Core)?;
186 let val_b = b.get_pixel(x, y).map_err(AlgorithmError::Core)?;
187
188 let val = match op {
189 RasterExpression::Add => val_a + val_b,
190 RasterExpression::Subtract => val_a - val_b,
191 RasterExpression::Multiply => val_a * val_b,
192 RasterExpression::Divide => {
193 if val_b.abs() < f64::EPSILON {
194 f64::NAN
195 } else {
196 val_a / val_b
197 }
198 }
199 RasterExpression::Max => val_a.max(val_b),
200 RasterExpression::Min => val_a.min(val_b),
201 };
202
203 result.set_pixel(x, y, val).map_err(AlgorithmError::Core)?;
204 }
205 }
206
207 Ok(result)
208 }
209
210 pub fn apply_unary<F>(src: &RasterBuffer, func: F) -> Result<RasterBuffer>
212 where
213 F: Fn(f64) -> f64,
214 {
215 let mut result = RasterBuffer::zeros(src.width(), src.height(), src.data_type());
216
217 for y in 0..src.height() {
218 for x in 0..src.width() {
219 let val = src.get_pixel(x, y).map_err(AlgorithmError::Core)?;
220 let new_val = func(val);
221 result
222 .set_pixel(x, y, new_val)
223 .map_err(AlgorithmError::Core)?;
224 }
225 }
226
227 Ok(result)
228 }
229}
230
231#[derive(Debug, Clone, Copy, PartialEq)]
233pub enum RasterExpression {
234 Add,
236 Subtract,
238 Multiply,
240 Divide,
242 Max,
244 Min,
246}