## # 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: # MetarViewer.py # GFS1-NHD:A6637.0000-SCRIPT;1.21 # # Status: # DELIVERED # # History: # Revision 1.21 (DELIVERED) # Created: 30-OCT-2007 14:19:26 OBERFIEL # Removed CR characters # # Revision 1.20 (REVIEW) # Created: 26-OCT-2007 09:33:59 GILMOREDM # Related to GFS1-NHD_SPR_7324 # # Revision 1.19 (INITIALIZE) # Created: 26-OCT-2007 09:32:03 GILMOREDM # Added functionality that allows forecasters to determine # the number of observations shown by default when "all" TAF # sites are shown in the metar viewer window. # # Revision 1.18 (DELIVERED) # Created: 20-JUN-2007 08:56:55 OBERFIEL # Updates to take care of PIT DRs # # Revision 1.17 (DELIVERED) # Created: 19-SEP-2006 06:46:39 OBERFIEL # Added VC types to display # # Revision 1.16 (DELIVERED) # Created: 23-FEB-2006 14:40:59 TROJAN # multiple METAR sites were not processed # # Revision 1.15 (DELIVERED) # Created: 06-FEB-2006 09:53:24 TROJAN # missing check for undefined alternate sites # # Revision 1.14 (APPROVED) # Created: 31-JAN-2006 18:14:19 TROJAN # spr 7081 # # Revision 1.13 (APPROVED) # Created: 23-JAN-2006 08:23:15 TROJAN # stdr 956 # # Revision 1.12 (APPROVED) # Created: 12-OCT-2005 18:26:36 TROJAN # spr 7040 # # Revision 1.11 (DELIVERED) # Created: 06-JUL-2005 18:16:40 TROJAN # spr 6548 # # Revision 1.10 (DELIVERED) # Created: 07-MAY-2005 11:36:07 OBERFIEL # Added Item Header Block # # Revision 1.9 (DELIVERED) # Created: 04-APR-2005 14:47:32 TROJAN # spr 6781 # # Revision 1.8 (APPROVED) # Created: 02-APR-2005 17:47:24 TROJAN # spr 6772 # # Revision 1.7 (DELIVERED) # Created: 28-FEB-2005 21:37:47 TROJAN # spr 6686 # # Revision 1.6 (DELIVERED) # Created: 19-AUG-2004 20:04:11 OBERFIEL # Bug fixes # # Revision 1.5 (APPROVED) # Created: 09-JUL-2004 19:11:35 OBERFIEL # Replaced busy dialogs # # Revision 1.4 (APPROVED) # Created: 01-JUL-2004 14:59:43 OBERFIEL # Update # # Revision 1.3 (DELIVERED) # Created: 06-FEB-2004 15:53:43 OBERFIEL # Fixed MOFF error # # Revision 1.2 (DELIVERED) # Created: 08-JAN-2004 21:40:11 PCMS # Updating for code cleanup # # Revision 1.1 (APPROVED) # Created: 06-NOV-2003 16:45:58 OBERFIEL # date and time created -2147483647/-2147483648/-2147481748 # -2147483648:-2147483648:-2147483648 by oberfiel # # Change Document History: # 1: # Change Document: GFS1-NHD_SPR_7324 # Action Date: 19-MAR-2008 07:58:32 # Relationship Type: In Response to # Status: CLOSED # Title: AvnFPS: Number of obs shown in TAF Editor/Viewer dependent on 'All' button # # import logging, re, time import AvnLib, AvnParser, Globals, MetarMonitor TAG = 'warning' sfcvis = re.compile(r'SFC\s+VIS\s+((\d{1,2}(?!/)\s*)?([1357]/1?[2468])?)') _Logger = logging.getLogger(__name__) ############################################################################## # Local functions used to create strings for elements in Items below def _makeType(dcd): return dcd['type']['str'][0] def _makeTime(dcd): return dcd['itime']['str'][:-1] def _makeSky(dcd): if 'sky' in dcd: return ' '.join(dcd['sky']['str'].split()) else: return '' def _makeVsby(dcd): if 'vsby' in dcd: vsbystr = dcd['vsby']['str'] # if vsbystr.endswith('SM'): return vsbystr[:-2] # # If SFC VIS is found in remarks strip that off and # any extraneous new line and whitespaces # else: re_result = sfcvis.match(vsbystr) if re_result: return ' '.join(re_result.group(1).split()) else: return vsbystr else: return '' def _makeWx(dcd): tmp = [] if 'pcp' in dcd: if dcd['pcp']['str'][0] not in ('+', '-'): tmp.append(' ' + dcd['pcp']['str']) else: tmp.append(dcd['pcp']['str']) if 'obv' in dcd: tmp.append(dcd['obv']['str']) if 'vcnty' in dcd: tmp.append(dcd['vcnty']['str']) if len(tmp) == 0 : return '' return ' '.join(tmp) def _makeSlp(dcd): if 'mslp' in dcd: return dcd['mslp']['str'][3:] else: return '' def _makeTemp(dcd): if 'tempdec' in dcd and 'tt' in dcd['tempdec']: return '%3.0f' % (dcd['tempdec']['tt']*9.0/5.0+32.0) elif 'temp' in dcd and 'tt' in dcd['temp']: return '%3.0f' % (dcd['temp']['tt']*9.0/5.0+32.0) else: return '' def _makeDewp(dcd): if 'tempdec' in dcd and 'td' in dcd['tempdec']: return '%3.0f' % (dcd['tempdec']['td']*9.0/5.0+32.0) elif 'temp' in dcd and 'td' in dcd['temp']: return '%3.0f' % (dcd['temp']['td']*9.0/5.0+32.0) else: return '' def _makeWind(dcd): if 'wind' in dcd: return dcd['wind']['str'][:-2] else: return '' def _makeAltim(dcd): if 'alt' in dcd: return dcd['alt']['str'][2:] else: return '' def _makePcp1h(dcd): if 'pcp1h' in dcd: return dcd['pcp1h']['str'][1:] else: return '' ############################################################################## # Items is a list of element dictionaries, each consists of # item, length, left-alignment-flag: either '' or '-', header # (always left-aligned), output-function (defined above) # sky, vsby, wx and wind are mandatory items ############################################################################## Items = [ \ {'item': 'type', 'length': 1, 'left': '', \ 'header': 'T', 'fun': _makeType, \ 'startTag' : '', 'endTag' : '', \ 'adjustLength' : False}, \ {'item': 'time', 'length': 6, 'left': '', \ 'header': 'TIME', 'fun': _makeTime, \ 'startTag' : '', 'endTag' : '', \ 'adjustLength' : False}, \ {'item': 'wind', 'length': 8, 'left': '-', \ 'header': 'WIND', 'fun': _makeWind, \ 'startTag' : '', 'endTag' : '', \ 'adjustLength' : False}, \ {'item': 'vsby', 'length': 5, 'left': '-', \ 'header': 'VSBY', 'fun': _makeVsby, \ 'startTag' : '', 'endTag' : '', \ 'adjustLength' : False}, \ {'item': 'wx', 'length': 11, 'left': '-', \ 'header': 'WX', 'fun': _makeWx, \ 'startTag' : '', 'endTag' : '', \ 'adjustLength' : True}, \ {'item': 'sky', 'length': 13, 'left': '-', \ 'header': 'SKY CONDITION', 'fun': _makeSky, \ 'startTag' : '', 'endTag' : '', \ 'adjustLength' : True}, \ {'item': 'slp', 'length': 3, 'left': '', \ 'header': 'SLP', 'fun': _makeSlp, \ 'startTag' : '', 'endTag' : '', \ 'adjustLength' : False}, \ {'item': 'temp', 'length': 3, 'left': '', \ 'header': ' TT', 'fun': _makeTemp, \ 'startTag' : '', 'endTag' : '', 'adjustLength' : False}, \ {'item': 'dewp', 'length': 3, 'left': '', \ 'header': ' DP', 'fun': _makeDewp, \ 'startTag' : '', 'endTag' : '', \ 'adjustLength' : False}, \ {'item': 'altim', 'length': 3, 'left': '', \ 'header': 'ALT', 'fun': _makeAltim, \ 'startTag' : '', 'endTag' : '', \ 'adjustLength' : False}, \ {'item': 'pcp1h', 'length': 4, 'left': '', \ 'header': 'PCP', 'fun': _makePcp1h, \ 'startTag' : '', 'endTag' : '', \ 'adjustLength' : False}, \ ] ############################################################################## def _makeIndices(): sky, vsby, wx, wind = 0, 0, 0, 0 ix = {} for it in Items: if it['item'] == 'sky': ix['sky'] = ['1.%d' % sky, '1.%d' % (sky+it['length'])] else: sky += it['length'] + 1 if it['item'] == 'vsby': ix['vsby'] = ['1.%d' % vsby, '1.%d' % (vsby+it['length'])] else: vsby += it['length'] + 1 if it['item'] == 'wx': ix['wx'] = ['1.%d' % wx, '1.%d' % (wx+it['length'])] else: wx += it['length'] + 1 if it['item'] == 'wind': ix['wind'] = ['1.%d' % wind, '1.%d' % (wind+it['length'])] else: wind += it['length'] + 1 return ix def _makeHeaderLine(): return ' '.join(['%%-%ds' % it['length'] % it['header'] for it in Items]) def _adjustLengths(data): for item in Items: if item['adjustLength']: maxLen = len(item['header']) fun = item['fun'] for d in data: length = len(fun(d['dcd'])) if maxLen < length : maxLen = length item['length'] = maxLen ############################################################################## class Viewer(object): def _format(dcd, addTages = False): if addTages : return '%s' % ' '.join(['%%s%%%s%ds%%s' % (it['left'], \ it['length']) % \ (it['startTag'], it['fun'](dcd), it['endTag']) for it in Items]) else: return '%s' % ' '.join(['%%%s%ds' % (it['left'], it['length']) % \ it['fun'](dcd) for it in Items]) _format = staticmethod(_format) def __init__(self, master, tafviewer, **kw): # master: parent widget (page in a notebook) # getcmd: data access method, returnig dictionary # d = {'text': text, 'dcd': decoded, 'wind': (), 'vsby': (), # 'wx': (), 'cig': ()} # tafviewer: TafViewer instance self._master = master self._id = None self._mtrid = None self._ids = [] self._data = None self._getcmd = kw['command'] self.messagebar = kw['messagebar'] self._tafviewer = tafviewer self._header = _makeHeaderLine() # decoded METARs self._index = _makeIndices() # for highlightning self._tkNumHours = IntVar() self._tkNumHours.set(int(master.option_get('numHours', ''))) self._tkNumHoursHold = IntVar() self._tkNumHoursAll = IntVar() self._tkNumHoursAll.set(int(master.option_get('numHoursAll', ''))) self._tkShowDecoded = IntVar() self._tkShowDecoded.set(int(master.option_get('showDecoded', ''))) self._tkShowHeaders = IntVar() self._tkShowHeaders.set(int(master.option_get('showHeaders', ''))) self._tkHighlightFlightCat = IntVar() self._tkHighlightFlightCat.set(int(master.option_get( \ 'highlightFlightCat', ''))) self._tkAll = IntVar() frame = Frame(master) menu = Pmw.OptionMenu(frame, labelpos='w', label_text='Num Hours', menubutton_width=3, menubutton_textvariable=self._tkNumHours, items=('1', '3', '6', '12', '24', '99'), command=self.load, ) menu.pack(side='right', expand='no', fill='x', padx=2) Balloon().bind(menu, 'Number of hours to display') cb = Checkbutton(frame, text='Decoded', variable=self._tkShowDecoded, command=self.load, ) cb.pack(side='right', padx=5) Balloon().bind(cb, 'Display decoded obs ARONET style') cb = Checkbutton(frame, text='Headers', variable=self._tkShowHeaders, command=self.load, ) cb.pack(side='right', padx=5) Balloon().bind(cb, 'Display WMO header for raw reports') cb = Checkbutton(frame, text='Flight Categories', variable=self._tkHighlightFlightCat, command=self.load, ) cb.pack(side='right', padx=5) Balloon().bind(cb, 'Background color depicts flight categories') cb = Checkbutton(frame, text='All', variable=self._tkAll, command=self.setAll, ) cb.pack(side='right', padx=5) Balloon().bind(cb, 'Displays data for all sites') frame.pack(side='top', expand='no', fill='x') self.text = Pmw.ScrolledText(master, columnheader=1, columnheader_name='textViewer', borderframe=1, vscrollmode='static', hscrollmode='dynamic', text_state='disabled', text_wrap='none', text_setgrid='true', text_name='textViewer', ) self.text.pack(side='top', expand='yes', fill='both') self.text.tag_configure(TAG, background='red') self.text.tag_raise(TAG) self.text.component('text').bind('', self.__popupMenu) self.popmenu = Menu(master, tearoff=0, type='normal', ) self.popmenu.add_command(label='Copy', command=self.__copy) AvnLib.configureFlightCatColors(self.text) def __copy(self): try: t = self.text.component('text') t.selection_own() selection = t.selection_get() t.clipboard_clear() t.clipboard_append(selection) except: pass def __popupMenu(self, e): self.popmenu.tk_popup(e.widget.winfo_rootx() + e.x, e.widget.winfo_rooty() + e.y) def __highlightDecoded(self): for e in self._data['status']: if self._data['status'][e].severity < 2: continue if e == 'tempo': pass elif e == 'cat': for e1 in ['vsby', 'sky']: self.text.tag_add(TAG, *self._index[e1]) else: self.text.tag_add(TAG, *self._index[e]) def __highlightRaw(self): if self._tkShowHeaders.get(): hlen = self._data['header'].count('\n') else: hlen = 0 dcd = self._data['dcd'] for e in self._data['status']: if self._data['status'][e].severity < 2: continue for ix in AvnLib.findIndex(e, dcd, hlen): self.text.tag_add(*(TAG,)+ix) def __showDecoded(self): htw = self.text.component('columnheader') htw.configure(state='normal') htw.delete('1.0', 'end') htw.insert('1.0', self._header) htw.configure(state='disabled') showfc = self._tkHighlightFlightCat.get() if 'dcd' in self._data: formatted = self._format(self._data['dcd']) self.text.insert('end', formatted) self.__highlightDecoded() if showfc: fcat = AvnLib.flightCategory(self._data['dcd']) self.text.tag_add(fcat, 1.0, 2.0) ftime = time.time() - 3600.0*self._tkNumHours.get() for n, metar in enumerate([x for x in Globals.DRC.getMetars( \ self._mtrid, True, ftime, 99)[1:] if 'fatal' not in x.dcd]): self.text.insert('end', self._format(metar.dcd)) if showfc: fcat = AvnLib.flightCategory(metar.dcd) self.text.tag_add(fcat, float(n+2), float(n+3)) # First line of most recent METAR return 1 def __showRaw(self): htw = self.text.component('columnheader') htw.configure(state='normal') htw.delete('1.0', 'end') htw.configure(state='disabled') if self._tkShowHeaders.get(): self.text.insert('end', self._data['header']) self.text.insert('end', self._data['text']) if 'dcd' in self._data: self.__highlightRaw() ftime = time.time() - 3600.0 * self._tkNumHours.get() for metar in Globals.DRC.getMetars(self._mtrid, False, ftime, 99)[1:]: if self._tkShowHeaders.get(): self.text.insert('end', metar.header) self.text.insert('end', metar.text) def __showAll(self): htw = self.text.component('columnheader') htw.configure(state='normal') htw.delete('1.0', 'end') htw.configure(state='disabled') ftime = time.time() - 3600.0*self._tkNumHours.get() last = None for metar in Globals.DRC.getMetars(self._ids, False, ftime): try: id_ = metar.text.split(None, 3)[1] if last and last != id_: self.text.insert('end', '\n') last = id_ except IndexError: pass if self._tkShowHeaders.get(): self.text.insert('end', metar.header) self.text.insert('end', metar.text) self.text.configure(text_state='disabled') ############################################################################### # public methods ############################################################################### def load(self, arg=None): self._tafviewer.load(self._id) try: self.text.configure(text_state='normal') self.text.clear() if self._tkAll.get(): self.__showAll() else: self._data = self._getcmd(self._id) if self._tkShowDecoded.get(): self.__showDecoded() else: self.__showRaw() self._tafviewer.highlight(self._data) except Exception: msg = 'Cannot load data for %s' % self._mtrid _Logger.exception(msg) if self._master.winfo_ismapped(): self.messagebar().message('usererror', msg) self.text.configure(text_state='disabled') def setAll(self, arg=None): if self._tkAll.get(): self._tkNumHoursHold.set(self._tkNumHours.get()) self._tkNumHours.set(self._tkNumHoursAll.get()) else: self._tkNumHoursAll.set(self._tkNumHours.get()) self._tkNumHours.set(self._tkNumHoursHold.get()) self.load(arg) def setSite(self, id_, ids): if self._id == id_: return self._id = id_ self._mtrid = None self._ids = [] for i in ids: siteinfo = AvnParser.getTafSiteCfg(i) if siteinfo and siteinfo['sites'].get('metar'): self._ids.extend(siteinfo['sites']['metar']) if i == self._id: self._mtrid = siteinfo['sites']['metar']