Recipe #1: Clutter and attenuation correction plus composition for two DWD radars#

This recipe shows a workflow to process radar data provided by the German Weather Service (DWD). The processing includes:

[1]:
import wradlib as wrl
import matplotlib.pyplot as pl
import warnings

warnings.filterwarnings("ignore")
try:
    get_ipython().run_line_magic("matplotlib inline")
except:
    pl.ion()
import numpy as np
/home/runner/micromamba-root/envs/wradlib-tests/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
[2]:
import glob
import os


def process_polar_level_data(radarname):
    """Reading and processing polar level data (DX) for radar <radarname>"""
    print("Polar level processing for radar %s..." % radarname)
    # preparations for loading sample data in source directory
    files = glob.glob(
        os.path.join(
            wrl.util.get_wradlib_data_path(), "dx/recipe1_data/raa*%s*bin" % radarname
        )
    )

    if len(files) == 0:
        print(
            "WARNING: No data files found - maybe you did not extract "
            "the data from data/recipe1_data.zip?"
        )
    data = np.empty((len(files), 360, 128))
    # loading the data (two hours of 5-minute images)
    for i, f in enumerate(files):
        # print(i, f)
        data[i], attrs = wrl.io.read_dx(f)
    # Clutter filter on an event base
    clmap = wrl.clutter.filter_gabella(data.mean(axis=0), tr1=12, n_p=6, tr2=1.1)
    for i, scan in enumerate(data):
        data[i] = wrl.ipol.interpolate_polar(scan, clmap)
    # correcting for attenuation
    pia = wrl.atten.correct_attenuation_constrained(
        data,
        a_max=1.67e-4,
        a_min=2.33e-5,
        n_a=100,
        b_max=0.7,
        b_min=0.65,
        n_b=6,
        gate_length=1.0,
        constraints=[wrl.atten.constraint_dbz, wrl.atten.constraint_pia],
        constraint_args=[[59.0], [10.0]],
    )
    data = data + pia
    # converting to precipitation depth
    R = wrl.zr.z_to_r(wrl.trafo.idecibel(data), a=256, b=1.4)
    depth = wrl.trafo.r_to_depth(R, 300.0)
    # calculate hourly accumulation
    accum = depth.sum(axis=0)

    return accum
[3]:
def bbox(*args):
    """Get bounding box from a set of radar bin coordinates"""
    x = np.array([])
    y = np.array([])
    for arg in args:
        x = np.append(x, arg[:, 0])
        y = np.append(y, arg[:, 1])
    xmin = x.min()
    xmax = x.max()
    ymin = y.min()
    ymax = y.max()

    return xmin, xmax, ymin, ymax
[4]:
import zipfile
import shutil
import datetime as dt

# set timer
start = dt.datetime.now()
# unzip data
filename = wrl.util.get_wradlib_data_file("dx/recipe1_data.zip")
targetdir = wrl.util.get_wradlib_data_path() + "/dx/recipe1_data"
with zipfile.ZipFile(filename, "r") as z:
    z.extractall(targetdir)

# set scan geometry and radar coordinates
r = np.arange(500.0, 128500.0, 1000.0)
az = np.arange(0, 360)
tur_sitecoords = (9.7839, 48.5861)
fbg_sitecoords = (8.005, 47.8744)

# processing polar level radar data
#   Tuerkheim
tur_accum = process_polar_level_data("tur")
#   Feldberg
fbg_accum = process_polar_level_data("fbg")

# remove unzipped files
if os.path.exists(targetdir):
    try:
        shutil.rmtree(targetdir)
    except Exception:
        print("WARNING: Could not remove directory data/recipe1_data")

# derive UTM Zone 32 coordinates of range-bin centroids
# create osr projection using epsg number for UTM Zone 32
proj_utm = wrl.georef.epsg_to_osr(32632)

#  for Tuerkheim radar
tur_coord = wrl.georef.spherical_to_centroids(r, az, 0, tur_sitecoords, proj=proj_utm)
tur_coord = tur_coord[..., 0:2]
tur_coord = tur_coord.reshape(-1, tur_coord.shape[-1])

# for Feldberg radar
fbg_coord = wrl.georef.spherical_to_centroids(r, az, 0, fbg_sitecoords, proj=proj_utm)
fbg_coord = fbg_coord[..., 0:2]
fbg_coord = fbg_coord.reshape(-1, fbg_coord.shape[-1])

# define target grid for composition
xmin, xmax, ymin, ymax = bbox(tur_coord, fbg_coord)
x = np.linspace(xmin, xmax + 1000.0, 1000)
y = np.linspace(ymin, ymax + 1000.0, 1000)
grid_coords = wrl.util.gridaspoints(y, x)

# derive quality information - in this case, the pulse volume
pulse_volumes = np.tile(wrl.qual.pulse_volume(r, 1000.0, 1.0), 360)
# interpolate polar radar-data and quality data to the grid
print("Gridding Tuerkheim data...")
tur_quality_gridded = wrl.comp.togrid(
    tur_coord,
    grid_coords,
    r.max() + 500.0,
    tur_coord.mean(axis=0),
    pulse_volumes,
    wrl.ipol.Nearest,
)
tur_gridded = wrl.comp.togrid(
    tur_coord,
    grid_coords,
    r.max() + 500.0,
    tur_coord.mean(axis=0),
    tur_accum.ravel(),
    wrl.ipol.Nearest,
)

print("Gridding Feldberg data...")
fbg_quality_gridded = wrl.comp.togrid(
    fbg_coord,
    grid_coords,
    r.max() + 500.0,
    fbg_coord.mean(axis=0),
    pulse_volumes,
    wrl.ipol.Nearest,
)
fbg_gridded = wrl.comp.togrid(
    fbg_coord,
    grid_coords,
    r.max() + 500.0,
    fbg_coord.mean(axis=0),
    fbg_accum.ravel(),
    wrl.ipol.Nearest,
)

# compose the both radar-data based on the quality information
# calculated above
print("Composing Tuerkheim and Feldbarg data on a common grid...")
composite = wrl.comp.compose_weighted(
    [tur_gridded, fbg_gridded],
    [1.0 / (tur_quality_gridded + 0.001), 1.0 / (fbg_quality_gridded + 0.001)],
)
composite = np.ma.masked_invalid(composite)

print("Processing took:", dt.datetime.now() - start)
Downloading file 'dx/recipe1_data.zip' from 'https://github.com/wradlib/wradlib-data/raw/pooch/data/dx/recipe1_data.zip' to '/home/runner/work/wradlib/wradlib/wradlib-data'.
Polar level processing for radar tur...
Polar level processing for radar fbg...
Gridding Tuerkheim data...
Gridding Feldberg data...
Composing Tuerkheim and Feldbarg data on a common grid...
Processing took: 0:00:05.372593
[5]:
# Plotting rainfall map
pl.figure(figsize=(10, 8))
pl.subplot(111, aspect="equal")
pm = pl.pcolormesh(x, y, composite.reshape((len(x), len(y))), cmap="viridis")
pl.grid()
pl.xlim(min(x), max(x))
pl.ylim(min(y), max(y))
pl.colorbar(pm, shrink=0.85)
[5]:
<matplotlib.colorbar.Colorbar at 0x7f457aeeaf90>
../../_images/notebooks_workflow_recipe1_7_1.png

Download required data at the wradlib-data repository.

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.