oxigdal-netcdf 0.1.3

NetCDF driver for OxiGDAL - Pure Rust NetCDF-3 with optional NetCDF-4 support
Documentation

oxigdal-netcdf

Pure Rust NetCDF-3 driver for OxiGDAL with CF (Climate and Forecast) conventions support and optional HDF5-based NetCDF-4.

Crates.io Documentation License

Overview

oxigdal-netcdf provides comprehensive support for reading and writing NetCDF files, with emphasis on climate and weather data through CF (Climate and Forecast) Conventions compliance. The crate offers a Pure Rust implementation for NetCDF-3 files by default, with optional feature-gated support for NetCDF-4 (HDF5-based) format.

Features

  • Pure Rust NetCDF-3 - Default Pure Rust implementation (no C dependencies)
  • CF Conventions 1.8 - Full support for Climate and Forecast metadata standards
  • Multi-dimensional Arrays - Fixed and unlimited dimensions
  • Comprehensive Data Types - i8, i16, i32, f32, f64, char (NetCDF-3); u8-u64, i64, strings (NetCDF-4)
  • Attributes - Global and variable-level attributes
  • Coordinate Variables - Automatic detection and handling
  • Async I/O - Optional async/await support via tokio
  • Feature-gated NetCDF-4 - HDF5-based format with groups, compression, and unlimited dimensions
  • Error Handling - Comprehensive error types with no unwrap() calls
  • Format Detection - Automatic NetCDF-3/4 format detection

Installation

Add to your Cargo.toml:

# Pure Rust NetCDF-3 (default, recommended)
[dependencies]
oxigdal-netcdf = "0.1"

# With CF conventions validation
[dependencies]
oxigdal-netcdf = { version = "0.1", features = ["cf_conventions"] }

# With async I/O support
[dependencies]
oxigdal-netcdf = { version = "0.1", features = ["async"] }

# With NetCDF-4/HDF5 support (requires system libraries)
# WARNING: Not Pure Rust - requires libnetcdf and libhdf5
[dependencies]
oxigdal-netcdf = { version = "0.1", features = ["netcdf4"] }

Quick Start

Reading a NetCDF File (Pure Rust)

use oxigdal_netcdf::NetCdfReader;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Open a NetCDF-3 file
    let reader = NetCdfReader::open("temperature.nc")?;

    // Get metadata summary
    println!("{}", reader.metadata().summary());

    // List dimensions
    for dim in reader.dimensions().iter() {
        println!("Dimension: {} (size: {})", dim.name(), dim.len());
    }

    // List variables
    for var in reader.variables().iter() {
        println!("Variable: {} (type: {})", var.name(), var.data_type().name());
    }

    // Read variable data
    let temperature = reader.read_f32("temperature")?;
    println!("Temperature data: {:?}", temperature);

    Ok(())
}

Writing a NetCDF File (Pure Rust)

use oxigdal_netcdf::{NetCdfWriter, NetCdfVersion};
use oxigdal_netcdf::dimension::Dimension;
use oxigdal_netcdf::variable::{Variable, DataType};
use oxigdal_netcdf::attribute::{Attribute, AttributeValue};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Create a new NetCDF-3 file
    let mut writer = NetCdfWriter::create("output.nc", NetCdfVersion::Classic)?;

    // Add dimensions
    writer.add_dimension(Dimension::new("lat", 180)?)?;
    writer.add_dimension(Dimension::new("lon", 360)?)?;
    writer.add_dimension(Dimension::new_unlimited("time", 0)?)?;

    // Add coordinate variables
    let lat_var = Variable::new_coordinate("lat", DataType::F32)?;
    let lon_var = Variable::new_coordinate("lon", DataType::F32)?;
    let time_var = Variable::new_coordinate("time", DataType::F64)?;

    writer.add_variable(lat_var)?;
    writer.add_variable(lon_var)?;
    writer.add_variable(time_var)?;

    // Add data variable
    let temp_var = Variable::new(
        "temperature",
        DataType::F32,
        vec!["time".to_string(), "lat".to_string(), "lon".to_string()],
    )?;
    writer.add_variable(temp_var)?;

    // Add variable attributes
    writer.add_variable_attribute(
        "temperature",
        Attribute::new("units", AttributeValue::text("kelvin"))?,
    )?;
    writer.add_variable_attribute(
        "temperature",
        Attribute::new("long_name", AttributeValue::text("Air Temperature"))?,
    )?;

    // Add global attributes
    writer.add_global_attribute(
        Attribute::new("Conventions", AttributeValue::text("CF-1.8"))?,
    )?;
    writer.add_global_attribute(
        Attribute::new("title", AttributeValue::text("Global Temperature Data"))?,
    )?;
    writer.add_global_attribute(
        Attribute::new("institution", AttributeValue::text("Climate Research Institute"))?,
    )?;

    // End define mode
    writer.end_define_mode()?;

    // Write coordinate data
    let lat_data: Vec<f32> = (-90..90).map(|i| i as f32).collect();
    writer.write_f32("lat", &lat_data)?;

    let lon_data: Vec<f32> = (-180..180).map(|i| i as f32).collect();
    writer.write_f32("lon", &lon_data)?;

    let time_data = vec![0.0, 1.0, 2.0];
    writer.write_f64("time", &time_data)?;

    // Write temperature data
    let temp_data = vec![273.15f32; 3 * 180 * 360];
    writer.write_f32("temperature", &temp_data)?;

    // Close file
    writer.close()?;

    Ok(())
}

