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

500 lines
15 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:
# 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<tend)]
# data.sort()
# return [x[1] for x in data]
def get_decoded(id_, table, tstart, tend):
dt = table['date_time']
dt1 = (tstart <= dt)
dt2 = (dt < tend)
import numpy
index = numpy.where(dt1&dt2)[0]
rows = []
for i in index:
rows.append(table[i])
data = [(row[items['date_time']], mk_metar_dcd(id_, row)) for row in rows]
data.sort()
return [x[1] for x in data]
#def get_formatted(id_, table, tstart, tend):
# data = [(row['date_time'], mk_metar(id_, row)) \
# for row in table.where(tstart<=table.cols.date_time<tend)]
# data.sort()
# return [x[1] for x in data]
def get_formatted(id_, table, tstart, tend):
dt = table['date_time']
dt1 = (tstart <= dt)
dt2 = (dt < tend)
import numpy
index = numpy.where(dt1&dt2)[0]
rows = []
for i in index:
rows.append(table[i])
data = [(row[items['date_time']], mk_metar(id_, row)) for row in rows]
data.sort()
return [x[1] for x in data]
##############################################################################
def get_metars(id_, year, month, day, ndays, decoded, fname, queue):
try:
import os
if not os.path.isfile(fname):
raise Avn.AvnError('File %s does not exist' % fname)
import h5py
fh = h5py.File(fname, 'r')
try:
table = fh['obs']
names = table.dtype.names
for i in range(len(names)):
items[names[i]] = i
tms = (int(year), int(month), int(day), 0, 0, 0, 0, 0, 0)
tstart = time.mktime(tms)-600.0
tend = tstart + int(ndays)*86400.0 + 600.0
if decoded:
data = get_decoded(id_, table, tstart, tend)
else:
data = get_formatted(id_, table, tstart, tend)
if data:
if decoded:
header_dcd = ' '.join(['%%-%ds' % it['length'] % it['header'] \
for it in Items])
else:
header_dcd = ''
header = '%s METARs for %s %d\n%s' % (id_, Month[int(month)], int(year), header_dcd)
if decoded:
# h = ' '.join(['%%-%ds' % it['length'] % it['header'] for it in Items])
# header = header + '\n' + h
queue.put(header)
for d in data:
queue.put(d)
else:
queue.put(header)
for d in data:
queue.put(d)
else:
msg = 'No %s data for %s %d %d' % \
(id_, Month[int(month)], int(day), int(year))
errMsg = 'Date %s %d %d not present in climate file %s.hd5' % \
(Month[int(month)], int(day), int(year), id_)
queue.put(errMsg)
finally:
fh.close()
except Exception, e:
print e