How Herbie Handles Coordinate Reference Systems in GRIB-2 Files

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, 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, 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 at lines 1345-1346, this fallback converts the pygrib parameters to CF conventions using:

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 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 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).

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.

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.

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 (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).

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:

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:

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.
  • 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, 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 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →