## # 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: # MetarDisplay.py # GFS1-NHD:A9183.0000-SCRIPT;9 # # Status: # DELIVERED # # History: # Revision 9 (DELIVERED) # Created: 22-APR-2009 09:40:08 OBERFIEL # Removed hardwired initial year. Use previous year instead. # Expanded the Year: text field valid range to 1940 to 2020. # # Revision 8 (REVIEW) # Created: 17-APR-2009 10:23:31 GILMOREDM # Changed to only include the 24 hour max/min group in the # decoded metar # # Revision 7 (REVIEW) # Created: 08-APR-2009 14:35:33 GILMOREDM # Added code for max/min temps # # Revision 6 (DELIVERED) # Created: 20-MAR-2007 13:36:54 OBERFIEL # Updated text input fields and printer dialogs to be # consistent. Frame tag removed. # # Revision 5 (DELIVERED) # Created: 03-JAN-2007 08:03:30 OBERFIEL # If any EntryText validate function fails, do not update. # # Revision 4 (DELIVERED) # Created: 30-AUG-2006 10:56:09 OBERFIEL # Added auto update function when year and month are changed # # Revision 3 (DELIVERED) # Created: 30-MAY-2006 15:10:40 TROJAN # spr 7144: added auto-update feature, number of years in # database # # Revision 2 (DELIVERED) # Created: 18-MAY-2006 10:51:18 TROJAN # fixed rounding of mslp # # Revision 1 (DELIVERED) # Created: 18-MAY-2006 10:14:54 TROJAN # spr 7150 # # Change Document History: # 1: # Change Document: GFS1-NHD_SPR_7422 # Action Date: 15-AUG-2009 20:19:42 # Relationship Type: In Response to # Status: TEST # Title: AvnFPS: Balloon Message does not appear when mouse over 'wnd' indicator # # import logging, os, time import ConfigParser, Queue #import tables #import Tkinter as tk #import Pmw #import Avn, AvnLib, Busy, ClimLib, ErrorRedirect, MessageBar import Avn, AvnLib, Busy, ClimLib #from HelpDialog import HelpDialog _Help = { 'title': 'METAR Display Help', 'content': """ This dialog is used to display METARs reconstructed from climate data. Menu Bar File: Print: invokes printer selection dialog. Save As: invokes file selection dialog. Options: Show Decoded: toggles between METAR and decoded (ARONET) display format Update on Selection: when selected, "Station", "Month", "Day" and "Num Days" changes update the display without pressing "Show" Date Selection Year: select start year. Month: select start month. Day: select start day. Range of days is always 1-31, year 2000, month 2, day 31 results in data for March 2, 2000. Num Days: number of days of data to display. Show: displays reconstructed METARs for the selected dates and display format. """ } _Logger = logging.getLogger() Month = ['', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', \ 'Oct', 'Nov', 'Dec'] items = {} ############################################################################## # functions to build METAR from climate data def _mk_time(row): return time.strftime('%d%H%M', time.gmtime(row[items['date_time']])) def _mk_wind(row): wspeed = ClimLib.hd2us_wind_speed(row[items['wind_spd']]) wgust = ClimLib.hd2us_wind_speed(row[items['wd_gust']]) if wspeed < 0: return '' wspeed_str = '%02d' % wspeed if wgust < 0: wgust_str = '' else: wgust_str = 'G%02d' % wgust if row[items['wdir_type']] == 'C' or wspeed == 0: return '00000' elif row[items['wdir_type']] == 'V': return 'VRB'+wspeed_str+wgust_str else: wdir = row[items['wind_dir']] if not 0 <= wdir <= 360: return '' wdir = (wdir+5)//10*10 if wdir == 0: wdir = 360 return ('%03d' % wdir)+wspeed_str+wgust_str def _mk_vis(row): if row[items['vis']] > 100000: return ' ' vis = ClimLib.hd2us_vsby(row[items['vis']]) vi = int(vis) vf = vis - vi if vi == 0: if vf < 0.2: return 'M1/4' elif vf < 0.3: return '1/4' elif vf < 0.6: return '1/2' else: return '3/4' elif vi == 1: if vf < 0.3: return '1 1/4' elif vf < 0.6: return '1 1/2' else: return '1 3/4' else: return str(vi) def _mk_wx(row): codes = row[items['pres_wxm_code']] # first, precipitation tstm = [ClimLib.MWxTable[x] for x in codes if 91<=x<=99] pcp = [ClimLib.MWxTable[x] for x in codes if 50<=x<=90] pcp_ = [] if tstm: pcp_.append(tstm[0]) if pcp: tmp = [(len(p['str']), p) for p in pcp] tmp.sort() tmp.reverse() for n, p in tmp: for p_ in pcp_: if p['str'][-2:] == p_['str'][-2:]: break else: p['str'] = p['str'][-2:] pcp_.append(p) if pcp_: i = max([p['int'] for p in pcp_]) if i & ClimLib.PInt.hvy: i_str = '+' elif i & ClimLib.PInt.mod: i_str = '' else: i_str = '-' pcp_str = i_str + ''.join([p['str'] for p in pcp_]) else: pcp_str = '' # other weather obv = [ClimLib.MWxTable[x] for x in codes if x<=49] obv_ = [] if 'TS' in [o['str'] for o in obv]: obv_.append(o['str']) for o in obv: for o_ in obv_: if o['str'] in o_: break # fix for FG/BR if o['str'] == 'BR' and o_ == 'FG' or \ o['str'] == 'FG' and o_ == 'BR': break else: obv_.append(o['str']) if obv_: # fix for FG/BR vis = row[items['vis']] try: i = obv_.index('FG') if 1000 <= vis < 100000: obv_[i] = 'BR' except ValueError: pass try: i = obv_.index('BR') if vis < 1000: obv_[i] = 'FG' except ValueError: pass obv_str = ' '.join(obv_) else: obv_str = '' return (pcp_str + ' ' + obv_str).strip() def _mk_sky(row): cld = {1: 'FEW', 2: 'SCT', 3: 'BKN', 4: 'OVC', 5: 'VV'} sky = [] for cvr, h in zip(row[items['cov_sum_st_code']], row[items['cov_sum_st_dim']]): if cvr == 0 or cvr > 6: break hgt = ClimLib.hd2us_cig(h)/100 if hgt < 0: break if cvr == 6: # partially obscured continue if hgt < 30: h1 = int(hgt+0.49) elif hgt < 50: h1 = (hgt+2)//5 * 5 else: h1 = (hgt+5)//10 * 10 sky.append('%s%03d' % (cld[cvr], h1)) if not sky: sky.append('SKC') return ' '.join(sky) def _mk_temp(row): tt, td = row[items['temp']], row[items['dewt']] if tt < 0.0: tt_str = 'M%02d/' % -tt elif tt < 60.0: tt_str = '%02d/' % tt else: tt_str = '/' if td < 0.0: td_str = 'M%02d' % -td elif td <= tt < 60.0: td_str = '%02d' % td else: td_str = '' return tt_str+td_str def _mk_maxmintemp(row): try: maxt, mint = row[items['x_tp']] except: print row[items['x_tp']], len(row[items['x_tp']]) return if not (-80.0 < maxt < 70.0 and -80.0 < mint < 70.0): return '' if not (row[items['x_tp_period']][0] == row[items['x_tp_period']][1] == 24.): return '' if maxt < 0.0: maxt_str = "1%03d" % (-maxt * 10) else: maxt_str = "0%03d" % (maxt * 10) if mint < 0.0: mint_str = "1%03d" % (-mint * 10) else: mint_str = "0%03d" % (mint * 10) return "4%s%s" % (maxt_str, mint_str) def _mk_alt(row): alt = row[items['altimeter']] if not 900 <= alt < 1100: return '' return 'A%4.0f' % (2.953*alt) def _mk_alt_dcd(row): alt = row[items['altimeter']] if not 900 <= alt < 1100: return '' return ('%4.0f' % (2.953*alt))[1:] def _mk_mslp(row): if 900 <= row[items['pres']] < 1100: p = int(row[items['pres']]+0.49) if p >= 1000.0: p -= 1000.0 return '%03d' % p else: return '' def _mk_tt_us(row): if -80.0 < row[items['temp']] < 70.0: return '%.0f' % (row[items['temp']]*1.8+32.0) else: return '' def _mk_td_us(row): if -80.0 < row[items['dewt']] < 70.0: return '%.0f' % (row[items['dewt']]*1.8+32.0) else: return '' def _mk_past_pcp(row): tok = [] for p, d, c in zip(row[items['prec_period']], row[items['prec_depth']], row[items['prec_code']]): if d == 0.0 and c > 1 or d > 1000.0: continue if p == 1: tok.append('P%04.0f' % (d/0.254)) elif p in [3, 6]: tok.append('R%04.0f' % (d/0.254)) elif p == 24: tok.append('7%04.0f' % (d/0.254)) return ' '.join(tok) def _mk_past_pcp_dcd(row): tok = [] for p, d, c in zip(row[items['prec_period']], row[items['prec_depth']], row[items['prec_code']]): if d == 0.0 and c > 1 or d > 1000.0: continue if p == 1: return '%04.0f' % (d/0.254) else: break return '' ############################################################################### # for decoded METAR Items = [ \ {'item': 'time', 'length': 6, 'left': '', \ 'header': 'TIME', 'fun': _mk_time}, \ {'item': 'wind', 'length': 8, 'left': '-', \ 'header': 'WIND', 'fun': _mk_wind}, \ {'item': 'vsby', 'length': 5, 'left': '-', \ 'header': 'VSBY', 'fun': _mk_vis}, \ {'item': 'wx', 'length': 11, 'left': '-', \ 'header': 'WX', 'fun': _mk_wx}, \ {'item': 'sky', 'length': 28, 'left': '-', \ 'header': 'SKY CONDITION', 'fun': _mk_sky}, \ {'item': 'slp', 'length': 3, 'left': '', \ 'header': 'SLP', 'fun': _mk_mslp}, \ {'item': 'temp', 'length': 3, 'left': '', \ 'header': ' TT', 'fun': _mk_tt_us}, \ {'item': 'dewp', 'length': 3, 'left': '', \ 'header': ' DP', 'fun': _mk_td_us}, \ {'item': 'altim', 'length': 3, 'left': '', \ 'header': 'ALT', 'fun': _mk_alt_dcd}, \ {'item': 'pcp1h', 'length': 3, 'left': '', \ 'header': 'PCP', 'fun': _mk_past_pcp_dcd}, \ ] def mk_metar_dcd(id_, row): return ' '.join(['%%%s%ds' % (it['left'], it['length']) % it['fun'](row) \ for it in Items]) def mk_metar(id_, row): tok = ['METAR', id_, _mk_time(row)+'Z'] wind = _mk_wind(row) if wind: tok.append(wind+'KT') vis = _mk_vis(row) if vis: tok.append(vis+'SM') tok.append(_mk_wx(row)) tok.append(_mk_sky(row)) tok.append(_mk_temp(row)) tok.append(_mk_alt(row)) atok = ['RMK'] mslp = _mk_mslp(row) if mslp: atok.append('SLP'+mslp) atok.append(_mk_past_pcp(row)) atok.append(_mk_maxmintemp(row)) atok = filter(None, atok) if len(atok) > 1: tok.extend(atok) metar = ' '.join(filter(None, tok)) if len(metar) > 70: i = metar.rfind(' ', 0, 71) return metar[:i]+'\n '+metar[i+1:] else: return metar #def get_decoded(id_, table, tstart, tend): # data = [(row['date_time'], mk_metar_dcd(id_, row)) \ # for row in table.where(tstart<=table.cols.date_time