Using Python to get the latest NEXRAD Composite

Objective: Visualize the latest available reflectivity data composited data

Steps involved:

  • Define a color map based on the one used by the National Weather Service
  • Construct the appropriate URL to get the latest data file
  • Open the URL using netCDF4-python
  • Read the basic metadata
  • Create the appropriate CartoPy projection and plot the Radar Reflectivity

In [ ]:
# Set-up for notebook
%matplotlib inline

# Some needed imports
import datetime as dt
import matplotlib.pyplot as plt
import matplotlib as mpl
import cartopy
import numpy as np
from netCDF4 import Dataset
from pyudl.tds import TDSCatalog

Define the NWS Color Map for reflectivity

  • While matplotlib has some really nice, built-in colormaps, nothing quite matches the NWS colormap
  • Python List of HTML colors (used photoshop color picker on a screenshot of the colorbar off of the NWS radar page)
  • Converted into matplotlib colormap using ListedColormap

In [ ]:
# Function to create a colortable that matches the NWS colortable
def radar_colormap():
    nws_reflectivity_colors = [
    "#646464", # ND
    "#ccffff", # -30
    "#cc99cc", # -25
    "#996699", # -20
    "#663366", # -15
    "#cccc99", # -10
    "#999966", # -5
    "#646464", # 0
    "#04e9e7", # 5
    "#019ff4", # 10
    "#0300f4", # 15
    "#02fd02", # 20
    "#01c501", # 25
    "#008e00", # 30
    "#fdf802", # 35
    "#e5bc00", # 40
    "#fd9500", # 45
    "#fd0000", # 50
    "#d40000", # 55
    "#bc0000", # 60
    "#f800fd", # 65
    "#9854c6", # 70
    "#fdfdfd" # 75
    ]

    return mpl.colors.ListedColormap(nws_reflectivity_colors)

Get the latest data URL, grab the metadata, and request the data


In [ ]:
# Get today's date...
today = dt.datetime.utcnow()

# ...and use that to assemble the URL and grab the catalog
url = "http://thredds.ucar.edu/thredds/catalog/nexrad/composite/gini/n0r/1km/{}/catalog.xml".format(today.strftime("%Y%m%d"))
cat = TDSCatalog(url)

# Get the list of names of datasets and sort, so that the last dataset is the latest
names = cat.datasets.keys()
names.sort()
latest = names[-1]

# Grab the dataset for the latest
latestDs = cat.datasets[latest]

In [ ]:
# Construct a NetCDF dataset using the OPeNDAP access URL
dataset = Dataset(latestDs.accessUrls['OPENDAP'])
print dataset.variables.keys()

In [ ]:
print dir(dataset.variables['LambertConformal'])

In [ ]:
##################
# Projection fun #
##################

# get basic info from the file regarding projection attributes
# see the following for more info on projections as described here:
#   http://www.nco.ncep.noaa.gov/pmb/docs/on388/tableb.html#GRID218
#   http://www.wmo.int/pages/prog/www/WDM/Guides/Guide-binary-2.html [see LAMBERT CONFORMAL SECANT OR TANGENT CONE GRIDS]
#   http://www.unidata.ucar.edu/mailing_lists/archives/netcdf-java/2006/msg00006.html [starndard parallels in CDM]
proj = dataset.variables['LambertConformal']
rsphere = proj.earth_radius

# lat_0	: center of desired map domain (in degrees) [Basemap]
# CDM : 'latitude_of_projection_origin'
lat_0 = proj.latitude_of_projection_origin

# lon_0	: center of desired map domain (in degrees) [Basemap]
# CDM : 'longitude_of_central_meridian'
lon_0 = proj.longitude_of_central_meridian

# lat_1, lat_2 : 1st and second parallels [Basemap]
# CDM : 'standard_parallel' - this attr contains both 1st and 2nd
lat_1 = proj.standard_parallel
print lat_0, lon_0, lat_1, rsphere

# hardcoded from WMS/WCS metadata - need to add metadata into catalog class
# to grab this programatically
llcrnrlon = -129.2192094372674
llcrnrlat = 21.742620467908093
urcrnrlon = -63.559241752667674
urcrnrlat = 49.126151231865485

Grab the data


In [ ]:
# Used to subset the data
xstride = 10
ystride = 10

# download x and y coords and convert them from km to m
x = dataset.variables['x'][::xstride] * 1000.
y = dataset.variables['y'][::ystride] * 1000.

# Grab the reflectivity data. Mask values less than -30 dBz
data = dataset.variables["Reflectivity"][0, 0::ystride, 0::xstride]
data = np.ma.array(data, mask=data<=-30)

Create the Plot


In [ ]:
# Set up the projection for the LambertConformal projection we know we have
lcc = cartopy.crs.LambertConformal(central_longitude=lon_0, central_latitude=lat_0, secant_latitudes=(lat_0, lat_1))

# Create a large figure and axes with this projection
fig = plt.figure(figsize=(24, 12))
ax = fig.add_subplot(1, 1, 1, projection=lcc)

# Limit to the bounds of the data we have
ax.set_extent([llcrnrlon, urcrnrlon, llcrnrlat, urcrnrlat], cartopy.crs.Geodetic())

# Add some map features
ax.stock_img()
ax.coastlines(resolution='50m')
ax.add_feature(cartopy.feature.NaturalEarthFeature(category='cultural', name='admin_1_states_provinces_lines', scale='50m',
                                                   facecolor='none'))
ax.add_feature(cartopy.feature.BORDERS, linewidth='2', edgecolor='black')
ax.gridlines()

# Convert the time to text and add as the title
time = dataset.variables["time"][:][0] / 1000.
title = dt.datetime.fromtimestamp(time).isoformat()
ax.set_title(title)

# Plot the data as an image, using the x and y values we have as the extents of the image
# NOTE: This assumes equal-spaced points
cmap = radar_colormap()
norm = mpl.colors.Normalize(vmin=-35, vmax=80)
cax = ax.imshow(data, extent=(x.min(), x.max(), y.min(), y.max()), cmap=cmap, norm=norm, origin="upper",
                transform=lcc)
plt.colorbar(cax)

Exercise

Using what was done above, plot the digital hybrid reflectivity (DHR):