grib 0.18.2

GRIB format parser & writer for Rust
Documentation
use grib::{def::grib2, encoder, encoder::WriteGrib2Message as _};

#[allow(unused)]
fn grib2_message_from_references(
    discipline: u8,
    ident: &grib2::Section1Payload,
    product: &[u8],
    lats: &[f64],
    lngs: &[f64],
    values: &[f64],
) -> Result<Vec<u8>, String> {
    let grid = LatLngGridDef::try_from((lats, lngs))?;
    if values.len() != grid.num_points() {
        return Err(format!(
            "inconsistent numbers of points: values = {}, grid = {}",
            values.len(),
            grid.num_points()
        ));
    }
    let encoded = encoder::GpvEncoder::new(
        std::borrow::Cow::Borrowed(values),
        encoder::EncodingMethod::ComplexPacking(
            encoder::SimplePackingStrategy::Decimal(1),
            encoder::ComplexPackingStrategy::LookAhead(4),
            encoder::SpatialDifferencingOption::None,
        ),
    );

    let sect3_payload = grid.section3();
    let message = encoder::SingleGrib2Message::new(
        discipline,
        ident,
        None::<&[u8]>,
        sect3_payload,
        product,
        encoded,
    );
    let mut buf = vec![0; message.num_octets()];
    let mut _pos = 0;
    message.write(&mut buf).map_err(|_e| "unexpected error")?;

    Ok(buf)
}

struct LatLngGridDef(encoder::LatLonGrid);

impl LatLngGridDef {
    fn section3(&self) -> grib2::Section3Payload {
        grib2::Section3Payload {
            grid_def_source: 0,
            num_points: self.num_points() as u32,
            num_point_list_octets: 0,
            point_list_interpretation: 0,
            template_num: 0,
            template: grib2::GridDefinitionTemplate::_3_0(grib2::template::Template3_0 {
                earth: grib2::template::param_set::EarthShape {
                    shape: 6,
                    spherical_earth_radius: grib2::template::param_set::ScaledValue {
                        scale_factor: 0xff,
                        scaled_value: 0xffffffff,
                    },
                    major_axis: grib2::template::param_set::ScaledValue {
                        scale_factor: 0xff,
                        scaled_value: 0xffffffff,
                    },
                    minor_axis: grib2::template::param_set::ScaledValue {
                        scale_factor: 0xff,
                        scaled_value: 0xffffffff,
                    },
                },
                lat_lon: grib2::template::param_set::LatLonGrid::from(&self.0),
            }),
        }
    }

    fn num_points(&self) -> usize {
        self.0.shape.0 * self.0.shape.1
    }
}

impl TryFrom<(&[f64], &[f64])> for LatLngGridDef {
    type Error = String;

    fn try_from(value: (&[f64], &[f64])) -> Result<Self, Self::Error> {
        let (lats, lngs) = value;
        let ni = lngs.len();
        let nj = lats.len();
        if ni == 0 || nj == 0 {
            return Err(format!("invalid shape: ({ni}, {nj})"));
        }

        Ok(Self(encoder::LatLonGrid {
            shape: (ni, nj),
            first_point: (lats[0], lngs[0]),
            last_point: (*lats.last().unwrap(), *lngs.last().unwrap()),
            i_consecutive: true,
        }))
    }
}