diff --git a/.travis.yml b/.travis.yml index 355ae923ad..f51185275e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,11 +25,15 @@ matrix: env: - PYTHON_VERSION="3.7" - PYTEST_ARGS="-v --cov pyart" + - python: 3.8 + env: + - PYTHON_VERSION="3.8" + - PYTEST_ARGS="-v --cov pyart" - DOC_BUILD="true" - COVERALLS="true" - - python: 3.7 + - python: 3.8 env: - - PYTHON_VERSION="3.7" + - PYTHON_VERSION="3.8" - PYTEST_ARGS="-v --pyargs pyart" - FROM_RECIPE="true" install: source continuous_integration/install.sh diff --git a/appveyor.yml b/appveyor.yml index 872ca03568..ed4c857ecf 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -5,7 +5,7 @@ environment: matrix: - PYTHON_VERSION: "2.7" - - PYTHON_VERSION: "3.7" + - PYTHON_VERSION: "3.8" platform: - x64 @@ -47,4 +47,4 @@ build: false test_script: # run the unit tests - "if \"%PYTHON_VERSION%\"==\"2.7\" (pytest -v --cov pyart --ignore pyart/util)" - - "if \"%PYTHON_VERSION%\"==\"3.7\" (pytest -v --cov pyart)" + - "if \"%PYTHON_VERSION%\"==\"3.8\" (pytest -v --cov pyart)" diff --git a/continuous_integration/environment-3.8.yml b/continuous_integration/environment-3.8.yml new file mode 100644 index 0000000000..68b688b193 --- /dev/null +++ b/continuous_integration/environment-3.8.yml @@ -0,0 +1,17 @@ +name: testenv +channels: + - conda-forge + - defaults +dependencies: + - python=3.8 + - gdal + - numpy + - scipy + - matplotlib + - netcdf4 + - pytest + - trmm_rsl + - wradlib + - cartopy + - cvxopt + - xarray diff --git a/pyart/graph/radardisplay.py b/pyart/graph/radardisplay.py index 5ca017c345..e777e69707 100644 --- a/pyart/graph/radardisplay.py +++ b/pyart/graph/radardisplay.py @@ -18,6 +18,8 @@ from matplotlib.dates import DateFormatter import numpy as np import netCDF4 +from pandas.plotting import register_matplotlib_converters +register_matplotlib_converters() from . import common from ..core.transforms import antenna_to_cartesian @@ -124,7 +126,7 @@ def plot(self, field, sweep=0, **kwargs): Create a plot appropiate for the radar. This function calls the plotting function corresponding to - the scan_type of the radar. Additional keywords can be passed to + the scan_type of the radar. Additional keywords can be passed to customize the plot, see the appropiate plot function for the allowed keywords. @@ -181,7 +183,7 @@ def plot_ray(self, field, ray, format_str='k-', mask_tuple=None, Maximum ray value, None for default value, ignored if mask_outside is False. mask_outside : bool - True to mask data outside of vmin, vmax. False performs no + True to mask data outside of vmin, vmax. False performs no masking. title : str Title to label plot with, None to use default title generated from @@ -193,8 +195,8 @@ def plot_ray(self, field, ray, format_str='k-', mask_tuple=None, GateFilter instance. None will result in no gatefilter mask being applied to data. 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 + 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. @@ -261,14 +263,14 @@ def plot_ppi( Luminance maximum value, None for default value. Parameter is ignored is norm is not None. norm : Normalize or None, optional - matplotlib Normalize instance used to scale luminance data. If not - None the vmax and vmin parameters are ignored. If None, vmin and + matplotlib Normalize instance used to scale luminance data. If not + None the vmax and vmin parameters are ignored. If None, vmin and vmax are used for luminance scaling. 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. mask_outside : bool - True to mask data outside of vmin, vmax. False performs no + True to mask data outside of vmin, vmax. False performs no masking. title : str Title to label plot with, None to use default title generated from @@ -277,18 +279,18 @@ def plot_ppi( title_datetime_format : str Format of datetime in the title (using strftime format). title_use_sweep_time : bool - True for the current sweep's beginning time to be used for the title. - False for the radar's beginning time. + True for the current sweep's beginning time to be used for the + title. False for the radar's beginning time. 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 + 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 + 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 @@ -298,12 +300,12 @@ def plot_ppi( ticks : array Colorbar custom tick label locations. ticklabs : array - Colorbar custom tick labels. + Colorbar custom tick labels. edges : bool True will interpolate and extrapolate the gate edges from the range, azimuth and elevations in the radar, treating these - as specifying the center of each gate. False treats these - coordinates themselved as the gate edges, resulting in a plot + as specifying the center of each gate. False treats these + coordinates themselves as the gate edges, resulting in a plot in which the last gate in each ray and the entire last ray are not plotted. gatefilter : GateFilter @@ -311,7 +313,7 @@ def plot_ppi( applied to data. filter_transitions : bool True to remove rays where the antenna was in transition between - sweeps from the plot. False will include these rays in the plot. + sweeps from the plot. False will include these rays in the plot. No rays are filtered when the antenna_transition attribute of the underlying radar is not present. ax : Axis @@ -319,9 +321,9 @@ def plot_ppi( fig : Figure Figure to add the colorbar to. None will use the current figure. 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 + 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). @@ -400,8 +402,8 @@ def plot_rhi( Luminance maximum value, None for default value. Parameter is ignored is norm is not None. norm : Normalize or None, optional - matplotlib Normalize instance used to scale luminance data. If not - None the vmax and vmin parameters are ignored. If None, vmin and + matplotlib Normalize instance used to scale luminance data. If not + None the vmax and vmin parameters are ignored. If None, vmin and vmax are used for luminance scaling. cmap : str or None Matplotlib colormap name. None will use the default colormap for @@ -413,22 +415,22 @@ def plot_rhi( title_datetime_format : str Format of datetime in the title (using strftime format). title_use_sweep_time : bool - True for the current sweep's beginning time to be used for the title. - False for the radar's beginning time. + True for the current sweep's beginning time to be used for the + title. False for the radar's beginning time. 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 + 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. reverse_xaxis : bool or None True to reverse the x-axis so the plot reads east to west, False - to have east to west. None (the default) will reverse the axis + to have east to west. None (the default) will reverse the axis only when all the distances are negative. colorbar_flag : bool - True to add a colorbar with label to the axis. False leaves off + 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 @@ -438,12 +440,12 @@ def plot_rhi( ticks : array Colorbar custom tick label locations. ticklabs : array - Colorbar custom tick labels. + Colorbar custom tick labels. edges : bool True will interpolate and extrapolate the gate edges from the range, azimuth and elevations in the radar, treating these - as specifying the center of each gate. False treats these - coordinates themselved as the gate edges, resulting in a plot + as specifying the center of each gate. False treats these + coordinates themselves as the gate edges, resulting in a plot in which the last gate in each ray and the entire last ray are not not plotted. gatefilter : GateFilter @@ -451,7 +453,7 @@ def plot_rhi( applied to data. filter_transitions : bool True to remove rays where the antenna was in transition between - sweeps from the plot. False will include these rays in the plot. + sweeps from the plot. False will include these rays in the plot. No rays are filtered when the antenna_transition attribute of the underlying radar is not present. ax : Axis @@ -459,9 +461,9 @@ def plot_rhi( fig : Figure Figure to add the colorbar to. None will use the current figure. 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 + 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). @@ -550,14 +552,14 @@ def plot_vpt( Luminance maximum value, None for default value. Parameter is ignored is norm is not None. norm : Normalize or None, optional - matplotlib Normalize instance used to scale luminance data. If not - None the vmax and vmin parameters are ignored. If None, vmin and + matplotlib Normalize instance used to scale luminance data. If not + None the vmax and vmin parameters are ignored. If None, vmin and vmax are used for luminance scaling. 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. mask_outside : bool - True to mask data outside of vmin, vmax. False performs no + True to mask data outside of vmin, vmax. False performs no masking. title : str Title to label plot with, None to use default title generated from @@ -566,13 +568,13 @@ def plot_vpt( 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 + 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 + 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 @@ -580,14 +582,14 @@ def plot_vpt( ticks : array Colorbar custom tick label locations. ticklabs : array - Colorbar custom tick labels. + Colorbar custom tick labels. colorbar_orient : 'vertical' or 'horizontal' Colorbar orientation. edges : bool True will interpolate and extrapolate the gate edges from the range, azimuth and elevations in the radar, treating these - as specifying the center of each gate. False treats these - coordinates themselved as the gate edges, resulting in a plot + as specifying the center of each gate. False treats these + coordinates themselves as the gate edges, resulting in a plot in which the last gate in each ray and the entire last ray are not not plotted. gatefilter : GateFilter @@ -595,7 +597,7 @@ def plot_vpt( applied to data. filter_transitions : bool True to remove rays where the antenna was in transition between - sweeps from the plot. False will include these rays in the plot. + sweeps from the plot. False will include these rays in the plot. No rays are filtered when the antenna_transition attribute of the underlying radar is not present. time_axis_flag : bool @@ -612,8 +614,8 @@ def plot_vpt( fig : Figure Figure to add the colorbar to. None will use the current figure. 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 + 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). @@ -642,7 +644,8 @@ def plot_vpt( # set up the time axis if time_axis_flag: self._set_vpt_time_axis(ax, date_time_form=date_time_form, tz=tz) - x = datetimes_from_radar(self._radar) + times = datetimes_from_radar(self._radar) + x = times.astype('datetime64[ns]') # mask the data where outside the limits data = _mask_outside(mask_outside, data, vmin, vmax) @@ -707,8 +710,8 @@ def plot_azimuth_to_rhi( Luminance maximum value, None for default value. Parameter is ignored is norm is not None. norm : Normalize or None, optional - matplotlib Normalize instance used to scale luminance data. If not - None the vmax and vmin parameters are ignored. If None, vmin and + matplotlib Normalize instance used to scale luminance data. If not + None the vmax and vmin parameters are ignored. If None, vmin and vmax are used for luminance scaling. cmap : str or None Matplotlib colormap name. None will use the default colormap for @@ -720,17 +723,17 @@ def plot_azimuth_to_rhi( 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 + 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. reverse_xaxis : bool or None True to reverse the x-axis so the plot reads east to west, False - to have east to west. None (the default) will reverse the axis + to have east to west. None (the default) will reverse the axis only when all the distances are negative. colorbar_flag : bool - True to add a colorbar with label to the axis. False leaves off + 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 @@ -738,14 +741,14 @@ def plot_azimuth_to_rhi( ticks : array Colorbar custom tick label locations. ticklabs : array - Colorbar custom tick labels. + Colorbar custom tick labels. colorbar_orient : 'vertical' or 'horizontal' Colorbar orientation. edges : bool True will interpolate and extrapolate the gate edges from the range, azimuth and elevations in the radar, treating these - as specifying the center of each gate. False treats these - coordinates themselved as the gate edges, resulting in a plot + as specifying the center of each gate. False treats these + coordinates themselves as the gate edges, resulting in a plot in which the last gate in each ray and the entire last ray are not not plotted. gatefilter : GateFilter @@ -753,7 +756,7 @@ def plot_azimuth_to_rhi( applied to data. filter_transitions : bool True to remove rays where the antenna was in transition between - sweeps from the plot. False will include these rays in the plot. + sweeps from the plot. False will include these rays in the plot. No rays are filtered when the antenna_transition attribute of the underlying radar is not present. ax : Axis @@ -761,8 +764,8 @@ def plot_azimuth_to_rhi( fig : Figure Figure to add the colorbar to. None will use the current figure. 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 + 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). @@ -819,7 +822,7 @@ def plot_range_rings(self, range_rings, ax=None, col='k', ls='-', lw=2): range_rings : list List of locations in km to draw range rings. ax : Axis - Axis to plot on. None will use the current axis. + Axis to plot on. None will use the current axis. col : str or value Color to use for range rings. ls : str @@ -843,7 +846,7 @@ def plot_range_ring( npts: int Number of points in the ring, higher for better resolution. ax : Axis - Axis to plot on. None will use the current axis. + Axis to plot on. None will use the current axis. col : str or value Color to use for range rings. ls : str @@ -865,7 +868,7 @@ def plot_grid_lines(ax=None, col='k', ls=':'): Parameters ---------- ax : Axis - Axis to plot on. None will use the current axis. + Axis to plot on. None will use the current axis. col : str or value Color to use for grid lines. ls : str @@ -886,15 +889,15 @@ def plot_labels( List of labels to place just above symbols. locations : list of 2-tuples List of latitude, longitude (in degrees) tuples at which symbols - will be place. Labels are placed just above the symbols. + will be place. Labels are placed just above the symbols. symbols : list of str or str List of matplotlib color+marker strings defining symbols to place - at given locations. If a single string is provided, that symbol + at given locations. If a single string is provided, that symbol will be placed at all locations. text_color : str Matplotlib color defining the color of the label text. ax : Axis - Axis to plot on. None will use the current axis. + Axis to plot on. None will use the current axis. """ ax = common.parse_ax(ax) @@ -924,14 +927,14 @@ def plot_label( Label text to place just above symbol. location : 2-tuples Tuple of latitude, longitude (in degrees) at which the symbol - will be place. The label is placed just above the symbol. + will be place. The label is placed just above the symbol. symbol : str Matplotlib color+marker strings defining the symbol to place at the given location. text_color : str Matplotlib color defining the color of the label text. ax : Axis - Axis to plot on. None will use the current axis. + Axis to plot on. None will use the current axis. """ ax = common.parse_ax(ax) @@ -956,7 +959,7 @@ def plot_cross_hair(size, npts=100, ax=None): npts: int Number of points in the cross-hair, higher for better resolution. ax : Axis - Axis to plot on. None will use the current axis. + Axis to plot on. None will use the current axis. """ ax = common.parse_ax(ax) @@ -974,25 +977,26 @@ def plot_colorbar(self, mappable=None, field=None, label=None, Parameters ---------- mappable : Image, ContourSet, etc. - Image, ContourSet, etc to which the colorbar applied. If None the + Image, ContourSet, etc to which the colorbar applied. If None the last mappable object will be used. field : str Field to label colorbar with. label : str - Colorbar label. None will use a default value from the last field + Colorbar label. None will use a default value from the last field plotted. orient : str Colorbar orientation, either 'vertical' [default] or 'horizontal'. cax : Axis - Axis onto which the colorbar will be drawn. None is also valid. + Axis onto which the colorbar will be drawn. None is also valid. ax : Axes Axis onto which the colorbar will be drawn. None is also valid. fig : Figure - Figure to place colorbar on. None will use the current figure. + Figure to place colorbar on. None will use the current figure. ticks : array Colorbar custom tick label locations. ticklabs : array - Colorbar custom tick labels. + Colorbar custom tick labels. + """ if fig is None: fig = plt.gcf() @@ -1028,7 +1032,7 @@ def set_limits(xlim=None, ylim=None, ax=None): ylim : tuple, optional 2-Tuple containing x-axis limits in km. None uses default limits. ax : Axis - Axis to adjust. None will adjust the current axis. + Axis to adjust. None will adjust the current axis. """ common.set_limits(xlim, ylim, ax) @@ -1157,7 +1161,8 @@ def _label_axes_vpt(self, axis_labels, time_axis_flag, ax): @staticmethod def _set_vpt_time_axis(ax, date_time_form=None, tz=None): - """ Set the x axis as a time formatted axis. + """ + Set the x axis as a time formatted axis. Parameters ---------- @@ -1183,7 +1188,9 @@ def _set_vpt_time_axis(ax, date_time_form=None, tz=None): # name generator methods # ########################## - def generate_filename(self, field, sweep, ext='png', datetime_format='%Y%m%d%H%M%S', use_sweep_time=False): + def generate_filename(self, field, sweep, ext='png', + datetime_format='%Y%m%d%H%M%S', + use_sweep_time=False): """ Generate a filename for a plot. @@ -1209,9 +1216,11 @@ def generate_filename(self, field, sweep, ext='png', datetime_format='%Y%m%d%H%M Filename suitable for saving a plot. """ - return common.generate_filename(self._radar, field, sweep, ext, datetime_format, use_sweep_time) + return common.generate_filename( + self._radar, field, sweep, ext, datetime_format, use_sweep_time) - def generate_title(self, field, sweep, datetime_format=None, use_sweep_time=True): + def generate_title(self, field, sweep, datetime_format=None, + use_sweep_time=True): """ Generate a title for a plot. @@ -1232,7 +1241,8 @@ def generate_title(self, field, sweep, datetime_format=None, use_sweep_time=True Plot title. """ - return common.generate_title(self._radar, field, sweep, datetime_format, use_sweep_time) + return common.generate_title( + self._radar, field, sweep, datetime_format, use_sweep_time) def generate_vpt_title(self, field): """ @@ -1359,7 +1369,7 @@ def _get_ray_data(self, field, ray, mask_tuple, gatefilter): def _get_azimuth_rhi_data_x_y_z(self, field, target_azimuth, edges, mask_tuple, filter_transitions, gatefilter): - """Retrieve and return pseudo-RHI data from a plot function. """ + """ Retrieve and return pseudo-RHI data from a plot function. """ # determine which rays from the ppi radar make up the pseudo RHI data = self.fields[field]['data'] diff --git a/pyart/util/datetime_utils.py b/pyart/util/datetime_utils.py index 49f0ddb61e..40e7ca0996 100644 --- a/pyart/util/datetime_utils.py +++ b/pyart/util/datetime_utils.py @@ -14,60 +14,66 @@ """ -from netCDF4 import num2date, date2num +try: + from cftime import num2date, date2num +except ImportError: + from netCDF4 import num2date, date2num EPOCH_UNITS = "seconds since 1970-01-01T00:00:00Z" -def datetime_from_radar(radar, epoch=False): +def datetime_from_radar(radar, epoch=False, **kwargs): """ Return a datetime for the first ray in a Radar. """ if epoch: dtrad = num2date(radar.time['data'][0], radar.time['units']) epnum = date2num(dtrad, EPOCH_UNITS) - return num2date(epnum, EPOCH_UNITS) + return num2date(epnum, EPOCH_UNITS, **kwargs) else: - return num2date(radar.time['data'][0], radar.time['units']) + return num2date(radar.time['data'][0], radar.time['units'], + **kwargs) -def datetimes_from_radar(radar, epoch=False): +def datetimes_from_radar(radar, epoch=False, **kwargs): """ Return an array of datetimes for the rays in a Radar. """ if epoch: dtrad = num2date(radar.time['data'][:], radar.time['units']) epnum = date2num(dtrad, EPOCH_UNITS) - return num2date(epnum, EPOCH_UNITS) + return num2date(epnum, EPOCH_UNITS, **kwargs) else: - return num2date(radar.time['data'][:], radar.time['units']) + return num2date(radar.time['data'][:], radar.time['units'], + **kwargs) -def datetime_from_dataset(dataset, epoch=False): +def datetime_from_dataset(dataset, epoch=False, **kwargs): """ Return a datetime for the first time in a netCDF Dataset. """ if epoch: dtdata = num2date(dataset.variables['time'][0], dataset.variables['time'].units) epnum = date2num(dtdata, EPOCH_UNITS) - return num2date(epnum, EPOCH_UNITS) + return num2date(epnum, EPOCH_UNITS, **kwargs) else: return num2date(dataset.variables['time'][0], - dataset.variables['time'].units) + dataset.variables['time'].units, **kwargs) -def datetimes_from_dataset(dataset, epoch=False): +def datetimes_from_dataset(dataset, epoch=False, **kwargs): """ Return an array of datetimes for the times in a netCDF Dataset. """ if epoch: dtdata = num2date(dataset.variables['time'][:], dataset.variables['time'].units) epnum = date2num(dtdata, EPOCH_UNITS) - return num2date(epnum, EPOCH_UNITS) + return num2date(epnum, EPOCH_UNITS, **kwargs) else: return num2date(dataset.variables['time'][:], - dataset.variables['time'].units) + dataset.variables['time'].units, **kwargs) -def datetime_from_grid(grid, epoch=False): +def datetime_from_grid(grid, epoch=False, **kwargs): """ Return a datetime for the volume start in a Grid. """ if epoch: dtrad = num2date(grid.time['data'][0], grid.time['units']) epnum = date2num(dtrad, EPOCH_UNITS) - return num2date(epnum, EPOCH_UNITS) + return num2date(epnum, EPOCH_UNITS, **kwargs) else: - return num2date(grid.time['data'][0], grid.time['units']) + return num2date(grid.time['data'][0], grid.time['units'], + **kwargs)