CF Conventions Support

use oxigdal_netcdf::NetCdfReader;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let reader = NetCdfReader::open("cf_compliant_data.nc")?;

    // Check CF compliance
    if let Some(cf) = reader.cf_metadata() {
        if cf.is_cf_compliant() {
            println!("CF Conventions: {}", cf.conventions.as_deref().unwrap_or("N/A"));
            println!("Title: {}", cf.title.as_deref().unwrap_or("N/A"));
            println!("Institution: {}", cf.institution.as_deref().unwrap_or("N/A"));
            println!("History: {}", cf.history.as_deref().unwrap_or("N/A"));
        }
    }

    Ok(())
}

Async I/O (with async feature)

use oxigdal_netcdf::NetCdfReader;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Read file asynchronously
    let reader = NetCdfReader::open("large_dataset.nc")?;
    let data = reader.read_f32_async("temperature").await?;

    println!("Read {} temperature values", data.len());

    Ok(())
}

API Overview

Core Modules

Module Purpose
reader Read NetCDF files (automatic format detection)
writer Write NetCDF files with fine-grained control
metadata File metadata, versions, and CF compliance
dimension Dimension management (fixed and unlimited)
variable Variable definitions, data types, and attributes
attribute Global and variable-level attributes
cf_conventions CF 1.8 compliance validation and utilities
netcdf4 NetCDF-4/HDF5 format support (feature-gated)

Key Types

// File readers/writers
pub struct NetCdfReader { ... }
pub struct NetCdfWriter { ... }

// Format versions
pub enum NetCdfVersion {
    Classic,           // NetCDF-3 Classic
    Offset64Bit,       // NetCDF-3 with 64-bit offsets
    NetCdf4,           // NetCDF-4 with HDF5
    NetCdf4Classic,    // NetCDF-4 with classic model
}

// Data types
pub enum DataType {
    I8, U8, I16, U16, I32, U32, I64, U64,
    F32, F64, Char, String,
}

// Dimension management
pub struct Dimension { ... }
pub enum DimensionSize {
    Fixed(usize),
    Unlimited(usize),
}

// Variables and attributes
pub struct Variable { ... }
pub struct Attribute { ... }
pub enum AttributeValue {
    Text(String),
    F64(f64),
    F32(f32),
    I32(i32),
    U8(u8),
    // ... more types
}

// CF conventions
pub struct CfMetadata { ... }
pub enum CfComplianceLevel {
    Required,
    Recommended,
    Optional,
}

Supported Data Types

NetCDF-3 (Pure Rust, Default)

  • Integers: i8, i16, i32
  • Floating Point: f32, f64
  • Character: char

NetCDF-4 (Feature-Gated, Requires netcdf4 Feature)

Additional types (with HDF5):

  • Unsigned Integers: u8, u16, u32, u64
  • 64-bit Integers: i64
  • Variable-length Strings: String

Format Features

NetCDF-3 (Pure Rust Default)

  • ✅ Fixed dimensions
  • ✅ Single unlimited dimension
  • ✅ Multi-dimensional arrays
  • ✅ Global and variable attributes
  • ✅ Coordinate variables
  • ⚠️ No compression
  • ⚠️ No groups
  • ⚠️ No user-defined types

NetCDF-4 (HDF5-Based, Feature-Gated)

Additional features (requires netcdf4 feature and system libraries):

  • ✅ HDF5 compression (deflate, shuffle, etc.)
  • ✅ Multiple unlimited dimensions
  • ✅ Group hierarchies
  • ✅ User-defined types
  • ✅ All NetCDF-3 features

CF Conventions Support

When cf_conventions feature is enabled:

  • Metadata Validation - Check CF compliance
  • Coordinate Detection - Automatic coordinate variable identification
  • Units Validation - Verify standard CF units
  • Grid Mapping - Support for map projections
  • Cell Methods - Time/area averaging metadata
  • Bounds Variables - Cell boundary support
  • Cell Measures - Area/volume metadata

