NEXRAD Level3 Radar

Notebook Python-AWIPS Tutorial Notebook


Objectives

  • Use python-awips to connect to an edex server

  • Define and filter data request for radar data

  • Plot NEXRAD 3 algorithm, precipitation, and derived products (not base data)


Table of Contents

1 Imports

The imports below are used throughout the notebook. Note the first import is coming directly from python-awips and allows us to connect to an EDEX server. The subsequent imports are for data manipulation and visualization.

import warnings
from awips.dataaccess import DataAccessLayer
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import numpy as np
from cartopy.mpl.gridliner import LONGITUDE_FORMATTER, LATITUDE_FORMATTER

Top


2 EDEX Connection

First we establish a connection to Unidata’s public EDEX server. This sets the proper server on the DataAccessLayer, which we will use numerous times throughout the notebook.

DataAccessLayer.changeEDEXHost("edex-cloud.unidata.ucar.edu")
request = DataAccessLayer.newDataRequest("radar")

Top


3 Investigate Data

Now that we’ve created a new radar data request, let’s take a look at what locations and parameters are available for our current request.

3.1 Available Locations

We can take a look at what “locations” are available for our radar request. For radar, we’ll see that radar station names are returned when looking at the availalbe location names.

For this example we’ll use Baltimore, MD/Washington DC as our region of interest. You can easily look up other station IDs and where they are using this NWS webpage.

available_locs = DataAccessLayer.getAvailableLocationNames(request)
available_locs.sort()
print(available_locs)

# Set our location to Baltimore (klwx)
request.setLocationNames("klwx")
['kabr', 'kabx', 'kakq', 'kama', 'kamx', 'kapx', 'karx', 'katx', 'kbbx', 'kbgm', 'kbhx', 'kbis', 'kblx', 'kbmx', 'kbox', 'kbro', 'kbuf', 'kbyx', 'kcae', 'kcbw', 'kcbx', 'kccx', 'kcle', 'kclx', 'kcrp', 'kcxx', 'kcys', 'kdax', 'kddc', 'kdfx', 'kdgx', 'kdix', 'kdlh', 'kdmx', 'kdox', 'kdtx', 'kdvn', 'kdyx', 'keax', 'kemx', 'kenx', 'keox', 'kepz', 'kesx', 'kevx', 'kewx', 'keyx', 'kfcx', 'kfdr', 'kfdx', 'kffc', 'kfsd', 'kfsx', 'kftg', 'kfws', 'kggw', 'kgjx', 'kgld', 'kgrb', 'kgrk', 'kgrr', 'kgsp', 'kgwx', 'kgyx', 'khdc', 'khdx', 'khgx', 'khnx', 'khpx', 'khtx', 'kict', 'kicx', 'kiln', 'kilx', 'kind', 'kinx', 'kiwa', 'kiwx', 'kjax', 'kjgx', 'kjkl', 'klbb', 'klch', 'klgx', 'klnx', 'klot', 'klrx', 'klsx', 'kltx', 'klvx', 'klwx', 'klzk', 'kmaf', 'kmax', 'kmbx', 'kmhx', 'kmkx', 'kmlb', 'kmob', 'kmpx', 'kmqt', 'kmrx', 'kmsx', 'kmtx', 'kmux', 'kmvx', 'kmxx', 'knkx', 'knqa', 'koax', 'kohx', 'kokx', 'kotx', 'kpah', 'kpbz', 'kpdt', 'kpoe', 'kpux', 'krax', 'krgx', 'kriw', 'krlx', 'krtx', 'ksfx', 'ksgf', 'kshv', 'ksjt', 'ksox', 'ksrx', 'ktbw', 'ktfx', 'ktlh', 'ktlx', 'ktwx', 'ktyx', 'kudx', 'kuex', 'kvax', 'kvbx', 'kvnx', 'kvtx', 'kvwx', 'kyux', 'pabc', 'pacg', 'paec', 'pahg', 'paih', 'pakc', 'papd', 'phki', 'phkm', 'phmo', 'phwa', 'rkjk', 'rksg', 'tadw', 'tatl', 'tbna', 'tbos', 'tbwi', 'tclt', 'tcmh', 'tcvg', 'tdal', 'tday', 'tdca', 'tden', 'tdfw', 'tdtw', 'tewr', 'tfll', 'thou', 'tiad', 'tiah', 'tich', 'tids', 'tjfk', 'tjua', 'tlas', 'tlve', 'tmci', 'tmco', 'tmdw', 'tmem', 'tmia', 'tmke', 'tmsp', 'tmsy', 'tokc', 'tord', 'tpbi', 'tphl', 'tphx', 'tpit', 'trdu', 'tsdf', 'tsju', 'tslc', 'tstl', 'ttpa', 'ttul']

3.2 Available Parameters

Next, let’s look at the parameters returned from the available parameters request. If we look closely, we can see that some of the parameters appear different from the others.

availableParms = DataAccessLayer.getAvailableParameters(request)
availableParms.sort()
print(availableParms)
['134', '135', '141', '153', '154', '159', '161', '163', '165', '166', '169', '170', '172', '173', '176', '177', '32', '37', '56', '57', '58', '81', '99', 'CC', 'CZ', 'Composite Refl', 'Correlation Coeff', 'DAA', 'DHR', 'DPA', 'DPR', 'DUA', 'DVL', 'Diff Reflectivity', 'Digital Hybrid Scan Refl', 'Digital Inst Precip Rate', 'Digital Precip Array', 'Digital Vert Integ Liq', 'EET', 'Enhanced Echo Tops', 'HC', 'HHC', 'HV', 'HZ', 'Hybrid Hydrometeor Class', 'Hydrometeor Class', 'KDP', 'MD', 'ML', 'Melting Layer', 'Mesocyclone', 'OHA', 'One Hour Accum', 'One Hour Unbiased Accum', 'Reflectivity', 'SRM', 'STA', 'STI', 'Specific Diff Phase', 'Storm Rel Velocity', 'Storm Total Accum', 'Storm Track', 'User Select Accum', 'V', 'VIL', 'Velocity', 'Vert Integ Liq', 'ZDR']

3.3 Radar Product IDs and Names

As we saw above, some parameters seem to be describing different things from the rest. The DataAccessLayer has a built in function to parse the available parameters into the separate Product IDs and Product Names. Here, we take a look at the two different arrays that are returned when parsing the availableParms array we just recieved in the previous code cell.

productIDs = DataAccessLayer.getRadarProductIDs(availableParms)
productNames = DataAccessLayer.getRadarProductNames(availableParms)
print(productIDs)
print(productNames)
['134', '135', '141', '153', '154', '159', '161', '163', '165', '166', '169', '170', '172', '173', '176', '177', '32', '37', '56', '57', '58', '81', '99']
['Composite Refl', 'Correlation Coeff', 'Diff Reflectivity', 'Digital Hybrid Scan Refl', 'Digital Inst Precip Rate', 'Digital Precip Array', 'Digital Vert Integ Liq', 'Enhanced Echo Tops', 'Hybrid Hydrometeor Class', 'Hydrometeor Class', 'Melting Layer', 'Mesocyclone', 'One Hour Accum', 'One Hour Unbiased Accum', 'Reflectivity', 'Specific Diff Phase', 'Storm Rel Velocity', 'Storm Total Accum', 'Storm Track', 'User Select Accum', 'Velocity', 'Vert Integ Liq']

Top


4 Function: make_map()

In order to plot more than one image, it’s easiest to define common logic in a function. Here, a new function called make_map is defined. This function uses the matplotlib.pyplot package (plt) to create a figure and axis. The coastlines (continental boundaries) are added, along with lat/lon grids.

def make_map(bbox, projection=ccrs.PlateCarree()):
    fig, ax = plt.subplots(figsize=(16, 16),
                           subplot_kw=dict(projection=projection))
    ax.set_extent(bbox)
    ax.coastlines(resolution='50m')
    gl = ax.gridlines(draw_labels=True)
    gl.top_labels = gl.right_labels = False
    gl.xformatter = LONGITUDE_FORMATTER
    gl.yformatter = LATITUDE_FORMATTER
    return fig, ax

Top


5 Plot the Data!

Here we’ll create a plot for each of the Radar Product Names from our productNames array from the previous section.

# suppress a few warnings that come from plotting
warnings.filterwarnings("ignore",category =RuntimeWarning)
warnings.filterwarnings("ignore",category =UserWarning)

# Cycle through all of the products to try and plot each one
for prod in productNames:

    request.setParameters(prod)
    availableLevels = DataAccessLayer.getAvailableLevels(request)

    # Check the available levels, if there are none, then skip this product
    if availableLevels:
        request.setLevels(availableLevels[0])
    else:
        print("No levels found for " + prod)
        continue

    cycles = DataAccessLayer.getAvailableTimes(request, True)
    times = DataAccessLayer.getAvailableTimes(request)

    if times:
        print()
        response = DataAccessLayer.getGridData(request, [times[-1]])
        print("Recs : ", len(response))

        if response:
            grid = response[0]
        else:
            continue
        data = grid.getRawData()
        lons, lats = grid.getLatLonCoords()

        print('Time :', str(grid.getDataTime()))
        flat = np.ndarray.flatten(data)
        print('Name :', str(grid.getLocationName()))
        print('Prod :', str(grid.getParameter()))
        print('Range:' , np.nanmin(flat), " to ", np.nanmax(flat), " (Unit :", grid.getUnit(), ")")
        print('Size :', str(data.shape))
        print()

        cmap = plt.get_cmap('rainbow')
        bbox = [lons.min()-0.5, lons.max()+0.5, lats.min()-0.5, lats.max()+0.5]
        fig, ax = make_map(bbox=bbox)
        cs = ax.pcolormesh(lons, lats, data, cmap=cmap)
        cbar = fig.colorbar(cs, extend='both', shrink=0.5, orientation='horizontal')
        cbar.set_label(grid.getParameter() +" " + grid.getLevel() + " " \
                       + grid.getLocationName() + " (" + prod + "), (" + grid.getUnit() + ") " \
                       + "valid " + str(grid.getDataTime().getRefTime()))
        plt.show()
Recs :  1
Time : 2024-05-22 21:53:42
Name : klwx_0.0_464_464
Prod : Composite Refl
Range: 5.0  to  60.0  (Unit : dBZ )
Size : (464, 464)
../../_images/NEXRAD_Level3_Radar_21_1.png
No levels found for Correlation Coeff
No levels found for Diff Reflectivity

Recs :  1
Time : 2024-05-22 21:57:59
Name : klwx_0.0_230_360_0.0_359.0
Prod : Digital Hybrid Scan Refl
Range: -16.0  to  57.0  (Unit : dBZ )
Size : (230, 360)
../../_images/NEXRAD_Level3_Radar_21_3.png
Recs :  1
Time : 2024-05-22 21:57:59
Name : klwx_0.0_920_360_0.0_359.0
Prod : Digital Inst Precip Rate
Range: 7.0555557e-09  to  4.0117888e-05  (Unit : m*sec^-1 )
Size : (920, 360)
../../_images/NEXRAD_Level3_Radar_21_5.png
Recs :  1
Time : 2024-05-22 21:57:59
Name : klwx_0.0_13_13
Prod : Digital Precip Array
Range: -60.0  to  690.0  (Unit : count )
Size : (13, 13)
../../_images/NEXRAD_Level3_Radar_21_7.png
Recs :  1
Time : 2024-05-22 21:53:42
Name : klwx_0.0_460_360_0.0_359.0
Prod : Digital Vert Integ Liq
Range: 0.0  to  46.34034  (Unit : kg*m^-2 )
Size : (460, 360)
../../_images/NEXRAD_Level3_Radar_21_9.png
Recs :  1
Time : 2024-05-22 21:53:42
Name : klwx_0.0_346_360_0.0_359.0
Prod : Enhanced Echo Tops
Range: nan  to  nan  (Unit : m )
Size : (346, 360)
../../_images/NEXRAD_Level3_Radar_21_11.png
Recs :  1
Time : 2024-05-22 21:57:59
Name : klwx_0.0_920_360_0.0_359.0
Prod : Hybrid Hydrometeor Class
Range: 1.0  to  10.0  (Unit : count )
Size : (920, 360)
../../_images/NEXRAD_Level3_Radar_21_13.png
No levels found for Hydrometeor Class
No levels found for Melting Layer

Recs :  0

Recs :  1
Time : 2024-05-22 21:57:59
Name : klwx_0.0_115_360_359.0_359.0
Prod : One Hour Accum
Range: 0.0  to  0.0254  (Unit : m )
Size : (115, 360)
../../_images/NEXRAD_Level3_Radar_21_15.png
Recs :  1
Time : 2024-05-22 21:57:59
Name : klwx_0.0_920_360_0.0_359.0
Prod : One Hour Unbiased Accum
Range: 2.54e-05  to  0.030784799  (Unit : m )
Size : (920, 360)
../../_images/NEXRAD_Level3_Radar_21_17.png
No levels found for Reflectivity
No levels found for Specific Diff Phase
No levels found for Storm Rel Velocity

Recs :  1
Time : 2024-05-22 21:57:59
Name : klwx_0.0_920_360_0.0_359.0
Prod : Storm Total Accum
Range: 0.000254  to  0.051054  (Unit : m )
Size : (920, 360)
../../_images/NEXRAD_Level3_Radar_21_19.png
Recs :  0
No levels found for User Select Accum
No levels found for Velocity

Recs :  1
Time : 2024-05-22 21:57:59
Name : klwx_0.0_116_116
Prod : Vert Integ Liq
Range: 1.0  to  45.0  (Unit : kg*m^-2 )
Size : (116, 116)
../../_images/NEXRAD_Level3_Radar_21_21.png

Top


6 See Also

6.2 Additional Documentation

python-awips

matplotlib

Top