Recipe #2: Reading and visualizing an ODIM_H5 polar volume

Recipe #2: Reading and visualizing an ODIM_H5 polar volume#

This recipe shows how extract the polar volume data from an ODIM_H5 hdf5 file (KNMI example file from OPERA), construct a 3-dimensional Cartesian volume and produce a diagnostic plot. The challenge for this file is that for each elevation angle, the scan strategy is different.

import datetime as dt
import warnings

import numpy as np
import wradlib as wrl
import wradlib_data
import xarray as xr
import xradar as xd
from IPython.display import display
from osgeo import osr

warnings.filterwarnings("ignore")
# read the data (sample file in WRADLIB_DATA)
filename = wradlib_data.DATASETS.fetch("hdf5/knmi_polar_volume.h5")

raw_dt = xd.io.open_odim_datatree(filename)
display(raw_dt)
<xarray.DataTree>
Group: /
│   Dimensions:              (sweep: 14)
│   Coordinates:
│       latitude             float32 4B ...
│       longitude            float32 4B ...
│       altitude             float32 4B ...
│   Dimensions without coordinates: sweep
│   Data variables:
│       volume_number        int64 8B 0
│       platform_type        <U5 20B 'fixed'
│       instrument_type      <U5 20B 'radar'
│       time_coverage_start  <U20 80B '2011-06-10T11:40:02Z'
│       time_coverage_end    <U20 80B '2011-06-10T11:43:54Z'
│       sweep_fixed_angle    (sweep) float32 56B 0.3 0.4 0.8 1.1 ... 15.0 20.0 25.0
│       sweep_group_name     (sweep) int64 112B 0 1 2 3 4 5 6 7 8 9 10 11 12 13
│   Attributes:
│       Conventions:      ODIM_H5/V2_2
│       instrument_name:  None
│       version:          None
│       title:            None
│       institution:      None
│       references:       None
│       source:           None
│       history:          None
│       comment:          im/exported using xradar
├── Group: /sweep_0
│       Dimensions:            (azimuth: 360, range: 320)
│       Coordinates:
│         * azimuth            (azimuth) float32 1kB 0.5 1.5 2.5 ... 357.5 358.5 359.5
│           elevation          (azimuth) float32 1kB ...
│           time               (azimuth) datetime64[ns] 3kB 2011-06-10T11:40:17.36111...
│         * range              (range) float32 1kB 500.0 1.5e+03 ... 3.185e+05 3.195e+05
│       Data variables:
│           DBZH               (azimuth, range) float32 461kB ...
│           sweep_mode         <U20 80B ...
│           sweep_number       int64 8B ...
│           prt_mode           <U7 28B ...
│           follow_mode        <U7 28B ...
│           sweep_fixed_angle  float32 4B ...
│           nyquist_velocity   object 8B ...
├── Group: /sweep_1
│       Dimensions:            (azimuth: 360, range: 240)
│       Coordinates:
│         * azimuth            (azimuth) float32 1kB 0.5 1.5 2.5 ... 357.5 358.5 359.5
│           elevation          (azimuth) float32 1kB ...
│           time               (azimuth) datetime64[ns] 3kB 2011-06-10T11:40:36.80555...
│         * range              (range) float32 960B 500.0 1.5e+03 ... 2.395e+05
│       Data variables:
│           DBZH               (azimuth, range) float32 346kB ...
│           sweep_mode         <U20 80B ...
│           sweep_number       int64 8B ...
│           prt_mode           <U7 28B ...
│           follow_mode        <U7 28B ...
│           sweep_fixed_angle  float32 4B ...
│           nyquist_velocity   object 8B ...
├── Group: /sweep_2
│       Dimensions:            (azimuth: 360, range: 240)
│       Coordinates:
│         * azimuth            (azimuth) float32 1kB 0.5 1.5 2.5 ... 357.5 358.5 359.5
│           elevation          (azimuth) float32 1kB ...
│           time               (azimuth) datetime64[ns] 3kB 2011-06-10T11:40:56.30555...
│         * range              (range) float32 960B 500.0 1.5e+03 ... 2.395e+05
│       Data variables:
│           DBZH               (azimuth, range) float32 346kB ...
│           sweep_mode         <U20 80B ...
│           sweep_number       int64 8B ...
│           prt_mode           <U7 28B ...
│           follow_mode        <U7 28B ...
│           sweep_fixed_angle  float32 4B ...
│           nyquist_velocity   object 8B ...
├── Group: /sweep_3
│       Dimensions:            (azimuth: 360, range: 240)
│       Coordinates:
│         * azimuth            (azimuth) float32 1kB 0.5 1.5 2.5 ... 357.5 358.5 359.5
│           elevation          (azimuth) float32 1kB ...
│           time               (azimuth) datetime64[ns] 3kB 2011-06-10T11:41:15.80555...
│         * range              (range) float32 960B 500.0 1.5e+03 ... 2.395e+05
│       Data variables:
│           DBZH               (azimuth, range) float32 346kB ...
│           sweep_mode         <U20 80B ...
│           sweep_number       int64 8B ...
│           prt_mode           <U7 28B ...
│           follow_mode        <U7 28B ...
│           sweep_fixed_angle  float32 4B ...
│           nyquist_velocity   object 8B ...
├── Group: /sweep_4
│       Dimensions:            (azimuth: 360, range: 240)
│       Coordinates:
│         * azimuth            (azimuth) float32 1kB 0.5 1.5 2.5 ... 357.5 358.5 359.5
│           elevation          (azimuth) float32 1kB ...
│           time               (azimuth) datetime64[ns] 3kB 2011-06-10T11:41:36.30555...
│         * range              (range) float32 960B 500.0 1.5e+03 ... 2.395e+05
│       Data variables:
│           DBZH               (azimuth, range) float32 346kB ...
│           sweep_mode         <U20 80B ...
│           sweep_number       int64 8B ...
│           prt_mode           <U7 28B ...
│           follow_mode        <U7 28B ...
│           sweep_fixed_angle  float32 4B ...
│           nyquist_velocity   object 8B ...
├── Group: /sweep_5
│       Dimensions:            (azimuth: 360, range: 340)
│       Coordinates:
│         * azimuth            (azimuth) float32 1kB 0.5 1.5 2.5 ... 357.5 358.5 359.5
│           elevation          (azimuth) float32 1kB ...
│           time               (azimuth) datetime64[ns] 3kB 2011-06-10T11:42:10.47919...
│         * range              (range) float32 1kB 250.0 750.0 ... 1.692e+05 1.698e+05
│       Data variables:
│           DBZH               (azimuth, range) float32 490kB ...
│           sweep_mode         <U20 80B ...
│           sweep_number       int64 8B ...
│           prt_mode           <U7 28B ...
│           follow_mode        <U7 28B ...
│           sweep_fixed_angle  float32 4B ...
│           nyquist_velocity   object 8B ...
...
├── Group: /sweep_8
│       Dimensions:            (azimuth: 360, range: 300)
│       Coordinates:
│         * azimuth            (azimuth) float32 1kB 0.5 1.5 2.5 ... 357.5 358.5 359.5
│           elevation          (azimuth) float32 1kB ...
│           time               (azimuth) datetime64[ns] 3kB 2011-06-10T11:42:49.01665...
│         * range              (range) float32 1kB 250.0 750.0 ... 1.492e+05 1.498e+05
│       Data variables:
│           DBZH               (azimuth, range) float32 432kB ...
│           sweep_mode         <U20 80B ...
│           sweep_number       int64 8B ...
│           prt_mode           <U7 28B ...
│           follow_mode        <U7 28B ...
│           sweep_fixed_angle  float32 4B ...
│           nyquist_velocity   object 8B ...
├── Group: /sweep_9
│       Dimensions:            (azimuth: 360, range: 240)
│       Coordinates:
│         * azimuth            (azimuth) float32 1kB 0.5 1.5 2.5 ... 357.5 358.5 359.5
│           elevation          (azimuth) float32 1kB ...
│           time               (azimuth) datetime64[ns] 3kB 2011-06-10T11:42:59.79165...
│         * range              (range) float32 960B 250.0 750.0 ... 1.192e+05 1.198e+05
│       Data variables:
│           DBZH               (azimuth, range) float32 346kB ...
│           sweep_mode         <U20 80B ...
│           sweep_number       int64 8B ...
│           prt_mode           <U7 28B ...
│           follow_mode        <U7 28B ...
│           sweep_fixed_angle  float32 4B ...
│           nyquist_velocity   object 8B ...
├── Group: /sweep_10
│       Dimensions:            (azimuth: 360, range: 240)
│       Coordinates:
│         * azimuth            (azimuth) float32 1kB 0.5 1.5 2.5 ... 357.5 358.5 359.5
│           elevation          (azimuth) float32 1kB ...
│           time               (azimuth) datetime64[ns] 3kB 2011-06-10T11:43:09.54166...
│         * range              (range) float32 960B 250.0 750.0 ... 1.192e+05 1.198e+05
│       Data variables:
│           DBZH               (azimuth, range) float32 346kB ...
│           sweep_mode         <U20 80B ...
│           sweep_number       int64 8B ...
│           prt_mode           <U7 28B ...
│           follow_mode        <U7 28B ...
│           sweep_fixed_angle  float32 4B ...
│           nyquist_velocity   object 8B ...
├── Group: /sweep_11
│       Dimensions:            (azimuth: 360, range: 240)
│       Coordinates:
│         * azimuth            (azimuth) float32 1kB 0.5 1.5 2.5 ... 357.5 358.5 359.5
│           elevation          (azimuth) float32 1kB ...
│           time               (azimuth) datetime64[ns] 3kB 2011-06-10T11:43:29.87496...
│         * range              (range) float32 960B 250.0 750.0 ... 1.192e+05 1.198e+05
│       Data variables:
│           DBZH               (azimuth, range) float32 346kB ...
│           sweep_mode         <U20 80B ...
│           sweep_number       int64 8B ...
│           prt_mode           <U7 28B ...
│           follow_mode        <U7 28B ...
│           sweep_fixed_angle  float32 4B ...
│           nyquist_velocity   object 8B ...
├── Group: /sweep_12
│       Dimensions:            (azimuth: 360, range: 240)
│       Coordinates:
│         * azimuth            (azimuth) float32 1kB 0.5 1.5 2.5 ... 357.5 358.5 359.5
│           elevation          (azimuth) float32 1kB ...
│           time               (azimuth) datetime64[ns] 3kB 2011-06-10T11:43:39.23608...
│         * range              (range) float32 960B 250.0 750.0 ... 1.192e+05 1.198e+05
│       Data variables:
│           DBZH               (azimuth, range) float32 346kB ...
│           sweep_mode         <U20 80B ...
│           sweep_number       int64 8B ...
│           prt_mode           <U7 28B ...
│           follow_mode        <U7 28B ...
│           sweep_fixed_angle  float32 4B ...
│           nyquist_velocity   object 8B ...
└── Group: /sweep_13
        Dimensions:            (azimuth: 360, range: 240)
        Coordinates:
          * azimuth            (azimuth) float32 1kB 0.5 1.5 2.5 ... 357.5 358.5 359.5
            elevation          (azimuth) float32 1kB ...
            time               (azimuth) datetime64[ns] 3kB 2011-06-10T11:43:48.76387...
          * range              (range) float32 960B 250.0 750.0 ... 1.192e+05 1.198e+05
        Data variables:
            DBZH               (azimuth, range) float32 346kB ...
            sweep_mode         <U20 80B ...
            sweep_number       int64 8B ...
            prt_mode           <U7 28B ...
            follow_mode        <U7 28B ...
            sweep_fixed_angle  float32 4B ...
            nyquist_velocity   object 8B ...
proj = osr.SpatialReference()
proj.ImportFromEPSG(32632)
for key in list(raw_dt.children):
    if "sweep" in key:
        raw_dt[key].ds = raw_dt[key].to_dataset(inherit="all_coords").wrl.georef.georeference(crs=proj)
swp_list = []
for key in list(raw_dt.children):
    if "sweep" in key:
        ds = raw_dt[key].ds
        xyz = (
            xr.concat(
                [
                    ds.coords["x"].reset_coords(drop=True),
                    ds.coords["y"].reset_coords(drop=True),
                    ds.coords["z"].reset_coords(drop=True),
                ],
                "xyz",
            )
            .stack(npoints=("azimuth", "range"))
            .transpose(..., "xyz")
        )
        swp_list.append(xyz)
xyz = xr.concat(swp_list, "npoints")
swp_list[0]
<xarray.DataArray 'x' (npoints: 115200, xyz: 3)> Size: 3MB
array([[2.17262259e+05, 5.87587733e+06, 5.26327115e+01],
       [2.17329665e+05, 5.87687562e+06, 5.79865115e+01],
       [2.17397078e+05, 5.87787390e+06, 6.34581474e+01],
       ...,
       [2.33423522e+05, 6.19241546e+06, 7.64849169e+03],
       [2.33475527e+05, 6.19341275e+06, 7.69116241e+03],
       [2.33527539e+05, 6.19441005e+06, 7.73395058e+03]],
      shape=(115200, 3))
Coordinates:
  * npoints  (npoints) object 922kB MultiIndex
  * azimuth  (npoints) float32 461kB 0.5 0.5 0.5 0.5 ... 359.5 359.5 359.5 359.5
  * range    (npoints) float32 461kB 500.0 1.5e+03 ... 3.185e+05 3.195e+05
Dimensions without coordinates: xyz
Attributes:
    axis:           X
    long_name:      Easting
    standard_name:  projection_x_coordinate
    units:          metre
data_list = []
for key in list(raw_dt.children):
    if "sweep" in key:
        ds = raw_dt[key].ds
        data = ds.DBZH.stack(npoints=("azimuth", "range"))
        data_list.append(data)
data = xr.concat(data_list, "npoints")
# generate 3-D Cartesian target grid coordinates
sitecoords = (raw_dt.longitude.values, raw_dt.latitude.values, raw_dt.altitude.values)
maxrange = 200000.0
minelev = 0.1
maxelev = 25.0
maxalt = 5000.0
horiz_res = 2000.0
vert_res = 250.0
trgxyz, trgshape = wrl.vpr.make_3d_grid(
    sitecoords, proj, maxrange, maxalt, horiz_res, vert_res
)
# interpolate to Cartesian 3-D volume grid
tstart = dt.datetime.now()
gridder = wrl.vpr.CAPPI(
    xyz.values,
    trgxyz,
    # gridshape=trgshape,
    maxrange=maxrange,
    minelev=minelev,
    maxelev=maxelev,
)
vol = np.ma.masked_invalid(gridder(data.values).reshape(trgshape))
print("3-D interpolation took:", dt.datetime.now() - tstart)
3-D interpolation took: 0:00:00.601973
# diagnostic plot
trgx = trgxyz[:, 0].reshape(trgshape)[0, 0, :]
trgy = trgxyz[:, 1].reshape(trgshape)[0, :, 0]
trgz = trgxyz[:, 2].reshape(trgshape)[:, 0, 0]
wrl.vis.plot_max_plan_and_vert(
    trgx,
    trgy,
    trgz,
    vol,
    unit="dBZH",
    levels=range(-32, 60),
    cmap="turbo",
)
../../_images/5644f25c0995c8f841f68f5d55fdebaf9b4212df16351fa920eb066751ee6ff0.png

Note

In order to run the recipe code, you need to extract the sample data into a directory pointed to by environment variable WRADLIB_DATA.