How to Select Specific Spatial Points from Weather Model Data Using Herbie

Use the ds.herbie.pick_points xarray accessor to extract values at specific latitude/longitude locations from any gridded weather model dataset loaded through Herbie.

Herbie is a Python package that simplifies downloading and reading GRIB2 weather model data. When you need to extract forecast values at specific station locations, buoy coordinates, or custom points rather than working with full grids, Herbie provides a high-performance spatial indexing system through an xarray accessor interface.

Understanding the Herbie Point Selection Architecture

The point extraction capability relies on two core components that work together to provide fast, model-agnostic spatial queries.

The Xarray Accessor Interface

Herbie registers a custom accessor under xarray.Dataset.herbie through the HerbieAccessor class defined in src/herbie/accessors.py. This accessor acts as a namespace that attaches Herbie-specific methods to any dataset loaded via Herbie's xarray() method. When you call ds.herbie.pick_points(), the accessor forwards the request to the underlying GridPointPicker implementation while preserving the dataset's metadata and coordinate system.

Spatial Indexing with BallTree

The actual spatial logic resides in src/herbie/pick_points.py within the GridPointPicker class. Rather than performing expensive distance calculations for every query, Herbie builds a BallTree spatial index using sklearn.neighbors.BallTree on the model's grid coordinates (latitude/longitude). This tree structure enables logarithmic-time nearest-neighbor searches, making it efficient to query thousands of points against high-resolution grids like HRRR or GFS. The index is constructed once per dataset and can be cached to disk for reuse across sessions.

How to Select Spatial Points from GRIB2 Data

The pick_points method provides two interpolation strategies: nearest-neighbor extraction for speed and inverse-distance weighting for smoother results at grid scale boundaries.

Loading Model Data and Defining Points

First, load your weather model data through Herbie and prepare a pandas DataFrame containing your target coordinates:

import pandas as pd
from herbie import Herbie

# Load HRRR 2-meter temperature data

H = Herbie("2024-03-01 00:00", model="hrrr")
ds = H.xarray("TMP:2 m")

# Define points of interest (stations, cities, sensors)

points = pd.DataFrame({
    "longitude": [-100.0, -105.2, -98.4],
    "latitude":  [  40.0,   29.5,  42.3],
    "stid":      ["AAA",   "BBB",  "CCC"]   # Extra metadata preserved

})

Nearest-Neighbor Extraction

Use the nearest method to extract values from the closest grid point to each location. This is fastest and appropriate when you want exact model values without interpolation:


# Extract nearest grid point for each location

nearest_ds = ds.herbie.pick_points(points, method="nearest")
print(nearest_ds)

The resulting dataset contains your original data variables plus new coordinates: point_latitude, point_longitude, and point_grid_distance (the distance in kilometers between your requested point and the actual grid point used).

Inverse-Distance Weighted Interpolation

Use the weighted method to compute an inverse-distance weighted mean of the k nearest grid points. This smooths values across grid boundaries and reduces discontinuities:


# Weighted interpolation using default k=4 neighbors

weighted_ds = ds.herbie.pick_points(points, method="weighted")

By default, Herbie uses the 4 nearest neighbors. The weights are calculated as the inverse of the distance to each neighbor, normalized so that closer points contribute more to the final value.

Advanced Point Selection Options

The pick_points method exposes several parameters to control search behavior, performance, and result formatting.

Controlling Search Radius with max_distance

Prevent extraction from grid points that are too far from your locations using the max_distance parameter (specified in kilometers). This is useful when working with sparse global models like GFS where distant grid points may not represent local conditions:


# Only return values if the nearest grid point is within 200 km

filtered_ds = ds.herbie.pick_points(points, max_distance=200)

If no grid points fall within the specified radius for a given location, that point is excluded from the results.

Caching the BallTree for Performance

When processing multiple variables from the same model run, building the spatial index can be reused. Herbie automatically caches BallTree objects to disk when use_cached_tree=True (the default):


# Force rebuilding the cache (useful if grid definition changes)

cached_ds = ds.herbie.pick_points(
    points,
    use_cached_tree="replant",  # Options: True, False, or "replant"

    tree_name="hrrr_20240301"   # Custom name for cache file

)

Cached trees are stored as pickle files under the configured cache_dir in a BallTree/ subdirectory, significantly speeding up repeated extractions from the same model grid.

Customizing Neighbor Count (k)

Adjust the number of neighbors used for weighted interpolation to control smoothing. Higher values produce smoother fields but may blur sharp gradients:


# Use 8 neighbors for smoother weighted interpolation

smooth_ds = ds.herbie.pick_points(points, method="weighted", k=8)

Working with Results

The output of pick_points is a standard xarray Dataset that integrates seamlessly with the scientific Python ecosystem.

Converting to Pandas DataFrames

For tabular analysis or export to CSV, convert the result to a pandas DataFrame:

df = nearest_ds.to_dataframe().reset_index()
print(df.head())

The DataFrame contains columns for the original data variables, the point index, and the point_* coordinate columns including distances and any custom metadata you provided.

Indexing by Station ID

If your input points DataFrame contains a station identifier column (like stid in the examples above), you can swap the generic point dimension for your custom ID to make selection more intuitive:


# Replace point index with station ID for easier selection

by_station = nearest_ds.swap_dims({"point": "point_stid"})
print(by_station.sel(point_stid="AAA"))

This allows you to select data using meaningful identifiers rather than integer indices.

Summary

  • Herbie provides the ds.herbie.pick_points accessor to extract values at specific lat/lon coordinates from any loaded GRIB2 dataset.
  • Two interpolation methods are available: nearest for exact grid-point values and weighted for inverse-distance interpolation using k neighbors (default 4).
  • Spatial indexing uses sklearn's BallTree (src/herbie/pick_points.py) for efficient logarithmic-time queries, with optional disk caching to speed up repeated extractions.
  • Key parameters include max_distance (km), k (neighbor count), and use_cached_tree for cache control.
  • Results are returned as xarray Datasets with point_* coordinates including grid distance in kilometers, and can be converted to pandas DataFrames or indexed by custom station IDs.

Frequently Asked Questions

What is the difference between the "nearest" and "weighted" methods in Herbie?

The nearest method extracts the exact value from the single closest grid point to your requested location, making it fastest and preserving original model values. The weighted method calculates an inverse-distance weighted average of the k nearest grid points (default 4), which smooths values across grid boundaries and reduces discontinuities when sampling near terrain features or coastlines.

How does Herbie handle points that are far from the model grid?

Herbie uses the max_distance parameter (specified in kilometers) to filter out grid points that are too distant from requested locations. If the nearest grid point exceeds this distance threshold, that point is excluded from results. This prevents inappropriate extrapolation when working with sparse global models like GFS or when requesting points outside the model domain.

Can I reuse the spatial index for multiple variables from the same model run?

Yes, Herbie automatically caches the BallTree spatial index to disk when use_cached_tree=True (the default). The cache is stored as a pickle file under the configured cache_dir/BallTree/ directory using a hash of the grid coordinates. When processing multiple variables from the same model run, subsequent calls reuse the cached tree, eliminating the overhead of rebuilding the spatial index and significantly speeding up batch extractions.

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 →