awips2/cave/com.raytheon.viz.avnconfig/localization/aviation/python/CigVisDist.py
Max Schenkelberg 6f60751ec6 Issue #2033 moved avnfps and text workstation files into respective plugins.
Change-Id: If95cb839ad81ca2a842ff7f6926847ac3928d8f2

Former-commit-id: 77e1a4d8f5237e5fae930c1e00589c752f8b3738
2013-08-15 12:21:43 -05:00

1121 lines
41 KiB
Python

##
# This software was developed and / or modified by Raytheon Company,
# pursuant to Contract DG133W-05-CQ-1067 with the US Government.
#
# U.S. EXPORT CONTROLLED TECHNICAL DATA
# This software product contains export-restricted data whose
# export/transfer/disclosure is restricted by U.S. law. Dissemination
# to non-U.S. persons whether in the United States or abroad requires
# an export license or other authorization.
#
# Contractor Name: Raytheon Company
# Contractor Address: 6825 Pine Street, Suite 340
# Mail Stop B8
# Omaha, NE 68106
# 402.291.0100
#
# See the AWIPS II Master Rights File ("Master Rights File.pdf") for
# further licensing information.
##
#
# Name:
# CigVisDist.py
# GFS1-NHD:A9171.0000-SCRIPT;17
#
# Status:
# DELIVERED
#
# History:
# Revision 17 (DELIVERED)
# Created: 14-MAR-2008 09:58:05 OBERFIEL
# Fixed misspelling of occurrence.
#
# Revision 16 (DELIVERED)
# Created: 26-FEB-2008 14:20:54 OBERFIEL
# imported re module in CigVisDist; Fix output and help
# documentation in WindRose
#
# Revision 15 (DELIVERED)
# Created: 12-FEB-2008 17:11:55 OBERFIEL
# Change print dialog to be consistent with other climatology
# tools.
#
# Revision 14 (DELIVERED)
# Created: 06-FEB-2008 09:52:11 OBERFIEL
# Fixed Print Stats dialog to set dialog to what forecaster
# is seeing.
#
# Revision 13 (REVIEW)
# Created: 31-JAN-2008 09:56:25 GILMOREDM
# changed code so that save stats only saves for either 24
# hours or the current hour.
#
# Revision 12 (DELIVERED)
# Created: 16-NOV-2007 10:18:56 OBERFIEL
# Added exception handling to get_month_range() and
# pop-down 'Print Stats' GUI and status when successful.
#
# Revision 11 (REVIEW)
# Created: 09-NOV-2007 09:19:41 OBERFIEL
# Remove dependency on another, temporary module. Functions
# in that module have been incorporated into this one.
#
# Revision 10 (INITIALIZE)
# Created: 02-NOV-2007 07:43:28 OBERFIEL
# Removed CR characters and added doc block
#
# Revision 9 (REVIEW)
# Created: 31-OCT-2007 12:49:19 GILMOREDM
# Added functionality to save climo statistics
#
# Revision 8 (REVIEW)
# Created: 23-JUL-2007 08:45:36 GILMOREDM
# Added code to allow saving of tables of stats
#
# Revision 7 (DELIVERED)
# Created: 20-MAR-2007 13:36:54 OBERFIEL
# Updated text input fields and printer dialogs to be
# consistent. Frame tag removed.
#
# Revision 6 (DELIVERED)
# Created: 05-FEB-2007 07:51:00 OBERFIEL
# Corrected misspellings in help text and graphic.
#
# Revision 5 (DELIVERED)
# Created: 04-JAN-2007 12:52:14 OBERFIEL
# Corrected misspelling.
#
# Revision 4 (REVIEW)
# Created: 28-DEC-2006 11:33:09 OBERFIEL
# Made validate function less restrictive.
#
# Revision 3 (DELIVERED)
# Created: 30-JUN-2006 10:58:41 TROJAN
# spr 7190: added plot area window parameters to
# app-recources file
#
# Revision 2 (DELIVERED)
# Created: 30-MAY-2006 15:10:38 TROJAN
# spr 7144: added auto-update feature, number of years in
# database
#
# Revision 1 (DELIVERED)
# Created: 16-MAY-2006 16:00:30 TROJAN
# spr 7144
#
# Change Document History:
# 1:
# Change Document: GFS1-NHD_SPR_7373
# Action Date: 02-JUN-2008 20:44:52
# Relationship Type: In Response to
# Status: CLOSED
# Title: AvnFPS: AvnWatch monitoring not reliable.
#
#
import logging, os, re, time
import ConfigParser, cPickle, Queue
import numarray as na
import matplotlib.backends.backend_tkagg as mpltk
from matplotlib.ticker import MultipleLocator
import Tkinter as tk
import Pmw
import Avn, AvnThread, Busy, ClimLib, ErrorRedirect, MessageBar, ClimateProcessLogger
from Balloon import Balloon
from HelpDialog import HelpDialog
_Help = {
'title': 'AvnFPS - Ceiling and Visibility Display Help', \
'content': """
This application displays ceiling and visiblity distribution for
selected time range.
The distribution is plotted in a form of stacked histogram.
Plot Type
By Month - the X axis is month. The category frequency is
calculated for a selected range of hours.
By Hour - the X axis is hour of day. The category frequency
is calculated for a selected range of months.
By Wind Dir - the X axis is 16 point wind direction. The category
frequency is calculated for a selected range of
months and hours.
Auto Redraw
Forces screen redraw every time a new sites selected or date/time
widgets are modified.
Element
Visibility - only visibility thresholds are checked to determine
flight category
Ceiling - only ceiling thresholds are checked to determine
flight category
Joint - both ceiling and visibility thresholds are checked
to determine flight category
y-axis scale
Sets maximum value for the vertical axis. If 'auto' toggle is set,
the value is computed. Behaves sensibly if selected value is too
low.
"""
}
_Logger = logging.getLogger(ClimateProcessLogger.CLIMATE_CATEGORY)
multipleLocator = MultipleLocator(1)
na.Error.pushMode(invalid='ignore')
###############################################################################
def _int_validator(widget, min_, max_, text):
'''Validates month and hour. Allows for cycles.'''
try:
n = int(text)
if n < min_ or n >= max_:
widget.setvalue(str((n-min_)%(max_-min_)+min_))
return Pmw.OK
except ValueError:
return Pmw.ERROR
###############################################################################
class Gui(Pmw.MegaWidget):
AppName = 'AvnFPS - Ceiling/Visibility Distribution'
IdsFile = os.path.join('etc', 'ids.cfg')
GetData = 'cvdata'
Key = 'cvdata'
Month = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', \
'Oct', 'Nov', 'Dec']
RowLabels = ['MVFR', 'IFR', 'LIFR', 'VLIFR']
Element = {'cig': 'Ceiling', 'vis': 'Visibility', \
'joint': 'Flight Category'}
def __init__(self, **kw):
optiondefs = ( \
)
self.defineoptions(kw, optiondefs)
self.root = tk.Tk()
self.root.withdraw()
self._tkUpdate = tk.IntVar()
self.get_option_db()
Pmw.initialise(self.root, useTkOptionDb=1)
self.root.title(self.AppName)
Pmw.MegaWidget.__init__(self, parent=self.root)
self.root.resizable(0, 0)
self.initialiseoptions(Gui)
self.graphics = {}
self.id_ = None
self.cache = ClimLib.Cache()
self.resultqueue = Queue.Queue()
self.worker = AvnThread.Worker(Queue.Queue(), self.resultqueue)
self.create_print_dialog()
self.create_stats_dialog()
interior = self.interior()
ErrorRedirect.fixlogging(_Logger, self.interior())
Busy.instantiate(interior)
self.menubar = Pmw.MenuBar(interior,
hull_relief='raised',
hull_borderwidth=2,
)
self.menubar.grid(row=0, column=0, columnspan=2, sticky='ew')
self.menubar.addmenu('Help', '', side='right')
self.menubar.addmenuitem('Help', 'command',
label='Usage',
command=self.show_help_dialog,
)
self.menubar.addmenu('File', '', side='left')
self.menubar.addmenuitem('File', 'command',
label = 'Save Image',
command=self.save_image,
)
self.menubar.addmenuitem('File', 'command',
label = 'Print Image',
command=self.show_print_dialog,
)
self.menubar.addmenuitem('File', 'command',
label = 'Save Stats',
command=self.show_stats_dialog,
)
self.menubar.addmenuitem('File', 'separator')
self.menubar.addmenuitem('File', 'command',
label = 'Quit',
command=self.quit,
)
frame = tk.Frame(interior,
bd=2,
relief='ridge',
)
self.station_w = Pmw.ScrolledListBox(frame,
labelpos='n',
label_text='Sites',
listbox_width=5,
listbox_exportselection=0,
vscrollmode='static',
selectioncommand=self.update,
)
self.station_w.pack(side='top', expand='yes', fill='y', padx=5, pady=5)
self.element_w = Pmw.RadioSelect(frame,
buttontype='radiobutton',
orient='vertical',
labelpos='n',
label_text='Element',
hull_borderwidth=2,
padx=1,
pady=1,
hull_relief='ridge',
)
self.element_w.pack(side='top', expand='no', padx=2, pady=3)
for ele, text in [('vis', 'Visibility'), ('cig', 'Ceiling'),
('joint', 'Joint')]:
self.element_w.add(ele, text=text)
self.element_w.setvalue('vis')
f = tk.Frame(frame, relief='ridge', bd=2)
label = tk.Label(f, text='y-axis scale')
label.pack(side='top', pady=2)
self.tkScale = tk.DoubleVar()
self.tkScale.set(100.0)
scale = tk.Scale(f,
tickinterval=0,
resolution=5.0,
showvalue=1,
from_=100.0,
to=5.0,
variable=self.tkScale,
)
scale.pack(side='top', pady=2)
self.tkAutoScale = tk.BooleanVar()
self.tkAutoScale.set(True)
btn = tk.Checkbutton(f,
text='auto',
variable=self.tkAutoScale,
)
btn.pack(side='top', pady=2)
f.pack(side='top')
bbox = Pmw.ButtonBox(frame,
labelpos='w',
frame_borderwidth=2,
frame_relief='ridge',
)
btn = bbox.add('Draw', command=self.display)
Balloon().bind(btn, 'Draws histogram')
bbox.pack(side='top', padx=2, pady=5)
frame.grid(row=1, column=0, sticky='ns')
# notebook
notebook = Pmw.NoteBook(interior,
raisecommand=self.setpage,
)
notebook.grid(row=1, column=1, sticky='news')
for item, label in [('month', 'By Month'), ('hour', 'By Hour'), \
('wdir', 'By Wind Dir')]:
page = notebook.add(item, tab_text=label)
f = tk.Frame(page, relief='ridge', bd=2)
g = self.graphics[item] = {'selection': {}, 'ax': None, \
'canvas': None}
col = 0
if item == 'month':
g['selection']['hour'] = self.add_hour_counter(f)
g['selection']['hour'].grid(row=0, column=col, padx=5, pady=1)
col += 1
g['selection']['hrange'] = self.add_hour_range_counter(f)
g['selection']['hrange'].grid(row=0, column=col, padx=5, pady=1)
col += 1
elif item == 'hour':
g['selection']['month'] = self.add_month_counter(f)
g['selection']['month'].grid(row=0, column=col, padx=5, pady=1)
col += 1
g['selection']['mrange'] = self.add_month_range_counter(f)
g['selection']['mrange'].grid(row=0, column=col, padx=5, pady=1)
col += 1
elif item == 'wdir':
g['selection']['month'] = self.add_month_counter(f)
g['selection']['month'].grid(row=0, column=col, padx=5, pady=1)
col += 1
g['selection']['mrange'] = self.add_month_range_counter(f)
g['selection']['mrange'].grid(row=0, column=col, padx=5, pady=1)
col += 1
g['selection']['hour'] = self.add_hour_counter(f)
g['selection']['hour'].grid(row=0, column=col, padx=5, pady=1)
col += 1
g['selection']['hrange'] = self.add_hour_range_counter(f)
g['selection']['hrange'].grid(row=0, column=col, padx=5, pady=1)
col += 1
toggle = tk.Checkbutton(f,
text='Auto Redraw',
variable=self._tkUpdate,
)
toggle.grid(row=0, column=col, padx=20, pady=1, sticky='news')
f.grid(row=0, column=0, sticky='ew')
try:
figsize = (float(self.root.option_get('plotWidth', '')), \
float(self.root.option_get('plotHeight', '')))
dpi = int(self.root.option_get('dpi', ''))
x0 = float(self.root.option_get('plotX0', ''))
y0 = float(self.root.option_get('plotY0', ''))
x1 = float(self.root.option_get('plotX1', ''))
y1 = float(self.root.option_get('plotY1', ''))
except Exception, e:
_Logger.error('Invalid plot area parameters')
figsize = (8.5, 5.0)
dpi = 90
x0 = 0.1
y0 = 0.22
x1 = 0.98
y1 = 0.9
figure = mpltk.Figure(figsize=figsize, dpi=dpi)
g['ax'] = figure.add_axes([x0, y0, x1-x0, y1-y0])
g['ax'].set_xticks([])
g['ax'].set_yticks([])
g['canvas'] = mpltk.FigureCanvasTkAgg(figure, master=page)
g['canvas'].get_tk_widget().grid(row=1, column=0, sticky='news')
notebook.setnaturalsize()
# message bar
self.messagebar = MessageBar.MessageBar(interior,
entry_relief='sunken',
entry_bd=1,
history=True,
)
self.messagebar.grid(row=2, column=0, columnspan=2, sticky='ew')
self.get_site_config()
interior.pack(side='top', fill='both', expand='yes')
self.focus_set()
def add_month_counter(self, master):
w = Pmw.Counter(master,
labelpos='w',
label_text='Month:',
entry_width=2,
entryfield_value=time.gmtime().tm_mon,
entryfield_modifiedcommand=self.update,
)
w.configure(entryfield_validate=Avn.curry(_int_validator, w, 1, 13))
return w
def add_hour_counter(self, master):
w = Pmw.Counter(master,
labelpos='w',
label_text='Hour:',
entry_width=2,
entryfield_value=time.gmtime().tm_hour,
entryfield_modifiedcommand=self.update,
)
w.configure(entryfield_validate=Avn.curry(_int_validator, w, 0, 24))
return w
def add_hour_range_counter(self, master):
w = Pmw.Counter(master,
labelpos='w',
label_text='Num Hours:',
entry_width=2,
entryfield_value=1,
entryfield_modifiedcommand=self.update,
)
w.configure(entryfield_validate=Avn.curry(_int_validator, w, 1, 25))
return w
def add_month_range_counter(self, master):
w = Pmw.Counter(master,
labelpos='w',
label_text='Num Months:',
entry_width=2,
entryfield_value=1,
entryfield_modifiedcommand=self.update,
)
w.configure(entryfield_validate=Avn.curry(_int_validator, w, 1, 13))
return w
def setpage(self, name):
self.current_plot = name
def set_geometry(self, widget):
"""forces dialog 30 pixels off parent's left top position"""
x, y = self.interior().winfo_rootx(), self.interior().winfo_rooty()
s_w, s_h = self.interior().winfo_screenwidth(), \
self.interior().winfo_screenheight()
w_w, w_h = widget.winfo_reqwidth(), widget.winfo_reqheight()
x, y = min(x+30, s_w-w_w), min(y+30, s_h-w_h)
widget.wm_geometry('+%d+%d' % (x, y))
widget.resizable(1, 0)
def get_option_db(self):
# set default X resources. Read resource file, if available
self.root.option_add('*font', 'fixed')
self.root.option_add('*background', 'grey')
self.root.option_add('*foreground', 'black')
self.root.option_add('*wrapLength', '0')
self.root.option_add('*MessageBar.Entry.background', 'grey85')
self.root.option_add('*updateOnSelection', '0')
self.root.option_add('*plotWidth', '8.5')
self.root.option_add('*plotHeight', '5.0')
self.root.option_add('*dpi', '90')
self.root.option_add('*vlifr', 'purple')
self.root.option_add('*lifr', 'red')
self.root.option_add('*ifr', 'yellow')
self.root.option_add('*mvfr', 'blue')
self.root.option_add('*plotX0', '0.10')
self.root.option_add('*plotY0', '0.22')
self.root.option_add('*plotX1', '0.98')
self.root.option_add('*plotY1', '0.90')
path = os.path.join('etc', 'app-resources', 'X'+self.__module__)
if os.path.isfile(path):
self.root.option_readfile(path)
self._tkUpdate.set(int(self.root.option_get('updateOnSelection', '')))
vlifr_color = self.root.option_get('vlifr', '')
lifr_color = self.root.option_get('lifr', '')
ifr_color = self.root.option_get('ifr', '')
mvfr_color = self.root.option_get('mvfr', '')
self.colors = [mvfr_color, ifr_color, lifr_color, vlifr_color]
Pmw.Color.setscheme(self.root,
background=self.root.option_get('background', ''))
def get_site_config(self):
# get list of data files
cp = ConfigParser.SafeConfigParser()
cp.read(self.IdsFile)
self.ids = dict([(x, dict(cp.items(x))) for x in cp.sections()])
if self.ids:
tmp = self.ids.keys()
tmp.sort()
self.station_w.setlist(tmp)
self.station_w.selection_set(0)
def save_image(self):
# save image to a file
opts = {'initialdir': 'tmp', 'filetypes': [('PostScript', '.ps'), \
('JPEG', '.jpg'), ('PNG', '.png'), ('all', '*')]}
filename = Busy.asksaveasfilename(self.interior(), **opts)
if not filename:
return
canvas = self.graphics[self.current_plot]['canvas'].get_tk_widget()
if '.' not in filename:
msg = '%s does not have an extension, defaulting to Postscript' % \
filename
if Busy.askokcancel(msg, self.interior()):
canvas.postscript(file=filename, colormode='color')
elif filename.endswith('.ps'):
canvas.postscript(file=filename, colormode='color')
else:
tmpfile = os.tmpnam()+'.ps'
canvas.postscript(file=tmpfile, colormode='color')
command = 'convert %s %s' % (tmpfile, filename)
chldin, chldout = os.popen4(command, -1)
chldin.close()
msg = chldout.read()
if msg:
Busy.showinfo(msg, self.interior())
os.system('convert %s %s' % (tmpfile, filename))
os.unlink(tmpfile)
def create_print_dialog(self):
# create print dialog
self.print_dialog = Pmw.Dialog(self.interior(),
buttons=('OK', 'Cancel'),
defaultbutton='OK',
title='Print',
command=self.print_,
)
self.print_dialog.withdraw()
interior = self.print_dialog.interior()
self.print_dialog.mode = Pmw.RadioSelect(interior,
buttontype = 'radiobutton',
labelpos='w',
label_text='Palette',
orient='horizontal',
hull_borderwidth=2,
hull_relief='ridge',
)
self.print_dialog.mode.pack(side='top', fill='x', expand='yes', padx=5)
for btn in ['gray', 'color']:
self.print_dialog.mode.add(btn)
self.print_dialog.mode.setvalue('gray')
self.print_dialog.command = Pmw.EntryField(interior,
labelpos='w',
label_text='command',
entry_width=12,
validate={'min': 1, 'max': 50, 'minstrict':0},
value='lpr',
)
self.print_dialog.command.pack(side='top', fill='x', expand='yes',
padx=5)
def create_stats_dialog(self):
# create print dialog
self.stats_dialog = Pmw.Dialog(self.interior(),
buttons=('OK', 'Close'),
defaultbutton='OK',
title='Save Stats',
command=self.save_stats,
)
self.stats_dialog.withdraw()
interior = self.stats_dialog.interior()
self.stats_dialog.element = Pmw.RadioSelect(interior,
buttontype = 'radiobutton',
labelpos='w',
label_text='Element',
orient='horizontal',
hull_borderwidth=2,
hull_relief='ridge',
)
self.stats_dialog.element.pack(side='top', fill='x',
expand='yes', padx=5)
for btn in ['vis', 'cig', 'joint']:
self.stats_dialog.element.add(btn)
self.stats_dialog.element.setvalue('vis')
self.stats_dialog.columns = Pmw.RadioSelect(interior,
buttontype = 'checkbutton',
labelpos='w',
label_text='Columns',
orient='horizontal',
hull_borderwidth=2,
hull_relief='ridge',
selectmode='multiple',
)
self.stats_dialog.columns.pack(side='top', fill='x',
expand='yes', padx=5)
for btn in ['month', 'hour', 'wdir']:
self.stats_dialog.columns.add(btn)
self.stats_dialog.columns.setvalue(['month'])
self.stats_dialog.file = Pmw.EntryField(interior,
labelpos='w',
label_text='File',
entry_width=12,
value='tmp/stats.txt',
)
self.stats_dialog.file.pack(side='top', fill='x',
expand='yes', padx=5)
def show_help_dialog(self):
dialog = HelpDialog()
if dialog.winfo_ismapped():
return
self.set_geometry(dialog)
dialog.display(_Help)
def show_print_dialog(self):
if self.print_dialog.winfo_ismapped():
return
self.set_geometry(self.print_dialog)
self.print_dialog.transient(self.interior())
self.print_dialog.show()
def make_list(self,count):
"""Flattens count to a list of tuples (ixlist, category-count)"""
result = []
n = len(count.shape)
if n == 1:
result = [((), count.tolist())]
elif n == 2:
for i in range(count.shape[0]):
result.append(((i,), count[i,:].tolist()))
elif n == 3:
for i in range(count.shape[0]):
for j in range(count.shape[1]):
result.append(((i, j), count[i,j,:].tolist()))
elif n == 4:
for i in range(count.shape[0]):
for j in range(count.shape[1]):
for k in range(count.shape[2]):
result.append(((i, j, k), count[i,j,k,:].tolist()))
else:
raise ValueError('Invalid shape for count')
return result
def combine_count(self):
"""Combines 4-dimensional dictionary along items"""
month_ix, hour_ix, wind_ix = range(3)
tc = self.data[self.stats_dialog.element.getvalue()]
items=self.stats_dialog.columns.getvalue()
if 'month' not in items:
tc = na.sum(tc, month_ix)
hour_ix -= 1
wind_ix -= 1
if 'hour' not in items:
tc = na.sum(tc, hour_ix)
wind_ix -= 1
if 'wdir' not in items:
tc = na.sum(tc, wind_ix)
return tc
def show_stats_dialog(self):
if self.stats_dialog.winfo_ismapped():
return
#
# Set 'Save Stats' element & columns to the parent selection
self.stats_dialog.element.setvalue(self.element_w.getvalue())
self.stats_dialog.columns.setvalue([self.current_plot])
try:
self.stats_dialog.file.setvalue('tmp/%s-%s-%s.txt' % \
(self.id_.strip(),
self.element_w.getvalue(),
self.current_plot))
except AttributeError:
msg = 'Press Draw first'
Busy.showerror(msg, self.interior())
return
self.set_geometry(self.stats_dialog)
self.stats_dialog.transient(self.interior())
self.stats_dialog.show()
def print_(self, action):
# print image
if action == 'OK' and self.data is not None and \
self.print_dialog.command.valid():
command = self.print_dialog.command.getvalue()
mode = self.print_dialog.mode.getvalue()
#
# If color palette selected, then make sure the -P flag is present
if mode == 'color':
lpdest = re.compile('-P\s+\w+')
if not lpdest.search(command):
cmd = command.split(' ')
cmd.insert(1,'lp2')
cmd.insert(1,'-P')
command = ' '.join(cmd)
chldin, chldout = os.popen4(command, -1)
canvas = self.graphics[self.current_plot]['canvas'].get_tk_widget()
chldin.write(canvas.postscript(colormode=mode))
chldin.close()
msg = chldout.read()
if msg:
Busy.showinfo(msg, self.interior())
self.print_dialog.withdraw()
self.print_dialog.deactivate()
def save_stats(self, action):
# save stats to a file
if action == 'Close':
self.stats_dialog.withdraw()
self.stats_dialog.deactivate()
else:
if self.data is None:
return
element = self.stats_dialog.element.getvalue()
columns = self.stats_dialog.columns.getvalue()
path = self.stats_dialog.file.getvalue()
fh = open(path,'a')
if not columns:
msg = 'Select at least one column'
Busy.showerror(msg, self.interior())
return
output = self.make_list(self.combine_count())
col_labels = self.RowLabels[:]
col_labels.reverse()
elements = {'vis':'Visibility', 'cig':'Ceiling','joint':'Joint'}
hours = self.get_climate_hour_range()
if 'wdir' in columns:
months = self.get_month_range()
num_cat = len(ClimLib.FlightCats)
dir_labels = ['N', 'NNE', 'NE', 'ENE', 'E', 'ESE', 'SE', 'SSE', 'S', \
'SSW', 'SW', 'WSW', 'W', 'WNW', 'NW', 'NNW', 'VRB', 'C']
fmtw0 = '%4s ' + '(%4d-%4d)'
fmtw4 = 'MONTH: %-5s '
fmtw1 = 'HOUR: %02dZ' + ' '*9 + '%10s'
fmtw2 = '%-5s ' + '%-7s '*5
fmtw3 = '%-5s ' + '%-7.2f '*5
for month in months:
for hour in hours:
print >> fh, fmtw0 % (self.id_.strip(),self.data['years'][0],self.data['years'][1])
print >> fh, fmtw4 % (self.Month[month])
print >> fh, fmtw1 % (hour, elements[element])
print >> fh, fmtw2 % tuple(['DIR'] + col_labels[:] + ['VFR'])
tmp = na.sum(na.sum(na.take(na.take(self.data[element], [month], 0), [hour], 1), 0), 0)
total = na.sum(tmp, -1)
wind_hours = []
wind_hours.append(dir_labels)
for row in range(num_cat):
yvals = tmp[:,row]
yvals[na.ieeespecial.isnan(yvals)] = 0.0
wind_hours.append(yvals)
wind_hours.append(total)
for row in zip(*wind_hours):
print >> fh, fmtw3 % row
print >> fh, ' '
self.messagebar.message('userevent','Stats saved to ' + path)
self.stats_dialog.withdraw()
self.stats_dialog.deactivate()
return
fmt0 = '%4s ' + '(%4d-%4d)'
fmt1 = 'HOUR: %02dZ' + ' '*9 + '%10s'
fmt2 = '%-5s ' + '%-7s '*5
fmt3 = '%-5s ' + '%-7.2f '*5
print >> fh, fmt0 % (self.id_,self.data['years'][0],self.data['years'][1])
for hour in hours:
print >> fh, fmt1 % (hour, elements[element])
print >> fh, fmt2 % tuple(['MONTH'] + col_labels[:] + ['VFR'])
mth_ctr = 0
for row in na.sum(na.sum(na.take(self.data[element], [hour], 1), 1), 1):
try:
print >> fh, fmt3 % tuple([self.Month[mth_ctr]] + [x for x in row])
mth_ctr = mth_ctr + 1
except Exception, e:
print self.Month[mth_ctr]
print row[:]
print e
print >> fh, ' '
fh.close()
self.messagebar.message('userevent','Stats saved to ' + path)
self.stats_dialog.withdraw()
self.stats_dialog.deactivate()
def get_climate_hour_range(self):
if 'hour' in self.stats_dialog.columns.getvalue():
return range(24)
sel = self.graphics[self.current_plot]['selection']
return [int(sel['hour'].get())]
def get_hour_range(self):
# get hour selection range from widget
sel = self.graphics[self.current_plot]['selection']
try:
start_hour = int(sel['hour'].get())
except Exception, e:
return range(24)
end_hour = (start_hour+int(sel['hrange'].get()))%24
if start_hour < end_hour:
return range(start_hour, end_hour)
else:
return range(start_hour, 24) + range(end_hour)
def get_month_range(self):
# get hour selection range from widget
sel = self.graphics[self.current_plot]['selection']
try:
start_month = int(sel['month'].get())-1
except Exception, e:
return range(12)
end_month = (start_month+int(sel['mrange'].get()))%12
if start_month < end_month:
return range(start_month, end_month)
else:
return range(start_month, 12) + range(end_month)
def draw(self, id_):
if self.data is None:
return
self.id_ = id_
if self.current_plot == 'month':
self.draw_month()
elif self.current_plot == 'hour':
self.draw_hour()
elif self.current_plot == 'wdir':
self.draw_wdir()
def wait_for_data(self):
try:
i, (id_, data) = self.resultqueue.get_nowait()
except Queue.Empty:
self.after(100, self.wait_for_data)
return
if 'error' in data:
self.messagebar.message('systemerror', data['error'])
self.data = None
else:
self.messagebar.resetmessages('systemerror')
self.data = data
self.cache.put(id_, self.Key, data)
self.messagebar.message('userevent', 'Done')
Busy.Manager.notbusy()
self.draw(id_)
def get_data(self, id_):
prog = os.path.join('bin', 'avnstart.sh')
command = '%s %s %s' % (prog, self.GetData, self.ids[id_]['file'])
try:
chldin, chldout = os.popen2(command, -1)
chldin.close()
return id_, cPickle.load(chldout)
except Exception:
return id_, {'error': 'Failed to retrieve data'}
def display(self, cb=None):
# get and display data
try:
id_ = self.station_w.getcurselection()[0]
except IndexError:
Busy.showerror('Select site id', self.interior())
return
if self.id_ != id_:
# get mtime for climate file, redo cache if needed
self.data = self.cache.get(id_, self.Key, self.ids[id_]['file'])
if self.data is None:
msg = 'Retrieving data for %s, this will take a while' % id_
self.messagebar.message('userevent', msg)
Busy.Manager.busy()
self.update_idletasks()
self.worker.performWork(self.get_data, id_)
self.wait_for_data()
return
self.draw(id_)
def update(self):
if self._tkUpdate.get():
self.display()
def set_yticks(self, elem, item):
if self.tkAutoScale.get():
if item in ['month', 'hour']:
# sum over wind directions and categories
total = na.sum(na.sum(self.data[elem], 3), 2)
# fudge columns with no events
total[na.ieeespecial.index(total, na.ieeespecial.ZERO)] = 1.0
ymax = 100.0*(na.sum(na.sum(self.data[elem][:,:,:,:-1], 3),
2)/total).max()
elif item in ['wdir']:
# sum categories
total = na.sum(self.data[elem], 3)
# fudge columns with no events
total[na.ieeespecial.index(total, na.ieeespecial.ZERO)] = 1.0
ymax = 100.0*(na.sum(self.data[elem][:,:,:,:-1], 3)/total).max()
else:
raise Avn.AvnError('Bug in set_yticks(): unexpected item: %s' % \
item)
self.tkScale.set(ymax)
else:
ymax = int(self.tkScale.get())
if ymax > 30:
delta = 10
elif ymax > 10:
delta = 5
else:
delta = 2
return ymax+delta, delta
def draw_hour(self):
months = self.get_month_range()
elem = self.element_w.getvalue()
Busy.Manager.busy()
self.update_idletasks()
g = self.graphics['hour']
# clear display
g['ax'].cla()
# title
if len(months) == 1:
title = '%s %s %s (%d-%d)' % ((self.id_, \
self.Element.get(elem, ''), self.Month[months[0]]) + \
tuple(self.data['years']))
else:
title = '%s %s %s-%s (%d-%d)' % ((self.id_, \
self.Element.get(elem, ''), self.Month[months[0]], \
self.Month[months[-1]]) + tuple(self.data['years']))
g['ax'].set_title(title)
cell_text = []
col_labels = ['%02d' % x for x in range(24)]
xvals = na.arange(24) + 0.2
# the bottom values for stacked bar chart
yoff = na.zeros(len(col_labels), na.Float32)
num_cat = len(ClimLib.FlightCats)
bar = [None]*num_cat
widths = [0.6]*24
# stacked bars
g['ax'].xaxis.set_major_locator(multipleLocator)
# sum over selected range of months and wind directions
tmp = na.sum(na.sum(na.take(self.data[elem], months, 0), 0), 1)
# sum over all categories
total = na.sum(tmp, -1)
for row in range(num_cat):
yvals = 100.0*tmp[:,row]/total
bar[row] = g['ax'].bar(xvals, yvals, widths, bottom=yoff,
color=self.colors[num_cat-row-1])
yoff += yvals
cell_row = ['%.0f' % yvals[n] for n in range(24)]
cell_text.append(cell_row)
cell_text.reverse()
g['ax'].table(cellText=cell_text, rowLabels=self.RowLabels,
rowColours=self.colors, colLabels=col_labels,
loc='bottom')
# legend
legend_bars = [x[0] for x in bar]
legend_bars.reverse()
g['ax'].legend(legend_bars, self.RowLabels)
# axes
g['ax'].set_ylabel('Percent occurrence')
g['ax'].set_xticks([])
ymax, delta = self.set_yticks(elem, 'hour')
g['ax'].set_yticks(na.arange(0, ymax, delta))
g['ax'].grid(True)
g['canvas'].draw()
Busy.Manager.notbusy()
def draw_month(self):
hours = self.get_hour_range()
elem = self.element_w.getvalue()
Busy.Manager.busy()
self.update_idletasks()
g = self.graphics['month']
# clear display
g['ax'].cla()
g['ax'].xaxis.set_major_locator(multipleLocator)
# title
if len(hours) == 1:
title = '%s %s %02dZ (%d-%d)' % ((self.id_, \
self.Element.get(elem, ''), hours[0]) + \
tuple(self.data['years']))
else:
title = '%s %s %02d-%02dZ (%d-%d)' % ((self.id_, \
self.Element.get(elem, ''), hours[0], hours[-1]) + \
tuple(self.data['years']))
g['ax'].set_title(title)
cell_text = []
col_labels = self.Month[:]
col_labels.append('Annual')
xvals = list(na.arange(len(self.Month)) + 0.2)
xvals.append(xvals[-1]+1.2)
# the bottom values for stacked bar chart
yoff = na.array([0.0] * len(col_labels))
num_cat = len(ClimLib.FlightCats)
bar = [None]*num_cat
widths = [0.6]*12 + [1.2] # 0.6 for monthly, 1.2 for annual
# stacked bars
# sum over selected range of hours and wind directions
tmp = na.sum(na.sum(na.take(self.data[elem], hours, 1), 1), 1)
# sum over all categories
total = na.sum(tmp, -1)
total_sum = total.sum()
for row in range(num_cat):
yvals = 100.0*tmp[:,row]/total
yvals.resize(13)
yvals[12] = 100.0*tmp[:,row].sum()/total_sum
bar[row] = g['ax'].bar(xvals, yvals, widths, bottom=yoff,
color=self.colors[num_cat-row-1])
yoff += yvals
cell_row = ['%.0f' % yvals[n] for n in range(13)]
cell_text.append(cell_row)
cell_text.reverse()
widths = [1.0/14]*12+[1.0/7.0]
g['ax'].table(cellText=cell_text, rowLabels=self.RowLabels,
rowColours=self.colors, colLabels=col_labels, colWidths=widths,
loc='bottom')
# legend
legend_bars = [x[0] for x in bar]
legend_bars.reverse()
g['ax'].legend(legend_bars, self.RowLabels)
# axes
g['ax'].set_ylabel('Percent occurrence')
g['ax'].set_xticks([])
ymax, delta = self.set_yticks(elem, 'month')
g['ax'].set_yticks(na.arange(0, ymax, delta))
g['ax'].grid(True)
g['canvas'].draw()
Busy.Manager.notbusy()
def draw_wdir(self):
months = self.get_month_range()
hours = self.get_hour_range()
elem = self.element_w.getvalue()
Busy.Manager.busy()
self.update_idletasks()
g = self.graphics['wdir']
# clear display
g['ax'].cla()
# title
if len(months) == 1:
if len(hours) == 1:
title = '%s %s %02dZ %s (%d-%d)' % ((self.id_, \
self.Element.get(elem, ''), hours[0], \
self.Month[months[0]]) + tuple(self.data['years']))
else:
title = '%s %s %02d-%02dZ %s (%d-%d)' % ((self.id_, \
self.Element.get(elem, ''), hours[0], hours[-1], \
self.Month[months[0]]) + tuple(self.data['years']))
else:
if len(hours) == 1:
title = '%s %s %02dZ %s-%s (%d-%d)' % ((self.id_, \
self.Element.get(elem, ''), hours[0], self.Month[months[0]], \
self.Month[months[-1]]) + tuple(self.data['years']))
else:
title = '%s %s %02d-%02dZ %s-%s (%d-%d)' % ((self.id_, \
self.Element.get(elem, ''), hours[0], hours[-1], \
self.Month[months[0]], self.Month[months[-1]]) + \
tuple(self.data['years']))
g['ax'].set_title(title)
cell_text = []
col_labels = ['N', 'NNE', 'NE', 'ENE', 'E', 'ESE', 'SE', 'SSE', 'S', \
'SSW', 'SW', 'WSW', 'W', 'WNW', 'NW', 'NNW', 'VRB', 'C']
num_dir = len(col_labels)
xvals = na.arange(num_dir) + 0.2
# the bottom values for stacked bar chart
yoff = na.zeros(num_dir, na.Float32)
num_cat = len(ClimLib.FlightCats)
bar = [None]*num_cat
widths = [0.6]*num_dir
# stacked bars
g['ax'].xaxis.set_major_locator(multipleLocator)
# sum over selected range of months, hours
tmp = na.sum(na.sum(na.take(na.take(self.data[elem], months, 0),
hours, 1), 0), 0)
# sum over all categories
total = na.sum(tmp, -1)
for row in range(num_cat):
yvals = 100.0*tmp[:,row]/total
yvals[na.ieeespecial.isnan(yvals)] = 0.0
bar[row] = g['ax'].bar(xvals, yvals, widths, bottom=yoff,
color=self.colors[num_cat-row-1])
yoff += yvals
cell_row = ['%.0f' % yvals[n] for n in range(num_dir)]
cell_text.append(cell_row)
cell_text.reverse()
cell_row = ['%.0f' % total[n] for n in range(num_dir)]
cell_text.append(cell_row)
row_labels = self.RowLabels[:]
row_labels.append('HOURS')
row_colors = self.colors[:]
row_colors.append('white')
g['ax'].table(cellText=cell_text, rowLabels=row_labels,
rowColours=row_colors, colLabels=col_labels, loc='bottom')
# legend
legend_bars = [x[0] for x in bar]
legend_bars.reverse()
g['ax'].legend(legend_bars, self.RowLabels)
# axes
g['ax'].set_ylabel('Percent occurrence')
g['ax'].set_xticks([])
ymax, delta = self.set_yticks(elem, 'wdir')
g['ax'].set_yticks(na.arange(0, ymax, delta))
g['ax'].grid(True)
g['canvas'].draw()
Busy.Manager.notbusy()
def run(self):
self.pack()
self.root.deiconify()
self.mainloop()