Skip to article frontmatterSkip to article content

Xradar Basics

xradar

Xradar Basics


Overview

  1. Xradar general overview
  2. Radar data IO
  3. Radar data georeferencing
  4. Data visualization

Prerequisites

ConceptsImportanceNotes
Intro to XarrayNecessaryBasic features
Radar CookbookNecessaryRadar basics
MatplotlibNecessaryPlotting basic features
  • Time to learn: 30 minutes

Imports

import xradar as xd
import pyart
import wradlib as wrl

import fsspec
import numpy as np
from xarray.backends.api import open_datatree

import matplotlib.pyplot as plt
import cartopy.crs as ccrs

## You are using the Python ARM Radar Toolkit (Py-ART), an open source
## library for working with weather radar data. Py-ART is partly
## supported by the U.S. Department of Energy as part of the Atmospheric
## Radiation Measurement (ARM) Climate Research Facility, an Office of
## Science user facility.
##
## If you use this software to prepare a publication, please cite:
##
##     JJ Helmus and SM Collis, JORS 2016, doi: 10.5334/jors.119

Xradar

Xradar is a Python package developed to streamline the reading and writing of radar data across various formats, with exports aligned to standards like ODIM_H5 and CfRadial. Born from the Open Radar Science Community’s collaboration at ERAD2022, Xradar uses an xarray-based data model, compatible with the upcoming CfRadial2.1/FM301 standard. This ensures seamless integration with other xarray-based software and existing open-source radar processing tools.

Xradar data model

Xradar leverages the upcoming FM301 standard, a subset of CfRadial2.0, using Xarray to efficiently manage radar data.

DataTree Structure (CfRadial2.1/FM301 standard)

Xradar employs xarray.DataTree objects to organize radar sweeps within a single structure, where each sweep is an xarray.Dataset containing relevant metadata and variables.

xradar

Xradar importers

Xradar supports several importers to handle different radar data formats. These importers typically include:

  • ODIM_H5: For reading radar data in the ODIM_H5 format, which is widely used in Europe and supported by the EUMETNET OPERA program.

  • CfRadial: For importing data in the CfRadial format, which is commonly used in the atmospheric sciences community.

  • SIGMET/IRIS: For ingesting radar data from SIGMET/IRIS formats, which are used by various weather radar systems globally.

  • GAMIC: For reading data from GAMIC radars, which use their proprietary format.

For more importers check here

Let’s find some Canadian radar data located at the ams 2025 Bucket

# Set the URL and path for the cloud
URL = "https://js2.jetstream-cloud.org:8001/"
path = f"pythia/radar/ams2025"

fs = fsspec.filesystem("s3", anon=True, client_kwargs=dict(endpoint_url=URL))

fs.glob(f"{path}/*")
['pythia/radar/ams2025/CalgaryHailStorm2024', 'pythia/radar/ams2025/OntarioDerecho2022']
files = fs.glob(
    "pythia/radar/ams2025/CalgaryHailStorm2024/*.h5"
)
files[:3]
['pythia/radar/ams2025/CalgaryHailStorm2024/2024080600_00_ODIMH5_PVOL6S_VOL_CASSM.h5', 'pythia/radar/ams2025/CalgaryHailStorm2024/2024080600_06_ODIMH5_PVOL6S_VOL_CASSM.h5', 'pythia/radar/ams2025/CalgaryHailStorm2024/2024080600_12_ODIMH5_PVOL6S_VOL_CASSM.h5']
radar_files = [f"s3://{i}" for i in files]
radar_files[-3:]
['s3://pythia/radar/ams2025/CalgaryHailStorm2024/2024080605_42_ODIMH5_PVOL6S_VOL_CASSM.h5', 's3://pythia/radar/ams2025/CalgaryHailStorm2024/2024080605_48_ODIMH5_PVOL6S_VOL_CASSM.h5', 's3://pythia/radar/ams2025/CalgaryHailStorm2024/2024080605_54_ODIMH5_PVOL6S_VOL_CASSM.h5']
local_files = [
    fsspec.open_local(
        f"simplecache::{URL}{i}", s3={"anon": True}, filecache={"cache_storage": "."}
    )
    for i in files
]

We can open one of this nc files using xradar.io.open_cfradial1_datree method

# with fs.open(radar_files[0], mode="rb", anon=True, client_kwargs=dict(endpoint_url=URL)) as file:
#     dtree = xd.io.open_cfradial1_datatree(file)
dt = xd.io.open_odim_datatree(local_files[-1])
display(dt)
Loading...

In this Xarray.Datatree object, the first two nodes contains radar metadata and the others contains Xarray.Datasets for each sweeps.

for sweep in dt.children:
    try:
        print(
            f"{sweep} - elevation {np.round(dt[sweep]['sweep_fixed_angle'].values[...], 1)}"
        )
    except KeyError:
        print(sweep)
sweep_0 - elevation 24.4
sweep_1 - elevation 20.2
sweep_2 - elevation 16.6
sweep_3 - elevation 13.7
sweep_4 - elevation 11.3
sweep_5 - elevation 9.4
sweep_6 - elevation 7.7
sweep_7 - elevation 6.4
sweep_8 - elevation 5.3
sweep_9 - elevation 4.4
sweep_10 - elevation 3.5
sweep_11 - elevation 2.7
sweep_12 - elevation 2.1
sweep_13 - elevation 1.6
sweep_14 - elevation 1.2
sweep_15 - elevation 0.8
sweep_16 - elevation 0.4

Let’s explore the 1.0 degrees elevation (‘sweep_2’)

ds_sw2 = dt["sweep_2"].ds
display(ds_sw2)
Loading...

Xradar visualization

We can make a plot using xarray.plot functionality.

ds_sw2.DBZH.plot(
    cmap="ChaseSpectral",
    vmax=60,
    vmin=-10,
)
<Figure size 640x480 with 2 Axes>

The radar data in the Xarray.Dataset object includes range and azimuth as coordinates. To create a radial plot, apply the xradar.georeference method. This method will generate x, y, and z coordinates for the plot.

ds_sw2 = ds_sw2.xradar.georeference()
display(ds_sw2)
Loading...

We can now create a radial plot passing x and y coordinates

ds_sw2.DBZH.plot(
    x="x",
    y="y",
    cmap="ChaseSpectral",
    vmax=60,
    vmin=-10,
    xlim=(-50_000, 50_000),
    ylim=(-50_000, 50_000)
)
<Figure size 640x480 with 2 Axes>

Data slicing

We can use the power of Xarray to acces data within the first 50 kilometers by slicing along coordinates.

ds_sw2.sel(range=slice(0, 5e4)).DBZH.plot(
    x="x",
    y="y",
    cmap="ChaseSpectral",
    vmax=60,
    vmin=-10,
)
<Figure size 640x480 with 2 Axes>

Let’s suposse we want to subset between 90 and 180 degrees angle in azumith

ds_sw2.sel(azimuth=slice(90, 180), range=slice(0, 5e4)).DBZH.plot(
    x="x",
    y="y",
    cmap="ChaseSpectral",
    vmax=60,
    vmin=-10,
)
<Figure size 640x480 with 2 Axes>

Perhaps, we just what to see radar reflectivity along the 100 degrees angle in azimuth

ds_sw2.sel(azimuth=125, method="nearest").DBZH.plot()
<Figure size 640x480 with 1 Axes>

Xradar integration

Py-Art

Xradar datatree objects can be ported to Py-ART radar objects using the pyart.xradar.Xradar method.

dt
Loading...
dt = xd.io.open_odim_datatree(local_files[-1])
radar = dt.pyart.to_radar()
radar.combined_sweeps
Loading...
radar.combined_sweeps.sel(sweep_number=2)
Loading...
def generate_title(radar, field, sweep, datetime_format=None, use_sweep_time=True):
    """
    Generate a title for a plot.

    Parameters
    ----------
    radar : Radar
        Radar structure.
    field : str
        Field plotted.
    sweep : int
        Sweep plotted.
    datetime_format : str
        Format of datetime (using strftime format).
    use_sweep_time : bool
        If true, the current sweep's beginning time is used.

    Returns
    -------
    title : str
        Plot title.

    """
    if use_sweep_time:
        begin_time = pyart.graph.generate_radar_time_sweep(radar, sweep)
    else:
        begin_time = pyart.graph.generate_radar_time_begin(radar)
    if datetime_format:
        time_str = begin_time.strftime(datetime_format)
    else:
        time_str = begin_time.isoformat() + "Z"
    fixed_angle = radar.fixed_angle["data"][sweep]
    l1 = f"{pyart.graph.generate_radar_name(radar)} {fixed_angle:.1f} Deg. {time_str} "
    field_name = generate_field_name(radar, field)
    return l1 + "\n" + field_name
radar["sweep_0"].ds.xradar.georeference()
Loading...
fig = plt.figure(figsize=[10, 8])
display = pyart.graph.RadarDisplay(radar)
display.plot_ppi("DBZH", sweep=4)
plt.ylim(-50, 50)
plt.xlim(-50, 50)
(-50.0, 50.0)
<Figure size 1000x800 with 2 Axes>
del display

Challenge Problem!

Go to the Py-ART Example Gallery, and reproduce one of the examples using the data provided in this tutorial.
# Insert your solution here

Wradlib

Wradlib functionality can also be applied to Xarray.Datatree objects. For example, the wradlib.georef module can be used to enrich data by adding geographical coordinates.

for key in list(dt.children):
    if "sweep" in key:
        dt[key].ds = dt[key].ds.wrl.georef.georeference(
            crs=wrl.georef.get_default_projection()
        )
dt["sweep_0"].ds.DBZH.sel(range=slice(0, 5e4)).plot(
    x="x",
    y="y",
    cmap="ChaseSpectral",
    vmax=60,
    vmin=-10,
)
<Figure size 640x480 with 2 Axes>

Challenge Problem!

Go to the wradlib example gallery, and reproduce one of the examples using the data provided in this tutorial.

Xradar exporters

In xradar, exporters convert radar data into various formats for analysis or integration. Exporting is supported for recognized standards, including:

  • CfRadial1
  • CfRadial2
  • ODIM
  • Zarr

As an example, we can output our data to cfradial2 format!

xd.io.to_cfradial2(dt, "radar-data.nc")
dt_back = open_datatree("radar-data.nc", engine="netcdf4")
display(dt_back)
Loading...

Summary

Xradar is a Python library designed for working with radar data. It extends xarray to include radar-specific functionality, such as purpose-based accessors and georeferencing methods. It supports exporting data in various formats, including CfRadial1, CfRadial2, ODIM, and Zarr. xradar facilitates the analysis, visualization, and integration of radar data with other tools and systems.