Many meteorological and climate datasets are not stored on simple regular latitude/longitude grids, or even on regular geospatial coordinate systems. Modern NWP models often use alternative grid structures - such as HEALPix (Hierarchical Equal Area isoLatitude Pixelization) and Octahedral reduced Gaussian grids — which offer better spatial uniformity or computational efficiency. Visualising these grids naively can be frustrating with typical open-source tooling: the data points don’t align to a regular mesh, so you can’t simply call standard matplotlib/cartopy methods like contour or pcolormesh.
With earthkit, as long as your data carries the right metadata (e.g. a GRIB file with a gridType key), earthkit-plots (underpinned by earthkit-geo) can identify the grid type and choose an appropriate rendering strategy automatically — no manual coordinate juggling required.
Plotting methods for non-regular grids¶
earthkit-plots provides four methods that work out of the box with unstructured or reduced grids:
| Method | What it draws | Best for |
|---|---|---|
point_cloud | A scatter marker at every grid point, coloured by value | Quick previews; very fast |
grid_cells | Each grid cell rendered as a filled polygon | Accurate cell-boundary visualisation |
contourf | Interpolated filled contours resampled onto a regular grid | Smooth, continuous visualisations that look more “physical” |
grid_points | Marker at every grid point (no fill) | Inspecting grid density/distribution |
point_cloud and grid_points are the lightest options — they plot one marker per data point and require no interpolation. grid_cells draws the true shape of each cell, which is most faithful to the underlying discretisation but can be slower for high-resolution grids. contourf resamples the irregular data onto a regular target grid first (using earthkit-geo under the hood) and then applies standard matplotlib contouring, giving smooth, visually clean results.
HEALPix grids¶
Let’s start with some temperature data on a HEALPix grid. First, let’s access a sample HEALPix dataset and inspect the grid.
| NOTE: To visualise HEALPix grid cells, you need the healpy library:
!pip install healpyimport earthkit.data as ekd
import earthkit.plots as ekp
healpix_data = ekd.from_source("sample", "healpix-h128-nested-2t.grib")
healpix_data.to_fieldlist().geography.grid_spec()Now let’s visualise the data over a small domain (France and Spain) so that we can see the details of the grid, using the four methods described above.
figure = ekp.Figure(rows=2, columns=2, domain=["France", "Spain"])
style = ekp.styles.Style(
levels=range(0, 21),
colors="Spectral_r",
units="celsius",
)
for method in ["point_cloud", "grid_cells", "contourf"]:
subplot = figure.add_map()
getattr(subplot, method)(healpix_data, style=style)
subplot.title(f"${method}$".replace("_", "\_"))
subplot = figure.add_map()
subplot.grid_points(healpix_data)
subplot.title("$grid\_points$")
figure.coastlines()
figure.gridlines()
figure.title("Plotting HEALPix grid data with various methods")
figure.legend(location="right")
figure.show()Octahedral reduced Gaussian¶
Now let’s do the same with Octahedral reduced Gaussian grid data. First, let’s access a sample dataset and inspect the grid.
rgg_data = ekd.from_source("url", "https://get.ecmwf.int/repository/test-data/earthkit-regrid/test-data/global_0_360/O32.grib")
rgg_data.to_fieldlist().geography.grid_spec()Now let’s plot it. Note that other than the change in domain and style, the code we use to plot this dataset is exactly the same as the code we used to plot the HEALPix data above.
figure = ekp.Figure(rows=2, columns=2, domain="Arctic")
style = ekp.styles.Style(
levels=range(-40, 40),
colors="Spectral_r",
units="celsius",
)
for method in ["point_cloud", "grid_cells", "contourf"]:
subplot = figure.add_map()
getattr(subplot, method)(rgg_data, style=style)
subplot.title(f"${method}$".replace("_", "\_"))
subplot = figure.add_map()
subplot.grid_points(rgg_data)
subplot.title("$grid\_points$")
figure.title("Plotting Octahedral grid data with various methods")
figure.coastlines()
figure.borders()
figure.legend(location="right")
figure.show()When metadata is missing¶
So far we have been loading data from GRIB files, which carry rich metadata about the grid that earthkit can read directly. When you convert GRIB data to an xarray Dataset with to_xarray(), that metadata is embedded in the _earthkit attribute on each variable. earthkit-plots reads this attribute automatically, so the same plotting calls work without any extra configuration.
However, the cell below deliberately removes the _earthkit attribute to simulate a common real-world scenario: you have an xarray Dataset whose spatial structure is not described in its metadata — perhaps it was produced by a third-party tool, loaded from a NetCDF file, or had its attributes stripped. In this case earthkit-plots cannot infer the grid type on its own.
healpix_ring_data = ekd.from_source("sample", "healpix-h128-ring-2t.grib")
ds = healpix_ring_data.to_xarray()
ds.t.attrs.pop("_earthkit")
dsWhen your dataset lacks the _earthkit metadata, you can supply the grid specification directly via the resample argument. The Regrid resampler accepts an in_grid dictionary that describes the source grid — here {"grid": "H128", "order": "ring"} tells earthkit-geo that the data lives on a HEALPix grid with nside=128 in ring ordering.
earthkit-plots passes this information to earthkit-geo, which regrids the data onto a regular lat/lon grid before plotting. The result is identical to the GRIB-backed example above; the only difference is that we had to be explicit about the grid type because the metadata was absent.
from earthkit.plots.resample import Regrid
style = ekp.styles.Style(
levels=range(0, 21),
colors="Spectral_r",
units="celsius",
)
ekp.geo.plot(
ds, domain=["France", "Spain"], style=style,
resample=Regrid(in_grid={"grid": "H128", "order": "ring"}),
).show()Exercises¶
Given the regular lat-lon grid data below, can you visualise it in the same four ways we visualised reduced Gaussian and HEALPix data, over Spain?
era5_2t = ekd.from_source("sample", "era5-monthly-mean-2t-199312.grib")