gistools/readers/grib2/sections/_3/
tables.rs

1#![cfg_attr(feature = "nightly", coverage(off))]
2
3/// # Table 3.0 - Source of Grid Definition
4///
5/// **Details**:
6/// - **Section**: 3
7/// - **Octet**: 6
8///
9/// **Reserved Ranges**:
10/// - `2-191`: Reserved
11/// - `192-254`: Reserved for Local Use
12///
13/// **Special Value**:
14/// - `255`: A grid definition does not apply to this product.
15///
16/// ## Description
17/// This table specifies the source of grid definitions used in GRIB2 files,
18/// providing context for how the grid is defined, whether through predefined templates or originating centers.
19///
20/// ## Links
21/// - [Read more...](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table3-0.shtml)
22///
23/// ## Notes
24/// - Created 05/11/2005
25#[repr(u8)]
26#[allow(missing_docs)]
27#[derive(Debug, Clone, Copy, PartialEq, Eq)]
28pub enum Grib2Table3_0 {
29    SpecifiedInCodeTable3_1 = 0,
30    PredeterminedGridDefinitionDefinedByOriginatingCenter = 1,
31    AGridDefinitionDoesNotApplyToThisProduct = 255,
32}
33impl From<u8> for Grib2Table3_0 {
34    fn from(val: u8) -> Self {
35        match val {
36            0 => Self::SpecifiedInCodeTable3_1,
37            1 => Self::PredeterminedGridDefinitionDefinedByOriginatingCenter,
38            _ => Self::AGridDefinitionDoesNotApplyToThisProduct,
39        }
40    }
41}
42impl core::fmt::Display for Grib2Table3_0 {
43    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
44        let desc = match self {
45            Self::SpecifiedInCodeTable3_1 => "Specified in Code Table 3.1",
46            Self::PredeterminedGridDefinitionDefinedByOriginatingCenter => {
47                "Predetermined Grid Definition - Defined by Originating Center"
48            }
49            Self::AGridDefinitionDoesNotApplyToThisProduct => {
50                "A grid definition does not apply to this product."
51            }
52        };
53        f.write_str(desc)
54    }
55}
56
57/// # Table 3.1 - Grid Definition Template Number
58///
59/// **Details**:
60/// - **Section**: 3
61/// - **Octet**: 13-14
62///
63/// **Reserved Ranges**:
64/// - `3-32767`: Reserved
65/// - `32768-65534`: Reserved for Local Use
66///
67/// **Special Value**:
68/// - `65535`: Missing
69///
70/// ## Description
71/// This table enumerates the grid definition templates used in GRIB2 files,
72/// providing detailed classifications for various grid types, projections, and modeling subdomains.
73///
74/// ## Links
75/// - [Read more...](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table3-1.shtml)
76///
77/// ## Notes
78/// - Revised 12/07/2023
79/// - (1). WGS84 is a geodetic system that uses IAG-GRS80 as a basis.
80/// - (2). With respect to code figures 0, 1, 3, 6, and 7, coordinates can only be unambiguously interpreted if the coordinate reference system in which they are embedded is known. Therefore, defining the shape of the Earth alone without coordinate system axis origins is ambiguous. Generally, the prime meridian defined in the geodetic system WGS-84 can be safely assumed to be the longitudinal origin. However, because these code figures do not specify the longitudinal origin explicitly, it is suggested to contact the originating center if high precision coordinates are needed to obtain the precise details of the coordinate system used (effective as from 16 November 2016).
81#[repr(u16)]
82#[allow(missing_docs)]
83#[derive(Debug, Clone, Copy, PartialEq, Eq)]
84pub enum Grib2Table3_1 {
85    LatitudeLongitude = 0,
86    RotatedLatitudeLongitude = 1,
87    StretchedLatitudeLongitude = 2,
88    RotatedAndStretchedLatitudeLongitude = 3,
89    VariableResolutionLatitudeLongitude = 4,
90    VariableResolutionRotatedLatitudeLongitude = 5,
91    Mercator = 10,
92    Reserved11 = 11, // Explicitly reserved value
93    TransverseMercator = 12,
94    MercatorWithModellingSubdomainsDefinition = 13,
95    PolarStereographicProjection = 20,
96    PolarStereographicWithModellingSubdomainsDefinition = 23,
97    LambertConformal = 30,
98    AlbersEqualArea = 31,
99    Reserved32 = 32, // Explicitly reserved value
100    LambertConformalWithModellingSubdomainsDefinition = 33,
101    GaussianLatitudeLongitude = 40,
102    RotatedGaussianLatitudeLongitude = 41,
103    StretchedGaussianLatitudeLongitude = 42,
104    RotatedAndStretchedGaussianLatitudeLongitude = 43,
105    SphericalHarmonicCoefficients = 50,
106    RotatedSphericalHarmonicCoefficients = 51,
107    StretchedSphericalHarmonicCoefficients = 52,
108    RotatedAndStretchedSphericalHarmonicCoefficients = 53,
109    CubedSphereGnomonic = 60,
110    SpectralMercatorWithModellingSubdomainsDefinition = 61,
111    SpectralPolarStereographicWithModellingSubdomainsDefinition = 62,
112    SpectralLambertConformalWithModellingSubdomainsDefinition = 63,
113    SpaceViewPerspectiveOrOrthographic = 90,
114    TriangularGridBasedOnAnIcosahedron = 100,
115    GeneralUnstructuredGrid = 101,
116    EquatorialAzimuthalEquidistantProjection = 110,
117    AzimuthRangeProjection = 120,
118    LambertAzimuthalEqualAreaProjection = 140,
119    HierarchicalEqualAreaIsoLatitudePixelizationGridHealpix = 150,
120    CurvilinearOrthogonalGrids = 204,
121    CrossSectionGridWithPointsEquallySpacedOnTheHorizontal = 1000,
122    HovmollerDiagramWithPointsEquallySpacedOnTheHorizontal = 1100,
123    TimeSectionGrid = 1200,
124    RotatedLatitudeLongitudeArakawaStaggeredEGrid = 32768,
125    RotatedLatitudeLongitudeArakawaNonEStaggeredGrid = 32769,
126    Missing = 65535,
127}
128impl From<u16> for Grib2Table3_1 {
129    fn from(val: u16) -> Self {
130        match val {
131            0 => Self::LatitudeLongitude,
132            1 => Self::RotatedLatitudeLongitude,
133            2 => Self::StretchedLatitudeLongitude,
134            3 => Self::RotatedAndStretchedLatitudeLongitude,
135            4 => Self::VariableResolutionLatitudeLongitude,
136            5 => Self::VariableResolutionRotatedLatitudeLongitude,
137            10 => Self::Mercator,
138            11 => Self::Reserved11,
139            12 => Self::TransverseMercator,
140            13 => Self::MercatorWithModellingSubdomainsDefinition,
141            20 => Self::PolarStereographicProjection,
142            23 => Self::PolarStereographicWithModellingSubdomainsDefinition,
143            30 => Self::LambertConformal,
144            31 => Self::AlbersEqualArea,
145            32 => Self::Reserved32,
146            33 => Self::LambertConformalWithModellingSubdomainsDefinition,
147            40 => Self::GaussianLatitudeLongitude,
148            41 => Self::RotatedGaussianLatitudeLongitude,
149            42 => Self::StretchedGaussianLatitudeLongitude,
150            43 => Self::RotatedAndStretchedGaussianLatitudeLongitude,
151            50 => Self::SphericalHarmonicCoefficients,
152            51 => Self::RotatedSphericalHarmonicCoefficients,
153            52 => Self::StretchedSphericalHarmonicCoefficients,
154            53 => Self::RotatedAndStretchedSphericalHarmonicCoefficients,
155            60 => Self::CubedSphereGnomonic,
156            61 => Self::SpectralMercatorWithModellingSubdomainsDefinition,
157            62 => Self::SpectralPolarStereographicWithModellingSubdomainsDefinition,
158            63 => Self::SpectralLambertConformalWithModellingSubdomainsDefinition,
159            90 => Self::SpaceViewPerspectiveOrOrthographic,
160            100 => Self::TriangularGridBasedOnAnIcosahedron,
161            101 => Self::GeneralUnstructuredGrid,
162            110 => Self::EquatorialAzimuthalEquidistantProjection,
163            120 => Self::AzimuthRangeProjection,
164            140 => Self::LambertAzimuthalEqualAreaProjection,
165            150 => Self::HierarchicalEqualAreaIsoLatitudePixelizationGridHealpix,
166            204 => Self::CurvilinearOrthogonalGrids,
167            1000 => Self::CrossSectionGridWithPointsEquallySpacedOnTheHorizontal,
168            1100 => Self::HovmollerDiagramWithPointsEquallySpacedOnTheHorizontal,
169            1200 => Self::TimeSectionGrid,
170            32768 => Self::RotatedLatitudeLongitudeArakawaStaggeredEGrid,
171            32769 => Self::RotatedLatitudeLongitudeArakawaNonEStaggeredGrid,
172            _ => Self::Missing,
173        }
174    }
175}
176impl core::fmt::Display for Grib2Table3_1 {
177    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
178        let desc = match self {
179            Self::LatitudeLongitude => {
180                "Latitude/Longitude (See Template 3.0) Also called Equidistant Cylindrical or \
181                 Plate Caree"
182            }
183            Self::RotatedLatitudeLongitude => "Rotated Latitude/Longitude (See Template 3.1)",
184            Self::StretchedLatitudeLongitude => "Stretched Latitude/Longitude (See Template 3.2)",
185            Self::RotatedAndStretchedLatitudeLongitude => {
186                "Rotated and Stretched Latitude/Longitude (See Template 3.3)"
187            }
188            Self::VariableResolutionLatitudeLongitude => {
189                "Variable Resolution Latitude/longitude (See Template 3.4)"
190            }
191            Self::VariableResolutionRotatedLatitudeLongitude => {
192                "Variable Resolution Rotated Latitude/longitude (See Template 3.5)"
193            }
194            Self::Mercator => "Mercator (See Template 3.10)",
195            Self::Reserved11 => "Reserved",
196            Self::TransverseMercator => "Transverse Mercator (See Template 3.12)",
197            Self::MercatorWithModellingSubdomainsDefinition => {
198                "Mercator with modelling subdomains definition (See Template 3.13)"
199            }
200            Self::PolarStereographicProjection => {
201                "Polar Stereographic Projection (Can be North or South) (See Template 3.20)"
202            }
203            Self::PolarStereographicWithModellingSubdomainsDefinition => {
204                "Polar Stereographic with modelling subdomains definition (See Template 3.23)"
205            }
206            Self::LambertConformal => {
207                "Lambert Conformal (Can be Secant, Tangent, Conical, or Bipolar) (See Template \
208                 3.30)"
209            }
210            Self::AlbersEqualArea => "Albers Equal Area (See Template 3.31)",
211            Self::Reserved32 => "Reserved",
212            Self::LambertConformalWithModellingSubdomainsDefinition => {
213                "Lambert conformal with modelling subdomains definition (See Template 3.33)"
214            }
215            Self::GaussianLatitudeLongitude => "Gaussian Latitude/Longitude (See Template 3.40)",
216            Self::RotatedGaussianLatitudeLongitude => {
217                "Rotated Gaussian Latitude/Longitude (See Template 3.41)"
218            }
219            Self::StretchedGaussianLatitudeLongitude => {
220                "Stretched Gaussian Latitude/Longitude (See Template 3.42)"
221            }
222            Self::RotatedAndStretchedGaussianLatitudeLongitude => {
223                "Rotated and Stretched Gaussian Latitude/Longitude (See Template 3.43)"
224            }
225            Self::SphericalHarmonicCoefficients => {
226                "Spherical Harmonic Coefficients (See Template 3.50)"
227            }
228            Self::RotatedSphericalHarmonicCoefficients => {
229                "Rotated Spherical Harmonic Coefficients (See Template 3.51)"
230            }
231            Self::StretchedSphericalHarmonicCoefficients => {
232                "Stretched Spherical Harmonic Coefficients (See Template 3.52)"
233            }
234            Self::RotatedAndStretchedSphericalHarmonicCoefficients => {
235                "Rotated and Stretched Spherical Harmonic Coefficients (See Template 3.53)"
236            }
237            Self::CubedSphereGnomonic => "Cubed-Sphere Gnomonic (See Template 3.60) Validation",
238            Self::SpectralMercatorWithModellingSubdomainsDefinition => {
239                "Spectral Mercator with modelling subdomains definition (See Template 3.61)"
240            }
241            Self::SpectralPolarStereographicWithModellingSubdomainsDefinition => {
242                "Spectral Polar Stereographic with modelling subdomains definition (See Template \
243                 3.62)"
244            }
245            Self::SpectralLambertConformalWithModellingSubdomainsDefinition => {
246                "Spectral Lambert conformal with modelling subdomains definition (See Template \
247                 3.63)"
248            }
249            Self::SpaceViewPerspectiveOrOrthographic => {
250                "Space View Perspective or Orthographic (See Template 3.90)"
251            }
252            Self::TriangularGridBasedOnAnIcosahedron => {
253                "Triangular Grid Based on an Icosahedron (See Template 3.100)"
254            }
255            Self::GeneralUnstructuredGrid => "General Unstructured Grid (see Template 3.101)",
256            Self::EquatorialAzimuthalEquidistantProjection => {
257                "Equatorial Azimuthal Equidistant Projection (See Template 3.110)"
258            }
259            Self::AzimuthRangeProjection => "Azimuth-Range Projection (See Template 3.120)",
260            Self::LambertAzimuthalEqualAreaProjection => {
261                "Lambert Azimuthal Equal Area Projection (See Template 3.140)"
262            }
263            Self::HierarchicalEqualAreaIsoLatitudePixelizationGridHealpix => {
264                "Hierarchical Equal Area isoLatitude Pixelization grid (HEALPix) (See Template \
265                 3.150)"
266            }
267            Self::CurvilinearOrthogonalGrids => "Curvilinear Orthogonal Grids (See Template 3.204)",
268            Self::CrossSectionGridWithPointsEquallySpacedOnTheHorizontal => {
269                "Cross Section Grid with Points Equally Spaced on the Horizontal (See Template \
270                 3.1000)"
271            }
272            Self::HovmollerDiagramWithPointsEquallySpacedOnTheHorizontal => {
273                "Hovmoller Diagram with Points Equally Spaced on the Horizontal (See Template \
274                 3.1100)"
275            }
276            Self::TimeSectionGrid => "Time Section Grid (See Template 3.1200)",
277            Self::RotatedLatitudeLongitudeArakawaStaggeredEGrid => {
278                "Rotated Latitude/Longitude (Arakawa Staggered E-Grid) (See Template 3.32768)"
279            }
280            Self::RotatedLatitudeLongitudeArakawaNonEStaggeredGrid => {
281                "Rotated Latitude/Longitude (Arakawa Non-E Staggered Grid) (See Template 3.32769)"
282            }
283            Self::Missing => "Missing",
284        };
285        f.write_str(desc)
286    }
287}
288
289/// # Table 3.2 - Shape of the Reference System
290///
291/// **Details**:
292/// - **Section**: 3
293/// - **Octet**: 15
294///
295/// **Reserved Ranges**:
296/// - `12-191`: Reserved
297/// - `192-254`: Reserved for Local Use
298///
299/// **Special Value**:
300/// - `255`: Missing
301///
302/// ## Description
303/// This table defines the shape of the reference system used in GRIB2 files,
304/// providing context for interpreting the Earth's shape and the coordinate reference system.
305///
306/// ## Links
307/// - [Read more...](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table3-2.shtml)
308///
309/// ## Notes
310/// - (1) WGS84 is a geodetic system that uses IAG-GRS80 as a basis.
311/// - (2) With respect to code figures 0, 1, 3, 6, and 7, coordinates can only be unambiguously interpreted if the coordinate reference system in which they are embedded is known. Therefore, defining the shape of the Earth alone without coordinate system axis origins is ambiguous. Generally, the prime meridian defined in the geodetic system WGS-84 can be safely assumed to be the longitudinal origin. However, because these code figures do not specify the longitudinal origin explicitly, it is suggested to contact the originating center if high precision coordinates are needed to obtain the precise details of the coordinate system used (effective as from 16 November 2016).
312#[repr(u8)]
313#[allow(missing_docs)]
314#[derive(Debug, Clone, Copy, PartialEq, Eq)]
315pub enum Grib2Table3_2 {
316    EarthSphericalRadius6367470 = 0,
317    EarthSphericalRadiusSpecifiedByProducer = 1,
318    EarthOblateSpheroidIau1965 = 2,
319    EarthOblateSpheroidMajorMinorAxesSpecifiedByProducerKm = 3,
320    EarthOblateSpheroidIagGrs80 = 4,
321    EarthRepresentedByWgs84 = 5,
322    EarthSphericalRadius6371229 = 6,
323    EarthOblateSpheroidMajorMinorAxesSpecifiedByProducerM = 7,
324    EarthSphericalRadius6371200Wgs84Datum = 8,
325    EarthOsgb1936Datum = 9,
326    EarthWgs84CorrectedGeomagnetic = 10,
327    SunSphericalRadius695990000 = 11,
328    Missing = 255,
329}
330impl From<u8> for Grib2Table3_2 {
331    fn from(val: u8) -> Self {
332        match val {
333            0 => Self::EarthSphericalRadius6367470,
334            1 => Self::EarthSphericalRadiusSpecifiedByProducer,
335            2 => Self::EarthOblateSpheroidIau1965,
336            3 => Self::EarthOblateSpheroidMajorMinorAxesSpecifiedByProducerKm,
337            4 => Self::EarthOblateSpheroidIagGrs80,
338            5 => Self::EarthRepresentedByWgs84,
339            6 => Self::EarthSphericalRadius6371229,
340            7 => Self::EarthOblateSpheroidMajorMinorAxesSpecifiedByProducerM,
341            8 => Self::EarthSphericalRadius6371200Wgs84Datum,
342            9 => Self::EarthOsgb1936Datum,
343            10 => Self::EarthWgs84CorrectedGeomagnetic,
344            11 => Self::SunSphericalRadius695990000,
345            _ => Self::Missing,
346        }
347    }
348}
349impl core::fmt::Display for Grib2Table3_2 {
350    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
351        let desc = match self {
352            Self::EarthSphericalRadius6367470 => {
353                "Earth assumed spherical with radius = 6,367,470.0 m"
354            }
355            Self::EarthSphericalRadiusSpecifiedByProducer => {
356                "Earth assumed spherical with radius specified (in m) by data producer"
357            }
358            Self::EarthOblateSpheroidIau1965 => {
359                "Earth assumed oblate spheroid with size as determined by IAU in 1965 (major axis \
360                 = 6,378,160.0 m, minor axis = 6,356,775.0 m, f = 1/297.0)"
361            }
362            Self::EarthOblateSpheroidMajorMinorAxesSpecifiedByProducerKm => {
363                "Earth assumed oblate spheroid with major and minor axes specified (in km) by data \
364                 producer"
365            }
366            Self::EarthOblateSpheroidIagGrs80 => {
367                "Earth assumed oblate spheroid as defined in IAG-GRS80 model (major axis = \
368                 6,378,137.0 m, minor axis = 6,356,752.314 m, f = 1/298.257222101)"
369            }
370            Self::EarthRepresentedByWgs84 => {
371                "Earth assumed represented by WGS84 (as used by ICAO since 1998) (Uses IAG-GRS80 \
372                 as a basis)"
373            }
374            Self::EarthSphericalRadius6371229 => {
375                "Earth assumed spherical with radius = 6,371,229.0 m"
376            }
377            Self::EarthOblateSpheroidMajorMinorAxesSpecifiedByProducerM => {
378                "Earth assumed oblate spheroid with major and minor axes specified (in m) by data \
379                 producer"
380            }
381            Self::EarthSphericalRadius6371200Wgs84Datum => {
382                "Earth model assumed spherical with radius 6,371,200 m, but the horizontal datum \
383                 of the resulting Latitude/Longitude field is the WGS84 reference frame"
384            }
385            Self::EarthOsgb1936Datum => {
386                "Earth represented by the OSGB 1936 Datum, using the Airy_1830 Spheroid, the \
387                 Greenwich meridian as 0 Longitude, the Newlyn datum as mean sea level, 0 height."
388            }
389            Self::EarthWgs84CorrectedGeomagnetic => {
390                "Earth model assumed WGS84 with corrected geomagnetic coordinates (latitude and \
391                 longitude) defined by Gustafsson et al., 1992\". (see Note 1)"
392            }
393            Self::SunSphericalRadius695990000 => {
394                "Sun assumed spherical with radius = 695 990 000 m (Allen, C.W., Astrophysical \
395                 Quantities, 3rd ed.; Athlone: London, 1976) and Stonyhurst latitude and longitude \
396                 system with origin at the intersection of the solar central meridian (as seen \
397                 from Earth) and the solar equator (Thompson, W., Coordinate systems for solar \
398                 image data, Astron. Astrophys. 2006, 449, 791-803)"
399            }
400            Self::Missing => "Missing",
401        };
402        f.write_str(desc)
403    }
404}
405
406/// # Table 3.3 - RESOLUTION AND COMPONENT FLAGS
407///
408/// **Details**:
409/// - **Section**: 3
410/// - **Octet**: 55
411/// - **Applicable Grid Templates**: 0-3, 40-43
412///
413/// **Reserved Bits**:
414/// - `1-2`: Reserved
415/// - `6-8`: Reserved - set to zero
416///
417/// **Special Values**:
418/// - None
419///
420/// ## Description
421/// This table defines the resolution and component flags used in GRIB2 files,
422/// specifying various increments and component resolutions for vector quantities.
423///
424/// ## Links
425/// - [Read more...](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table3-3.shtml)
426///
427/// ## Notes
428/// - Created 05/11/2005
429#[allow(missing_docs)]
430#[derive(Debug, Clone, Copy, PartialEq, Eq)]
431pub struct Grib2Table3_3 {
432    /// Bit 3: i Direction Increments
433    pub bit3: Grib2Table3_3Bit3,
434    /// Bit 4: j Direction Increments
435    pub bit4: Grib2Table3_3Bit4,
436    /// Bit 5: Resolved Components of Vector Quantities
437    pub bit5: Grib2Table3_3Bit5,
438    /// If any reserved bits (1-2, 6-8) are set, this field will indicate the original byte value.
439    /// Otherwise, it will be 0.
440    pub reserved_bits_set: u8,
441}
442impl From<u8> for Grib2Table3_3 {
443    fn from(val: u8) -> Self {
444        // Reserved Bits:
445        // 1-2 (0-indexed: 0-1)
446        // 6-8 (0-indexed: 5-7)
447        let reserved_bits_mask: u8 = 0b1110_0011; // Bits 1, 2, 6, 7, 8
448
449        Self {
450            bit3: Grib2Table3_3Bit3::from((val >> 2) & 1),
451            bit4: Grib2Table3_3Bit4::from((val >> 3) & 1),
452            bit5: Grib2Table3_3Bit5::from((val >> 4) & 1),
453            reserved_bits_set: val & reserved_bits_mask,
454        }
455    }
456}
457impl core::fmt::Display for Grib2Table3_3 {
458    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
459        if self.reserved_bits_set != 0 {
460            write!(
461                f,
462                "Unknown Resolution and Component Flags (Reserved bits set: {:#010b})",
463                self.reserved_bits_set
464            )
465        } else {
466            write!(f, "Bit 3: {}; Bit 4: {}; Bit 5: {}", self.bit3, self.bit4, self.bit5)
467        }
468    }
469}
470#[repr(u8)]
471#[allow(missing_docs)]
472#[derive(Debug, Clone, Copy, PartialEq, Eq)]
473pub enum Grib2Table3_3Bit3 {
474    IDirectionIncrementsNotGiven = 0,
475    IDirectionIncrementsGiven = 1,
476    Unknown(u8),
477}
478impl From<u8> for Grib2Table3_3Bit3 {
479    fn from(val: u8) -> Self {
480        match val {
481            0 => Self::IDirectionIncrementsNotGiven,
482            1 => Self::IDirectionIncrementsGiven,
483            other => Self::Unknown(other),
484        }
485    }
486}
487impl core::fmt::Display for Grib2Table3_3Bit3 {
488    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
489        let desc = match self {
490            Self::IDirectionIncrementsNotGiven => "i direction increments not given",
491            Self::IDirectionIncrementsGiven => "i direction increments given",
492            Self::Unknown(v) => return write!(f, "Unknown Bit 3 value ({v})"),
493        };
494        f.write_str(desc)
495    }
496}
497#[repr(u8)]
498#[allow(missing_docs)]
499#[derive(Debug, Clone, Copy, PartialEq, Eq)]
500pub enum Grib2Table3_3Bit4 {
501    JDirectionIncrementsNotGiven = 0,
502    JDirectionIncrementsGiven = 1,
503    Unknown(u8),
504}
505impl From<u8> for Grib2Table3_3Bit4 {
506    fn from(val: u8) -> Self {
507        match val {
508            0 => Self::JDirectionIncrementsNotGiven,
509            1 => Self::JDirectionIncrementsGiven,
510            other => Self::Unknown(other),
511        }
512    }
513}
514impl core::fmt::Display for Grib2Table3_3Bit4 {
515    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
516        let desc = match self {
517            Self::JDirectionIncrementsNotGiven => "j direction increments not given",
518            Self::JDirectionIncrementsGiven => "j direction increments given",
519            Self::Unknown(v) => return write!(f, "Unknown Bit 4 value ({v})"),
520        };
521        f.write_str(desc)
522    }
523}
524/// Enum for Bit 5 of Table 3.3 (Resolved Components of Vector Quantities)
525#[repr(u8)]
526#[allow(missing_docs)]
527#[derive(Debug, Clone, Copy, PartialEq, Eq)]
528pub enum Grib2Table3_3Bit5 {
529    ResolvedUvComponentsEasterlyNortherly = 0,
530    ResolvedUvComponentsGridIncreasingXy = 1,
531    Unknown(u8),
532}
533impl From<u8> for Grib2Table3_3Bit5 {
534    fn from(val: u8) -> Self {
535        match val {
536            0 => Self::ResolvedUvComponentsEasterlyNortherly,
537            1 => Self::ResolvedUvComponentsGridIncreasingXy,
538            other => Self::Unknown(other),
539        }
540    }
541}
542impl core::fmt::Display for Grib2Table3_3Bit5 {
543    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
544        let desc = match self {
545            Self::ResolvedUvComponentsEasterlyNortherly => {
546                "Resolved u and v components of vector quantities relative to easterly and \
547                 northerly directions"
548            }
549            Self::ResolvedUvComponentsGridIncreasingXy => {
550                "Resolved u and v components of vector quantities relative to the defined grid in \
551                 the direction of increasing x and y (or i and j) coordinates, respectively."
552            }
553            Self::Unknown(v) => return write!(f, "Unknown Bit 5 value ({v})"),
554        };
555        f.write_str(desc)
556    }
557}
558
559/// # Table 3.4 - SCANNING MODE
560///
561/// **Details**:
562/// - **Section**: 3
563/// - **Octet**: 72
564/// - **Applicable Grid Templates**: 0-3, 40-43, 204
565///
566/// **Reserved Bits**:
567/// - None
568///
569/// **Special Values**:
570/// - None
571///
572/// ## Description
573/// This table defines the scanning mode flags used in GRIB2 files,
574/// specifying the scanning direction and row/column offsets.
575///
576/// ## Links
577/// - [Read more...](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table3-4.shtml)
578///
579/// ## Notes
580/// - (1).  i direction - West to east along a parallel or left to right along an x-axis.
581/// - (2).  j direction - South to north along a meridian, or bottom to top along a y-axis.
582/// - (3).  If bit number 4 is set, the first row scan is defined by previous flags.
583/// - (4).  La1 and Lo1 define the first row, which is an odd row.
584/// - (5).  Di and Dj are assumed to be positive, with the direction of i and j being given by bits 1 and 2.
585/// - (6).  Bits 5 through 8 may be used to generate staggered grids, such as Arakawa grids (see Attachment, Volume 1.2, Part A, Att. GRIB).
586/// - (7).  If any of bits 5, 6, 7 or 8 are set, Di and Dj are not optional.
587///
588/// This table defines individual bit flags. To use them, you will need to extract the
589/// relevant bit from the byte at Octet 72 (index 71) and convert it using the
590/// corresponding `From<u8>` implementation.
591#[allow(missing_docs)]
592#[derive(Debug, Clone, Copy, PartialEq, Eq)]
593pub struct Grib2Table3_4 {
594    pub bit1: Grib2Table3_4Bit1,
595    pub bit2: Grib2Table3_4Bit2,
596    pub bit3: Grib2Table3_4Bit3,
597    pub bit4: Grib2Table3_4Bit4,
598    pub bit5: Grib2Table3_4Bit5,
599    pub bit6: Grib2Table3_4Bit6,
600    pub bit7: Grib2Table3_4Bit7,
601    pub bit8: Grib2Table3_4Bit8,
602}
603impl From<u8> for Grib2Table3_4 {
604    fn from(val: u8) -> Self {
605        Grib2Table3_4 {
606            bit1: Grib2Table3_4Bit1::from(val & 1),
607            bit2: Grib2Table3_4Bit2::from((val >> 1) & 1),
608            bit3: Grib2Table3_4Bit3::from((val >> 2) & 1),
609            bit4: Grib2Table3_4Bit4::from((val >> 3) & 1),
610            bit5: Grib2Table3_4Bit5::from((val >> 4) & 1),
611            bit6: Grib2Table3_4Bit6::from((val >> 5) & 1),
612            bit7: Grib2Table3_4Bit7::from((val >> 6) & 1),
613            bit8: Grib2Table3_4Bit8::from((val >> 7) & 1),
614        }
615    }
616}
617impl core::fmt::Display for Grib2Table3_4 {
618    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
619        write!(
620            f,
621            "Bit 1: {}; Bit 2: {}; Bit 3: {}; Bit 4: {}; Bit 5: {}; Bit 6: {}; Bit 7: {}; Bit 8: \
622             {}",
623            self.bit1, self.bit2, self.bit3, self.bit4, self.bit5, self.bit6, self.bit7, self.bit8
624        )
625    }
626}
627#[repr(u8)]
628#[allow(missing_docs)]
629#[derive(Debug, Clone, Copy, PartialEq, Eq)]
630pub enum Grib2Table3_4Bit1 {
631    PointsFirstRowColumnScanPlusIDirection = 0,
632    PointsFirstRowColumnScanMinusIDirection = 1,
633    Unknown(u8),
634}
635impl From<u8> for Grib2Table3_4Bit1 {
636    fn from(val: u8) -> Self {
637        match val {
638            0 => Self::PointsFirstRowColumnScanPlusIDirection,
639            1 => Self::PointsFirstRowColumnScanMinusIDirection,
640            other => Self::Unknown(other),
641        }
642    }
643}
644impl core::fmt::Display for Grib2Table3_4Bit1 {
645    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
646        let desc = match self {
647            Self::PointsFirstRowColumnScanPlusIDirection => {
648                "Points in the first row or column scan in the +i (+x) direction"
649            }
650            Self::PointsFirstRowColumnScanMinusIDirection => {
651                "Points in the first row or column scan in the -i (-x) direction"
652            }
653            Self::Unknown(v) => return write!(f, "Unknown Bit 1 value ({v})"),
654        };
655        f.write_str(desc)
656    }
657}
658#[repr(u8)]
659#[allow(missing_docs)]
660#[derive(Debug, Clone, Copy, PartialEq, Eq)]
661pub enum Grib2Table3_4Bit2 {
662    PointsFirstRowColumnScanMinusJDirection = 0,
663    PointsFirstRowColumnScanPlusJDirection = 1,
664    Unknown(u8),
665}
666impl From<u8> for Grib2Table3_4Bit2 {
667    fn from(val: u8) -> Self {
668        match val {
669            0 => Self::PointsFirstRowColumnScanMinusJDirection,
670            1 => Self::PointsFirstRowColumnScanPlusJDirection,
671            other => Self::Unknown(other),
672        }
673    }
674}
675impl core::fmt::Display for Grib2Table3_4Bit2 {
676    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
677        let desc = match self {
678            Self::PointsFirstRowColumnScanMinusJDirection => {
679                "Points in the first row or column scan in the -j (-y) direction"
680            }
681            Self::PointsFirstRowColumnScanPlusJDirection => {
682                "Points in the first row or column scan in the +j (+y) direction"
683            }
684            Self::Unknown(v) => return write!(f, "Unknown Bit 2 value ({v})"),
685        };
686        f.write_str(desc)
687    }
688}
689#[repr(u8)]
690#[allow(missing_docs)]
691#[derive(Debug, Clone, Copy, PartialEq, Eq)]
692pub enum Grib2Table3_4Bit3 {
693    AdjacentPointsIDirectionConsecutive = 0,
694    AdjacentPointsJDirectionConsecutive = 1,
695    Unknown(u8),
696}
697impl From<u8> for Grib2Table3_4Bit3 {
698    fn from(val: u8) -> Self {
699        match val {
700            0 => Self::AdjacentPointsIDirectionConsecutive,
701            1 => Self::AdjacentPointsJDirectionConsecutive,
702            other => Self::Unknown(other),
703        }
704    }
705}
706impl core::fmt::Display for Grib2Table3_4Bit3 {
707    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
708        let desc = match self {
709            Self::AdjacentPointsIDirectionConsecutive => {
710                "Adjacent points in the i (x) direction are consecutive"
711            }
712            Self::AdjacentPointsJDirectionConsecutive => {
713                "Adjacent points in the j (y) direction are consecutive"
714            }
715            Self::Unknown(v) => return write!(f, "Unknown Bit 3 value ({v})"),
716        };
717        f.write_str(desc)
718    }
719}
720#[repr(u8)]
721#[allow(missing_docs)]
722#[derive(Debug, Clone, Copy, PartialEq, Eq)]
723pub enum Grib2Table3_4Bit4 {
724    AllRowsScanSameDirection = 0,
725    AdjacentRowsScanOppositeDirection = 1,
726    Unknown(u8),
727}
728impl From<u8> for Grib2Table3_4Bit4 {
729    fn from(val: u8) -> Self {
730        match val {
731            0 => Self::AllRowsScanSameDirection,
732            1 => Self::AdjacentRowsScanOppositeDirection,
733            other => Self::Unknown(other),
734        }
735    }
736}
737impl core::fmt::Display for Grib2Table3_4Bit4 {
738    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
739        let desc = match self {
740            Self::AllRowsScanSameDirection => "All rows scan in the same direction",
741            Self::AdjacentRowsScanOppositeDirection => {
742                "Adjacent rows scan in the opposite direction"
743            }
744            Self::Unknown(v) => return write!(f, "Unknown Bit 4 value ({v})"),
745        };
746        f.write_str(desc)
747    }
748}
749#[repr(u8)]
750#[allow(missing_docs)]
751#[derive(Debug, Clone, Copy, PartialEq, Eq)]
752pub enum Grib2Table3_4Bit5 {
753    PointsOddRowsNotOffsetIDirection = 0,
754    PointsOddRowsOffsetDi2IDirection = 1,
755    Unknown(u8),
756}
757impl From<u8> for Grib2Table3_4Bit5 {
758    fn from(val: u8) -> Self {
759        match val {
760            0 => Self::PointsOddRowsNotOffsetIDirection,
761            1 => Self::PointsOddRowsOffsetDi2IDirection,
762            other => Self::Unknown(other),
763        }
764    }
765}
766impl core::fmt::Display for Grib2Table3_4Bit5 {
767    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
768        let desc = match self {
769            Self::PointsOddRowsNotOffsetIDirection => {
770                "Points within odd rows are not offset in i(x) direction"
771            }
772            Self::PointsOddRowsOffsetDi2IDirection => {
773                "Points within odd rows are offset by Di/2 in i(x) direction"
774            }
775            Self::Unknown(v) => return write!(f, "Unknown Bit 5 value ({v})"),
776        };
777        f.write_str(desc)
778    }
779}
780#[repr(u8)]
781#[allow(missing_docs)]
782#[derive(Debug, Clone, Copy, PartialEq, Eq)]
783pub enum Grib2Table3_4Bit6 {
784    PointsEvenRowsNotOffsetIDirection = 0,
785    PointsEvenRowsOffsetDi2IDirection = 1,
786    Unknown(u8),
787}
788impl From<u8> for Grib2Table3_4Bit6 {
789    fn from(val: u8) -> Self {
790        match val {
791            0 => Self::PointsEvenRowsNotOffsetIDirection,
792            1 => Self::PointsEvenRowsOffsetDi2IDirection,
793            other => Self::Unknown(other),
794        }
795    }
796}
797impl core::fmt::Display for Grib2Table3_4Bit6 {
798    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
799        let desc = match self {
800            Self::PointsEvenRowsNotOffsetIDirection => {
801                "Points within even rows are not offset in i(x) direction"
802            }
803            Self::PointsEvenRowsOffsetDi2IDirection => {
804                "Points within even rows are offset by Di/2 in i(x) direction"
805            }
806            Self::Unknown(v) => return write!(f, "Unknown Bit 6 value ({v})"),
807        };
808        f.write_str(desc)
809    }
810}
811#[repr(u8)]
812#[allow(missing_docs)]
813#[derive(Debug, Clone, Copy, PartialEq, Eq)]
814pub enum Grib2Table3_4Bit7 {
815    PointsNotOffsetJDirection = 0,
816    PointsOffsetDj2JDirection = 1,
817    Unknown(u8),
818}
819impl From<u8> for Grib2Table3_4Bit7 {
820    fn from(val: u8) -> Self {
821        match val {
822            0 => Self::PointsNotOffsetJDirection,
823            1 => Self::PointsOffsetDj2JDirection,
824            other => Self::Unknown(other),
825        }
826    }
827}
828impl core::fmt::Display for Grib2Table3_4Bit7 {
829    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
830        let desc = match self {
831            Self::PointsNotOffsetJDirection => "Points are not offset in j(y) direction",
832            Self::PointsOffsetDj2JDirection => "Points are offset by Dj/2 in j(y) direction",
833            Self::Unknown(v) => return write!(f, "Unknown Bit 7 value ({v})"),
834        };
835        f.write_str(desc)
836    }
837}
838#[repr(u8)]
839#[allow(missing_docs)]
840#[derive(Debug, Clone, Copy, PartialEq, Eq)]
841pub enum Grib2Table3_4Bit8 {
842    RowsNiColumnsNjGridPoints = 0,
843    RowsNiOrNiMinus1ColumnsNjOrNjMinus1GridPoints = 1,
844    Unknown(u8),
845}
846impl From<u8> for Grib2Table3_4Bit8 {
847    fn from(val: u8) -> Self {
848        match val {
849            0 => Self::RowsNiColumnsNjGridPoints,
850            1 => Self::RowsNiOrNiMinus1ColumnsNjOrNjMinus1GridPoints,
851            other => Self::Unknown(other),
852        }
853    }
854}
855impl core::fmt::Display for Grib2Table3_4Bit8 {
856    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
857        let desc = match self {
858            Self::RowsNiColumnsNjGridPoints => {
859                "Rows have Ni grid points and columns have Nj grid points"
860            }
861            Self::RowsNiOrNiMinus1ColumnsNjOrNjMinus1GridPoints => {
862                "Rows have Ni grid points if points are not offset in i direction; Rows have Ni-1 \
863                 grid points if points are offset by Di/2 in i direction. Columns have Nj grid \
864                 points if points are not offset in j direction; Columns have Nj-1 grid points if \
865                 points are offset by Dj/2 in j(y) direction."
866            }
867            Self::Unknown(v) => return write!(f, "Unknown Bit 8 value ({v})"),
868        };
869        f.write_str(desc)
870    }
871}
872
873/// # Table 3.5 - PROJECTION CENTER
874///
875/// **Details**:
876/// - **Section**: 3
877/// - **Octet**: 55
878/// - **Applicable Grid Templates**: 20, 30, 31
879///
880/// **Reserved Bits**:
881/// - `3-8`: Reserved
882///
883/// **Special Values**:
884/// - None
885///
886/// ## Description
887/// This table defines the projection center flags used in GRIB2 files,
888/// specifying the pole location and projection type.
889///
890/// ## Links
891/// - [Read more...](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table3-5.shtml)
892///
893/// ## Notes
894/// - Created 05/11/2005
895///
896/// This table defines individual bit flags. To use them, you will need to extract the
897/// relevant bit from the byte at Octet 55 (index 54) and convert it using the
898/// corresponding `From<u8>` implementation.
899#[allow(missing_docs)]
900#[derive(Debug, Clone, Copy, PartialEq, Eq)]
901pub struct Grib2Table3_5 {
902    /// Bit 1: North Pole on projection plane (0) or South Pole on projection plane (1).
903    pub bit1: Grib2Table3_5Bit1,
904    /// Bit 2: Only one projection center used (0) or projection is bi-polar and symmetric (1).
905    pub bit2: Grib2Table3_5Bit2,
906    /// If any reserved bits (3-8) are set, this field will indicate the original byte value.
907    /// Otherwise, it will be 0.
908    pub reserved_bits_set: u8,
909}
910impl From<u8> for Grib2Table3_5 {
911    fn from(val: u8) -> Self {
912        // Reserved Bits: 3-8 (0-indexed: 2-7) must be zero.
913        let reserved_bits_mask: u8 = 0b1111_1100; // Bits 3, 4, 5, 6, 7, 8
914
915        Self {
916            bit1: Grib2Table3_5Bit1::from(val & 1),
917            bit2: Grib2Table3_5Bit2::from((val >> 1) & 1),
918            reserved_bits_set: val & reserved_bits_mask,
919        }
920    }
921}
922impl core::fmt::Display for Grib2Table3_5 {
923    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
924        if self.reserved_bits_set != 0 {
925            write!(
926                f,
927                "Unknown Projection Center (Reserved bits set: {:#010b})",
928                self.reserved_bits_set
929            )
930        } else {
931            write!(f, "Bit 1: {}; Bit 2: {}", self.bit1, self.bit2)
932        }
933    }
934}
935#[repr(u8)]
936#[allow(missing_docs)]
937#[derive(Debug, Clone, Copy, PartialEq, Eq)]
938pub enum Grib2Table3_5Bit1 {
939    NorthPoleOnProjectionPlane = 0,
940    SouthPoleOnProjectionPlane = 1,
941    Unknown(u8),
942}
943impl From<u8> for Grib2Table3_5Bit1 {
944    fn from(val: u8) -> Self {
945        match val {
946            0 => Self::NorthPoleOnProjectionPlane,
947            1 => Self::SouthPoleOnProjectionPlane,
948            other => Self::Unknown(other),
949        }
950    }
951}
952impl core::fmt::Display for Grib2Table3_5Bit1 {
953    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
954        let desc = match self {
955            Self::NorthPoleOnProjectionPlane => "North Pole is on the projection plane",
956            Self::SouthPoleOnProjectionPlane => "South Pole is on the projection plane",
957            Self::Unknown(v) => return write!(f, "Unknown Bit 1 value ({v})"),
958        };
959        f.write_str(desc)
960    }
961}
962#[repr(u8)]
963#[allow(missing_docs)]
964#[derive(Debug, Clone, Copy, PartialEq, Eq)]
965pub enum Grib2Table3_5Bit2 {
966    OnlyOneProjectionCenterUsed = 0,
967    ProjectionBiPolarAndSymmetric = 1,
968    Unknown(u8),
969}
970impl From<u8> for Grib2Table3_5Bit2 {
971    fn from(val: u8) -> Self {
972        match val {
973            0 => Self::OnlyOneProjectionCenterUsed,
974            1 => Self::ProjectionBiPolarAndSymmetric,
975            other => Self::Unknown(other),
976        }
977    }
978}
979impl core::fmt::Display for Grib2Table3_5Bit2 {
980    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
981        let desc = match self {
982            Self::OnlyOneProjectionCenterUsed => "Only one projection center is used",
983            Self::ProjectionBiPolarAndSymmetric => "Projection is bi-polar and symmetric",
984            Self::Unknown(v) => return write!(f, "Unknown Bit 2 value ({v})"),
985        };
986        f.write_str(desc)
987    }
988}
989
990/// # Table 3.6 - SPECTRAL DATA REPRESENTATION TYPE
991///
992/// **Details**:
993/// - **Section**: 3
994/// - **Octet**: [Not Specified]
995///
996/// **Reserved Ranges**:
997/// - `3-254`: Reserved
998///
999/// **Special Value**:
1000/// - `255`: Missing
1001///
1002/// ## Description
1003/// This table defines the spectral data representation types used in GRIB2 files,
1004/// specifying the mathematical representations employed for spectral data.
1005///
1006/// ## Links
1007/// - [Read more...](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table3-6.shtml)
1008///
1009/// ## Notes
1010/// - Revised 08/23/2023
1011#[repr(u8)]
1012#[allow(missing_docs)]
1013#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1014pub enum Grib2Table3_6 {
1015    AssociatedLegendreFunctionsFirstKind = 1,
1016    BiFourierRepresentation = 2,
1017    Missing = 255,
1018}
1019
1020impl From<u8> for Grib2Table3_6 {
1021    fn from(val: u8) -> Self {
1022        match val {
1023            1 => Self::AssociatedLegendreFunctionsFirstKind,
1024            2 => Self::BiFourierRepresentation,
1025            _ => Self::Missing,
1026        }
1027    }
1028}
1029
1030impl core::fmt::Display for Grib2Table3_6 {
1031    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1032        let desc = match self {
1033            Self::AssociatedLegendreFunctionsFirstKind => {
1034                "The Associated Legendre Functions of the first kind are defined by:"
1035            }
1036            Self::BiFourierRepresentation => "Bi-Fourier representation",
1037            Self::Missing => "Missing",
1038        };
1039        f.write_str(desc)
1040    }
1041}
1042
1043/// # Table 3.7 - SPECTRAL DATA REPRESENTATION MODE
1044///
1045/// **Details**:
1046/// - **Section**: 3
1047/// - **Octet**: 55
1048///
1049/// **Reserved Ranges**:
1050/// - `2-254`: Reserved
1051///
1052/// **Special Value**:
1053/// - `255`: Missing
1054///
1055/// ## Description
1056/// This table defines the spectral data representation modes used in GRIB2 files,
1057/// specifying how spectral data is represented, including the mathematical representations employed.
1058///
1059/// ## Links
1060/// - [Read more...](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table3-6.shtml)
1061///
1062/// ## Notes
1063/// - (1) Values of N(m) for common truncation cases are as follows:
1064///   - Triangular:     M = J = K,        N(m) = J
1065///   - Rhomboidal:     K = J + M,        N(m) = J + m
1066///   - Trapezoidal:    K = J, K > M,     N(m) = J
1067#[repr(u8)]
1068#[allow(missing_docs)]
1069#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1070pub enum Grib2Table3_7 {
1071    ComplexNumbersFnmStoredAsPairsOfRealNumbers = 1,
1072    Missing = 255,
1073}
1074impl From<u8> for Grib2Table3_7 {
1075    fn from(val: u8) -> Self {
1076        match val {
1077            1 => Self::ComplexNumbersFnmStoredAsPairsOfRealNumbers,
1078            _ => Self::Missing,
1079        }
1080    }
1081}
1082impl core::fmt::Display for Grib2Table3_7 {
1083    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1084        let desc = match self {
1085            Self::ComplexNumbersFnmStoredAsPairsOfRealNumbers => {
1086                "The complex numbers Fnm (See Code Table 3.6) are stored for M>=0 as pairs of real \
1087                 numbers Re(Fnm), lm(Fnm) ordered with n increasing from m to N(m), first for m=0 \
1088                 and then for m=1, 2, ... M (see note below)."
1089            }
1090            Self::Missing => "Missing",
1091        };
1092        f.write_str(desc)
1093    }
1094}
1095
1096/// # Table 3.8 - GRID POINT POSITION
1097///
1098/// **Details**:
1099/// - **Section**: 3
1100/// - **Octet**: 32
1101/// - **Applicable Grid Templates**: 100
1102///
1103/// **Reserved Ranges**:
1104/// - `6-191`: Reserved
1105/// - `192-254`: Reserved for Local Use
1106///
1107/// **Special Value**:
1108/// - `255`: Missing
1109///
1110/// ## Description
1111/// This table defines the grid point positions used in GRIB2 files,
1112/// specifying where grid points are located relative to grid shapes.
1113///
1114/// ## Links
1115/// - [Read more...](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table3-8.shtml)
1116///
1117/// ## Notes
1118/// - Revised 12/07/2023
1119#[repr(u8)]
1120#[allow(missing_docs)]
1121#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1122pub enum Grib2Table3_8 {
1123    GridPointsAtTriangleVertices = 0,
1124    GridPointsAtCentersOfTriangles = 1,
1125    GridPointsAtMidpointsOfTriangleSides = 2,
1126    GridPointsAtShapeVertices = 3,
1127    GridPointsAtCentreOfShapes = 4,
1128    GridPointsAtMidpointsOfShapeSides = 5,
1129    Missing = 255,
1130}
1131impl From<u8> for Grib2Table3_8 {
1132    fn from(val: u8) -> Self {
1133        match val {
1134            0 => Self::GridPointsAtTriangleVertices,
1135            1 => Self::GridPointsAtCentersOfTriangles,
1136            2 => Self::GridPointsAtMidpointsOfTriangleSides,
1137            3 => Self::GridPointsAtShapeVertices,
1138            4 => Self::GridPointsAtCentreOfShapes,
1139            5 => Self::GridPointsAtMidpointsOfShapeSides,
1140            _ => Self::Missing,
1141        }
1142    }
1143}
1144impl core::fmt::Display for Grib2Table3_8 {
1145    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1146        let desc = match self {
1147            Self::GridPointsAtTriangleVertices => "Grid points at triangle vertices",
1148            Self::GridPointsAtCentersOfTriangles => "Grid points at centers of triangles",
1149            Self::GridPointsAtMidpointsOfTriangleSides => {
1150                "Grid points at midpoints of triangle sides"
1151            }
1152            Self::GridPointsAtShapeVertices => "Grid points at shape vertices",
1153            Self::GridPointsAtCentreOfShapes => "Grid points at centre of shapes",
1154            Self::GridPointsAtMidpointsOfShapeSides => "Grid points at midpoints of shape sides",
1155            Self::Missing => "Missing",
1156        };
1157        f.write_str(desc)
1158    }
1159}
1160
1161/// # Table 3.9 - GRID POINT POSITION AS SEEN FROM THE CORRESPONDING POLE
1162///
1163/// **Details**:
1164/// - **Section**: 3
1165/// - **Octet**: 33
1166/// - **Applicable Grid Templates**: 100
1167///
1168/// **Reserved Bits**:
1169/// - `2-8`: Reserved
1170///
1171/// **Special Values**:
1172/// - `255`: Missing
1173///
1174/// ## Description
1175/// This table defines the grid point positions as seen from the corresponding pole in GRIB2 files,
1176/// specifying where grid points are located relative to grid shapes.
1177///
1178/// ## Links
1179/// - [Read more...](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table3-9.shtml)
1180///
1181/// ## Notes
1182/// - Revised 12/07/2023
1183///
1184/// This table defines individual bit flags. To use them, you will need to extract the
1185/// relevant bit from the byte at Octet 33 (index 32) and convert it using the
1186/// corresponding `From<u8>` implementation. For example, to get the meaning of Bit 1,
1187/// you would do `Grib2Table3_9Bit1::from((octet_33 >> 0) & 1)`.
1188#[repr(u8)]
1189#[allow(missing_docs)]
1190#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1191pub enum Grib2Table3_9Bit1 {
1192    ClockwiseOrientation = 0,
1193    CounterClockwiseOrientation = 1,
1194    Unknown(u8),
1195}
1196impl From<u8> for Grib2Table3_9Bit1 {
1197    fn from(val: u8) -> Self {
1198        match val {
1199            0 => Self::ClockwiseOrientation,
1200            1 => Self::CounterClockwiseOrientation,
1201            other => Self::Unknown(other),
1202        }
1203    }
1204}
1205impl core::fmt::Display for Grib2Table3_9Bit1 {
1206    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1207        let desc = match self {
1208            Self::ClockwiseOrientation => "Clockwise orientation",
1209            Self::CounterClockwiseOrientation => "Counter-clockwise orientation",
1210            Self::Unknown(v) => return write!(f, "Unknown Bit 1 value ({v})"),
1211        };
1212        f.write_str(desc)
1213    }
1214}
1215
1216/// # Table 3.10 - SCANNING MODE FOR ONE DIAMOND AS SEEN FROM THE CORRESPONDING POLE
1217///
1218/// **Details**:
1219/// - **Section**: 3
1220/// - **Octet**: 34
1221/// - **Applicable Grid Templates**: 100
1222///
1223/// **Reserved Bits**:
1224/// - `4-8`: Reserved
1225///
1226/// **Special Values**:
1227/// - None
1228///
1229/// ## Description
1230/// This table defines the scanning mode flags for one diamond as seen from the corresponding pole in GRIB2 files,
1231/// specifying the scanning directions and grid points alignment.
1232///
1233/// ## Links
1234/// - [Read more...](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table3-10.shtml)
1235///
1236/// ## Notes
1237/// - Created 05/11/2005
1238///
1239/// This table defines individual bit flags. To use them, you will need to extract the
1240/// relevant bit from the byte at Octet 34 (index 33) and convert it using the
1241/// corresponding `From<u8>` implementation.
1242#[allow(missing_docs)]
1243#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1244pub struct Grib2Table3_10 {
1245    /// Bit 1: Points scan in +i (pole to Equator) (0) or -i (Equator to pole) (1).
1246    pub bit1: Grib2Table3_10Bit1,
1247    /// Bit 2: Points scan in +j (west to east) (0) or -j (east to west) (1).
1248    pub bit2: Grib2Table3_10Bit2,
1249    /// Bit 3: Adjacent points in i direction are consecutive (0) or j direction are consecutive (1).
1250    pub bit3: Grib2Table3_10Bit3,
1251    /// If any reserved bits (4-8) are set, this field will indicate the original byte value.
1252    /// Otherwise, it will be 0.
1253    pub reserved_bits_set: u8,
1254}
1255impl From<u8> for Grib2Table3_10 {
1256    fn from(val: u8) -> Self {
1257        // Reserved Bits: 4-8 (0-indexed: 3-7) must be zero.
1258        let reserved_bits_mask: u8 = 0b1111_1000; // Bits 4, 5, 6, 7, 8
1259
1260        Self {
1261            bit1: Grib2Table3_10Bit1::from(val & 1),
1262            bit2: Grib2Table3_10Bit2::from((val >> 1) & 1),
1263            bit3: Grib2Table3_10Bit3::from((val >> 2) & 1),
1264            reserved_bits_set: val & reserved_bits_mask,
1265        }
1266    }
1267}
1268impl core::fmt::Display for Grib2Table3_10 {
1269    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1270        if self.reserved_bits_set != 0 {
1271            write!(
1272                f,
1273                "Unknown Scanning Mode for One Diamond (Reserved bits set: {:#010b})",
1274                self.reserved_bits_set
1275            )
1276        } else {
1277            write!(f, "Bit 1: {}; Bit 2: {}; Bit 3: {}", self.bit1, self.bit2, self.bit3)
1278        }
1279    }
1280}
1281#[repr(u8)]
1282#[allow(missing_docs)]
1283#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1284pub enum Grib2Table3_10Bit1 {
1285    PointsScanPlusIDirectionPoleToEquator = 0,
1286    PointsScanMinusIDirectionEquatorToPole = 1,
1287    Unknown(u8),
1288}
1289impl From<u8> for Grib2Table3_10Bit1 {
1290    fn from(val: u8) -> Self {
1291        match val {
1292            0 => Self::PointsScanPlusIDirectionPoleToEquator,
1293            1 => Self::PointsScanMinusIDirectionEquatorToPole,
1294            other => Self::Unknown(other),
1295        }
1296    }
1297}
1298impl core::fmt::Display for Grib2Table3_10Bit1 {
1299    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1300        let desc = match self {
1301            Self::PointsScanPlusIDirectionPoleToEquator => {
1302                "Points scan in the +i direction, i.e. from pole to Equator"
1303            }
1304            Self::PointsScanMinusIDirectionEquatorToPole => {
1305                "Points scan in the -i direction, i.e. from Equator to pole"
1306            }
1307            Self::Unknown(v) => return write!(f, "Unknown Bit 1 value ({v})"),
1308        };
1309        f.write_str(desc)
1310    }
1311}
1312#[repr(u8)]
1313#[allow(missing_docs)]
1314#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1315pub enum Grib2Table3_10Bit2 {
1316    PointsScanPlusJDirectionWestToEast = 0,
1317    PointsScanMinusJDirectionEastToWest = 1,
1318    Unknown(u8),
1319}
1320impl From<u8> for Grib2Table3_10Bit2 {
1321    fn from(val: u8) -> Self {
1322        match val {
1323            0 => Self::PointsScanPlusJDirectionWestToEast,
1324            1 => Self::PointsScanMinusJDirectionEastToWest,
1325            other => Self::Unknown(other),
1326        }
1327    }
1328}
1329impl core::fmt::Display for Grib2Table3_10Bit2 {
1330    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1331        let desc = match self {
1332            Self::PointsScanPlusJDirectionWestToEast => {
1333                "Points scan in the +j direction, i.e. from west to east"
1334            }
1335            Self::PointsScanMinusJDirectionEastToWest => {
1336                "Points scan in the -j direction, i.e. from east to west"
1337            }
1338            Self::Unknown(v) => return write!(f, "Unknown Bit 2 value ({v})"),
1339        };
1340        f.write_str(desc)
1341    }
1342}
1343#[repr(u8)]
1344#[allow(missing_docs)]
1345#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1346pub enum Grib2Table3_10Bit3 {
1347    AdjacentPointsIDirectionConsecutive = 0,
1348    AdjacentPointsJDirectionConsecutive = 1,
1349    Unknown(u8),
1350}
1351impl From<u8> for Grib2Table3_10Bit3 {
1352    fn from(val: u8) -> Self {
1353        match val {
1354            0 => Self::AdjacentPointsIDirectionConsecutive,
1355            1 => Self::AdjacentPointsJDirectionConsecutive,
1356            other => Self::Unknown(other),
1357        }
1358    }
1359}
1360impl core::fmt::Display for Grib2Table3_10Bit3 {
1361    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1362        let desc = match self {
1363            Self::AdjacentPointsIDirectionConsecutive => {
1364                "Adjacent points in the i direction are consecutive"
1365            }
1366            Self::AdjacentPointsJDirectionConsecutive => {
1367                "Adjacent points in the j direction are consecutive"
1368            }
1369            Self::Unknown(v) => return write!(f, "Unknown Bit 3 value ({v})"),
1370        };
1371        f.write_str(desc)
1372    }
1373}
1374
1375/// # Table 3.11 - Interpretation of List of Numbers at End of Section 3
1376///
1377/// **Details**:
1378/// - **Section**: 3
1379/// - **Octet**: 12
1380/// - **Applicable Grid Templates**: 100
1381///
1382/// **Reserved Ranges**:
1383/// - `4-254`: Reserved
1384///
1385/// **Special Value**:
1386/// - `255`: Missing
1387///
1388/// ## Description
1389/// This table defines the interpretation of the list of numbers appended at the end of Section 3 in GRIB2 files,
1390/// specifying how the numbers correspond to points in the grid based on various definitions.
1391///
1392/// ## Links
1393/// - [Read more...](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table3-11.shtml)
1394///
1395/// ## Notes
1396/// - (1) For entry 1, it should be noted that depending on values of extreme (first/last) coordinates, and regardless of bit-map, effective number of points per row may be less than the number of points on the current circle.
1397/// - (2) For value for the constant direction increment Di (or Dx) in the accompanying Grid Definition Template should be set to all ones (missing).
1398#[repr(u8)]
1399#[allow(missing_docs)]
1400#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1401pub enum Grib2Table3_11 {
1402    NoAppendedList = 0,
1403    FullCoordinateCircles = 1,
1404    ExtremeCoordinateValues = 2,
1405    ActualLatitudesForEachRow = 3,
1406    Missing = 255,
1407}
1408impl From<u8> for Grib2Table3_11 {
1409    fn from(val: u8) -> Self {
1410        match val {
1411            0 => Self::NoAppendedList,
1412            1 => Self::FullCoordinateCircles,
1413            2 => Self::ExtremeCoordinateValues,
1414            3 => Self::ActualLatitudesForEachRow,
1415            _ => Self::Missing,
1416        }
1417    }
1418}
1419impl core::fmt::Display for Grib2Table3_11 {
1420    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1421        let desc = match self {
1422            Self::NoAppendedList => "There is no appended list",
1423            Self::FullCoordinateCircles => {
1424                "Numbers define number of points corresponding to full coordinate circles (i.e. \
1425                 parallels). Coordinate values on each circle are multiple of the circle mesh, and \
1426                 extreme coordinate values given in grid definition may not be reached in all rows."
1427            }
1428            Self::ExtremeCoordinateValues => {
1429                "Numbers define number of points corresponding to coordinate lines delimited by \
1430                 extreme coordinate values given in grid definition which are present in each row."
1431            }
1432            Self::ActualLatitudesForEachRow => {
1433                "Numbers define the actual latitudes for each row in the grid. The list of numbers \
1434                 are integer values of the valid latitudes in microdegrees (scale by 106) or in \
1435                 unit equal to the ratio of the basic angle and the subdivisions number for each \
1436                 row, in the same order as specified in the \"scanning mode flag\" (bit no. 2) \
1437                 (see note 2)"
1438            }
1439            Self::Missing => "Missing",
1440        };
1441        f.write_str(desc)
1442    }
1443}
1444
1445/// # Table 3.12 - HEALPix Rhomboids or Points Ordering
1446///
1447/// **Details**:
1448/// - **Section**: 3
1449/// - **Octet**: 34
1450/// - **Applicable Grid Templates**: 100
1451///
1452/// **Reserved Ranges**:
1453/// - `2-191`: Reserved
1454/// - `192-254`: Reserved for Local Use
1455///
1456/// **Special Value**:
1457/// - `255`: Missing
1458///
1459/// ## Description
1460/// This table defines the ordering of HEALPix rhomboids or points in GRIB2 files,
1461/// specifying how points are ordered within the HEALPix grid structure.
1462///
1463/// ## Links
1464/// - [Read more...](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table3-12.shtml)
1465///
1466/// ## Notes
1467/// - Created 12/07/2023
1468#[repr(u8)]
1469#[allow(missing_docs)]
1470#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1471pub enum Grib2Table3_12 {
1472    Reserved0 = 0, // Explicitly reserved value
1473    RingOrdering = 1,
1474    NestedOrdering = 2,
1475    Missing = 255,
1476}
1477
1478impl From<u8> for Grib2Table3_12 {
1479    fn from(val: u8) -> Self {
1480        match val {
1481            0 => Self::Reserved0,
1482            1 => Self::RingOrdering,
1483            2 => Self::NestedOrdering,
1484            _ => Self::Missing,
1485        }
1486    }
1487}
1488impl core::fmt::Display for Grib2Table3_12 {
1489    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1490        let desc = match self {
1491            Self::Reserved0 => "Reserved",
1492            Self::RingOrdering => "Ring ordering",
1493            Self::NestedOrdering => "Nested ordering",
1494            Self::Missing => "Missing",
1495        };
1496        f.write_str(desc)
1497    }
1498}
1499
1500/// # Table 3.13 - HEALPix Scanning Mode
1501///
1502/// **Details**:
1503/// - **Section**: 3
1504/// - **Octet**: 34
1505/// - **Applicable Grid Templates**: 100
1506///
1507/// **Reserved Bits**:
1508/// - `4-8`: Reserved
1509///
1510/// **Special Value**:
1511/// - None
1512///
1513/// ## Description
1514/// This table defines the HEALPix scanning mode flags used in GRIB2 files,
1515/// specifying the scanning directions and grid points alignment.
1516///
1517/// ## Links
1518/// - [Read more...](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table3-13.shtml)
1519///
1520/// ## Notes
1521/// - Created 12/07/2023
1522///
1523/// This table defines individual bit flags. To use them, you will need to extract the
1524/// relevant bit from the byte at Octet 34 (index 33) and convert it using the
1525/// corresponding `From<u8>` implementation.
1526#[allow(missing_docs)]
1527#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1528pub struct Grib2Table3_13 {
1529    /// Bit 1: Points scan in the +i (+x) direction (0) or -i (-x) direction (1).
1530    pub bit1: Grib2Table3_13Bit1,
1531    /// Bit 2: Points scan in -j (-y) direction (0) or +j (+y) direction (1).
1532    pub bit2: Grib2Table3_13Bit2,
1533    /// Bit 3: Adjacent points in the i (x) direction are consecutive (0) or j (y) direction are consecutive (1).
1534    pub bit3: Grib2Table3_13Bit3,
1535    /// If any reserved bits (4-8) are set, this field will indicate the original byte value.
1536    /// Otherwise, it will be 0.
1537    pub reserved_bits_set: u8,
1538}
1539impl From<u8> for Grib2Table3_13 {
1540    fn from(val: u8) -> Self {
1541        // Reserved Bits: 4-8 (0-indexed: 3-7) must be zero.
1542        let reserved_bits_mask: u8 = 0b1111_1000; // Bits 4, 5, 6, 7, 8
1543
1544        Self {
1545            bit1: Grib2Table3_13Bit1::from(val & 1),
1546            bit2: Grib2Table3_13Bit2::from((val >> 1) & 1),
1547            bit3: Grib2Table3_13Bit3::from((val >> 2) & 1),
1548            reserved_bits_set: val & reserved_bits_mask,
1549        }
1550    }
1551}
1552impl core::fmt::Display for Grib2Table3_13 {
1553    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1554        if self.reserved_bits_set != 0 {
1555            write!(
1556                f,
1557                "Unknown HEALPix Scanning Mode (Reserved bits set: {:#010b})",
1558                self.reserved_bits_set
1559            )
1560        } else {
1561            write!(f, "Bit 1: {}; Bit 2: {}; Bit 3: {}", self.bit1, self.bit2, self.bit3)
1562        }
1563    }
1564}
1565#[repr(u8)]
1566#[allow(missing_docs)]
1567#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1568pub enum Grib2Table3_13Bit1 {
1569    PointsScanPlusIDirection = 0,
1570    PointsScanMinusIDirection = 1,
1571    Unknown(u8),
1572}
1573impl From<u8> for Grib2Table3_13Bit1 {
1574    fn from(val: u8) -> Self {
1575        match val {
1576            0 => Self::PointsScanPlusIDirection,
1577            1 => Self::PointsScanMinusIDirection,
1578            other => Self::Unknown(other),
1579        }
1580    }
1581}
1582impl core::fmt::Display for Grib2Table3_13Bit1 {
1583    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1584        let desc = match self {
1585            Self::PointsScanPlusIDirection => "Points scan in the +i (+x) direction",
1586            Self::PointsScanMinusIDirection => "Points scan in the -i (-x) direction",
1587            Self::Unknown(v) => return write!(f, "Unknown Bit 1 value ({v})"),
1588        };
1589        f.write_str(desc)
1590    }
1591}
1592
1593#[repr(u8)]
1594#[allow(missing_docs)]
1595#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1596pub enum Grib2Table3_13Bit2 {
1597    PointsScanMinusJDirection = 0,
1598    PointsScanPlusJDirection = 1,
1599    Unknown(u8),
1600}
1601impl From<u8> for Grib2Table3_13Bit2 {
1602    fn from(val: u8) -> Self {
1603        match val {
1604            0 => Self::PointsScanMinusJDirection,
1605            1 => Self::PointsScanPlusJDirection,
1606            other => Self::Unknown(other),
1607        }
1608    }
1609}
1610impl core::fmt::Display for Grib2Table3_13Bit2 {
1611    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1612        let desc = match self {
1613            Self::PointsScanMinusJDirection => "Points scan in -j (-y) direction",
1614            Self::PointsScanPlusJDirection => "Points scan in +j (+y) direction",
1615            Self::Unknown(v) => return write!(f, "Unknown Bit 2 value ({v})"),
1616        };
1617        f.write_str(desc)
1618    }
1619}
1620
1621#[repr(u8)]
1622#[allow(missing_docs)]
1623#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1624pub enum Grib2Table3_13Bit3 {
1625    AdjacentPointsIDirectionConsecutive = 0,
1626    AdjacentPointsJDirectionConsecutive = 1,
1627    Unknown(u8),
1628}
1629impl From<u8> for Grib2Table3_13Bit3 {
1630    fn from(val: u8) -> Self {
1631        match val {
1632            0 => Self::AdjacentPointsIDirectionConsecutive,
1633            1 => Self::AdjacentPointsJDirectionConsecutive,
1634            other => Self::Unknown(other),
1635        }
1636    }
1637}
1638impl core::fmt::Display for Grib2Table3_13Bit3 {
1639    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1640        let desc = match self {
1641            Self::AdjacentPointsIDirectionConsecutive => {
1642                "Adjacent points in the i (x) direction are consecutive"
1643            }
1644            Self::AdjacentPointsJDirectionConsecutive => {
1645                "Adjacent points in the j (y) direction are consecutive"
1646            }
1647            Self::Unknown(v) => return write!(f, "Unknown Bit 3 value ({v})"),
1648        };
1649        f.write_str(desc)
1650    }
1651}
1652
1653/// # Table 3.15 - PHYSICAL MEANING OF VERTICAL COORDINATE
1654///
1655/// **Details**:
1656/// - **Section**: 3
1657/// - **Octet**: 63
1658/// - **Applicable Grid Templates**: 100
1659///
1660/// **Reserved Ranges**:
1661/// - `0-19`: Reserved
1662/// - `21-99`: Reserved
1663/// - `114-159`: Reserved
1664/// - `161-191`: Reserved
1665/// - `192-254`: Reserved for Local Use
1666///
1667/// **Special Value**:
1668/// - `255`: Missing
1669///
1670/// ## Description
1671/// This table defines the physical meanings of vertical coordinates used in GRIB2 files,
1672/// specifying various vertical coordinate systems and their corresponding units.
1673///
1674/// ## Links
1675/// - [Read more...](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table3-15.shtml)
1676///
1677/// ## Notes
1678/// - (1) For entry 103, it should be noted that depending on values of extreme (first/last) coordinates, and regardless of bit-map, the effective number of points per row may be less than the number of points on the current circle.
1679/// - (2) For the value of the constant direction increment Di (or Dx) in the accompanying Grid Definition Template, it should be set to all ones (missing).
1680#[repr(u8)]
1681#[allow(missing_docs)]
1682#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1683pub enum Grib2Table3_15 {
1684    Reserved0 = 0, // Explicitly reserved value
1685    TemperatureK = 20,
1686    PressurePa = 100,
1687    PressureDeviationFromMeanSeaLevelPa = 101,
1688    AltitudeAboveMeanSeaLevelM = 102,
1689    HeightAboveGroundM = 103,
1690    SigmaCoordinate = 104,
1691    HybridCoordinate = 105,
1692    DepthBelowLandSurfaceM = 106,
1693    PotentialTemperatureK = 107,
1694    PressureDeviationFromGroundToLevelPa = 108,
1695    PotentialVorticityKgm2s1 = 109,
1696    GeometricHeightM = 110,
1697    EtaCoordinate = 111,
1698    GeopotentialHeightGpm = 112,
1699    LogarithmicHybridCoordinate = 113,
1700    DepthBelowSeaLevelM = 160,
1701    Missing = 255,
1702}
1703impl From<u8> for Grib2Table3_15 {
1704    fn from(val: u8) -> Self {
1705        match val {
1706            0 => Self::Reserved0,
1707            20 => Self::TemperatureK,
1708            100 => Self::PressurePa,
1709            101 => Self::PressureDeviationFromMeanSeaLevelPa,
1710            102 => Self::AltitudeAboveMeanSeaLevelM,
1711            103 => Self::HeightAboveGroundM,
1712            104 => Self::SigmaCoordinate,
1713            105 => Self::HybridCoordinate,
1714            106 => Self::DepthBelowLandSurfaceM,
1715            107 => Self::PotentialTemperatureK,
1716            108 => Self::PressureDeviationFromGroundToLevelPa,
1717            109 => Self::PotentialVorticityKgm2s1,
1718            110 => Self::GeometricHeightM,
1719            111 => Self::EtaCoordinate,
1720            112 => Self::GeopotentialHeightGpm,
1721            113 => Self::LogarithmicHybridCoordinate,
1722            160 => Self::DepthBelowSeaLevelM,
1723            _ => Self::Missing,
1724        }
1725    }
1726}
1727impl core::fmt::Display for Grib2Table3_15 {
1728    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1729        let desc = match self {
1730            Self::Reserved0 => "Reserved",
1731            Self::TemperatureK => "Temperature (K)",
1732            Self::PressurePa => "Pressure (Pa)",
1733            Self::PressureDeviationFromMeanSeaLevelPa => {
1734                "Pressure deviation from mean sea level (Pa)"
1735            }
1736            Self::AltitudeAboveMeanSeaLevelM => "Altitude above mean sea level (m)",
1737            Self::HeightAboveGroundM => "Height above ground (see note 1) (m)",
1738            Self::SigmaCoordinate => "Sigma coordinate",
1739            Self::HybridCoordinate => "Hybrid coordinate",
1740            Self::DepthBelowLandSurfaceM => "Depth below land surface (m)",
1741            Self::PotentialTemperatureK => "Potential temperature (theta) (K)",
1742            Self::PressureDeviationFromGroundToLevelPa => {
1743                "Pressure deviation from ground to level (Pa)"
1744            }
1745            Self::PotentialVorticityKgm2s1 => "Potential vorticity (K m-2 kg-1 s-1)",
1746            Self::GeometricHeightM => "Geometric height (m)",
1747            Self::EtaCoordinate => "Eta coordinate (see note 2)",
1748            Self::GeopotentialHeightGpm => "Geopotential height (gpm)",
1749            Self::LogarithmicHybridCoordinate => "Logarithmic hybrid coordinate",
1750            Self::DepthBelowSeaLevelM => "Depth below sea level (m)",
1751            Self::Missing => "Missing",
1752        };
1753        f.write_str(desc)
1754    }
1755}
1756
1757/// # Table 3.20 - TYPE OF HORIZONTAL LINE AS SEEN FROM THE CORRESPONDING POLE
1758///
1759/// **Details**:
1760/// - **Section**: 3
1761/// - **Octet**: 60
1762/// - **Applicable Grid Templates**: 1000, 1100
1763///
1764/// **Reserved Ranges**:
1765/// - `2-191`: Reserved
1766/// - `192-254`: Reserved for Local Use
1767///
1768/// **Special Value**:
1769/// - `255`: Missing
1770///
1771/// ## Description
1772/// This table defines the types of horizontal lines used in GRIB2 files,
1773/// specifying whether lines are Rhumb or Great Circle, among other definitions.
1774///
1775/// ## Links
1776/// - [Read more...](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table3-20.shtml)
1777///
1778/// ## Notes
1779/// - Created 05/11/2005
1780/// - Red text in the original table depicts changes made since 05/11/2005.
1781#[repr(u8)]
1782#[allow(missing_docs)]
1783#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1784pub enum Grib2Table3_20 {
1785    Rhumb = 0,
1786    GreatCircle = 1,
1787    Missing = 255,
1788}
1789impl From<u8> for Grib2Table3_20 {
1790    fn from(val: u8) -> Self {
1791        match val {
1792            0 => Self::Rhumb,
1793            1 => Self::GreatCircle,
1794            _ => Self::Missing,
1795        }
1796    }
1797}
1798impl core::fmt::Display for Grib2Table3_20 {
1799    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1800        let desc = match self {
1801            Self::Rhumb => "Rhumb",
1802            Self::GreatCircle => "Great Circle",
1803            Self::Missing => "Missing",
1804        };
1805        f.write_str(desc)
1806    }
1807}
1808
1809/// # Table 3.21 - PHYSICAL MEANING OF VERTICAL COORDINATE VALUES DEFINITION
1810///
1811/// **Details**:
1812/// - **Section**: 3
1813/// - **Octet**: 64
1814/// - **Applicable Grid Templates**: 1000
1815///
1816/// **Reserved Ranges**:
1817/// - `0-19`: Reserved
1818/// - `21-99`: Reserved
1819/// - `114-159`: Reserved
1820/// - `161-191`: Reserved
1821/// - `192-254`: Reserved for Local Use
1822///
1823/// **Special Value**:
1824/// - `255`: Missing
1825///
1826/// ## Description
1827/// This table defines the physical meanings of vertical coordinates used in GRIB2 files,
1828/// specifying various vertical coordinate systems and their corresponding units.
1829///
1830/// ## Links
1831/// - [Read more...](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table3-21.shtml)
1832///
1833/// ## Notes
1834/// - (1) For entry 103, it should be noted that depending on values of extreme (first/last) coordinates, and regardless of bit-map, the effective number of points per row may be less than the number of points on the current circle.
1835/// - (2) For the value for the constant direction increment Di (or Dx) in the accompanying Grid Definition Template should be set to all ones (missing).
1836#[repr(u8)]
1837#[allow(missing_docs)]
1838#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1839pub enum Grib2Table3_21 {
1840    TemperatureK = 20,
1841    PressurePa = 100,
1842    PressureDeviationFromMeanSeaLevelPa = 101,
1843    AltitudeAboveMeanSeaLevelM = 102,
1844    HeightAboveGroundM = 103,
1845    SigmaCoordinate = 104,
1846    HybridCoordinate = 105,
1847    DepthBelowLandSurfaceM = 106,
1848    PotentialTemperatureK = 107,
1849    PressureDeviationFromGroundToLevelPa = 108,
1850    PotentialVorticityKgm2s1 = 109,
1851    GeometricHeightM = 110,
1852    EtaCoordinate = 111,
1853    GeopotentialHeightGpm = 112,
1854    LogarithmicHybridCoordinate = 113,
1855    DepthBelowSeaLevelM = 160,
1856    Missing = 255,
1857}
1858impl From<u8> for Grib2Table3_21 {
1859    fn from(val: u8) -> Self {
1860        match val {
1861            20 => Self::TemperatureK,
1862            100 => Self::PressurePa,
1863            101 => Self::PressureDeviationFromMeanSeaLevelPa,
1864            102 => Self::AltitudeAboveMeanSeaLevelM,
1865            103 => Self::HeightAboveGroundM,
1866            104 => Self::SigmaCoordinate,
1867            105 => Self::HybridCoordinate,
1868            106 => Self::DepthBelowLandSurfaceM,
1869            107 => Self::PotentialTemperatureK,
1870            108 => Self::PressureDeviationFromGroundToLevelPa,
1871            109 => Self::PotentialVorticityKgm2s1,
1872            110 => Self::GeometricHeightM,
1873            111 => Self::EtaCoordinate,
1874            112 => Self::GeopotentialHeightGpm,
1875            113 => Self::LogarithmicHybridCoordinate,
1876            160 => Self::DepthBelowSeaLevelM,
1877            _ => Self::Missing,
1878        }
1879    }
1880}
1881impl core::fmt::Display for Grib2Table3_21 {
1882    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1883        let desc = match self {
1884            Self::TemperatureK => "Temperature (K)",
1885            Self::PressurePa => "Pressure (Pa)",
1886            Self::PressureDeviationFromMeanSeaLevelPa => {
1887                "Pressure deviation from mean sea level (Pa)"
1888            }
1889            Self::AltitudeAboveMeanSeaLevelM => "Altitude above mean sea level (m)",
1890            Self::HeightAboveGroundM => "Height above ground (see note 1) (m)",
1891            Self::SigmaCoordinate => "Sigma coordinate",
1892            Self::HybridCoordinate => "Hybrid coordinate",
1893            Self::DepthBelowLandSurfaceM => "Depth below land surface (m)",
1894            Self::PotentialTemperatureK => "Potential temperature (theta) (K)",
1895            Self::PressureDeviationFromGroundToLevelPa => {
1896                "Pressure deviation from ground to level (Pa)"
1897            }
1898            Self::PotentialVorticityKgm2s1 => "Potential vorticity (K m-2 kg-1 s-1)",
1899            Self::GeometricHeightM => "Geometric height (m)",
1900            Self::EtaCoordinate => "Eta coordinate (see note 2)",
1901            Self::GeopotentialHeightGpm => "Geopotential height (gpm)",
1902            Self::LogarithmicHybridCoordinate => "Logarithmic hybrid coordinate",
1903            Self::DepthBelowSeaLevelM => "Depth below sea level (m)",
1904            Self::Missing => "Missing",
1905        };
1906        f.write_str(desc)
1907    }
1908}
1909
1910/// # Table 3.25 - TYPE OF BI-FOURIER TRUNCATION
1911///
1912/// **Details**:
1913/// - **Section**: 3
1914/// - **Octet**: [Not Specified]
1915/// - **Applicable Grid Templates**: [Not Specified]
1916///
1917/// **Reserved Ranges**:
1918/// - `0-76`: Reserved
1919/// - `78-87`: Reserved
1920/// - `89-98`: Reserved
1921/// - `100-191`: Reserved
1922/// - `192-254`: Reserved for Local Use
1923///
1924/// **Special Value**:
1925/// - `255`: Missing
1926///
1927/// ## Description
1928/// This table defines the types of Bi-Fourier truncation used in GRIB2 files,
1929/// specifying how spectral data is truncated in the horizontal direction.
1930///
1931/// ## Links
1932/// - [Read more...](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table3-25.shtml)
1933///
1934/// ## Notes
1935/// - Created 06/22/2022
1936#[repr(u8)]
1937#[allow(missing_docs)]
1938#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1939pub enum Grib2Table3_25 {
1940    Rectangular = 77,
1941    Elliptic = 88,
1942    Diamond = 99,
1943    Missing = 255,
1944}
1945impl From<u8> for Grib2Table3_25 {
1946    fn from(val: u8) -> Self {
1947        match val {
1948            77 => Self::Rectangular,
1949            88 => Self::Elliptic,
1950            99 => Self::Diamond,
1951            _ => Self::Missing,
1952        }
1953    }
1954}
1955impl core::fmt::Display for Grib2Table3_25 {
1956    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1957        let desc = match self {
1958            Self::Rectangular => "Rectangular",
1959            Self::Elliptic => "Elliptic",
1960            Self::Diamond => "Diamond",
1961            Self::Missing => "Missing",
1962        };
1963        f.write_str(desc)
1964    }
1965}