# How Herbie Handles Coordinate Reference Systems in GRIB-2 Files

> Learn how Herbie extracts and converts CRS metadata from GRIB-2 files into CF-compliant grid mapping for Xarray Datasets, improving your geospatial data workflows.

- Repository: [Brian Blaylock/herbie](https://github.com/blaylockbk/herbie)
- Tags: internals
- Published: 2026-02-26

---

**Herbie extracts projection metadata from GRIB-2 messages, converts it to a CF-compliant grid mapping, and attaches it as a `gribfile_projection` coordinate to every Xarray Dataset it returns.**

Herbie is an open-source Python library that simplifies downloading and reading GRIB-2 weather model data. When working with meteorological datasets, understanding the coordinate reference system (CRS) is essential for accurate geospatial analysis. Herbie automatically handles CRS extraction and representation by parsing GRIB-2 projection parameters and standardizing them into Climate and Forecast (CF) conventions that integrate seamlessly with Xarray, Cartopy, and other scientific Python tools.

## Understanding Herbie's CRS Pipeline

Herbie's approach to coordinate reference systems follows a three-stage pipeline: extraction, conversion, and attachment. The library first reads projection parameters embedded in GRIB-2 messages using the `get_cf_crs` function in [`src/herbie/crs.py`](https://github.com/blaylockbk/herbie/blob/main/src/herbie/crs.py), then maps these parameters to CF-compliant grid mapping attributes, and finally attaches this metadata as a coordinate variable to the resulting Xarray Dataset. This ensures that every data variable retains explicit georeferencing information compatible with geospatial visualization and analysis workflows.

## Extracting Projection Metadata from GRIB-2

### Primary Extraction via cfgrib

By default, Herbie relies on the `cfgrib` engine to open GRIB-2 files. The core logic resides in [`src/herbie/crs.py`](https://github.com/blaylockbk/herbie/blob/main/src/herbie/crs.py), specifically within the `get_cf_crs` function. This function analyzes GRIB field attributes such as `GRIB_gridType`, `GRIB_shapeOfTheEarth`, and model-specific parameters to construct a projection dictionary.

When processing a dataset, Herbie inspects these attributes to determine the grid type and corresponding projection parameters. For example, it identifies whether the data uses a Lambert Conformal Conic, regular latitude-longitude, or polar stereographic projection based on the `GRIB_gridType` value.

### Pygrib Fallback for Complex Projections

In cases where `cfgrib` does not provide sufficient projection metadata, Herbie implements a fallback mechanism using `pygrib`. When the user sets `_use_pygrib_for_crs=True`, Herbie reads the first GRIB message with `pygrib`, extracts the `projparams` attribute, and constructs a `pyproj.CRS` object.

As implemented in [`src/herbie/core.py`](https://github.com/blaylockbk/herbie/blob/main/src/herbie/core.py) at lines 1345-1346, this fallback converts the pygrib parameters to CF conventions using:

```python
CRS(msg.projparams).to_cf()

```

This ensures that even when the primary extraction method fails, Herbie can still produce a standardized CF-compliant grid mapping.

## Mapping GRIB Grid Types to CF Conventions

Herbie's `get_cf_crs` function in [`src/herbie/crs.py`](https://github.com/blaylockbk/herbie/blob/main/src/herbie/crs.py) contains specific logic for translating GRIB grid definitions into CF grid mapping parameters. The implementation handles four primary projection types:

### Lambert Conformal Conic

For Lambert Conformal Conic grids (`GRIB_gridType` == "lambert"), Herbie constructs a proj-params dictionary with `proj: "lcc"` and includes:

- Semi-major and semi-minor axes derived from `GRIB_shapeOfTheEarth`
- Central longitude (`lon_0`)
- Latitude of origin (`lat_0`)
- Standard parallels (`lat_1`, `lat_2`)

This logic appears in [`src/herbie/crs.py`](https://github.com/blaylockbk/herbie/blob/main/src/herbie/crs.py) at lines 71-78.

### Regular and Rotated Latitude-Longitude

For regular latitude-longitude grids, Herbie sets `proj: "longlat"` and includes the appropriate Earth radii (lines 80-84 in [`src/herbie/crs.py`](https://github.com/blaylockbk/herbie/blob/main/src/herbie/crs.py)).

For rotated latitude-longitude grids, which use an oblique transformation, Herbie uses `proj: "ob_tran"` and specifies the location of the rotated pole using parameters `lon_0`, `o_lon_p`, and `o_lat_p` (lines 85-92).

### Polar Stereographic

For polar stereographic projections, Herbie creates a proj-params dictionary with `proj: "stere"` and includes:

- `lat_ts` (latitude of true scale)
- `lat_0 = 90` (projection origin)
- `lon_0` (central meridian)

This implementation appears at lines 99-106 in [`src/herbie/crs.py`](https://github.com/blaylockbk/herbie/blob/main/src/herbie/crs.py).

### Earth Shape Parameters

Before constructing the projection, Herbie determines the appropriate Earth shape parameters based on `GRIB_shapeOfTheEarth` and the specific weather model. For example, the HRRR model uses a spherical radius of 6,371,229 meters, while GRAPHCAST uses 4,326 kilometers. This logic resides in lines 35-63 of [`src/herbie/crs.py`](https://github.com/blaylockbk/herbie/blob/main/src/herbie/crs.py).

## Attaching CRS to Xarray Datasets

Once Herbie converts the GRIB projection to CF conventions, it attaches this metadata to the Xarray Dataset. In [`src/herbie/core.py`](https://github.com/blaylockbk/herbie/blob/main/src/herbie/core.py) (lines 64-73), Herbie performs the following steps:

1. Creates a coordinate variable named `gribfile_projection`
2. Stores the CF grid mapping attributes in this coordinate's metadata
3. Sets each data variable's `grid_mapping` attribute to reference `"gribfile_projection"`

This approach ensures that the projection information is discoverable by any downstream library that understands CF conventions, such as Cartopy for mapping or Xarray's built-in plotting functions.

Additionally, Herbie includes a specific fallback for polar stereographic projections to address a known pyproj issue. If `latitude_of_projection_origin` is missing, Herbie explicitly defaults it to 90° (lines 38-44 in [`src/herbie/core.py`](https://github.com/blaylockbk/herbie/blob/main/src/herbie/core.py)).

## Working with Herbie CRS in Practice

### Accessing Projection Information from Herbie Datasets

When you load data using Herbie's high-level API, the CRS information is automatically attached:

```python
from herbie import Herbie

# Load HRRR data (uses Lambert Conformal Conic projection)

h = Herbie('2024022600', model='hrrr')
ds = h.xarray()  # Returns an xarray.Dataset with CRS attached

# Access the CF grid mapping

proj_attrs = ds.coords["gribfile_projection"].attrs
print(proj_attrs["grid_mapping_name"])

# Output: 'lambert_conformal_conic'

# Verify data variables reference the projection

print(ds["TMP_2m"].attrs["grid_mapping"])

# Output: 'gribfile_projection'

```

### Using the Low-Level CRS Helper

For advanced use cases or when working with cfgrib directly, you can access Herbie's CRS logic explicitly:

```python
from herbie.crs import get_cf_crs
import cfgrib

# Open GRIB file with cfgrib

datasets = cfgrib.open_datasets("hrrr.t12z.conus.grib2")

# Extract CF-compliant CRS dictionary

cf_crs = get_cf_crs(datasets[0])

print(cf_crs)

# {

#     'grid_mapping_name': 'lambert_conformal_conic',

#     'standard_parallel': [38.5, 38.5],

#     'longitude_of_central_meridian': 262.5,

#     'latitude_of_projection_origin': 38.5,

#     'earth_radius': 6371229.0

# }

```

This low-level access is particularly useful when preprocessing data before ingestion into geospatial workflows that require explicit CF grid mapping metadata.

## Summary

- Herbie automatically extracts coordinate reference system metadata from GRIB-2 files using the `get_cf_crs` function in [`src/herbie/crs.py`](https://github.com/blaylockbk/herbie/blob/main/src/herbie/crs.py).
- The library supports multiple projection types including Lambert Conformal Conic, regular and rotated latitude-longitude, and polar stereographic grids.
- Herbie converts GRIB-2 projection parameters to Climate and Forecast (CF) conventions using `pyproj.CRS.to_cf()`, ensuring interoperability with scientific Python tools.
- Every Xarray Dataset returned by Herbie includes a `gribfile_projection` coordinate containing the CF grid mapping attributes.
- Data variables automatically reference this projection via the `grid_mapping` attribute, enabling seamless integration with Cartopy and other geospatial libraries.

## Frequently Asked Questions

### How does Herbie determine which coordinate reference system to use for a GRIB file?

Herbie inspects the `GRIB_gridType` attribute and other GRIB-specific metadata to identify the projection type. In [`src/herbie/crs.py`](https://github.com/blaylockbk/herbie/blob/main/src/herbie/crs.py), the `get_cf_crs` function maps these attributes to specific proj-params dictionaries—for example, identifying "lambert" as Lambert Conformal Conic or "regular_ll" as regular latitude-longitude. The function also considers the `GRIB_shapeOfTheEarth` attribute and model-specific parameters to determine the correct Earth radius or ellipsoid parameters.

### What is the difference between using cfgrib and pygrib for CRS extraction in Herbie?

By default, Herbie uses the cfgrib engine to read GRIB-2 files and extract projection information directly from the dataset attributes. However, when `_use_pygrib_for_crs=True` is set, Herbie falls back to reading the first GRIB message using the pygrib library, extracting the `projparams` attribute, and converting it to CF conventions via `pyproj.CRS`. This fallback is implemented in [`src/herbie/core.py`](https://github.com/blaylockbk/herbie/blob/main/src/herbie/core.py) at lines 1345-1346 and is useful when cfgrib does not provide complete projection metadata for certain GRIB variants.

### Why does Herbie use CF conventions for representing coordinate reference systems?

Herbie converts GRIB-2 projection parameters to Climate and Forecast (CF) conventions because CF is the standard metadata convention used by the scientific Python ecosystem, including Xarray, Cartopy, and NetCDF tools. By storing the projection as a CF-compliant `gribfile_projection` coordinate with attributes like `grid_mapping_name` and `standard_parallel`, Herbie ensures that datasets can be automatically plotted on maps, reprojected, or exported to NetCDF with georeferencing intact. This approach bridges the gap between specialized GRIB-2 metadata and general-purpose geospatial workflows.

### How can I access the coordinate reference system information from a Herbie dataset?

After loading data with `Herbie().xarray()`, you can access the CRS information through the `gribfile_projection` coordinate in the returned Xarray Dataset. For example, `ds.coords["gribfile_projection"].attrs` returns a dictionary containing CF grid mapping attributes such as `grid_mapping_name`, `longitude_of_central_meridian`, and `standard_parallel`. Additionally, each data variable in the dataset has a `grid_mapping` attribute that references `"gribfile_projection"`, confirming that the variable is associated with that specific coordinate reference system.