README.md

# Excdf

[![Hex.pm](https://img.shields.io/hexpm/v/excdf.svg)](https://hex.pm/packages/excdf)
[![Docs](https://img.shields.io/badge/hex-docs-blue.svg)](https://hexdocs.pm/excdf)

Elixir NIF bindings for the [netCDF](https://www.unidata.ucar.edu/software/netcdf/) C library.
Read and write netCDF files — the standard format for array-oriented scientific data
(climate, ocean, atmospheric, geospatial, and more).

## Prerequisites

The netCDF C library must be installed on your system:

```bash
# macOS
brew install netcdf

# Ubuntu / Debian
sudo apt-get install libnetcdf-dev

# Fedora / RHEL
sudo dnf install netcdf-devel
```

The `nc-config` tool (installed alongside libnetcdf) is used at compile time
to locate headers and libraries automatically.

## Installation

Add `excdf` to your list of dependencies in `mix.exs`:

```elixir
def deps do
  [
    {:excdf, "~> 0.1.0"}
  ]
end
```

## Quick start

### Writing a netCDF file

```elixir
# Create a new netCDF-4 file
{:ok, nc} = Excdf.create("weather.nc", :netcdf4)

# Define dimensions
{:ok, x_dim} = Excdf.def_dim(nc, "x", 10)
{:ok, y_dim} = Excdf.def_dim(nc, "y", 20)
{:ok, time_dim} = Excdf.def_dim(nc, "time", :unlimited)

# Define a variable with its dimensions
{:ok, temp_var} = Excdf.def_var(nc, "temperature", :double, [time_dim, y_dim, x_dim])

# Add metadata as attributes
:ok = Excdf.put_att(nc, temp_var, "units", "kelvin")
:ok = Excdf.put_att(nc, temp_var, "long_name", "Surface Temperature")
:ok = Excdf.put_att_global(nc, "title", "Example weather dataset")

# Switch to data mode and write values
:ok = Excdf.enddef(nc)
:ok = Excdf.put_var(nc, temp_var, :double, List.duplicate(293.15, 10 * 20))

:ok = Excdf.close(nc)
```

### Reading a netCDF file

```elixir
{:ok, nc} = Excdf.open("weather.nc")

# Get a summary of everything in the file
{:ok, info} = Excdf.info(nc)
# => %{
#   dimensions: %{"x" => 10, "y" => 20, "time" => 1},
#   variables: [%{name: "temperature", type: :double, ...}],
#   global_attributes: %{"title" => "Example weather dataset"}
# }

# Read variable data by name or ID
{:ok, {:double, shape, data}} = Excdf.get_var(nc, "temperature")

# Read a slice (hyperslab)
{:ok, {:double, slice}} = Excdf.get_vara(nc, 0, [0, 0, 0], [1, 5, 5])

# Read attributes
{:ok, "kelvin"} = Excdf.get_att(nc, 0, "units")
{:ok, "Example weather dataset"} = Excdf.get_att_global(nc, "title")

:ok = Excdf.close(nc)
```

## Supported data types

| Atom       | netCDF type  | Elixir values    |
|------------|-------------|------------------|
| `:byte`    | `NC_BYTE`   | integers         |
| `:ubyte`   | `NC_UBYTE`  | integers         |
| `:char`    | `NC_CHAR`   | strings          |
| `:short`   | `NC_SHORT`  | integers         |
| `:ushort`  | `NC_USHORT` | integers         |
| `:int`     | `NC_INT`    | integers         |
| `:uint`    | `NC_UINT`   | integers         |
| `:int64`   | `NC_INT64`  | integers         |
| `:uint64`  | `NC_UINT64` | integers         |
| `:float`   | `NC_FLOAT`  | floats           |
| `:double`  | `NC_DOUBLE` | floats/integers  |

## Supported file formats

| Atom               | Format                    |
|--------------------|---------------------------|
| `:classic`         | netCDF Classic             |
| `:classic64`       | netCDF 64-bit Offset       |
| `:netcdf4`         | netCDF-4 (HDF5-backed)     |
| `:netcdf4_classic` | netCDF-4 Classic Model     |

## API reference

See the full API documentation on [HexDocs](https://hexdocs.pm/excdf).

## License

MIT License. See [LICENSE](LICENSE) for details.