933 lines
33 KiB
Python
933 lines
33 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.
|
|
##
|
|
# ----------------------------------------------------------------------------
|
|
# This software is in the public domain, furnished "as is", without technical
|
|
# support, and with no warranty, express or implied, as to its usefulness for
|
|
# any purpose.
|
|
#
|
|
# TableBuilder.py
|
|
# Methods for Smart Table products.
|
|
#
|
|
# Author: hansen
|
|
# ----------------------------------------------------------------------------
|
|
# SOFTWARE HISTORY
|
|
#
|
|
# Date Ticket# Engineer Description
|
|
# ------------ ---------- ----------- --------------------------
|
|
# 01/22/2015 4027 randerso Removed upper casing of weather and discrete phrases
|
|
# 03/15/2020 DR21820 NFTF Add Wx duration values to wxCoveragePercent
|
|
#
|
|
##
|
|
|
|
##
|
|
# This is a base file that is not intended to be overridden.
|
|
##
|
|
|
|
import importlib
|
|
|
|
import AbsTime
|
|
import TextUtils
|
|
import TimeRange
|
|
from WxMethods import *
|
|
|
|
from com.raytheon.uf.common.dataplugin.gfe.discrete import DiscreteKey
|
|
|
|
|
|
class TableBuilder(TextUtils.TextUtils):
|
|
def __init__(self):
|
|
pass
|
|
|
|
def columnLabels(self, statDict, argDict, weList):
|
|
# Return the time labels and the column length for
|
|
# the elements in weList
|
|
|
|
# Could be made more general, but currently gets
|
|
# grid time labels for each we listed
|
|
hours = []
|
|
maxLen = 0
|
|
for we in weList:
|
|
stats = statDict[we.name]
|
|
#print stats
|
|
for value, start in stats:
|
|
hourStr = repr(start.hour)+"Z/"+repr(start.day)
|
|
hours.append(hourStr)
|
|
if len(hourStr) > maxLen:
|
|
maxLen = len(hourStr)
|
|
|
|
colLen = maxLen + 4
|
|
colLabels = ""
|
|
for hour in hours:
|
|
colLabels = colLabels + hour.center(colLen)
|
|
colLabels = colLabels + "\n"
|
|
return colLabels,colLen
|
|
|
|
def columnValues(self, statDict, argDict, weList, label):
|
|
# Return the column values as text strings
|
|
values = []
|
|
for we in weList:
|
|
stats = statDict[we.name()]
|
|
for value, hour in stats:
|
|
values.append(fformat(value, we.roundVal()))
|
|
return label, values
|
|
|
|
def makeRow(self, rowLabel, colWidth, timeRangeList,
|
|
statList, method, argList=None, rowLabelWidth=None,
|
|
firstValWidth=None, justify = "r"):
|
|
# Produce a row beginning with the label followed by a value
|
|
# for each period. Note that colWidth can be a number, for a
|
|
# fixed column width, or a list, for a variable column width. If
|
|
# a list, then it is a parallel list with the timeRangeList.
|
|
# If provided, firstValWidth overrides the colWidth (list or value)
|
|
# Justify is "r" for right justify values, "l" for left justify values
|
|
# Justify may also be a list, which then is specified for each
|
|
# column entry.
|
|
# Each value is obtained via the given method which is given arguments:
|
|
# (statDict, timeRange, argList)
|
|
# and must return a text string value (i.e. numerical values
|
|
# must be converted to text strings before being returned).
|
|
if type(colWidth) is list:
|
|
fixedColWidth = colWidth[0]
|
|
else:
|
|
fixedColWidth = colWidth
|
|
if rowLabelWidth is None:
|
|
rowLabelWidth = fixedColWidth
|
|
if firstValWidth is None:
|
|
firstValWidth = fixedColWidth
|
|
values = []
|
|
index = 0
|
|
for timeRange, label in timeRangeList:
|
|
statDict = statList[index]
|
|
if statDict is None or method is None:
|
|
value = ""
|
|
else:
|
|
value = method(statDict, timeRange, argList)
|
|
values.append(value)
|
|
index = index + 1
|
|
row = rowLabel.ljust(rowLabelWidth)
|
|
for x in range(len(values)):
|
|
if x == 0:
|
|
width = firstValWidth
|
|
elif type(colWidth) is list:
|
|
width = colWidth[x]
|
|
else:
|
|
width = fixedColWidth
|
|
if type(justify) is list:
|
|
curJustify = justify[x]
|
|
else:
|
|
curJustify = justify
|
|
if curJustify == 'r':
|
|
row += str(values[x]).strip().rjust(width)
|
|
else:
|
|
row += str(values[x]).strip().ljust(width)
|
|
width = fixedColWidth
|
|
return row + "\n"
|
|
|
|
def addColValue(self, fcst, str, width):
|
|
return fcst + str.strip().rjust(width)
|
|
|
|
def addRowLabel(self, fcst, str, width):
|
|
return fcst + str.strip().ljust(width)
|
|
|
|
def maxVal(self, stats, timeRange, argList):
|
|
# Return a scalar text string value representing the max value
|
|
# The desired element name must be the first element of argList
|
|
element = argList[0]
|
|
value = self.getStats(stats, element)
|
|
if value is None:
|
|
return ""
|
|
min, max = value
|
|
return self.getScalarVal(max)
|
|
|
|
def minVal(self, stats, timeRange, argList):
|
|
# Return a scalar text string value representing the min value
|
|
# The desired element name must be the first element of argList
|
|
element = argList[0]
|
|
value = self.getStats(stats, element)
|
|
if value is None:
|
|
return ""
|
|
min, max = value
|
|
return self.getScalarVal(min)
|
|
|
|
def scalarVal(self, stats, timeRange, argList):
|
|
# Return a scalar text string value
|
|
# The desired element name must be the first element of argList
|
|
element = argList[0]
|
|
value = self.getStats(stats, element)
|
|
if value is None:
|
|
return ""
|
|
return self.getScalarVal(value)
|
|
|
|
def vectorVal(self, stats, timeRange, argList):
|
|
# Return a vector text string value
|
|
# The desired element name must be the first element of argList
|
|
# E.g. SW 19
|
|
element = argList[0]
|
|
value = self.getStats(stats, element)
|
|
if value is None:
|
|
return ""
|
|
return self.getVectorVal(value)
|
|
|
|
def wxVal(self, stats, timeRange, argList):
|
|
# Return a weather text string value
|
|
# The desired element name must be the first element of argList
|
|
# E.g. SNOW
|
|
element = argList[0]
|
|
wxStats = self.getStats(stats, element)
|
|
if wxStats is None:
|
|
return ""
|
|
value = ""
|
|
#print "\nIn wxVal"
|
|
for wxValue, timeRange in wxStats:
|
|
#print wxValue, timeRange
|
|
val = self.short_weather_phrase(
|
|
element,wxValue)
|
|
val = val.replace("|", " ")
|
|
val = val.replace("THUNDER STORMS", "THUNDERSTORMS")
|
|
val = val.replace("THUNDERSTORMS", "TSTMS")
|
|
if self.wxOrder(val) < self.wxOrder(value) or value == "":
|
|
value = val
|
|
#print "value", value
|
|
if value == "":
|
|
value = "NONE"
|
|
|
|
#print "Returning ", value
|
|
return value
|
|
|
|
|
|
def dayOrNightVal(self, statDict, timeRange, argList):
|
|
# Return a min or max value based on the timeRange
|
|
# as Day or Night
|
|
# The argList contains the weather element for the
|
|
# daytime value followed by the element for the nighttime
|
|
# value
|
|
# Try to report a trend as well
|
|
|
|
day = self.getPeriod(timeRange,1)
|
|
dayElement = argList[0]
|
|
nightElement = argList[1]
|
|
dayMinMax = argList[2]
|
|
nightMinMax = argList[3]
|
|
trendElement = argList[4]
|
|
priorStatDict = argList[5]
|
|
statList = argList[6]
|
|
timeRangeList = argList[7]
|
|
|
|
if day == self.DAYTIME():
|
|
element = dayElement
|
|
minMax = dayMinMax
|
|
else:
|
|
element = nightElement
|
|
minMax = nightMinMax
|
|
curVal = self.getStats(statDict, element)
|
|
if curVal is not None:
|
|
curVal = self.getValue(curVal, minMax)
|
|
value = self.getScalarVal(curVal)
|
|
else:
|
|
return ""
|
|
|
|
# Try to get trend
|
|
if trendElement is None:
|
|
return value
|
|
|
|
# Get trend 1st or 2nd period
|
|
index = 0
|
|
for i in range(len(timeRangeList)):
|
|
tr, label = timeRangeList[i]
|
|
if timeRange == tr:
|
|
index = i
|
|
break
|
|
if index >= 2:
|
|
return value
|
|
|
|
# Try the trend element first
|
|
stats = self.getStats(statDict, trendElement)
|
|
rawDiff = None
|
|
if stats is not None:
|
|
rawDiff = int(self.getValue(stats))
|
|
else:
|
|
# Get data from prior day
|
|
# Since we want 24 hours prior AND we are only
|
|
# looking at the first 2 periods for trends,
|
|
# we always look at the priorStatDict
|
|
val = None
|
|
val = self.getStats(priorStatDict, element)
|
|
if val is not None:
|
|
rawDiff = int(curVal) - int(self.getValue(val, minMax))
|
|
if rawDiff is None:
|
|
return value
|
|
else:
|
|
if rawDiff > 0:
|
|
sign = "+"
|
|
else:
|
|
sign = ""
|
|
return value + " (" + sign + repr(rawDiff) + ")"
|
|
|
|
####################################################################
|
|
# Weather Codes
|
|
####################################################################
|
|
|
|
def getCode(self, stats, timeRange):
|
|
# Return the weather code
|
|
# Assumes analysis list:
|
|
# "analysisList": [
|
|
# ("MinT", AnalysisMethods.avg),
|
|
# ("MaxT", AnalysisMethods.avg),
|
|
# ("PoP", AnalysisMethods.stdDevMaxAvg),
|
|
# ("Wx", AnalysisMethods.dominantWx),
|
|
# ("Sky", AnalysisMethods.avg),
|
|
# ("Wind", AnalysisMethods.vectorTextAvg)
|
|
# ],
|
|
#
|
|
|
|
# Get the statistics
|
|
popMax = self.getStats(stats, "PoP__stdDevMaxAvg")
|
|
maxT = self.getStats(stats,"MaxT__avg")
|
|
wxKey = self.getStats(stats,"Wx__dominantWx")
|
|
sky = self.getStats(stats,"Sky__avg")
|
|
wind = self.getStats(stats,"Wind__vectorAvg")
|
|
if wind is not None:
|
|
windMag, windDir = wind
|
|
else:
|
|
windMag = None
|
|
if wxKey is not None:
|
|
wxSize = len(wxKey)
|
|
wxStr = ""
|
|
for x in range(wxSize):
|
|
wxStr += str(wxKey[x])
|
|
if x < wxSize - 1:
|
|
wxStr += '^'
|
|
wxKey = wxStr
|
|
else:
|
|
return "?"
|
|
|
|
# the first if code statement satisfied is used
|
|
# the order of the if statements prioritizes the code returned
|
|
# eg. if fzrain code = Y is higher priority than snowrain code=O
|
|
# then move block of code
|
|
# code = self.getCode_Y(popMax, maxT, wxKey, sky, windMag)
|
|
# if code is not None:
|
|
# return code
|
|
##ahead of
|
|
# code = self.getCode_O(popMax, maxT, wxKey, sky, windMag)
|
|
# if code is not None:
|
|
# return code
|
|
|
|
|
|
code = self.getCode_P(popMax, maxT, wxKey, sky, windMag)
|
|
if code is not None:
|
|
return code
|
|
code = self.getCode_T(popMax, maxT, wxKey, sky, windMag)
|
|
if code is not None:
|
|
return code
|
|
code = self.getCode_O(popMax, maxT, wxKey, sky, windMag)
|
|
if code is not None:
|
|
return code
|
|
code = self.getCode_R(popMax, maxT, wxKey, sky, windMag)
|
|
if code is not None:
|
|
return code
|
|
code = self.getCode_S(popMax, maxT, wxKey, sky, windMag)
|
|
if code is not None:
|
|
return code
|
|
code = self.getCode_W(popMax, maxT, wxKey, sky, windMag)
|
|
if code is not None:
|
|
return code
|
|
code = self.getCode_J(popMax, maxT, wxKey, sky, windMag)
|
|
if code is not None:
|
|
return code
|
|
code = self.getCode_L(popMax, maxT, wxKey, sky, windMag)
|
|
if code is not None:
|
|
return code
|
|
code = self.getCode_X(popMax, maxT, wxKey, sky, windMag)
|
|
if code is not None:
|
|
return code
|
|
code = self.getCode_Y(popMax, maxT, wxKey, sky, windMag)
|
|
if code is not None:
|
|
return code
|
|
code = self.getCode_Z(popMax, maxT, wxKey, sky, windMag)
|
|
if code is not None:
|
|
return code
|
|
code = self.getCode_M(popMax, maxT, wxKey, sky, windMag)
|
|
if code is not None:
|
|
return code
|
|
code = self.getCode_Q(popMax, maxT, wxKey, sky, windMag)
|
|
if code is not None:
|
|
return code
|
|
code = self.getCode_N(popMax, maxT, wxKey, sky, windMag)
|
|
if code is not None:
|
|
return code
|
|
code = self.getCode_F(popMax, maxT, wxKey, sky, windMag)
|
|
if code is not None:
|
|
return code
|
|
code = self.getCode_G(popMax, maxT, wxKey, sky, windMag)
|
|
if code is not None:
|
|
return code
|
|
code = self.getCode_I(popMax, maxT, wxKey, sky, windMag)
|
|
if code is not None:
|
|
return code
|
|
code = self.getCode_D(popMax, maxT, wxKey, sky, windMag)
|
|
if code is not None:
|
|
return code
|
|
code = self.getCode_H(popMax, maxT, wxKey, sky, windMag)
|
|
if code is not None:
|
|
return code
|
|
code = self.getCode_K(popMax, maxT, wxKey, sky, windMag)
|
|
if code is not None:
|
|
return code
|
|
code = self.getCode_Sky(popMax, maxT, wxKey, sky, windMag, timeRange)
|
|
if code is not None:
|
|
return code
|
|
code = self.getCode_A(popMax, maxT, wxKey, sky, windMag)
|
|
if code is not None:
|
|
return code
|
|
return None
|
|
|
|
|
|
def getCode_P(self, popMax, maxT, wxKey, sky, windMag):
|
|
if windMag:
|
|
# P -- BLZZRD
|
|
# edit conditions for Blizzard below
|
|
if (WxContains(wxKey, "Wide S + 1/4SM") or \
|
|
WxContains(wxKey, "Wide S + 0SM")) and \
|
|
windMag > 35/1.15: # 35 = wind speed in mph
|
|
return "P"
|
|
return None
|
|
|
|
def getCode_T(self, popMax, maxT, wxKey, sky, windMag):
|
|
#RW and T needed
|
|
#Sct,Num,Wide,Chc,Lkly,Def needed
|
|
|
|
if popMax is not None:
|
|
# T -- TSTRMS
|
|
# ->first block (commented out) requires RW and T Chc or
|
|
# greater be in wx to satisfy. Second block requires
|
|
# only T Chc or greater to satisfy
|
|
# both blocks require pop >= 45 to satisfy
|
|
# if (WxContains(wxKey,"Lkly RW") or WxContains(wxKey,"Wide RW") or \
|
|
# WxContains(wxKey,"Num RW") or WxContains(wxKey,"Sct RW") or \
|
|
# WxContains(wxKey,"Ocnl RW") or \
|
|
# WxContains(wxKey,"Chc RW") or WxContains(wxKey,"Def RW")) and \
|
|
# (WxContains(wxKey,"Lkly T") or WxContains(wxKey,"Wide T") or \
|
|
# WxContains(wxKey,"Num T") or WxContains(wxKey,"Sct T") or \
|
|
# WxContains(wxKey,"Chc T") or WxContains(wxKey,"Def T")) and \
|
|
# popMax >= 45:
|
|
if (WxContains(wxKey,"Lkly T") or WxContains(wxKey,"Wide T") or \
|
|
WxContains(wxKey,"Num T") or WxContains(wxKey,"Sct T") or \
|
|
WxContains(wxKey,"Chc T") or WxContains(wxKey,"Def T") or \
|
|
WxContains(wxKey,"Ocnl T")) and \
|
|
popMax >= 45:
|
|
|
|
|
|
return "T"
|
|
|
|
return None
|
|
|
|
def getCode_O(self, popMax, maxT, wxKey, sky, windMag):
|
|
if popMax is not None:
|
|
# O -- RNSNOW
|
|
if WxContains(wxKey, "* R") and WxContains(wxKey, "* S") \
|
|
and popMax >= 45:
|
|
return "O"
|
|
return None
|
|
|
|
def getCode_R(self, popMax, maxT, wxKey, sky, windMag):
|
|
if popMax is not None:
|
|
# R -- RAIN
|
|
if WxContains(wxKey, "* R") and popMax >= 45:
|
|
return "R"
|
|
return None
|
|
|
|
def getCode_S(self, popMax, maxT, wxKey, sky, windMag):
|
|
if popMax is not None:
|
|
# S -- SNOW
|
|
if WxContains(wxKey, "* S") and popMax >= 45:
|
|
return "S"
|
|
return None
|
|
|
|
def getCode_W(self, popMax, maxT, wxKey, sky, windMag):
|
|
if popMax is not None:
|
|
# W -- SHWRS
|
|
if WxContains(wxKey, "* RW") and popMax >= 45:
|
|
return "W"
|
|
return None
|
|
|
|
def getCode_J(self, popMax, maxT, wxKey, sky, windMag):
|
|
if popMax is not None:
|
|
# J -- SNOWSHWR
|
|
if WxContains(wxKey, "* SW") and popMax >= 45:
|
|
return "J"
|
|
return None
|
|
|
|
def getCode_L(self, popMax, maxT, wxKey, sky, windMag):
|
|
# L -- DRZL
|
|
if WxContains(wxKey, "* L"):
|
|
return "L"
|
|
return None
|
|
|
|
def getCode_X(self, popMax, maxT, wxKey, sky, windMag):
|
|
if popMax is not None:
|
|
# X -- SLEET
|
|
if WxContains(wxKey, "* IP") and popMax >= 45:
|
|
return "X"
|
|
return None
|
|
|
|
def getCode_Y(self, popMax, maxT, wxKey, sky, windMag):
|
|
if popMax is not None:
|
|
# Y -- FZRAIN
|
|
if WxContains(wxKey, "* ZR") and popMax >= 45:
|
|
return "Y"
|
|
return None
|
|
|
|
def getCode_Z(self, popMax, maxT, wxKey, sky, windMag):
|
|
# Z -- FZDRZL
|
|
if WxContains(wxKey, "* ZL"):
|
|
return "Z"
|
|
return None
|
|
|
|
def getCode_M(self, popMax, maxT, wxKey, sky, windMag):
|
|
# M -- FLURRIES
|
|
# SW-- or S-- will return flurries as defined below
|
|
# if you want S-- to be returned as light snow then use
|
|
#if WxContains(wxKey, "* SW --"):
|
|
if WxContains(wxKey, "* SW --") or WxContains(wxKey, "* S --"):
|
|
return "M"
|
|
return None
|
|
|
|
def getCode_Q(self, popMax, maxT, wxKey, sky, windMag):
|
|
# Q -- BLGSNO
|
|
if WxContains(wxKey, "* BS"):
|
|
return "Q"
|
|
return None
|
|
|
|
def getCode_N(self, popMax, maxT, wxKey, sky, windMag):
|
|
# edit windthresh for threshhold for getting N windy code returned
|
|
windthresh = 25.0 # wind threshold in mph
|
|
#print "wind ",windthresh,windMag,"\n"
|
|
if windMag is not None:
|
|
# N -- WINDY
|
|
# the uncommented assumes all your wind speeds in gfe are in knts
|
|
# if you have converted them to mph then use
|
|
#if windMag > windthresh : #
|
|
if windMag > windthresh/1.15: #mph from kts
|
|
return "N"
|
|
return None
|
|
|
|
def getCode_F(self, popMax, maxT, wxKey, sky, windMag):
|
|
# F -- FOGGY
|
|
# if wx contains F+ any coverage return F
|
|
# you may want to change this to include F only if Areas of F
|
|
# are fcst any intensity
|
|
# if WxContains(wxKey, "Areas F *"):
|
|
if WxContains(wxKey, "* F +"):
|
|
return "F"
|
|
return None
|
|
|
|
def getCode_G(self, popMax, maxT, wxKey, sky, windMag):
|
|
if maxT is not None:
|
|
# G -- VRYHOT edit your threshhold in degs F
|
|
threshhold = 105
|
|
if maxT > threshhold:
|
|
return "G"
|
|
return None
|
|
|
|
def getCode_I(self, popMax, maxT, wxKey, sky, windMag):
|
|
if maxT is not None:
|
|
# I -- VRYCOLD edit your threshhold in degs F
|
|
threshhold = 20
|
|
if maxT < threshhold:
|
|
return "I"
|
|
return None
|
|
|
|
def getCode_D(self, popMax, maxT, wxKey, sky, windMag):
|
|
# D -- DUST
|
|
if WxContains(wxKey, "* BD"):
|
|
return "D"
|
|
return None
|
|
|
|
def getCode_H(self, popMax, maxT, wxKey, sky, windMag):
|
|
# H -- HAZE
|
|
if WxContains(wxKey, "* H"):
|
|
return "H"
|
|
return None
|
|
|
|
def getCode_K(self, popMax, maxT, wxKey, sky, windMag):
|
|
# K -- SMOKE
|
|
if WxContains(wxKey, "* K"):
|
|
return "K"
|
|
return None
|
|
|
|
def getCode_Sky(self, popMax, maxT, wxKey, sky, windMag, timeRange):
|
|
# C, E, B, U, V, depending upon sky and time of day
|
|
if sky is not None:
|
|
# C -- CLDY
|
|
if sky > 94:
|
|
return "C"
|
|
|
|
# E -- MCLDY
|
|
elif sky > 69:
|
|
return "E"
|
|
|
|
# B -- PTCLDY
|
|
elif sky > 31:
|
|
return "B"
|
|
|
|
# U -- SUNNY
|
|
else:
|
|
localTimeRange = self.shiftedTimeRange(timeRange)
|
|
dayNight = self.getPeriod(localTimeRange)
|
|
if dayNight == self.DAYTIME():
|
|
return "U"
|
|
else:
|
|
return "V"
|
|
return None
|
|
|
|
def getCode_A(self, popMax, maxT, wxKey, sky, windMag):
|
|
# A -- FAIR
|
|
return "A"
|
|
|
|
def getScalarVal(self, value):
|
|
# Return 4-digit right-justified text representation of value
|
|
|
|
# Check for no data
|
|
if not value:
|
|
return " "
|
|
else:
|
|
# Convert to integer string
|
|
return str(int(value)).rjust(4)
|
|
|
|
def getVectorVal(self, value):
|
|
# Return text representation of vector value
|
|
# Value is a tuple of magnitude and direction
|
|
# E.g. returned value: SW 19
|
|
|
|
# Check for no data
|
|
if value == () or value is None:
|
|
return " "
|
|
else:
|
|
mag = value[0]
|
|
dir = value[1]
|
|
magStr = str(int(mag)).rjust(3)
|
|
if type(dir) is not str:
|
|
dir = self.dirToText(dir)
|
|
dirStr = dir.rjust(2)
|
|
return dirStr + magStr
|
|
|
|
def getWxVal(self, value):
|
|
# Return text representation of value
|
|
return self.wx_phrase({}, {}, value)
|
|
|
|
def wxOrder(self, value):
|
|
value = value.lower()
|
|
if value == "thunderstorms":
|
|
return 0
|
|
elif value == "rain showers":
|
|
return 1
|
|
elif value == "rain":
|
|
return 2
|
|
elif value == "snow":
|
|
return 3
|
|
else:
|
|
return 4
|
|
|
|
def short_weather_phrase(self, element, stats):
|
|
" Develop short phrase for weather in a table"
|
|
# Weather Stats:
|
|
# SubKey List : list of all subkeys mentioned in time period
|
|
|
|
if stats is None:
|
|
return ""
|
|
subkeyList = self.makeSubkeyList(stats)
|
|
if len(subkeyList) == 0:
|
|
return ""
|
|
value = ""
|
|
#print "In short_weather_phrase"
|
|
for subKey in subkeyList:
|
|
val, cov = self.weather_value(None, None, subKey, typeOnly=1)
|
|
#print "subKey", val
|
|
if self.wxOrder(val) < self.wxOrder(value) or value == "":
|
|
value = val
|
|
#print "value", value
|
|
value = value.replace(" ", "|")
|
|
value = value.replace("thunderstorm", "thunder|storm")
|
|
#print "returning", value
|
|
return value
|
|
|
|
def long_weather_phrase(self, element, stats):
|
|
# Stats from SampleAnalysis method: weather_percentages
|
|
words = ""
|
|
index = 0
|
|
prevCoverage = None
|
|
conjunction = "|"
|
|
if stats is None:
|
|
return "None"
|
|
length = len(stats)
|
|
for subkey, percentage in stats:
|
|
#print "subkey, percent", subkey, percentage
|
|
if subkey is None or subkey.wxType() == "<NoWx>":
|
|
index += 1
|
|
continue
|
|
|
|
# If not last one, determine nextCoverage
|
|
if index < length-1:
|
|
nextSubkey, percentage = stats[index+1]
|
|
else:
|
|
nextSubkey = None
|
|
|
|
value, prevCoverage = self.weather_value(
|
|
None, None, subkey, prevCoverage, nextSubkey)
|
|
percentage = int(self.round(percentage,"Nearest", 1))
|
|
words = words + value + " (" + repr(percentage) + "%) "
|
|
#prevSubkey = subkey
|
|
|
|
# if last one, do not add conjunction
|
|
if index == length - 1:
|
|
break
|
|
words = words + conjunction
|
|
index = index + 1
|
|
|
|
if words == "":
|
|
words = "None"
|
|
return words
|
|
|
|
def discrete_value(self, element, stats):
|
|
" Return string of hazards"
|
|
# Weather Stats:
|
|
# SubKey List : list of all subkeys mentioned in time period
|
|
|
|
if stats is None:
|
|
return ""
|
|
subkeyList = self.makeSubkeyList(stats)
|
|
if len(subkeyList) == 0:
|
|
return ""
|
|
|
|
from com.raytheon.uf.viz.core.localization import LocalizationManager
|
|
siteId = LocalizationManager.getInstance().getSite()
|
|
value = ""
|
|
#print "In discrete_value"
|
|
for subKey in subkeyList:
|
|
str = subKey.split(":")[0]
|
|
discreteWords = DiscreteKey.discreteDefinition(siteId).keyDesc(
|
|
"Hazards" + "_SFC", str)
|
|
value = value + discreteWords + " "
|
|
#print "returning", value
|
|
return value
|
|
|
|
def long_discrete_phrase(self, element, stats):
|
|
# Stats from SampleAnalysis method: discrete_percentages
|
|
words = ""
|
|
index = 0
|
|
prevCoverage = None
|
|
conjunction = "|"
|
|
if stats is None:
|
|
return "None"
|
|
length = len(stats)
|
|
from com.raytheon.uf.viz.core.localization import LocalizationManager
|
|
siteId = LocalizationManager.getInstance().getSite()
|
|
for subkey, percentage in stats:
|
|
#print "subkey, percent", subkey, percentage
|
|
if subkey is None or subkey == "<None>":
|
|
index += 1
|
|
continue
|
|
|
|
percentage = int(self.round(percentage,"Nearest", 1))
|
|
str = subkey.split(":")[0]
|
|
discreteWords = DiscreteKey.discreteDefinition(siteId).keyDesc(
|
|
"Hazards" + "_SFC", str)
|
|
words = words + discreteWords + " (" + repr(percentage) + "%) "
|
|
|
|
# if last one, do not add conjunction
|
|
if index == length - 1:
|
|
break
|
|
words = words + conjunction
|
|
index = index + 1
|
|
|
|
if words == "":
|
|
words = "None"
|
|
return words
|
|
|
|
def cloudCover(self, element, stats):
|
|
# Return a text cloud cover given Sky average value
|
|
valStr = ""
|
|
if stats is None:
|
|
return valStr
|
|
|
|
val = self.callMethod(stats, element.conversion())
|
|
value = self.round(val, "Nearest", element.roundVal())
|
|
if value < 20:
|
|
shift = self.determineShift()
|
|
period = element.getPeriod()
|
|
tr = TimeRange.TimeRange(period.startTime() + shift, period.endTime() + shift)
|
|
dayNight = self.getPeriod(tr)
|
|
if dayNight == self.NIGHTTIME():
|
|
valStr = "Clear"
|
|
else:
|
|
valStr = "Sunny"
|
|
elif value < 55:
|
|
valStr = "Partly|Cloudy"
|
|
elif value < 85:
|
|
valStr = "Mostly|Cloudy"
|
|
else:
|
|
valStr = "Cloudy"
|
|
|
|
return valStr
|
|
|
|
def wxPrecipSubkey(self, subkey):
|
|
# List of wxTypes that should be counted as precipitation
|
|
# for the calculation of Wx Duration
|
|
if subkey.wxType() in ["R", "RW", "S", "SW", "ZR", "IP", "SA"]:
|
|
return 1
|
|
else:
|
|
return 0
|
|
|
|
def wxCoveragePercent(self, coverage):
|
|
percents = {
|
|
"Def": 100,
|
|
"Wide": 100,
|
|
"Pds": 90,
|
|
"Frq": 80,
|
|
"Ocnl": 80,
|
|
"Lkly": 75,
|
|
"Num": 60,
|
|
"Inter": 60,
|
|
"Sct": 40,
|
|
"Brf": 30,
|
|
"Chc": 20,
|
|
"SChc": 10,
|
|
"Iso": 10,
|
|
}
|
|
return percents.get(coverage, 0)
|
|
|
|
def wxDuration(self, statsByRange, timeRange):
|
|
# Used in the FWM and FWFTable for weather duration
|
|
# Weather duration is determined as follows:
|
|
# Coverages are weighted according to the
|
|
# values in wxCoveragePercent. For example, "Wide"
|
|
# gets 100% weighting while "Sct" on get 40%.
|
|
# Only precip weather types (according to the wxPrecipSubkey)
|
|
# are counted toward the duration.
|
|
#
|
|
# Create a total duration by adding a contribution from each grid:
|
|
# For each grid in the time range:
|
|
# Among the precip subkeys, find the maximum Coverage percent.
|
|
# Weight it's contibution according to the amount of time
|
|
# it overlaps the given time range and the maximum coverage percent.
|
|
#
|
|
total = 0.0
|
|
for subkeyList, subRange in statsByRange:
|
|
subkeyList = self.makeSubkeyList(subkeyList)
|
|
maxPercent = 0
|
|
for subkey in subkeyList:
|
|
if self.wxPrecipSubkey(subkey):
|
|
percent = self.wxCoveragePercent(subkey.coverage())
|
|
#print subkey.coverage(), percent
|
|
if percent > maxPercent:
|
|
maxPercent = percent
|
|
subRange = timeRange.intersection(subRange)
|
|
value = maxPercent/100.0 * subRange.duration()/3600
|
|
#print value
|
|
total = total + value
|
|
total = self.round(total, "Nearest", 1)
|
|
#print "wxDur", total, timeRange
|
|
return repr(int(total))
|
|
|
|
# Special interface to Multiple Element Table
|
|
def makeMultipleElementTable(self, areaLabel, timeRange, argDict,
|
|
byTimeRange=0, product="MultipleElementTable_Aux_Local"):
|
|
# For each area in the areaLabel group of Combinations,
|
|
# For each city
|
|
# Generate a MultipleElementTable
|
|
comboList = self.getCurrentAreaNames(argDict)
|
|
cityDictModule = importlib.import_module(self._cityDictionary)
|
|
cityDict = cityDictModule.CityDictionary
|
|
table = ""
|
|
|
|
if type(argDict) is not dict:
|
|
# "argDict" is really "tree"
|
|
argDict = argDict.get("argDict")
|
|
argDict["elementList"] = self._elementList
|
|
argDict["singleValueFormat"] = self._singleValueFormat
|
|
argDict["includeTitle"] = 1
|
|
argDict["byTimeRange"] = byTimeRange
|
|
|
|
for area in comboList:
|
|
try:
|
|
cities = cityDict[area]
|
|
except:
|
|
continue
|
|
for city, cityLabel in cities:
|
|
table = table + self.generateProduct(
|
|
product, argDict, city,
|
|
timeRange=timeRange, areaLabel=cityLabel)
|
|
# ensure table has valid data
|
|
if table == "":
|
|
continue
|
|
argDict["includeTitle"] = 0
|
|
table = "\n" + table + "\n\n"
|
|
return table
|
|
|
|
def getMultipleElementTableRanges(self, productIssuance, singleValueFormat,
|
|
timeRange=None):
|
|
if productIssuance in [
|
|
"Morning", "Morning Update", "Afternoon Update",
|
|
"Morning with Pre-1st Period"]:
|
|
self._productIssuance = "Morning"
|
|
startHour = self.DAY()
|
|
numPeriods = 3
|
|
else: # "Afternoon", "Afternoon with Pre-1st Period",
|
|
# "Evening Update", "Early Morning Update"
|
|
self._productIssuance = "Afternoon"
|
|
startHour = self.NIGHT()
|
|
numPeriods = 4
|
|
labelMethod = self.getLocalWeekday
|
|
|
|
#print "\nMET Ranges", productIssuance, timeRange
|
|
if timeRange is None:
|
|
currentLocalTime, self._shift = self.determineTimeShift()
|
|
day = currentLocalTime.day
|
|
month = currentLocalTime.month
|
|
year = currentLocalTime.year
|
|
|
|
# Use getPeriods to set up a list of
|
|
# time periods to be sampled
|
|
# Returns a list of tuples: (timeRange, timeRangeLabel)
|
|
|
|
# Convert to GMT time before making time range
|
|
if productIssuance == "Early Morning Update":
|
|
self._shift = self._shift + 24*3600
|
|
labelMethod = self.getLocalWeekdayName
|
|
|
|
startTime = AbsTime.absTimeYMD(year,month,day,startHour)
|
|
startTime = startTime - self._shift
|
|
timeRange = TimeRange.TimeRange(startTime, startTime + 12*3600)
|
|
|
|
if singleValueFormat == 1:
|
|
numPeriods = 1
|
|
timeRangeList = self.getPeriods(timeRange, 12, 12, numPeriods,
|
|
labelMethod=labelMethod)
|
|
|
|
# Adjust the first time range if an update issuance
|
|
if productIssuance not in ["Morning", "Afternoon"]:
|
|
updateTime = AbsTime.absTimeYMD(year, month, day, currentLocalTime.hour)
|
|
updateTime = updateTime - self._shift
|
|
tr, label = timeRangeList[0]
|
|
updateTR = TimeRange.TimeRange(updateTime, tr.endTime())
|
|
timeRangeList[0] = (updateTR, labelMethod(updateTR))
|
|
|
|
#print "Returning", timeRangeList
|
|
return timeRangeList
|