diff --git a/pyart/graph/radardisplay.py b/pyart/graph/radardisplay.py index fc91f40e5e..2378b14a30 100644 --- a/pyart/graph/radardisplay.py +++ b/pyart/graph/radardisplay.py @@ -9,6 +9,7 @@ from matplotlib.dates import DateFormatter import numpy as np import netCDF4 +from scipy.interpolate import griddata from pandas.plotting import register_matplotlib_converters register_matplotlib_converters() @@ -804,6 +805,150 @@ def plot_azimuth_to_rhi( mappable=pm, label=colorbar_label, orient=colorbar_orient, field=field, ax=ax, fig=fig, ticks=ticks, ticklabs=ticklabs) + def plot_cr_raster(self, field='reflectivity', target_range=None, ax=None, fig=None, + delta_x=None, delta_y=None, az_limits=None, el_limits=None, + vmin=None, vmax=None, cmap=None, title=None, title_flag=True, + axislabels=[None, None], axislabels_flag=True, + colorbar_flag=True, colorbar_label=None, + colorbar_orient='vertical', ticks=None, ticklabs=None, raster=False): + """ + Plot a corner reflector raster scan + + Parameters + ---------- + field : String + Field to plot if other than reflectivity + target_range : Float + Estimated range of the corner reflector + + Other Parameters + ---------------- + ax : Axis + Axis to plot on. None will use the current axis. + fig : Figure + Figure to add the colorbar to. None will use the current figure. + delta_x : Float + Azimuth grid spacing for griddata + delta_y : Float + Elevation grid spacing for griddata + az_limits : list + Azimuth limits in form [min, max] + el_limits : list + Elevation limits in form [min, max] + vmin : float + Luminance minimum value, None for default value. + Parameter is ignored is norm is not None. + vmax : float + Luminance maximum value, None for default value. + Parameter is ignored is norm is not None. + cmap : str or None + Matplotlib colormap name. None will use the default colormap for + the field being plotted as specified by the Py-ART configuration. + title : str + Title to label plot with, None to use default title generated from + the field and sweep parameters. Parameter is ignored if title_flag + is False. + title_flag : bool + True to add a title to the plot, False does not add a title. + axislabels : (str, str) + 2-tuple of x-axis, y-axis labels. None for either label will use + the default axis label. Parameter is ignored if axislabels_flag is + False. + axislabels_flag : bool + True to add label the axes, False does not label the axes. + colorbar_flag : bool + True to add a colorbar with label to the axis. False leaves off + the colorbar. + colorbar_label : str + Colorbar label, None will use a default label generated from the + field information. + ticks : array + Colorbar custom tick label locations. + ticklabs : array + Colorbar custom tick labels. + colorbar_orient : 'vertical' or 'horizontal' + Colorbar orientation. + raster : bool + False by default. Set to True to render the display as a raster + rather than a vector in call to pcolormesh. Saves time in plotting + high resolution data over large areas. Be sure to set the dpi + of the plot for your application if you save it as a vector format + (i.e., pdf, eps, svg). + + """ + + ax, fig = common.parse_ax_fig(ax, fig) + + # Get data and coordinate information + az = self._radar.azimuth['data'] + el = self._radar.elevation['data'] + rng = self._radar.range['data'] + data = self._radar.fields[field]['data'] + + # Calculate delta for x and y + if az_limits is None: + min_az = np.nanmin(az) + max_az = np.nanmax(az) + else: + min_az = az_limits[0] + max_az = az_limits[1] + + if el_limits is None: + min_el = np.nanmin(el) + max_el = np.nanmax(el) + else: + min_el = el_limits[0] + max_el = el_limits[1] + + if delta_x is None: + delta_x = max_az-min_az + if delta_y is None: + delta_y = max_el-min_el + + # Get range closest to target_range + if target_range is None: + target_index = 0 + else: + target_index = np.argmin(np.abs(np.array(rng) - target_range)) + + data = data[:, target_index] + + # Geet azimuth and elevation onto a meshgrid + xi, yi = np.meshgrid(np.linspace(min_az, max_az, int(delta_x/0.01)), + np.linspace(min_el, max_el, int(delta_y/0.01))) + + # Grid up the data for plotting + grid = griddata((az, el), data, (xi, yi), method='linear') + + # Plot data using pcolormesh + pm = ax.pcolormesh(xi[0, :], yi[:, 0], grid, vmin=vmin, vmax=vmax, cmap=cmap) + + if title_flag is True: + if title is None: + time_str = common.generate_radar_time_begin(self._radar) + title = ' '.join(['Corner Reflector', field.title(), time_str.strftime('%m/%d/%Y %H:%M:%S')]) + ax.set_title(title) + + if axislabels_flag is True: + if axislabels[0] is None: + axislabels[0] = 'Azimuth (deg)' + if axislabels[1] is None: + axislabels[1] = 'Elevation (deg)' + ax.set_xlabel(axislabels[0]) + ax.set_ylabel(axislabels[1]) + + if raster: + pm.set_rasterized(True) + + # add plot and field to lists + self.plots.append(pm) + self.plot_vars.append(field) + + if colorbar_flag: + self.plot_colorbar( + mappable=pm, label=colorbar_label, orient=colorbar_orient, + field=field, ax=ax, fig=fig, ticks=ticks, ticklabs=ticklabs) + def plot_range_rings(self, range_rings, ax=None, col='k', ls='-', lw=2): """ Plot a series of range rings. @@ -1333,7 +1478,7 @@ def _get_vpt_data(self, field, mask_tuple, filter_transitions, if gatefilter is not None: mask_filter = gatefilter.gate_excluded data = np.ma.masked_array(data, mask_filter) - + # filter out antenna transitions if filter_transitions and self.antenna_transition is not None: in_trans = self.antenna_transition diff --git a/pyart/graph/tests/test_radardisplay.py b/pyart/graph/tests/test_radardisplay.py index f3105822e6..6d074f42e9 100644 --- a/pyart/graph/tests/test_radardisplay.py +++ b/pyart/graph/tests/test_radardisplay.py @@ -84,6 +84,17 @@ def test_radardisplay_vpt(outfile=None): plt.close() +def test_radardisplay_cr_raster(outfile=None): + radar = pyart.io.read_cfradial(pyart.testing.CFRADIAL_CR_RASTER_FILE) + display = pyart.graph.RadarDisplay(radar) + fig = plt.figure() + ax = fig.add_subplot(111) + display.plot_cr_raster(target_range=478., el_limits=[-0.5, 2.5]) + if outfile: + fig.savefig(outfile) + plt.close() + + def test_radardisplay_vpt_time(outfile=None): radar = pyart.io.read_cfradial(pyart.testing.CFRADIAL_PPI_FILE) pyart.util.to_vpt(radar) # hack to make the data a VPT @@ -289,3 +300,4 @@ def test_radardisplay_get_colorbar_label(): test_radardisplay_ppi('figure_radar_display_ppi.png') test_radardisplay_ray('figure_radar_display_ray.png') test_radardisplay_vpt('figure_radar_display_vpt.png') + test_radardisplay_cr_raster('figure_radar_display_cr_raster.png') diff --git a/pyart/testing/__init__.py b/pyart/testing/__init__.py index bf4ce1cdcb..890d493e24 100644 --- a/pyart/testing/__init__.py +++ b/pyart/testing/__init__.py @@ -5,7 +5,7 @@ """ from .sample_files import MDV_PPI_FILE, MDV_RHI_FILE, MDV_GRID_FILE -from .sample_files import CFRADIAL_PPI_FILE, CFRADIAL_RHI_FILE +from .sample_files import CFRADIAL_PPI_FILE, CFRADIAL_RHI_FILE, CFRADIAL_CR_RASTER_FILE from .sample_files import CHL_RHI_FILE, UF_FILE from .sample_files import SIGMET_PPI_FILE, SIGMET_RHI_FILE from .sample_files import INTERP_SOUNDE_FILE, SONDE_FILE diff --git a/pyart/testing/data/baseline_figures/example_cfradial_cr_raster.png b/pyart/testing/data/baseline_figures/example_cfradial_cr_raster.png new file mode 100644 index 0000000000..f487682def Binary files /dev/null and b/pyart/testing/data/baseline_figures/example_cfradial_cr_raster.png differ diff --git a/pyart/testing/data/example_cfradial_cr_raster.nc b/pyart/testing/data/example_cfradial_cr_raster.nc new file mode 100644 index 0000000000..10d9eee7ea Binary files /dev/null and b/pyart/testing/data/example_cfradial_cr_raster.nc differ diff --git a/pyart/testing/sample_files.py b/pyart/testing/sample_files.py index b38d3ecd05..29bcb2fcf3 100644 --- a/pyart/testing/sample_files.py +++ b/pyart/testing/sample_files.py @@ -6,6 +6,7 @@ MDV_RHI_FILE CFRADIAL_PPI_FILE CFRADIAL_RHI_FILE +CFRADIAL_CR_RASTER_FILE CHL_RHI_FILE SIGMET_PPI_FILE SIGMET_RHI_FILE @@ -29,6 +30,7 @@ MDV_GRID_FILE = os.path.join(DATA_PATH, 'example_mdv_grid.mdv') CFRADIAL_PPI_FILE = os.path.join(DATA_PATH, 'example_cfradial_ppi.nc') CFRADIAL_RHI_FILE = os.path.join(DATA_PATH, 'example_cfradial_rhi.nc') +CFRADIAL_CR_RASTER_FILE = os.path.join(DATA_PATH, 'example_cfradial_cr_raster.nc') CHL_RHI_FILE = os.path.join(DATA_PATH, 'example_chl_rhi.chl') SIGMET_PPI_FILE = os.path.join(DATA_PATH, 'example_sigmet_ppi.sigmet') SIGMET_RHI_FILE = os.path.join(DATA_PATH, 'example_sigmet_rhi.sigmet')