Performance Considerations

  • Pure Rust NetCDF-3: Comparable performance to C libraries (libnetcdf)
  • Lazy Metadata Loading: Metadata parsed on-demand
  • Unlimited Dimensions: May have slight performance overhead
  • Large Files: Consider chunked reading for memory efficiency
  • Streaming API: Available for processing large datasets without loading all in memory

Error Handling

All operations return Result<T, NetCdfError> with comprehensive error variants:

pub enum NetCdfError {
    Io(String),
    InvalidFormat(String),
    DimensionNotFound { name: String },
    VariableNotFound { name: String },
    AttributeNotFound { name: String },
    DataTypeMismatch { expected: String, found: String },
    FeatureNotEnabled { feature: String, message: String },
    // ... more variants
}

Feature Flags

  • netcdf3 (enabled by default) - Pure Rust NetCDF-3 support
  • cf_conventions - CF 1.8 compliance validation
  • async - Async I/O with tokio
  • netcdf4 - NetCDF-4/HDF5 support (requires system libraries, NOT Pure Rust)
  • compression - Compression support (NetCDF-4 only)
  • std (enabled by default) - Standard library support
  • alloc - Allocation support for no_std environments

COOLJAPAN Policies

  • Pure Rust - Default mode has zero C dependencies
  • No unwrap() - All errors properly handled with Result types
  • CF Compliant - Full CF Conventions 1.8 support
  • Well Tested - Comprehensive test suite included
  • Workspace - Uses workspace dependencies and settings
  • Latest Dependencies - Always uses latest compatible versions

Advanced Usage

Reading NetCDF-4 Files (with feature-gate)

#[cfg(feature = "netcdf4")]
use oxigdal_netcdf::Nc4Reader;

#[cfg(feature = "netcdf4")]
fn read_hdf5() -> Result<()> {
    let reader = Nc4Reader::open("compressed_data.nc4")?;
    // NetCDF-4 specific operations
    Ok(())
}

Compression Settings

#[cfg(feature = "netcdf4")]
use oxigdal_netcdf::{NetCdfWriter, CompressionFilter};

#[cfg(feature = "netcdf4")]
fn write_compressed() -> Result<()> {
    let mut writer = NetCdfWriter::create("output.nc4", NetCdfVersion::NetCdf4)?;
    // Configure compression
    let compression = CompressionFilter::deflate(9); // Maximum compression
    // ... rest of implementation
    Ok(())
}

Examples

See the examples directory for complete working examples:

  • create_test_netcdf_samples.rs - Creating sample NetCDF files with various configurations

Run examples with:

cargo run --example create_test_netcdf_samples --features netcdf3

Testing

Run the test suite:

# Test Pure Rust (NetCDF-3)
cargo test --features netcdf3

# Test with CF conventions
cargo test --all-features

# Run doctests
cargo test --doc

Documentation

Full API documentation is available at docs.rs.

Key documentation:

Limitations

Pure Rust Mode (NetCDF-3)

When using the default Pure Rust implementation:

  • Single unlimited dimension only (NetCDF-3 Classic/64-bit limitation)
  • No HDF5-based compression
  • No group hierarchies
  • Limited to NetCDF-3 data types
  • No user-defined types

To Use NetCDF-4/HDF5 Features

Enable the netcdf4 feature (requires C dependencies):

oxigdal-netcdf = { version = "0.1", features = ["netcdf4"] }

Prerequisites:

  • libnetcdf ≥ 4.0
  • libhdf5 ≥ 1.8

Note: Enabling this violates the COOLJAPAN Pure Rust policy.

Integration with OxiGDAL

This driver integrates seamlessly with the OxiGDAL ecosystem:

use oxigdal_core::gdal::Driver;
use oxigdal_netcdf::NetCdfReader;

fn main() -> Result<()> {
    // Register NetCDF driver with OxiGDAL
    let reader = NetCdfReader::open("climate_data.nc")?;

    // Work with dimensions, variables, and attributes
    // through the unified OxiGDAL interface
    Ok(())
}

Comparison with Other Libraries

Feature oxigdal-netcdf netCDF-C netcdf4 crate
Pure Rust (default)
NetCDF-3
NetCDF-4/HDF5 ✅ (opt-in)
CF Conventions ⚠️ ⚠️
No unsafe code* ⚠️
Zero-copy reading
Async I/O

*In Pure Rust mode (default)

Performance Benchmarks

Typical performance (on 2.5GHz CPU with modern SSD):

Operation Time
Open 100MB file ~50ms
Read 1M f32 values ~100ms
Write 1M f32 values ~150ms
Parse CF metadata ~10ms

Performance varies based on system configuration and file complexity.

License

Licensed under Apache-2.0.

Copyright © 2025 COOLJAPAN OU (Team Kitasan)

See Also