Skip to main content

lerc_writer/lerc2/
options.rs

1use lerc_core::{Error, Result};
2
3/// Controls the accuracy and representation of a newly encoded LERC blob.
4#[derive(Debug, Clone, Copy, PartialEq)]
5#[non_exhaustive]
6pub struct EncodeOptions {
7    /// Maximum absolute reconstruction error for valid samples.
8    pub max_z_error: f64,
9    /// Width and height of the square micro-blocks used by tiled encoding.
10    pub micro_block_size: u32,
11    /// Optional per-sample no-data sentinel for rasters whose depth is greater than one.
12    pub no_data_value: Option<f64>,
13}
14
15impl EncodeOptions {
16    /// Creates lossless options with canonical 8-by-8 micro-blocks.
17    pub const fn new() -> Self {
18        Self {
19            max_z_error: 0.0,
20            micro_block_size: 8,
21            no_data_value: None,
22        }
23    }
24
25    /// Sets the requested maximum absolute reconstruction error.
26    pub const fn with_max_z_error(mut self, max_z_error: f64) -> Self {
27        self.max_z_error = max_z_error;
28        self
29    }
30
31    /// Sets the square micro-block size. Supported values are 2 through 32.
32    pub const fn with_micro_block_size(mut self, micro_block_size: u32) -> Self {
33        self.micro_block_size = micro_block_size;
34        self
35    }
36
37    /// Sets the no-data sentinel used by multidimensional rasters.
38    pub const fn with_no_data_value(mut self, no_data_value: f64) -> Self {
39        self.no_data_value = Some(no_data_value);
40        self
41    }
42
43    /// Removes a previously configured no-data sentinel.
44    pub const fn without_no_data_value(mut self) -> Self {
45        self.no_data_value = None;
46        self
47    }
48}
49
50impl Default for EncodeOptions {
51    fn default() -> Self {
52        Self::new()
53    }
54}
55
56pub(super) fn validate(options: EncodeOptions) -> Result<()> {
57    if !options.max_z_error.is_finite() || options.max_z_error < 0.0 {
58        return Err(Error::InvalidArgument(
59            "max_z_error must be finite and non-negative",
60        ));
61    }
62    if !(2..=32).contains(&options.micro_block_size) {
63        return Err(Error::InvalidArgument(
64            "micro_block_size must be in the range 2..=32",
65        ));
66    }
67    if options
68        .no_data_value
69        .is_some_and(|no_data_value| !no_data_value.is_finite())
70    {
71        return Err(Error::InvalidArgument(
72            "no_data_value must be finite when provided",
73        ));
74    }
75    Ok(())
76}