[1]:
# flake8: noqa

Routine verification measures for radar-based precipitation estimates

[2]:
import wradlib
import os
import numpy as np
import matplotlib.pyplot as pl
import warnings

warnings.filterwarnings("ignore")
try:
    get_ipython().run_line_magic("matplotlib inline")
except:
    pl.ion()
/home/runner/micromamba-root/envs/wradlib-notebooks/lib/python3.11/site-packages/tqdm/auto.py:22: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html
  from .autonotebook import tqdm as notebook_tqdm

Extract bin values from a polar radar data set at rain gage locations

Read polar data set

[3]:
filename = wradlib.util.get_wradlib_data_file("misc/polar_R_tur.gz")
data = np.loadtxt(filename)
Downloading file 'misc/polar_R_tur.gz' from 'https://github.com/wradlib/wradlib-data/raw/pooch/data/misc/polar_R_tur.gz' to '/home/runner/work/wradlib-notebooks/wradlib-notebooks/wradlib-data'.

Define site coordinates (lon/lat) and polar coordinate system

[4]:
r = np.arange(1, 129)
az = np.linspace(0, 360, 361)[0:-1]
sitecoords = (9.7839, 48.5861)

Make up two rain gauge locations (say we want to work in Gaus Krueger zone 3)

[5]:
# Define the projection via epsg-code
proj = wradlib.georef.epsg_to_osr(31467)
# Coordinates of the rain gages in Gauss-Krueger 3 coordinates
x, y = np.array([3557880, 3557890]), np.array([5383379, 5383375])

Now extract the radar values at those bins that are closest to our rain gauges

For this purppose, we use the PolarNeighbours class from wraldib’s verify module. Here, we extract the 9 nearest bins…

[6]:
polarneighbs = wradlib.verify.PolarNeighbours(r, az, sitecoords, proj, x, y, nnear=9)
radar_at_gages = polarneighbs.extract(data)
print("Radar values at rain gauge #1: %r" % radar_at_gages[0].tolist())
print("Radar values at rain gauge #2: %r" % radar_at_gages[1].tolist())
Radar values at rain gauge #1: [0.01, 0.02, 0.01, 0.01, 0.02, 0.05, 0.01, 0.01, 0.01]
Radar values at rain gauge #2: [0.2, 0.06, 0.15, 0.69, 0.06, 0.26, 0.05, 0.99, 0.32]

Retrieve the bin coordinates (all of them or those at the rain gauges)

[7]:
binx, biny = polarneighbs.get_bincoords()
binx_nn, biny_nn = polarneighbs.get_bincoords_at_points()

Plot the entire radar domain and zoom into the surrounding of the rain gauge locations

[8]:
fig = pl.figure(figsize=(12, 12))
ax = fig.add_subplot(121)
ax.plot(binx, biny, "r+")
ax.plot(binx_nn, biny_nn, "b+", markersize=10)
ax.plot(x, y, "bo")
ax.axis("tight")
ax.set_aspect("equal")
pl.title("Full view")
ax = fig.add_subplot(122)
ax.plot(binx, biny, "r+")
ax.plot(binx_nn, biny_nn, "b+", markersize=10)
ax.plot(x, y, "bo")
pl.xlim(binx_nn.min() - 5, binx_nn.max() + 5)
pl.ylim(biny_nn.min() - 7, biny_nn.max() + 8)
ax.set_aspect("equal")
txt = pl.title("Zoom into rain gauge locations")
pl.tight_layout()
../../_images/notebooks_verification_wradlib_verify_example_16_0.png

Create a verification report

In this example, we make up a true Kdp profile and verify our reconstructed Kdp.

Create synthetic data and reconstruct KDP

[9]:
# Synthetic truth
dr = 0.5
r = np.arange(0, 100, dr)
kdp_true = np.sin(0.3 * r)
kdp_true[kdp_true < 0] = 0.0
phidp_true = np.cumsum(kdp_true) * 2 * dr
# Synthetic observation of PhiDP with a random noise and gaps
np.random.seed(1319622840)
phidp_raw = phidp_true + np.random.uniform(-2, 2, len(phidp_true))
gaps = np.random.uniform(0, len(r), 20).astype("int")
phidp_raw[gaps] = np.nan

# linearly interpolate nan
nans = np.isnan(phidp_raw)
phidp_ipol = phidp_raw.copy()
phidp_ipol[nans] = np.interp(r[nans], r[~nans], phidp_raw[~nans])

# Reconstruct PhiDP and KDP
phidp_rawre, kdp_rawre = wradlib.dp.process_raw_phidp_vulpiani(phidp_raw, dr=dr)
phidp_ipre, kdp_ipre = wradlib.dp.process_raw_phidp_vulpiani(phidp_ipol, dr=dr)

# Plot results
fig = pl.figure(figsize=(12, 8))
ax = fig.add_subplot(211)
pl.plot(kdp_true, "g-", label="True KDP")
pl.plot(kdp_rawre, "r-", label="Reconstructed Raw KDP")
pl.plot(kdp_ipre, "b-", label="Reconstructed Ipol KDP")
pl.grid()
lg = pl.legend()

ax = fig.add_subplot(212)
pl.plot(r, phidp_true, "b--", label="True PhiDP")
pl.plot(r, np.ma.masked_invalid(phidp_raw), "b-", label="Raw PhiDP")
pl.plot(r, phidp_rawre, "r-", label="Reconstructed Raw PhiDP")
pl.plot(r, phidp_ipre, "g-", label="Reconstructed Ipol PhiDP")
pl.grid()
lg = pl.legend(loc="lower right")
txt = pl.xlabel("Range (km)")
../../_images/notebooks_verification_wradlib_verify_example_19_0.png

Create verification report

[10]:
metrics_raw = wradlib.verify.ErrorMetrics(kdp_true, kdp_rawre)
metrics_raw.pprint()
metrics_ip = wradlib.verify.ErrorMetrics(kdp_true, kdp_ipre)
metrics_ip.pprint()

pl.subplots_adjust(wspace=0.5)
ax = pl.subplot(121, aspect=1.0)
ax.plot(metrics_raw.obs, metrics_raw.est, "bo")
ax.plot([-1, 2], [-1, 2], "k--")
pl.xlim(-0.3, 1.1)
pl.ylim(-0.3, 1.1)
xlabel = ax.set_xlabel("True KDP (deg/km)")
ylabel = ax.set_ylabel("Reconstructed Raw KDP (deg/km)")
ax = pl.subplot(122, aspect=1.0)
ax.plot(metrics_ip.obs, metrics_ip.est, "bo")
ax.plot([-1, 2], [-1, 2], "k--")
pl.xlim(-0.3, 1.1)
pl.ylim(-0.3, 1.1)
xlabel = ax.set_xlabel("True KDP (deg/km)")
ylabel = ax.set_ylabel("Reconstructed Ipol KDP (deg/km)")
{'corr': 0.95,
 'mas': 0.1,
 'meanerr': -0.0,
 'mse': 0.01,
 'nash': 0.93,
 'pbias': -0.0,
 'r2': 0.9,
 'ratio': nan,
 'rmse': 0.1,
 'spearman': 0.92,
 'sse': 2.96}
{'corr': 0.95,
 'mas': 0.1,
 'meanerr': -0.01,
 'mse': 0.01,
 'nash': 0.93,
 'pbias': -3.0,
 'r2': 0.91,
 'ratio': nan,
 'rmse': 0.1,
 'spearman': 0.92,
 'sse': 2.91}
../../_images/notebooks_verification_wradlib_verify_example_21_1.png