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: metredata_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",
)
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.