Skip to main content

nexrad_process/
lib.rs

1//! Processing algorithms for NEXRAD weather radar data.
2//!
3//! This crate provides processing traits and algorithms that operate on the field types
4//! defined in [`nexrad_model`]. The core abstraction is the [`SweepProcessor`] trait,
5//! which transforms one [`SweepField`] into another. Processors can be composed into
6//! pipelines using [`SweepPipeline`].
7//!
8//! # Architecture
9//!
10//! Three processing traits cover different scopes:
11//!
12//! - [`SweepProcessor`] — single-sweep algorithms (filtering, smoothing)
13//! - [`ScanProcessor`] — multi-elevation algorithms (velocity dealiasing)
14//! - [`ScanDerivedProduct`] — produces a [`CartesianField`] from a full scan
15//!   (composite reflectivity, VIL)
16//!
17//! # Example
18//!
19//! ```ignore
20//! use nexrad_process::{SweepPipeline, filter::ThresholdFilter};
21//! use nexrad_model::data::{SweepField, Product};
22//!
23//! let field = SweepField::from_radials(sweep.radials(), Product::Reflectivity).unwrap();
24//! let filtered = SweepPipeline::new()
25//!     .then(ThresholdFilter { min: Some(5.0), max: None })
26//!     .execute(&field)?;
27//! ```
28
29#![forbid(unsafe_code)]
30#![deny(clippy::unwrap_used)]
31#![deny(clippy::expect_used)]
32#![warn(clippy::correctness)]
33#![deny(missing_docs)]
34
35/// Derived products computed from radar scans.
36pub mod derived;
37/// Storm cell detection algorithms.
38pub mod detection;
39/// Filtering algorithms for sweep field data.
40pub mod filter;
41/// Composable processing pipelines.
42pub mod pipeline;
43/// Result and error types for processing operations.
44pub mod result;
45
46pub use pipeline::SweepPipeline;
47pub use result::{Error, Result};
48
49use nexrad_model::data::{CartesianField, Scan, SweepField};
50use nexrad_model::geo::{GeoExtent, RadarCoordinateSystem};
51
52/// Transforms one [`SweepField`] into another.
53///
54/// This is the primary processing trait for single-sweep algorithms such as
55/// filtering, smoothing, and clutter removal.
56pub trait SweepProcessor {
57    /// A human-readable name for this processor.
58    fn name(&self) -> &str;
59
60    /// Process the input field, producing a new field with the same geometry.
61    fn process(&self, input: &SweepField) -> Result<SweepField>;
62}
63
64/// Processes fields with full scan context (multiple elevations).
65///
66/// This trait is for algorithms that need to consider data across elevations,
67/// such as velocity dealiasing.
68///
69/// No built-in implementations are provided. This trait is an extension point
70/// for downstream crates to implement scan-level processing algorithms.
71pub trait ScanProcessor {
72    /// A human-readable name for this processor.
73    fn name(&self) -> &str;
74
75    /// Process sweep fields using the full scan context.
76    ///
77    /// Takes the scan metadata and all sweep fields for the relevant product,
78    /// returning a new set of processed fields (one per input field).
79    fn process_scan(&self, scan: &Scan, fields: &[SweepField]) -> Result<Vec<SweepField>>;
80}
81
82/// Produces a [`CartesianField`] from a full scan.
83///
84/// This trait is for derived products that combine data from multiple elevations
85/// into a single geographic surface, such as composite reflectivity, echo tops,
86/// and vertically integrated liquid (VIL).
87pub trait ScanDerivedProduct {
88    /// A human-readable name for this product.
89    fn name(&self) -> &str;
90
91    /// Compute the derived product from the scan.
92    ///
93    /// # Parameters
94    ///
95    /// - `scan` — Scan metadata (site info, VCP, etc.)
96    /// - `fields` — Sweep fields for the relevant product, one per elevation
97    /// - `coord_system` — Radar coordinate system for geographic projection
98    /// - `output_extent` — Geographic extent of the output grid
99    /// - `output_resolution` — (width, height) of the output grid in cells
100    fn compute(
101        &self,
102        scan: &Scan,
103        fields: &[SweepField],
104        coord_system: &RadarCoordinateSystem,
105        output_extent: &GeoExtent,
106        output_resolution: (usize, usize),
107    ) -> Result<CartesianField>;
108}