Skip to content
Snippets Groups Projects
Unverified Commit c751ef4e authored by David Hoese's avatar David Hoese
Browse files

Rewrite of entire quicklook script (WIP)

Includes addition of 'calendar' attribute to time variable in netcdf
parent 445bf446
No related branches found
No related tags found
No related merge requests found
...@@ -151,8 +151,8 @@ def create_variables(nc_file, first_stamp, database, chunk_sizes=None, zlib=Fals ...@@ -151,8 +151,8 @@ def create_variables(nc_file, first_stamp, database, chunk_sizes=None, zlib=Fals
variable.ancillary_variables = 'base_time' variable.ancillary_variables = 'base_time'
# CF default # CF default
# if 'time' in key: if 'time' in key:
# variable.calendar = 'gregorian' variable.calendar = 'gregorian'
for entry in sorted(database.keys()): for entry in sorted(database.keys()):
if entry == 'stamp': if entry == 'stamp':
......
import os import os
from datetime import datetime as dt import sys
from datetime import timedelta as delta from datetime import datetime as dt, timedelta as delta
import logging import logging
import pandas as pd import pandas as pd
from netCDF4 import MFDataset, MFTime from netCDF4 import MFDataset, MFTime
...@@ -8,99 +8,184 @@ import numpy as np ...@@ -8,99 +8,184 @@ import numpy as np
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
import math import math
CHOICES = ['air_temp', 'dewpoint', LOG = logging.getLogger(__name__)
'rh', 'pressure', 'wind_speed', 'wind_dir', 'accum_precip', 'solar_flux']
# names of the plots used in title (default is `.title()` of plot name)
TITLES = {'air_temp': 'Temperature and Dewpoint(°C)', TITLES = {
'rh': 'Relative Humidity(%)', 'pressure': 'Pressure(hpa)', 'air_temp': 'Air Temperature',
'wind_speed': 'Wind Speed(m/s)', 'wind_dir': 'Wind Direction(°)', 'td': 'Air and Dewpoint Temperature',
'accum_precip': 'Accumulated Precipitation Since 00Z (mm)', 'rh': 'Relative Humidity',
'solar_flux': 'Solar Flux(W/m^2)'} 'wind_dir': 'Wind Direction',
'accum_precip': 'Accumulated Precipitation Since 0Z',
IND_TITLES = {'air_temp': 'Temperature and Dewpoint', }
'rh': 'Relative Humidity', 'pressure': 'Pressure',
'wind_speed': 'Wind Speed', 'wind_dir': 'Wind Direction',
'accum_precip': 'Accumulated Precipitation Since 00Z', class PlotMaker(object):
'solar_flux': 'Solar Flux'} """Object for making plots and storing/validating plot metadata"""
def __init__(self, name, dependencies, title=None, units=None):
# The purpose of this method is to take a string in the format self.name = name
# YYYY-MM-DDTHH:MM:SS and convert that to a datetime object self.deps = dependencies
# or a string in the format YY-MM-DD and convert that to a datetime object self._full_figure = None
# used in coordination with argparse -s and -e params if title is None:
# @param datetime string title = "{title_prefix}{title_name}{units} {start_time:%Y-%m-%d}"
# @return datetime object self._title = title
self.units = units
def _dt_convert(datetime_str):
#parse datetime string, return datetime object def missing_deps(self, frame):
try: """Get dependency variables missing from the provided frame"""
return dt.strptime(datetime_str, '%Y-%m-%dT%H:%M:%S') for var_name in self.deps:
if var_name not in frame:
except: yield var_name
return dt.strptime(datetime_str, '%Y-%m-%d')
def get_title(self, frame, is_subplot):
# The purpose of this method is to take a list of level_b1 filepaths if self._title:
# and convert those into a pandas frame with only good data title_prefix = "AO&SS Building Tower " if not is_subplot else ''
# @param input_files - list of level_b1 filepaths title_name = TITLES.get(self.name, self.name.replace('_', ' ').title())
# @return frame - pandas dataframe with only good data unit_str = '({})'.format(self.units) if self.units and is_subplot else ''
title = self._title.format(title_prefix=title_prefix,
title_name=title_name,
units=unit_str,
start_time=frame.index[0].to_pydatetime())
else:
title = ''
return title
def get_yticks(self, ymin, ymax, num_plots):
delta = math.ceil((ymax - ymin) / num_plots)
new_ticks = np.arange(ymin, (ymin + delta * num_plots), delta)
if not new_ticks:
return [ymin, ymin + 0.05, ymin + 0.1]
return new_ticks
def get_ylabel(self, is_subplot=False):
y_label = TITLES.get(self.name, self.name.replace('_', ' ').title())
if is_subplot:
return None
if self.units:
return "{} ({})".format(y_label, self.units)
return y_label
def create_plot(self, frame, fig, is_subplot=None, shared_x=None, title=None):
"""
:param frame:
:param fig:
:param is_subplot: None or (num plots, num columns, num_rows)
:param shared_x:
:return:
"""
if title is None:
title = self.get_title(frame, is_subplot)
if is_subplot:
ax = fig.add_subplot(*is_subplot, sharex=shared_x)
ax.set_title(title, x=0.5, y=get_subtitle_location(is_subplot[0]), fontsize=8)
else:
ax = fig.add_subplot(111, sharex=shared_x)
fig.suptitle(title, fontsize=13)
y_label = self.get_ylabel(is_subplot)
if y_label:
ax.set_ylabel(y_label)
plt.sca(ax)
# get the min for each column then combine them assuming we can
specific_frame = frame[[x for x in frame.columns if x in self.deps]]
ymin = np.floor(specific_frame.min().min())
ymax = np.ceil(specific_frame.max().max())
ax.plot(specific_frame.index, specific_frame, 'k')
if ymin == ymax:
ax.set_ylim(ymin, ymax + 0.1)
else:
ax.set_ylim(ymin, ymax)
def get_data(input_files): if is_subplot:
new_ticks = self.get_yticks(ymin, ymax, is_subplot[0])
ax.get_yaxis().get_major_ticks()[-1].set_visible(False)
ax.set_yticks(new_ticks)
else:
ax.set_xlabel('Time (UTC)')
return ax
class MeteorogramPlotMaker(PlotMaker):
def __init__(self, name, dependencies, plot_deps=None, title=None):
self.plot_deps = plot_deps
super(MeteorogramPlotMaker, self).__init__(name, dependencies, title=title)
def create_plot(self, frame, fig, is_subplot=False, shared_x=None):
if is_subplot or shared_x:
raise ValueError("Meteorogram Plot can not be a subplot or share X-axis")
title = self.get_title(frame, False)
fig.suptitle(title, fontsize=13)
num_plots = len(self.plot_deps)
shared_x = None
for idx, plot_name in enumerate(self.plot_deps):
plot_maker = PLOT_TYPES.get(plot_name, PlotMaker(plot_name, (plot_name,)))
title_name = TITLES.get(plot_name, plot_name.replace('_', ' ').title())
ax = plot_maker.create_plot(frame, fig,
is_subplot=(num_plots, 1, idx + 1),
shared_x=shared_x,
title=title_name)
if idx == 0:
shared_x = ax
if idx != num_plots - 1:
# Disable the x-axis ticks so we don't interfere with other subplots
ax.get_xaxis().get_major_ticks()[-1].set_visible(False)
ax.get_xaxis().get_major_ticks()[0].set_visible(False)
ax.set_xlabel('Time (UTC)')
return ax
# map plot name -> variable dependencies
# if not listed then plot name is assumed to be the same as the variable needed
PLOT_TYPES = {
'meteorogram': MeteorogramPlotMaker('meteorogram',
('air_temp', 'dewpoint', 'rh', 'wind_speed', 'wind_dir', 'accum_precip'),
('td', 'rh', 'wind_speed', 'wind_dir', 'accum_precip')),
'td': PlotMaker('td', ('air_temp', 'dewpoint'), units="°C"), # air_temp and dewpoint in one plot
'wind_dir': PlotMaker('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'),
'dewpoint': PlotMaker('dewpoint', ('air_temp',), units='°C'),
'wind_speed': PlotMaker('wind_speed', ('wind_speed',), units='m/s'),
'accum_precip': PlotMaker('accum_precip', ('accum_precip',), units='mm'),
'solar_flux': PlotMaker('solar_flux', ('solar_flux',), units='W/m^2'),
}
def convert_to_thumbnail(fig):
# TODO
pass
def get_data(input_files, columns):
data_dict = {}
files = MFDataset(input_files) files = MFDataset(input_files)
#dictionary with var_name: good data pairs # get the data from the files
dataDict = {} for name in columns:
if name not in files.variables:
#get the data from the files LOG.warning("Unknown file variable: {}".format(name))
for name in CHOICES: continue
data = files.variables[name][:] data_dict[name] = files.variables[name][:]
data_dict['qc_' + name] = files.variables['qc_' + name][:]
qc_data = files.variables['qc_' + name][:]
dataDict[name] = data
dataDict['qc_' + name] = qc_data
#for some reason, time has floating point precision issues # convert base_time epoch format into date_time object
#so I'm using base_time and offsets instead
#might need to fix later
base_time = files.variables['base_time'][:] base_time = files.variables['base_time'][:]
base_time_obj = dt(1970, 1, 1) + delta(seconds=int(base_time))
#convert base_time epoch format into date_time object
base_time_obj = dt(1970,1,1) + delta(seconds=int(base_time))
#get offsets from files # convert per-file offsets to offsets based on the first file's base_time
off_sets = files.variables['time_offset'] offsets = MFTime(files.variables['time_offset'])[:]
# for each offset, convert that into a datetime object
#convert that into offsets from the first file's base_time data_dict['stamps'] = [base_time_obj + delta(seconds=int(s)) for s in offsets]
off_sets = MFTime(off_sets)[:]
#for each offset, convert that into a datetime object
stamps = [base_time_obj + delta(seconds=int(s)) for s in off_sets]
#append all stamps into the data frame return pd.DataFrame(data_dict).set_index(['stamps'])
dataDict['stamps'] = stamps
return pd.DataFrame(dataDict).set_index(['stamps'])
# The purpose of this method is to use the qc mask
# to only get the good data
# @param qc_data - np array with nan for good values and various set bits for others
# @param stamps - all stamps corresponding to data
# @param data - data
# @return good data and their corresponding stamps
def get_good_data(qc_data, stamps, data):
#all good data and stamps associated with them
good_data = []
good_stamps = []
#get good indices
good_indices = np.where(np.isnan(qc_data))[0]
for idx in good_indices:
#get good_data
good_data.append(data[idx])
good_stamps.append(stamps[idx])
return [good_data, good_stamps]
# The purpose of this method is to determines all the 12:00:00 days # The purpose of this method is to determines all the 12:00:00 days
# within a start and end date # within a start and end date
...@@ -194,7 +279,7 @@ def thumbnail_plot(dates, data, ymin, ymax, ...@@ -194,7 +279,7 @@ def thumbnail_plot(dates, data, ymin, ymax,
# @param create_air_dew_plot - boolean specifying if air temp and dewpoint # @param create_air_dew_plot - boolean specifying if air temp and dewpoint
# @param output - output filename pattern # @param output - output filename pattern
# should be in same plot # should be in same plot
def create_thumbnail(frame, ymin, ymax, create_air_dew_plot, output): def create_thumbnail(plot_names, frame, ymin, ymax, create_air_dew_plot, output):
#see if we're going to need subplots #see if we're going to need subplots
#subplots needed when len is greater than 2 for one variable #subplots needed when len is greater than 2 for one variable
#or greater than 4 for two variables #or greater than 4 for two variables
...@@ -213,8 +298,7 @@ def create_thumbnail(frame, ymin, ymax, create_air_dew_plot, output): ...@@ -213,8 +298,7 @@ def create_thumbnail(frame, ymin, ymax, create_air_dew_plot, output):
numberPlots = len(list(frame.columns.values)) / 2 numberPlots = len(list(frame.columns.values)) / 2
plotNumber = numberPlots * 100 + 10 plotNumber = numberPlots * 100 + 10
stamps = list(frame.index) stamps = frame.index
#get dewpoint data #get dewpoint data
if create_air_dew_plot: if create_air_dew_plot:
all_data = frame['dewpoint'] all_data = frame['dewpoint']
...@@ -222,9 +306,9 @@ def create_thumbnail(frame, ymin, ymax, create_air_dew_plot, output): ...@@ -222,9 +306,9 @@ def create_thumbnail(frame, ymin, ymax, create_air_dew_plot, output):
good_list = get_good_data(qc_data, stamps, all_data) good_list = get_good_data(qc_data, stamps, all_data)
dewpoint_data = good_list[0] dewpoint_data = good_list[0]
dewpoint_stamps= good_list[1] dewpoint_stamps = good_list[1]
for name in CHOICES: for name in plot_names:
if name == 'dewpoint' and create_air_dew_plot: if name == 'dewpoint' and create_air_dew_plot:
continue continue
...@@ -257,18 +341,12 @@ def create_thumbnail(frame, ymin, ymax, create_air_dew_plot, output): ...@@ -257,18 +341,12 @@ def create_thumbnail(frame, ymin, ymax, create_air_dew_plot, output):
thumbnail_plot(good_stamps, good_data, ymin[name], ymax[name], thumbnail_plot(good_stamps, good_data, ymin[name], ymax[name],
dewpoint_stamps, dewpoint_data, output, False) dewpoint_stamps, dewpoint_data, output, False)
plt.savefig(output + '.thumbnail.png') plt.savefig(output.format(plot_name='test', start_time=good_stamps[0]))
def full_plot_stamps(stamps): def full_plot_stamps(stamps):
first_stamp = np.sort(stamps)[0] # assume the timestamps are monotonic
first_stamp = stamps[0].replace(hour=0, minute=0, second=0)
first_stamp = first_stamp.replace(hour=0, minute=0, second=0) return [((s - first_stamp).total_seconds()/3600) for s in stamps]
#print([((s - first_stamp).total_seconds()/3600) for s in stamps])
#print([(int((s - first_stamp).total_seconds()) / 3600) for s in stamps])
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 # The purpose of this method is to change how the y ticks are shown
# @param min - minimum of the y range right now # @param min - minimum of the y range right now
...@@ -277,8 +355,7 @@ def full_plot_stamps(stamps): ...@@ -277,8 +355,7 @@ def full_plot_stamps(stamps):
def get_new_labels(mini, maxi): def get_new_labels(mini, maxi):
delta = math.ceil((maxi - mini) / 6) delta = math.ceil((maxi - mini) / 6)
return np.arange(mini, (mini + delta*6), delta)
return np.arange(mini, (mini + delta*6), delta)
# The purpose of this method is to create a full_size plot # The purpose of this method is to create a full_size plot
# @param frame - all data I need in a data frame # @param frame - all data I need in a data frame
...@@ -292,7 +369,7 @@ def get_new_labels(mini, maxi): ...@@ -292,7 +369,7 @@ def get_new_labels(mini, maxi):
# @param wind_direction - says different yaxis labels # @param wind_direction - says different yaxis labels
# @param accum_precip - different yaxis handling # @param accum_precip - different yaxis handling
# @param see if we need subplots # @param see if we need subplots
def full_plot(dates, data, ymin, ymax, def full_plot(fig, axes, dates, data, ymin, ymax,
dewpoint_stamps, dewpoint_data, output, wind_dir, dewpoint_stamps, dewpoint_data, output, wind_dir,
accum_precip, need_subplots): accum_precip, need_subplots):
...@@ -318,15 +395,13 @@ def full_plot(dates, data, ymin, ymax, ...@@ -318,15 +395,13 @@ def full_plot(dates, data, ymin, ymax,
else: else:
# import ipdb; ipdb.set_trace()
plt.plot(dates, data, 'k') plt.plot(dates, data, 'k')
plt.axis([min(dates), max(dates), ymin, ymax]) # plt.axis([min(dates), max(dates), ymin, ymax])
if not need_subplots: if not need_subplots:
return return
#get axes
axes = plt.gca()
#if not wind_direction, reset yaxis ticks #if not wind_direction, reset yaxis ticks
if not wind_dir: if not wind_dir:
if not accum_precip: if not accum_precip:
...@@ -358,323 +433,207 @@ def full_plot(dates, data, ymin, ymax, ...@@ -358,323 +433,207 @@ def full_plot(dates, data, ymin, ymax,
def get_subtitle_location(numSubPlots): def get_subtitle_location(numSubPlots):
return 1 - 0.05*numSubPlots return 1 - 0.05*numSubPlots
# The purpose of this method is to create a full size quicklook
# @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
# should be in same plot
def create_full_plot(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
#get need supplot vars
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 = list(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 CHOICES:
#if dewpoint with temp or a name not in frame
# continue
if name == 'dewpoint' and create_air_dew_plot:
continue
if name not in list(frame.columns.values):
continue
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)
def create_full_plot(plot_names, frame, output, start_time=None, end_time=None):
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)')
#reverse code done in create_full_plot
axes = plt.gca()
axes.get_xaxis().get_major_ticks()[-1].set_visible(True)
axes.get_xaxis().get_major_ticks()[0].set_visible(True)
plt.savefig(output + '.png') Args:
plot_names:
frame:
output:
start_time:
end_time:
daily: Whether or not this plot should represent one day of data
# the purpose of this method is to use TW's algo to convert Returns:
# tempC and relhum to dewpoint
# @param tempC - temperature value in deg C
# @param relhum - relative humidity value in %
def calcDewpoint(tempC, relhum):
""" """
Algorithm from Tom Whittaker tempC is the temperature in degrees Celsius, #see if we're going to need subplots
relhum is the relative humidity as a percentage. #subplots needed when len is greater than 2 for one variable
#or greater than 4 for two variables
:param tempC: temperature in celsius
:param relhum: relative humidity as a percentage
"""
if tempC is None or relhum is None:
return np.NaN
gasconst = 461.5
latheat = 2500800.0
dp = 1.0 / (1.0 / (273.15 + tempC) - gasconst * np.log((0.0 + relhum) / 100) /
(latheat - tempC * 2397.5))
return min(dp - 273.15, tempC)
# The purpose of this method is to check to see if we have dewpoint data
# if we do not, fill in dewpoint data
# @param frame - pandas frame of nc data
# @return frame with dewpoint data and qc_dewpoint with only good data flagged
def check_dewpoint(frame):
dewpoint = frame['dewpoint']
qc_dewpoint = frame['qc_dewpoint']
good_list = get_good_data(qc_dewpoint.tolist(), list(frame.index), dewpoint.tolist())
# no dewpoint data that is viable
if len(good_list[0]) == 0:
temp = frame['air_temp'].tolist()
rel_hum = frame['rh'].tolist()
qc_temp = frame['qc_air_temp'].tolist()
qc_rel_hum = frame['qc_rh'].tolist()
stamps = list(frame.index)
newDewpoint = {}
qc_dew = {}
for idx, rh_quality in enumerate(qc_rel_hum):
rh = rel_hum[idx]
temp_quality = qc_temp[idx]
air_temp = temp[idx]
stamp = stamps[idx]
if not math.isnan(temp_quality):
air_temp = None
elif not math.isnan(rh_quality):
rh = None
dew = calcDewpoint(air_temp, rh) # 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
newDewpoint[stamp] = dew #get need supplot vars
# 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
for name in plot_names:
plot_maker = PLOT_TYPES.get(name, PlotMaker(name, (name,)))
var_names = []
for var_name in plot_maker.deps:
if var_name not in frame:
raise ValueError("Missing required variable '{}' for plot '{}'".format(var_name, name))
var_names.append(var_name)
# write NaNs where QC values are not 0
qc_name = 'qc_' + var_name
if qc_name in frame:
frame[var_name].mask(frame[qc_name] != 0)
var_names.append(qc_name)
# create a frame that doesn't include any of the bad values
plot_frame = frame[var_names]
plot_frame = plot_frame[~plot_frame.isnull().any(axis=1)]
fig = plt.figure()
ax = plot_maker.create_plot(plot_frame, fig)
ax.set_xlim(start_time, end_time)
import matplotlib.dates as md
xloc = md.AutoDateLocator(minticks=5, maxticks=8, interval_multiples=True)
xfmt = md.AutoDateFormatter(xloc)
def _fmt(interval, x, pos=None):
x_num = md.num2date(x).replace(tzinfo=None)
delta_seconds = (x_num - plot_frame.index[0].replace(hour=0, minute=0, second=0, microsecond=0)).total_seconds()
num_hours = delta_seconds / 3600.
if interval == md.HOURLY:
return "{:02.0f}".format(num_hours)
elif interval == md.MINUTELY:
num_minutes = delta_seconds / 60.
num_minutes -= int(num_hours) * 60.
return "{:02.0f}:{:02.0f}".format(int(num_hours), num_minutes)
else:
return x.strftime("{%Y-%m-%d}")
from functools import partial
xfmt.scaled[1. / md.MINUTES_PER_DAY] = plt.FuncFormatter(partial(_fmt, md.MINUTELY))
xfmt.scaled[1. / md.HOURS_PER_DAY] = plt.FuncFormatter(partial(_fmt, md.HOURLY))
ax.xaxis.set_major_locator(xloc)
ax.xaxis.set_major_formatter(xfmt)
out_fn = output.format(plot_name=name, start_time=plot_frame.index[0].to_pydatetime())
LOG.info("Saving plot '{}' 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)')
if math.isnan(dew):
qc_dew[stamp] = 1
else: def _dt_convert(datetime_str):
qc_dew[stamp] = np.NaN try:
return dt.strptime(datetime_str, '%Y-%m-%dT%H:%M:%S')
newDewpoint = pd.Series(newDewpoint) except ValueError:
qc_dew = pd.Series(qc_dew) return dt.strptime(datetime_str, '%Y-%m-%d')
frame['dewpoint'] = newDewpoint
frame['qc_dewpoint'] = qc_dew
return frame
def main(): def main():
import argparse import argparse
#argparse description
parser = argparse.ArgumentParser(description="Use data from level_b1 netCDF files to create netCDF files") parser = argparse.ArgumentParser(description="Use data from level_b1 netCDF files to create netCDF files")
parser.add_argument('-v', '--verbose', action='count',
#argparse verbosity info
parser.add_argument('-v', '--verbose', action='count',
default=int(os.environ.get("VERBOSITY", 2)), default=int(os.environ.get("VERBOSITY", 2)),
dest='verbosity', dest='verbosity',
help=('each occurence increases verbosity 1 level through' help=('each occurence increases verbosity 1 level through'
+ ' ERROR-WARNING-INFO-DEBUG (default INFO)')) + ' ERROR-WARNING-INFO-DEBUG (default INFO)'))
#argparse start and end times
parser.add_argument('-s', '--start-time', type=_dt_convert, parser.add_argument('-s', '--start-time', type=_dt_convert,
help="Start time of plot. If only -s is given, a plot of " + help="Start time of plot. If only -s is given, a plot of " +
"only that day is created. Formats allowed: \'YYYY-MM-DDTHH:MM:SS\', \'YYYY-MM-DD\'") "only that day is created. Formats allowed: \'YYYY-MM-DDTHH:MM:SS\', \'YYYY-MM-DD\'")
parser.add_argument('-e', '--end-time', type=_dt_convert, parser.add_argument('-e', '--end-time', type=_dt_convert,
help="End time of plot. If only -e is given, a plot of only that day is " + help="End time of plot. If only -e is given, a plot of only that day is " +
"created. Formats allowed: \'YYYY-MM-DDTHH:MM:SS\', \'YYYY-MM-DD\'") "created. Formats allowed: \'YYYY-MM-DDTHH:MM:SS\', \'YYYY-MM-DD\'")
parser.add_argument('--met-plots', nargs='+',
#netcdf file paths help="Override plots to use in the combined meteorogram plot")
parser.add_argument("input_files", nargs="+", help="aoss_tower_level_b1 paths") parser.add_argument("input_files", nargs="+", help="aoss_tower_level_b1 files")
parser.add_argument('-o', '--output', default="{plot_name}_{start_time:%Y%m%d_%H%M%S}.png", help="filename pattern")
#output filename pattern parser.add_argument('-t', '--thumbnail', action='store_true', help="if specified, script creates a thumbnail")
parser.add_argument('-o', '--output', help="filename pattern") parser.add_argument('-p', '--plot-names', nargs="+",
required=True,
#thumbnail or full help="the variable names or plot types to create")
parser.add_argument('-t', '--thumb-nail', action='store_true', help="if specified, script creates a thumbnail") parser.add_argument('-d', '--daily', action='store_true',
help="creates a plot for every day. Usually used to create plots " +
#plot names "that will line up for aoss tower quicklooks page")
parser.add_argument('--varnames', nargs="+", choices=CHOICES,
required=True,
help="the variable names for the desired plot. Valid choices: air_temp, " +
"dewpoint, rh, pressure, wind_speed, wind_dir, accum_precip, solar_flux")
#--daily flag
parser.add_argument('-d', '--daily', action='store_true',
help="creates a plot for every day. Usually used to create plots " +
"that will line up for aoss tower quicklooks page")
args = parser.parse_args() args = parser.parse_args()
levels = [logging.ERROR, logging.WARN, logging.INFO, logging.DEBUG] levels = [logging.ERROR, logging.WARN, logging.INFO, logging.DEBUG]
level = levels[min(3, args.verbosity)] level = levels[min(3, args.verbosity)]
logging.basicConfig(level=level) logging.basicConfig(level=level)
var_names = args.varnames if not os.path.splitext(args.output)[-1]:
LOG.warning("File pattern provided does not have a file extension")
create_air_dew_plot = False
#see if we need to plot air_temp and dewpoint on the same plot
if 'air_temp' in var_names and 'dewpoint' in var_names:
create_air_dew_plot = True
var_names.remove('dewpoint')
#get data # check the dependencies for the meteorogram
frame = get_data(args.input_files) if args.met_plots:
assert 'meteorogram' not in args.met_plots
PLOT_TYPES['meteorogram'].deps = args.met_plots
frame = check_dewpoint(frame) 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))
#only have the data we need frame = get_data(args.input_files, plot_deps)
for name in CHOICES: bad_plot_names = set(args.plot_names) - (set(frame.columns) | set(PLOT_TYPES.keys()))
if name == 'dewpoint' and create_air_dew_plot: if bad_plot_names:
continue raise ValueError("Unknown plot name(s): {}".format(", ".join(bad_plot_names)))
if name not in var_names:
del frame[name]
del frame['qc_' + name]
#frame only contains data from start-end times #frame only contains data from start-end times
if(args.start_time and args.end_time): if args.start_time and args.end_time:
start_filter = args.start_time.strftime('%Y-%m-%d %H:%M:%S') frame = frame[args.start_time: args.end_time]
end_filter = args.end_time.strftime('%Y-%m-%d %H:%M:%S') elif args.start_time:
frame = frame[start_filter: end_filter] #frame only contains data from start-end of that day
#frame only contains data from start-end of that day
elif(args.start_time):
end_time = args.start_time.replace(hour=23, minute=59, second=59) end_time = args.start_time.replace(hour=23, minute=59, second=59)
start_filter = args.start_time.strftime('%Y-%m-%d %H:%M:%S') frame = frame[args.start_time: end_time]
end_filter = end_time.strftime('%Y-%m-%d %H:%M:%S')
frame = frame[start_filter: end_filter]
ymin = {}
ymax = {}
if not args.daily: if not args.daily:
for name in CHOICES: # allow plotting methods to write inplace on a copy
if name == 'dewpoint' and create_air_dew_plot: frames = [frame.copy()]
continue
if name not in frame:
continue
ymin[name] = None
ymax[name] = None
if(args.thumb_nail):
create_thumbnail(frame, ymin, ymax, create_air_dew_plot, args.output)
else:
create_full_plot(frame, ymin, ymax, create_air_dew_plot, args.output)
else: else:
for name in CHOICES: frames = (group[1] for group in frame.groupby(frame.index.day))
if name == 'dewpoint' and create_air_dew_plot:
continue
if name not in frame:
continue
if name == 'accum_precip':
ymin[name] = frame[name].min()
ymax[name] = frame[name].max()
elif name == 'air_temp' and create_air_dew_plot:
ymin[name] = math.floor(frame[name].min())
ymax[name] = math.ceil(frame[name].max())
dew_min = math.floor(frame['dewpoint'].min()) for frame in frames:
dew_max = math.ceil(frame['dewpoint'].max()) if args.daily:
# modify start and end time to the current day
ymin[name] = min(dew_min, ymin[name]) start_time = frame.index[0].to_pydatetime().replace(hour=0, minute=0, second=0, microsecond=0)
ymax[name] = max(dew_max, ymax[name]) end_time = frame.index[0].to_pydatetime().replace(hour=23, minute=59, second=59, microsecond=999999)
else:
start_time = args.start_time
else: end_time = args.end_time
ymin[name] = math.floor(frame[name].min())
ymax[name] = math.ceil(frame[name].max())
frameList = [(group[1]) for group in frame.groupby(frame.index.day)]
counter = 0
for frame in frameList: if args.thumbnail:
if args.thumb_nail: create_thumbnail(args.plot_names, frame, args.output, start_time, end_time)
create_thumbnail(frame, ymin, ymax, create_air_dew_plot, args.output.format(str(counter))) else:
else: create_full_plot(args.plot_names, frame, args.output, start_time, end_time)
create_full_plot(frame, ymin, ymax, create_air_dew_plot, args.output.format(str(counter)))
counter += 1
plt.gcf().clear()
if __name__ == "__main__": if __name__ == "__main__":
main() sys.exit(main())
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment