use crate::result::Result;
use crate::SweepProcessor;
use nexrad_model::data::{GateStatus, SweepField};
pub struct ThresholdFilter {
pub min: Option<f32>,
pub max: Option<f32>,
}
impl SweepProcessor for ThresholdFilter {
fn name(&self) -> &str {
"ThresholdFilter"
}
fn process(&self, input: &SweepField) -> Result<SweepField> {
let mut output = input.clone();
for az_idx in 0..output.azimuth_count() {
for gate_idx in 0..output.gate_count() {
let (val, status) = output.get(az_idx, gate_idx);
if status == GateStatus::Valid {
let below_min = self.min.is_some_and(|m| val < m);
let above_max = self.max.is_some_and(|m| val > m);
if below_min || above_max {
output.set(az_idx, gate_idx, 0.0, GateStatus::NoData);
}
}
}
}
Ok(output)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::SweepProcessor;
fn make_test_field() -> SweepField {
let mut field =
SweepField::new_empty("Test", "dBZ", 0.5, vec![0.0, 1.0, 2.0], 1.0, 2.0, 0.25, 5);
for az in 0..3 {
for gate in 0..5 {
let value = (az * 5 + gate) as f32 * 5.0; field.set(az, gate, value, GateStatus::Valid);
}
}
field
}
#[test]
fn test_threshold_min_only() {
let field = make_test_field();
let filter = ThresholdFilter {
min: Some(20.0),
max: None,
};
let result = filter.process(&field).unwrap();
for az in 0..3 {
for gate in 0..5 {
let original_value = (az * 5 + gate) as f32 * 5.0;
let (val, status) = result.get(az, gate);
if original_value < 20.0 {
assert_eq!(status, GateStatus::NoData);
assert_eq!(val, 0.0);
} else {
assert_eq!(status, GateStatus::Valid);
assert_eq!(val, original_value);
}
}
}
}
#[test]
fn test_threshold_max_only() {
let field = make_test_field();
let filter = ThresholdFilter {
min: None,
max: Some(40.0),
};
let result = filter.process(&field).unwrap();
for az in 0..3 {
for gate in 0..5 {
let original_value = (az * 5 + gate) as f32 * 5.0;
let (_, status) = result.get(az, gate);
if original_value > 40.0 {
assert_eq!(status, GateStatus::NoData);
} else {
assert_eq!(status, GateStatus::Valid);
}
}
}
}
#[test]
fn test_threshold_both_bounds() {
let field = make_test_field();
let filter = ThresholdFilter {
min: Some(15.0),
max: Some(50.0),
};
let result = filter.process(&field).unwrap();
for az in 0..3 {
for gate in 0..5 {
let original_value = (az * 5 + gate) as f32 * 5.0;
let (_, status) = result.get(az, gate);
if original_value < 15.0 || original_value > 50.0 {
assert_eq!(status, GateStatus::NoData);
} else {
assert_eq!(status, GateStatus::Valid);
}
}
}
}
#[test]
fn test_threshold_preserves_nodata() {
let mut field = make_test_field();
field.set(1, 2, 0.0, GateStatus::NoData);
let filter = ThresholdFilter {
min: Some(0.0),
max: None,
};
let result = filter.process(&field).unwrap();
let (_, status) = result.get(1, 2);
assert_eq!(status, GateStatus::NoData);
}
#[test]
fn test_threshold_no_bounds() {
let field = make_test_field();
let filter = ThresholdFilter {
min: None,
max: None,
};
let result = filter.process(&field).unwrap();
for az in 0..3 {
for gate in 0..5 {
let (orig_val, _) = field.get(az, gate);
let (result_val, result_status) = result.get(az, gate);
assert_eq!(result_val, orig_val);
assert_eq!(result_status, GateStatus::Valid);
}
}
}
}