awips2/cave/com.raytheon.viz.avnconfig/localization/aviation/python/WxPlot.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

456 lines
16 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:
# WxPlot.py
# GFS1-NHD:A8544.0000-SCRIPT;19
#
# Status:
# DELIVERED
#
# History:
# Revision 19 (DELIVERED)
# Created: 16-JUN-2009 14:28:12 OBERFIEL
# If tafduration cannot be determined from the info.cfg file,
# default to 24 hours
#
# Revision 18 (REVIEW)
# Created: 20-MAR-2009 18:29:26 OBERFIEL
# Removed code cruft. ETA changed to NAM. NGMMOS removed.
#
# Revision 17 (DELIVERED)
# Created: 01-AUG-2008 15:44:46 OBERFIEL
# Synch'd up with changes in OB8.3.1
#
# Revision 16 (DELIVERED)
# Created: 25-JUN-2008 15:47:42 OBERFIEL
# Display now accounts for either 24- or 30-h TAFs
#
# Revision 15 (DELIVERED)
# Created: 29-JUN-2006 06:36:20 OBERFIEL
# Updated to remove references to HTML and other obsolete
# stuff
#
# Revision 14 (DELIVERED)
# Created: 02-JUN-2006 11:32:12 TROJAN
# spr 7163: fixed Display button action - was resetting
# guidance time before plotting
#
# Revision 13 (DELIVERED)
# Created: 02-JUN-2006 11:23:24 TROJAN
# spr 7162: fixed Display button action - was resetting
# guidance time before plotting
#
# Revision 12 (DELIVERED)
# Created: 20-MAY-2006 09:32:21 OBERFIEL
# Updated documentation
#
# Revision 11 (DELIVERED)
# Created: 23-FEB-2006 13:59:53 TROJAN
# MOS/LAMP data not plotted
#
# Revision 10 (DELIVERED)
# Created: 15-FEB-2006 14:34:49 TROJAN
# fixes transient dialog behavior - spr 7091
#
# Revision 9 (APPROVED)
# Created: 01-FEB-2006 15:15:40 TROJAN
# Incorrect handling of missing data
#
# Revision 8 (APPROVED)
# Created: 31-JAN-2006 18:18:50 TROJAN
# added check for nonexistent item in sites dictionary
#
# Revision 7 (APPROVED)
# Created: 31-JAN-2006 08:46:35 TROJAN
# added LAMP item
#
# Revision 6 (DELIVERED)
# Created: 24-AUG-2005 19:21:46 TROJAN
# spr 7002
#
# Revision 5 (DELIVERED)
# Created: 10-JUL-2005 20:08:05 TROJAN
# spr 6915
#
# Revision 4 (DELIVERED)
# Created: 07-MAY-2005 11:40:17 OBERFIEL
# Added Item Header Block
#
# Revision 3 (DELIVERED)
# Created: 18-APR-2005 17:32:34 OBERFIEL
# Changes to support gamin
#
# Revision 2 (DELIVERED)
# Created: 06-APR-2005 11:41:07 TROJAN
# spr 6763
#
# Revision 1 (APPROVED)
# Created: 02-APR-2005 17:15:17 TROJAN
# spr 6763
#
# Change Document History:
# 1:
# Change Document: GFS1-NHD_SPR_7417
# Action Date: 06-OCT-2009 09:42:01
# Relationship Type: In Response to
# Status: CLOSED
# Title: AvnFPS: TUG code does not handle transition from warm to cold seasons
#
#
import logging, math, os, time
from Tkinter import *
import Pmw
import Avn, AvnDialog, AvnLib, AvnParser, Busy, Globals, GraphWidget
from Balloon import Balloon
_Help = {
'title': 'Weather Plot Help',
'content': """
This dialog is used to display TAFs, METARs and guidance forecasts.
Menus:
Site ID - pulldown menu displaying list of all TAF sites.
Selection of a site from the list redraws the window.
zoom - zoom factor (time scale).
Buttons:
Display - Redraws the window.
Times - Displays forecast time selection window
Print - Dumps an image of the window to a command specified in
the configration file etc/wxplot.cfg.
Close - Closes this dialog.
Help - Displays this help.
Data Sources - selection of available data sources.
"""
}
_Logger = logging.getLogger(__name__)
def _formatCig(v):
return ' %03d' % (v/100)
def _formatVsby(v):
if v < 0.2:
return ' 1/8'
elif v < 0.3:
return ' 1/4'
elif v < 0.6:
return ' 1/2'
elif v < 0.8:
return ' 3/4'
elif v < 1.1:
return ' 1'
elif v < 1.6:
return '11/2'
else:
return '%.0f' % ((v+0.49)//1.0)
###############################################################################
class TimeSelector(Pmw.Dialog):
def __init__(self, parent, viewers, **kw):
Pmw.Dialog.__init__(self, parent, **kw)
self.withdraw()
self.title(Avn.Name + ' Time Selector')
self._ident = None
self._sites = None
self.configure(buttons=('Reset', 'Close'),
defaultbutton='Close',
command=self.__execute,
)
self.viewers = [v for v in viewers if v.tag != 'metar']
f = Frame(self.interior())
self.combo = {}
for v in self.viewers:
self.combo[v.tag] = Pmw.ComboBox(f,
label_text=v.label,
labelpos='w',
entry_width=22,
)
self.combo[v.tag].pack(side='top', padx=5)
Pmw.alignlabels(self.combo.values())
f.pack(side='top')
def __execute(self, result):
if result == 'Reset':
for v in self.viewers:
items = self.combo[v.tag].get()
if items:
self.combo[v.tag].selectitem(0, 1)
else:
self.withdraw()
self.deactivate()
def display(self, above=None):
if self.winfo_ismapped():
return
self.transient(self['master'])
self.show()
def setTimes(self, ident=None, sites=None):
if ident:
self._ident = ident
if sites:
self._sites = sites
for v in self.viewers:
if v.tag == 'taf':
tafs = Globals.DRC.getTafs(self._ident, False)
headers = [taf.header.split('\n')[0] for taf in tafs]
elif ('mos' in v.tag or 'lamp' in v.tag) and v.tag in self._sites:
files = Globals.DRC.getMosFiles(self._sites[v.tag], v.args['model'])
headers = [f.split('.')[0] for f in files]
elif (v.tag == 'nam' or v.tag == 'ruc' or v.tag == 'gfs') and v.tag in self._sites:
files = Globals.DRC.getEtaFiles(self._sites[v.tag], v.args['model'])
headers = [f.split('.')[0] for f in files]
else:
headers = []
if headers:
self.combo[v.tag].setlist(headers)
self.combo[v.tag].selectitem(0, 1)
else:
self.combo[v.tag].clear()
def getTimes(self):
d = dict([(v.tag, self.combo[v.tag].get()) for v in self.viewers])
d['metar'] = 'dummy'
return d
###############################################################################
class WxPlot(AvnDialog.Dialog):
Height = {'cig': 150, 'vsby': 150, 'wind': 100}
Width = 1000
def __init__(self, parent, **kw):
AvnDialog.Dialog.__init__(self, parent, **kw)
self.title(Avn.Name + ' Weather Plot')
self.cfg = AvnParser.getPlotCfg()
if not self.cfg:
raise SystemExit
self._viewer = {}
for v in self.cfg['viewers']:
self._viewer[v.tag] = v.module.Plot(**v.args)
self.ident = None
# from AvnDialog
self.createMessageBar(self.interior(), True)
# time selection dialog
self.timeselector = TimeSelector(self.interior(), self.cfg['viewers'],
master=self.interior())
# GUI
f = Frame(self.interior())
self.siteids = Pmw.ComboBox(f,
label_text='Site ID:',
labelpos='w',
entry_width=5,
selectioncommand=self.update,
)
self.siteids.pack(side='left', padx=5)
btnbox = Pmw.ButtonBox(f)
btn = btnbox.add('Display', command=self.draw)
Balloon().bind(btn, 'Draws weather')
btn = btnbox.add('Times', command=self.timeselector.display)
Balloon().bind(btn, 'Displays time selection dialog')
btn = btnbox.add('Print', command=self.print_)
Balloon().bind(btn, 'Saves window image to a file')
btn = btnbox.add('Close', command=self.close)
Balloon().bind(btn, 'Closes this dialog')
btn = btnbox.add('Help', command=Avn.curry(self.showHelp, _Help))
Balloon().bind(btn, 'Shows help')
btnbox.pack(side='right')
self.counter = Pmw.Counter(f,
labelpos='w',
label_text='zoom',
entry_width=1,
entryfield_value=1,
entryfield_validate={'validator': 'integer', 'min': 1, 'max': 3},
)
self.counter.pack(side='right', padx=5)
f.pack(side='top', expand='yes', fill='x')
f = Frame(self.interior())
self.chkbtns = Pmw.RadioSelect(f,
buttontype='checkbutton',
orient='horizontal',
labelpos='w',
label_text='Data Sources',
hull_borderwidth=2,
hull_relief='ridge',
)
self.chkbtns.pack(side='left', expand='yes', fill='x', padx=5)
for v in self.cfg['viewers']:
self.chkbtns.add(v.tag, text=v.label, background=v.args['color'])
self.chkbtns.setvalue(self.cfg['selected'])
f.pack(side='top', expand='yes', fill='x')
self.date = Label(self.interior(), text='')
self.date.pack(side='top', expand='no', padx=5)
self.sf = Pmw.ScrolledFrame(self.interior(),
usehullsize=1,
hscrollmode='static',
vscrollmode='static',
)
kwds = {'relief': 'sunken', 'background': 'gray90',
'font': 'fixed', 'border': 2}
f = self.sf.interior()
self.graph = {}
h = 0
for item in ['cig', 'vsby', 'wind']:
label = Label(f, text=item.upper())
label.pack(side='top')
h += label.winfo_reqheight()+4
self.graph[item] = GraphWidget.Graph(f, self.Width,
self.Height[item], **kwds)
self.graph[item].pack(side='top', fill='both', expand='yes')
h += self.Height[item]+4
self.sf.pack(side='left', expand='yes', fill='both')
h += self.sf.component('horizscrollbar').winfo_reqheight()
w = self.Width+self.sf.component('vertscrollbar').winfo_reqwidth()
self.sf.configure(hull_height=h, hull_width=w)
self.initialiseoptions(WxPlot)
self.setGeometry()
def getSite(self):
return self.siteids.get()
def setSite(self, sitemonitors):
self.siteids.setlist([sm.info['ident'] for sm in sitemonitors])
self.siteids.selectitem(0, 1)
self.update()
def print_(self):
if self.ident is None:
return
window = self.interior().winfo_id()
if '%s' in self.cfg['printcmd']:
tmp = self.cfg['printcmd'] % self.ident
command = 'xwd -id %d | %s' % (window, tmp)
else:
command = 'xwd -id %d | %s' % (window, self.cfg['printcmd'])
self.messagebar().message('userevent', command)
os.system(command)
def _getSiteConfig(self, ident):
self.ident = ident
siteinfo = AvnParser.getTafSiteCfg(ident)
if not siteinfo:
msg = 'Missing configuration file for %s' % ident
_Logger.error(msg)
Busy.showerror(msg, self.interior())
return False
self.sites = siteinfo['sites']
thresholds = siteinfo['thresholds']
self.tafduration = int(thresholds.get('tafduration','24'))
self.vsby = thresholds['vsby']
self.cig = thresholds['cig']
if self.cfg['vsby']['bot'] < thresholds['vsby'][0]:
self.vsby.insert(0, self.cfg['vsby']['bot'])
if self.cfg['vsby']['top'] > thresholds['vsby'][-1]:
self.vsby.append(self.cfg['vsby']['top'])
if self.cfg['cig']['bot'] < thresholds['cig'][0]:
self.cig.insert(0, self.cfg['cig']['bot'])
if self.cfg['cig']['top'] > thresholds['cig'][-1]:
self.cig.append(self.cfg['cig']['top'])
self.yaxis = { \
'cig': tuple([(math.log(h), _formatCig(h)) for h in self.cig]),\
'vsby': tuple([(math.log(v), _formatVsby(v)) for v in self.vsby]), \
'wind': ((-1, ' '*4), (1, ' '*4)), \
}
return True
def draw(self, arg=None):
self.messagebar().resetmessages('systemerror')
width = self.Width * int(self.counter.get())
graphics = {'cig': [], 'vsby': [], 'wind': []}
for key in graphics:
self.graph[key].clear()
event = Avn.Bunch(width=width, height=self.Height[key])
self.graph[key]._reconfigure(event)
items = self.chkbtns.getvalue()
if not items:
return
ident = self.siteids.get()
if ident != self.ident:
if not self._getSiteConfig(ident):
return
now = time.time()
ftime = now - 3600.0*self.cfg['hours']['back']
ttime = now + (3600.0*max(self.cfg['hours']['forward'],self.tafduration))
tticks = [t for t in Avn.frange((ftime//3600.0)*3600.0, ttime, 3600.0)]
self.xaxis = ((tticks[0]-1800.0, ''),) + \
tuple([(t, time.strftime('%H', time.gmtime(t))) for t in tticks]) +\
((tticks[-1]+1800.0, ''),)
times = self.timeselector.getTimes()
for item in items:
header = times.get(item)
if not header:
continue
try:
g = self._viewer[item].plot(ident, self.sites, now, tticks,
self.vsby, self.cig, header)
for key in graphics:
graphics[key].extend(g[key])
except Avn.AvnError, e:
msg = str(e)
_Logger.error(msg)
self.messagebar().message('usererror', msg)
except Exception:
msg = 'Unexpected error, check log file'
_Logger.exception(msg)
self.messagebar().message('systemerror', msg)
if graphics['cig']:
graphObject = GraphWidget.Objects(graphics['cig'])
self.graph['cig'].draw(graphObject, self.xaxis, self.yaxis['cig'],
grid=True, numYLabels=4)
if graphics['vsby']:
graphObject = GraphWidget.Objects(graphics['vsby'])
self.graph['vsby'].draw(graphObject, self.xaxis,
self.yaxis['vsby'], grid=True, numYLabels=4)
if graphics['wind']:
graphObject = GraphWidget.Objects(graphics['wind'])
self.graph['wind'].draw(graphObject, self.xaxis,
self.yaxis['wind'], grid=True)
self.date.configure(text='%s %s' % \
(self.ident, time.strftime('%x %X UTC')))
def update(self, ident=None):
if ident is None:
ident = self.siteids.get()
if not self._getSiteConfig(ident):
return
self.timeselector.setTimes(ident, self.sites)
self.draw()