From 25fe631dade831050f9a6e6215a9ccf7712cc926 Mon Sep 17 00:00:00 2001 From: davidh-ssec <david.hoese@ssec.wisc.edu> Date: Fri, 14 Jul 2017 08:54:03 -0500 Subject: [PATCH] Add units to meteorogram plots --- .../tower_quicklooks/create_quicklook.py | 312 +----------------- 1 file changed, 18 insertions(+), 294 deletions(-) diff --git a/aosstower/tower_quicklooks/create_quicklook.py b/aosstower/tower_quicklooks/create_quicklook.py index dd0459c..4215631 100644 --- a/aosstower/tower_quicklooks/create_quicklook.py +++ b/aosstower/tower_quicklooks/create_quicklook.py @@ -28,6 +28,10 @@ def remove_yticklines(ax): t.set_visible(False) +def get_subtitle_location(num_subplots): + return 1 - 0.055 * num_subplots + + class PlotMaker(object): """Object for making plots and storing/validating plot metadata""" def __init__(self, name, dependencies, title=None, units=None): @@ -86,8 +90,13 @@ class PlotMaker(object): if y_label and not is_subplot: ax.set_ylabel(y_label) + if is_subplot: + # put units on the top left of the plot axes + ax.text(.008, .9, self.units, + horizontalalignment='left', va='top', + transform=ax.transAxes, size=8) + def _call_plot(self, frame, ax): - print(self.name, frame.columns) lines = ax.plot(frame.index, frame, 'k') return lines @@ -190,6 +199,9 @@ class PlotMaker(object): ax.spines['top'].set_visible(False) ax.spines['bottom'].set_visible(False) + for t in ax.texts: + t.set_visible(False) + def convert_to_thumbnail(self, fig, fig_size=TN_SIZE): fig = self._convert_fig_to_thumbnail(fig, fig_size) self._convert_ax_to_thumbnail(fig) @@ -345,7 +357,7 @@ PLOT_TYPES = { 'wind_dir': WindDirPlotMaker('wind_dir', ('wind_dir',), units='°'), # special tick labels 'rh': PlotMaker('rh', ('rh',), units='%'), 'air_temp': PlotMaker('air_temp', ('air_temp',), units='°C'), - 'pressure': PlotMaker('pressure', ('pressure',), units='hpa'), + 'pressure': PlotMaker('pressure', ('pressure',), units='hPa'), 'dewpoint': PlotMaker('dewpoint', ('air_temp',), units='°C'), 'wind_speed': PlotMaker('wind_speed', ('wind_speed',), units='m/s'), 'accum_precip': PrecipPlotMaker('accum_precip', ('accum_precip',), units='mm'), @@ -377,255 +389,8 @@ def get_data(input_files, columns): return pd.DataFrame(data_dict).set_index(['stamps']) -# The purpose of this method is to determines all the 12:00:00 days -# within a start and end date -# @param cur_dt - start time -# @param dates - end time -# @return dates in lists - -def get_dates_in_range(cur_dt, end): - curr = cur_dt - while curr <= end: - yield curr - curr += timedelta(days=1) - -# The purpose of this method is to get the min and max of -# an array of stamps and determines all the 12:00:00 days -# in that array -# @param dewpoint stamps - time stamps for valid dewpoint data. Only specified if -# air temp and dew point need to be ploted in the same plot -# @param dates - time stamps for data -# @return datesInRange - all days with 12:00:00 -def find_half_days(dewpoint_stamps, dates): - date_max = np.amax(dates) - date_min = np.amin(dates) - - if(dewpoint_stamps): - dew_max = np.amax(dewpoint_stamps) - dew_min = np.amin(dewpoint_stamps) - - date_max = max(date_max, dew_max) - date_min = min(date_min, dew_min) - - cur_dt = date_min.replace(hour=12, minute=0, second=0) - - if cur_dt < date_min: - cur_dt += timedelta(days=1) - - datesInRange = list(get_dates_in_range(cur_dt, date_max)) - - return datesInRange - -# The purpose of this method is to create a thumbnail plot -# @param frame - all data I need in a data frame -# @param ymin - lower limit of the y axis if specified. set only when --daily is given -# @param ymax - upper limit of yaxis if specified. Set only when --daily is given -# @param dewpoint_stamps - timestamps for valid dewpoint data. Only specified if -# air temp and dew point need to be plotted in the same plot -# @param dewpoint_data - valid dewpoint data. Only specified if -# air temp and dew point need to be plotted in the same plot -#@param output - output filename pattern -def thumbnail_plot(dates, data, ymin, ymax, - dewpoint_stamps, dewpoint_data, output, wind_dir): - - if(dewpoint_stamps): - plt.plot(dates, data, 'r', dewpoint_stamps, dewpoint_data, 'b') - plt.axis([min(dates), max(dates), ymin, ymax]) - #plt.plot(dewpoint_stamps, dewpoint_data, 'b') - - elif(wind_dir): - plt.plot(dates, data, 'ko', markersize=0.1) - plt.axis([min(dates), max(dates), 0, 360]) - - else: - plt.plot(dates, data, 'k') - plt.axis([min(dates), max(dates), ymin, ymax]) - - #scale - fig = plt.gcf() - dpi = fig.get_dpi() - fig.set_size_inches(80/float(dpi), 80/float(dpi)) - - #create axes - axes = plt.gca() - axes.axes.get_xaxis().set_ticklabels([]) - axes.axes.get_xaxis().set_tick_params(length=7) - axes.axes.get_yaxis().set_ticklabels([]) - axes.axes.get_yaxis().set_visible(False) - axes.spines['right'].set_visible(False) - axes.spines['bottom'].set_visible(False) - - #got the basic shape of the plot - #still need to draw a line in the middle - datesInRange = find_half_days(dewpoint_stamps, dates) - - for date in datesInRange: - plt.axvline(x=date, color='k') - -# The purpose of this method is to create a thumbnail -# @param frame - all data I need in a data frame -# @param ymin - lower limit of the y axis if specified. set only when --daily is given -# @param ymax - upper limit of yaxis if specified. Set only when --daily is given -# @param create_air_dew_plot - boolean specifying if air temp and dewpoint -# @param output - output filename pattern -# should be in same plot -def create_thumbnail(plot_names, frame, ymin, ymax, create_air_dew_plot, output): - #see if we're going to need subplots - #subplots needed when len is greater than 2 for one variable - #or greater than 4 for two variables - - need_subplots = len(list(frame.columns.values)) > 2 and not create_air_dew_plot - need_subplots = need_subplots or len(list(frame.columns.values)) > 4 and create_air_dew_plot - - if need_subplots: - plots_created = 1 - - if create_air_dew_plot: - numberPlots = (len(list(frame.columns.values)) - 2) / 2 - plotNumber = numberPlots * 100 + 10 - - else: - numberPlots = len(list(frame.columns.values)) / 2 - plotNumber = numberPlots * 100 + 10 - - stamps = frame.index - #get dewpoint data - if create_air_dew_plot: - all_data = frame['dewpoint'] - qc_data = frame['qc_dewpoint'] - - good_list = get_good_data(qc_data, stamps, all_data) - dewpoint_data = good_list[0] - dewpoint_stamps = good_list[1] - - for name in plot_names: - if name == 'dewpoint' and create_air_dew_plot: - continue - - if name not in list(frame.columns.values): - continue - - elif need_subplots: - plt.subplot(plotNumber + plots_created) - plots_created += 1 - - - all_data = frame[name] - qc_data = frame['qc_' + name] - - good_list = get_good_data(qc_data, stamps, all_data) - good_data = good_list[0] - good_stamps= good_list[1] - - #if we don't need two lines in same plot, plot - if not (create_air_dew_plot and name == 'air_temp'): - if name != 'wind_dir': - thumbnail_plot(good_stamps, good_data, ymin[name], ymax[name], - None, None, output, False) - - if name == 'wind_dir': - thumbnail_plot(good_stamps, good_data, ymin[name], ymax[name], - None, None, output, True) - - else: - thumbnail_plot(good_stamps, good_data, ymin[name], ymax[name], - dewpoint_stamps, dewpoint_data, output, False) - - plt.savefig(output.format(plot_name='test', start_time=good_stamps[0])) - -def full_plot_stamps(stamps): - # assume the timestamps are monotonic - first_stamp = stamps[0].replace(hour=0, minute=0, second=0) - return [((s - first_stamp).total_seconds()/3600) for s in stamps] - -# The purpose of this method is to change how the y ticks are shown -# @param min - minimum of the y range right now -# @param max - maximum of the y range right now -# @return new tick labels - -def get_new_labels(mini, maxi): - delta = math.ceil((maxi - mini) / 6) - return np.arange(mini, (mini + delta*6), delta) - -# The purpose of this method is to create a full_size plot -# @param frame - all data I need in a data frame -# @param ymin - lower limit of the y axis if specified. set only when --daily is given -# @param ymax - upper limit of yaxis if specified. Set only when --daily is given -# @param dewpoint_stamps - timestamps for valid dewpoint data. Only specified if -# air temp and dew point need to be plotted in the same plot -# @param dewpoint_data - valid dewpoint data. Only specified if -# air temp and dew point need to be plotted in the same plot -# @param output - output filename pattern -# @param wind_direction - says different yaxis labels -# @param accum_precip - different yaxis handling -# @param see if we need subplots -def full_plot(fig, axes, dates, data, ymin, ymax, - dewpoint_stamps, dewpoint_data, output, wind_dir, - accum_precip, need_subplots): - - dates = full_plot_stamps(dates) - - if(dewpoint_stamps): - dewpoint_stamps = full_plot_stamps(dewpoint_stamps) - - plt.plot(dates, data, 'r', dewpoint_stamps, dewpoint_data, 'b') - plt.axis([min(dates), max(dates), ymin, ymax]) - - - #labels = [math.ceil(float(item.get_text())) for item in ticks] - #plt.plot(dewpoint_stamps, dewpoint_data, 'b') - - - elif(wind_dir): - plt.plot(dates, data, 'ko', markersize=1) - plt.axis([min(dates), max(dates), 0, 360]) - - axes = plt.gca() - axes.get_yaxis().set_ticks([0,90,180,270]) - - - else: - # import ipdb; ipdb.set_trace() - plt.plot(dates, data, 'k') - # plt.axis([min(dates), max(dates), ymin, ymax]) - - if not need_subplots: - return - - #if not wind_direction, reset yaxis ticks - if not wind_dir: - if not accum_precip: - mini, maxi = axes.get_ylim() - new_labels = get_new_labels(mini, maxi) - - else: - mini, maxi = axes.get_ylim() - delta = ((maxi - mini)/6) - new_labels = np.arange(mini, (mini + delta*6), delta) - - if len(new_labels) == 0: - new_labels = [0, 0.05, 0.1] - - plt.yticks(new_labels) - axes.get_yaxis().get_major_ticks()[-1].set_visible(False) - - #hid last tick because for subplots above the last one, it's peeping out... - # will show for the last subplot or when subplots are not needed - # in other words, this code should only affect plots above the last plot - # only when sublots are needed - axes.get_xaxis().get_major_ticks()[-1].set_visible(False) - axes.get_xaxis().get_major_ticks()[0].set_visible(False) - -# The purpose of this method is to get the subtitle's y coordinate -# based upon how many subplots there are -# @param numSubPlots - number of subplots -# @return y-coordinate of subtitle -def get_subtitle_location(numSubPlots): - return 1 - 0.055*numSubPlots - - -def create_full_plot(plot_names, frame, output, - start_time=None, end_time=None, thumbnail=False): +def create_plot(plot_names, frame, output, + start_time=None, end_time=None, thumbnail=False): """ Args: @@ -672,49 +437,9 @@ def create_full_plot(plot_names, frame, output, stem, ext = os.path.splitext(out_fn) out_fn = "{}_thumbnail{}".format(stem, ext) plot_maker.convert_to_thumbnail(fig) + LOG.info("Saving thumbnail '{}' to filename '{}'".format(name, out_fn)) fig.savefig(out_fn) - # all_data = frame[name] - # qc_data = frame['qc_' + name] - # - # good_list = get_good_data(qc_data, stamps, all_data) - # good_data = good_list[0] - # good_stamps = good_list[1] - # - # dateString = good_stamps[0].strftime('%m/%d/%Y') - # - # # create plots - # if need_subplots: - # if plots_created == 1: - # plt.figure().suptitle('AO&SS Building Tower Meteorogram ' + dateString, fontsize=13) - # ax1 = plt.subplot(plotNumber + plots_created) - # ax1.set_title(TITLES[name], x=0.5, y=get_subtitle_location(numberPlots), fontsize=8) - # else: - # curr_plot = plt.subplot(plotNumber + plots_created, sharex=ax1) - # curr_plot.set_title(TITLES[name], x=0.5, y=get_subtitle_location(numberPlots), fontsize=8) - # - # plots_created += 1 - # - # else: - # plt.figure().suptitle('AO&SS Building Tower ' + IND_TITLES[name] + ' ' + dateString, fontsize=13) - # plt.ylabel(TITLES[name]) - # - # #if we don't need two lines in same plot, plot - # if not (create_air_dew_plot and name == 'air_temp'): - # full_plot(good_stamps, good_data, ymin[name], ymax[name], - # None, None, output, name == 'wind_dir', - # name == 'accum_precip', need_subplots) - # - # else: - # full_plot(good_stamps, good_data, ymin[name], ymax[name], - # dewpoint_stamps, dewpoint_data, output, False, False, - # need_subplots) - - # if need_subplots: - # plt.subplots_adjust(hspace=0, bottom=0.125) - # - # plt.xlabel('Time (UTC)') - def _dt_convert(datetime_str): try: @@ -764,7 +489,6 @@ def main(): plot_deps = [PLOT_TYPES[k].deps if k in PLOT_TYPES else (k,) for k in args.plot_names] plot_deps = list(set(d for deps in plot_deps for d in deps)) - print(plot_deps) frame = get_data(args.input_files, plot_deps) bad_plot_names = set(args.plot_names) - (set(frame.columns) | set(PLOT_TYPES.keys())) if bad_plot_names: @@ -793,7 +517,7 @@ def main(): start_time = args.start_time end_time = args.end_time - create_full_plot(args.plot_names, frame, args.output, start_time, end_time, args.thumbnail) + create_plot(args.plot_names, frame, args.output, start_time, end_time, args.thumbnail) if __name__ == "__main__": sys.exit(main()) -- GitLab