8049 lines
301 KiB
Python
8049 lines
301 KiB
Python
# ----------------------------------------------------------------------------
|
|
# 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.
|
|
#
|
|
# BOIVerify - version 2.0.5
|
|
#
|
|
# Main tool to calculate and display verification information. The main
|
|
# dialog contains tabs to display:
|
|
# (1) the archived grids (or errors)
|
|
# (2) grids of stats calculated for every gridpoint
|
|
# (3) histograms of the errors over the current edit area
|
|
# (4) line graphs of stats over pre-defined edit areas
|
|
# (5) graphs of stats vs. scale
|
|
#
|
|
# The BOIVerifySave tool saves the grid into the verification database
|
|
# The BOIVerifyAutoCalc tool calculates and saves the stats for pre-defined
|
|
# editareas.
|
|
#
|
|
# Author: Tim Barker - SOO BOI
|
|
# 2005/07/01 - Original Implmentation
|
|
# 2005/07/29 - version 0.1 - update to grid database structure
|
|
# 2006/11/06 - version 1.0 - First version with time-series graphs. Still
|
|
# lots of bugs and not what I would like for a version 1.0 but
|
|
# I've put it off way too long anyway.
|
|
# 2007/10/25 - version 2.0
|
|
# . moved into a procedure rather than a tool
|
|
# . fixed problem with precision="0" for sites that do
|
|
# not have WG1 defined
|
|
# . fixed 'flashing' of user interface on startup
|
|
# . fixed so that clicking on 'stop' during long drawing
|
|
# of many stat grids will stop more quickly.
|
|
# . allowed program name in error messages to be something
|
|
# other than BOIVerify (could be GridVerify, etc.)
|
|
# . use labels of 'histogram' and 'scatterplot' rather than
|
|
# errors and values
|
|
# . use date label of 'ending on' rather than 'before'
|
|
# . added limits to forecast hours shown
|
|
# . added support for probabilistic parms
|
|
# . added support for handling common cases
|
|
# 2008/05/28 - version 2.0.5
|
|
# . fixed problem with forced large range of line graphs
|
|
# for QPF bias, etc. In old code if graph range was less
|
|
# than 0.01, it was automatically forced to be 1.0. Now,
|
|
# it only forcibly expands the graph range when the actual
|
|
# range is less than 1/10 the precision of the parm, and
|
|
# even then it only expands the graph range upward by 2
|
|
# times the parm precision.
|
|
#
|
|
#
|
|
# 2010/04/23 ryu Initial port to AWIPS II. Fixed bugs with the "Stat vs. Scale" tab.
|
|
#
|
|
#
|
|
# SOFTWARE HISTORY
|
|
# Date Ticket# Engineer Description
|
|
# ------------ ---------- ----------- --------------------------
|
|
# 12/02/2014 RM #625 ryu Changed checkGroup() as suggested to display models
|
|
# in multi-columns when a single column is insufficient.
|
|
# 04/16/2015 17390 ryu Replacing string.atoi with int for string/integer to integer conversion
|
|
# (ListBox.curselection() now returns ints instead of strings.)
|
|
# ----------------------------------------------------------------------------
|
|
#
|
|
MenuItems = ["Verify"]
|
|
|
|
from numpy import *
|
|
from math import *
|
|
import Tkinter
|
|
import TkDefaults
|
|
import AppDialog
|
|
import time,calendar,sys,re,traceback,string
|
|
import SmartScript
|
|
import BOIVerifyUtility
|
|
import os # for debug
|
|
|
|
from com.raytheon.viz.gfe import GFEPreference
|
|
from java.lang import Float
|
|
|
|
PROGNAME="BOIVerify" # you can change it if you dont like BOI. Shame on you!
|
|
COLORLIST=["blue","green","red","cyan","yellow","purple","orange",
|
|
"Gold","Coral","DarkOliveGreen","DarkOrchid","Brown","DeepPink",
|
|
"DodgerBlue","DarkSeaGreen"]
|
|
HOURSECS=60*60
|
|
DAYSECS=24*HOURSECS
|
|
|
|
|
|
def getFloatPref(key, dflt):
|
|
if not GFEPreference.contains(key):
|
|
print "no config value for", key
|
|
return dflt
|
|
|
|
val = GFEPreference.getFloatPreference(key)
|
|
print "preference for %s:" % key, val
|
|
return val
|
|
|
|
def setFloatPref(key, value):
|
|
if value is not None:
|
|
value = Float.parseFloat(str(value))
|
|
GFEPreference.setPreference(key, value)
|
|
|
|
|
|
class Procedure (SmartScript.SmartScript):
|
|
def __init__(self, dbss):
|
|
print "Start of %s - virtual memory:%d resident: %d"%(PROGNAME,memory(),resident())
|
|
self._dbss=dbss
|
|
self.userName=self._dbss.getWsId().getUserName()
|
|
SmartScript.SmartScript.__init__(self, dbss)
|
|
self.statusBarMsg("Starting %s"%PROGNAME,"R")
|
|
self.PROGNAME=PROGNAME
|
|
self.HOURSECS=60*60
|
|
self.DAYSECS=24*self.HOURSECS
|
|
self.COLORLIST=COLORLIST
|
|
self.__colorMapParams = {}
|
|
return
|
|
|
|
def execute(self):
|
|
|
|
print "starting execute routine with memory:%d resident: %d"%(memory(),resident())
|
|
#
|
|
# See if a child window of GFE is named "%self.PROGNAME Options"
|
|
# If so...then program is already running and just make
|
|
# a dialog box telling them that...
|
|
#
|
|
#if alreadyRunning:
|
|
# self.statusBarMsg("%s is already running"%self.PROGNAME,"U")
|
|
# return
|
|
#
|
|
|
|
try:
|
|
self.__setup()
|
|
|
|
self.root.withdraw()
|
|
self.root.mainloop()
|
|
#except:
|
|
# traceback.print_exc()
|
|
finally:
|
|
try:
|
|
self.root.destroy()
|
|
except:
|
|
pass
|
|
print "Exiting..."
|
|
return
|
|
|
|
def __setup(self):
|
|
tk = Tkinter.Tk()
|
|
self.root = tk
|
|
sw = tk.winfo_screenwidth()
|
|
sh = tk.winfo_screenheight()
|
|
tk.geometry("%dx%d+0+0" % (sw,sh))
|
|
|
|
TkDefaults.setDefaults(tk)
|
|
#
|
|
# Splash screen...
|
|
#
|
|
splash=Tkinter.Toplevel(None)
|
|
splash.overrideredirect(1)
|
|
f=Tkinter.Frame(splash,relief=Tkinter.RIDGE,borderwidth=2,
|
|
background="yellow")
|
|
txtwid=max(len(self.PROGNAME),10)
|
|
text="Starting up\n%s"%self.PROGNAME
|
|
lab=Tkinter.Label(f,justify=Tkinter.CENTER,text=text,
|
|
fg="black",bg="yellow",width=txtwid+2)
|
|
lab.pack(side=Tkinter.TOP)
|
|
wTxt=Tkinter.StringVar(f)
|
|
lab=Tkinter.Label(f,justify=Tkinter.CENTER,textvariable=wTxt,
|
|
fg="black",bg="yellow")
|
|
lab.pack(side=Tkinter.TOP)
|
|
f.pack(side=Tkinter.TOP,ipadx=50,ipady=10)
|
|
wTxt.set(". ")
|
|
splash.update_idletasks()
|
|
ww=splash.winfo_reqwidth()
|
|
wh=splash.winfo_reqheight()
|
|
sw=splash.winfo_screenwidth()
|
|
sh=splash.winfo_screenheight()
|
|
newgeom="%dx%d+%d+%d"%(ww,wh,int(float(sw-ww)/2.0),int(float(sh-wh)/2.0))
|
|
splash.geometry(newgeom)
|
|
splash.wait_visibility()
|
|
splash.update_idletasks()
|
|
#
|
|
# Start up the utility
|
|
#
|
|
try:
|
|
#
|
|
# Start up the utility
|
|
#
|
|
self.VU=BOIVerifyUtility.BOIVerifyUtility(self._dbss, None)
|
|
print "after setting up VU: memory:%d resident:%d"%(memory(),resident())
|
|
self.setToolType("numeric")
|
|
|
|
#
|
|
# Setup scaleList. This contains tuples of (numpts,label) where
|
|
# numpts is the +/- points to average over, and label is a label
|
|
# description of that area.
|
|
#
|
|
self.scaleList=[]
|
|
spacing=self.VU.getGridSpacing()
|
|
nominalSpacing=self.VU.getCFG('NOMINALSPACING')
|
|
rspacing=int((float(spacing)/float(nominalSpacing))+0.5)*nominalSpacing
|
|
maxk=max(self.getGridShape())
|
|
for k in xrange(maxk):
|
|
curTxt=wTxt.get()
|
|
last=curTxt[-1]
|
|
rest=curTxt[:-1]
|
|
newTxt=last+rest
|
|
wTxt.set(newTxt)
|
|
splash.update_idletasks()
|
|
if k>0:
|
|
scale=k*2.0*rspacing
|
|
else:
|
|
scale=rspacing
|
|
iscale=int(scale+0.5)
|
|
if ((scale>50)and(scale<100)and(iscale%10!=0)):
|
|
continue
|
|
if ((scale>=100)and(scale<200)and(iscale%25!=0)):
|
|
continue
|
|
if ((scale>=200)and(scale<500)and(iscale%50!=0)):
|
|
continue
|
|
if ((scale>=500)and(iscale%100!=0)):
|
|
continue
|
|
rdig=0
|
|
for digits in xrange(2):
|
|
mult=10**digits
|
|
iscale=int(scale*mult)
|
|
rscale=int((scale*mult)+0.5)
|
|
if iscale==rscale:
|
|
rdig=digits
|
|
break
|
|
rscale=round(scale,rdig)
|
|
|
|
if rdig==0:
|
|
lab="%d-km"%int(rscale)
|
|
else:
|
|
fmt="%%.%df-km"%rdig
|
|
lab=fmt%rscale
|
|
self.scaleList.append((k,lab))
|
|
#
|
|
# Setup the self.pts with number of points in named edit areas
|
|
#
|
|
self.VU.logMsg("Starting points generation memory:%d resident:%d"%(memory(),resident()))
|
|
maxareas=self.VU.CFG['STATAREAS']
|
|
editAreaNames=self.VU.listEditAreas()
|
|
self.pts=ones(maxareas,)
|
|
for i in xrange(len(editAreaNames)):
|
|
#
|
|
curTxt=wTxt.get()
|
|
last=curTxt[-1]
|
|
rest=curTxt[:-1]
|
|
newTxt=last+rest
|
|
wTxt.set(newTxt)
|
|
splash.update_idletasks()
|
|
#
|
|
areaname=editAreaNames[i]
|
|
self.VU.logMsg(" %3d memory:%d resident:%d"%(i,memory(),resident()))
|
|
npts=self.getPts(areaname)
|
|
self.VU.logMsg(" %3d after npts memory:%d resident:%d"%(i,memory(),resident()))
|
|
j=self.VU.getEditAreaNumberFromName(areaname)
|
|
self.pts[j]=npts
|
|
self.VU.logMsg(" %3d after pts memory:%d resident:%d"%(i,memory(),resident()))
|
|
self.VU.logMsg(" %3d after del ea memory:%d resident:%d"%(i,memory(),resident()))
|
|
if self.pts[j]<1:
|
|
self.pts[j]=1
|
|
self.VU.logMsg(" after edit area %3d memory:%d resident:%d"%(i,memory(),resident()))
|
|
except:
|
|
splash.destroy()
|
|
self.statusBarMsg("%s could not start up"%self.PROGNAME,"R")
|
|
raise Exception
|
|
#
|
|
# Create all the potential dialogs
|
|
#
|
|
#
|
|
self.VU.logMsg("Starting dialog generation memory:%d resident:%d"%(memory(),resident()))
|
|
try:
|
|
self.mini=MiniDiag(tk,callbackMethod=self.expandMini,
|
|
title="Change",buttonText="%s Options"%self.PROGNAME,loc="lr")
|
|
except:
|
|
splash.destroy()
|
|
self.statusBarMsg("%s could not start up"%self.PROGNAME,"R")
|
|
raise Exception
|
|
#
|
|
try:
|
|
self.cases=Cases(tk,callbackMethod=self.closeCases)
|
|
except:
|
|
splash.destroy()
|
|
self.statusBarMsg("%s could not start up"%self.PROGNAME,"R")
|
|
raise Exception
|
|
#
|
|
try:
|
|
self.miniCases=MiniDiag(tk, callbackMethod=self.expandCases,
|
|
title="Display",buttonText="Number of Cases",loc="ur")
|
|
except:
|
|
splash.destroy()
|
|
self.statusBarMsg("%s could not start up"%self.PROGNAME,"R")
|
|
raise Exception
|
|
#
|
|
try:
|
|
self.cd=CanvasDisplay(tk, title="Canvas",callbackMethod=self.closeCD)
|
|
except:
|
|
splash.destroy()
|
|
self.statusBarMsg("%s could not start up"%self.PROGNAME,"R")
|
|
raise Exception
|
|
#
|
|
try:
|
|
self.dialog=Verif(self.VU,self.userName,self.scaleList, tk, callbackMethod=self.doVerif)
|
|
except:
|
|
splash.destroy()
|
|
self.statusBarMsg("%s could not start up"%self.PROGNAME,"R")
|
|
raise Exception
|
|
#
|
|
# This one last...so it is always on top during 'working' periods
|
|
#
|
|
try:
|
|
self.working=Working(self.dialog,callbackMethod=self.tryToStop)
|
|
except:
|
|
splash.destroy()
|
|
self.statusBarMsg("%s could not start up"%self.PROGNAME,"R")
|
|
raise Exception
|
|
#
|
|
# Destroy the 'starting' message box
|
|
#
|
|
splash.destroy()
|
|
del wTxt
|
|
self.statusBarMsg("%s is now running - memory:%d resident:%d"%(self.PROGNAME,memory(),resident()),"R")
|
|
|
|
def getPts(self,areaname):
|
|
if areaname=="NONE":
|
|
ea=self.newGrid(True, bool)
|
|
else:
|
|
ea=self.encodeEditArea(areaname)
|
|
eb=ravel(ea)
|
|
num=add.reduce(eb)
|
|
pts=int(num)
|
|
del ea
|
|
del eb
|
|
del num
|
|
return pts
|
|
#==================================================================
|
|
#
|
|
# Routines for the 'working' dialog.
|
|
#
|
|
# When the stop button is pressed - tryToStop is called and the stop
|
|
# variable is set to 1.
|
|
# startWorking sets the stop variable to 0 and sets the label, then
|
|
# reveals the working dialog and withdraws the main dialog (unless
|
|
# overridden)
|
|
# setWorking just updates labels in the working dialog
|
|
# checkWorking updates the dialog, and returns the stop variable status
|
|
# stopWorking withdraws the working dialog and raises the main dialog
|
|
# finishWorking withdraws the working dialog and raises the mini dialog
|
|
#
|
|
def tryToStop(self):
|
|
self.working.stop.set(1)
|
|
print "tryToStop was called - should stop soon"
|
|
return
|
|
def startWorking(self,textString,optionRemove=1):
|
|
if optionRemove==1:
|
|
self.dialog.withdraw()
|
|
self.setWorking(textString)
|
|
self.working.stop.set(0)
|
|
self.working.deiconify()
|
|
self.working.lift()
|
|
return
|
|
def setWorking(self,textString):
|
|
self.working.label.set(textString)
|
|
self.working.update()
|
|
return
|
|
def checkWorking(self):
|
|
self.working.update()
|
|
return self.working.stop.get()
|
|
def setAndCheckWorking(self,textString):
|
|
self.working.label.set(textString)
|
|
self.working.update()
|
|
return self.working.stop.get()
|
|
def stopWorking(self):
|
|
self.working.withdraw()
|
|
self.dialog.deiconify()
|
|
self.dialog.lift()
|
|
self.dialog.update_idletasks()
|
|
return
|
|
def finishWorking(self):
|
|
self.working.withdraw()
|
|
self.working.stop.set(0)
|
|
self.mini.deiconify()
|
|
self.mini.lift()
|
|
self.mini.update_idletasks()
|
|
return
|
|
#==================================================================
|
|
#
|
|
# Routines for the 'mini' dialog.
|
|
#
|
|
# When the button is pressed (or X clicked) - expandMini is called
|
|
#
|
|
def expandMini(self):
|
|
self.mini.withdraw()
|
|
self.dialog.deiconify()
|
|
self.dialog.lift()
|
|
return
|
|
#
|
|
# Hide the main dialog, and reveal the mini-dialog.
|
|
#
|
|
def hideDialog(self):
|
|
self.dialog.withdraw()
|
|
self.mini.deiconify()
|
|
self.mini.lift()
|
|
self.mini.wait_visibility()
|
|
self.mini.update_idletasks()
|
|
return
|
|
#==================================================================
|
|
#
|
|
# Routines for the cases dialog, and it's mini dialog.
|
|
#
|
|
def expandCases(self):
|
|
geo1=self.miniCases.geometry()
|
|
(wh,of)=geo1.split("+",1)
|
|
(wid1,hgt1)=wh.split("x",1)
|
|
(ofx1,ofy1)=of.split("+",1)
|
|
self.miniCases.withdraw()
|
|
self.cases.deiconify()
|
|
self.cases.lift()
|
|
geo2=self.cases.geometry()
|
|
(wh,of)=geo2.split("+",1)
|
|
(wid2,hgt2)=wh.split("x",1)
|
|
(ofx2,ofy2)=of.split("+",1)
|
|
newgeo="%s+%d+%d"%(wh,int(ofx1)+int(wid1)-int(wid2),int(ofy1))
|
|
self.cases.geometry(newgeo)
|
|
return
|
|
def closeCases(self):
|
|
self.cases.withdraw()
|
|
self.cases.update_idletasks()
|
|
self.miniCases.deiconify()
|
|
self.miniCases.lift()
|
|
self.miniCases.update_idletasks()
|
|
return
|
|
#==================================================================
|
|
#
|
|
# Routines for the 'canvas' dialog
|
|
#
|
|
def closeCD(self):
|
|
self.cd.withdraw()
|
|
return
|
|
#==================================================================
|
|
# doVerif -
|
|
# This is the routine that really does the verification calculations
|
|
# It is called when the user clicks on "Run" "Hide" or "Cancel"
|
|
# in the verification dialog. The routine is called with the button
|
|
# type of "Run" (do NOT dismiss dialog), "OK" (DISMISS dialog when done)
|
|
# or "Quit". The actual removal of the dialog is handled by the
|
|
# dialog routines themselves...so all you have to do is return
|
|
# right away if the user hit cancel, or do the calculations if they
|
|
# hit anything else.
|
|
#
|
|
def doVerif(self,buttonType):
|
|
if buttonType=="Quit":
|
|
self.root.quit()
|
|
del self.pts
|
|
self.statusBarMsg("%s is finished with memory:%d resident:%s"%(self.PROGNAME,memory(),resident()),"R")
|
|
return
|
|
if buttonType=="Hide":
|
|
self.hideDialog()
|
|
return
|
|
#
|
|
# When doing calculations - make sure the cases windows
|
|
# are closed
|
|
#
|
|
self.cases.withdraw()
|
|
self.miniCases.withdraw()
|
|
#
|
|
# Do calculations
|
|
#
|
|
try:
|
|
dict=self.dialog.getValues()
|
|
tab=dict["tab"]
|
|
if tab=="Grid Displays":
|
|
self.ShowGrids(dict)
|
|
if tab=="Grid Stats":
|
|
self.ShowGridsStats(dict)
|
|
if tab=="Distributions":
|
|
self.ShowDists(dict)
|
|
if tab=="Point/Area Stats":
|
|
self.ShowStats(dict)
|
|
if tab=="Stat vs. Scale":
|
|
self.ShowScaleStats(dict)
|
|
#
|
|
# If something goes wrong during calculations - close everything
|
|
# and raise the exception
|
|
#
|
|
except:
|
|
(exctype,excvalue,trace)=sys.exc_info()
|
|
traceStrings=traceback.format_exception(exctype,excvalue,trace)
|
|
fullstring=string.join(traceStrings)
|
|
self.statusBarMsg("Error in %s:\n%s"%(self.PROGNAME,fullstring),"S")
|
|
self.root.quit()
|
|
return
|
|
#==================================================================
|
|
# showGrids - read and display the archived forecast/observed grids
|
|
#
|
|
def ShowGrids(self,DialogDict):
|
|
self.VU.logMsg("running ShowGrids:",0)
|
|
parmList=DialogDict["Parm"]
|
|
display=DialogDict["Display"]
|
|
groupBy=DialogDict["Group"]
|
|
cycleList=DialogDict["cycleList"]
|
|
modelList=DialogDict["Model"]
|
|
obsmodel=DialogDict["ObsModel"]
|
|
fcstrList=DialogDict["fcstrList"]
|
|
fhrStart=DialogDict["fhrStart"]
|
|
fhrEnd=DialogDict["fhrEnd"]
|
|
dateType=DialogDict["dateType"]
|
|
numDays=DialogDict["numDays"]
|
|
fromDay=DialogDict["fromDay"]
|
|
dayList=DialogDict["dayList"]
|
|
dateStyle=DialogDict["dateStyle"]
|
|
scale=DialogDict["scale"]
|
|
commonCases=DialogDict["commonCases"]
|
|
accumHours=DialogDict["accumHours"]
|
|
accumFreq=DialogDict["accumFreq"]
|
|
#
|
|
# Check for good GUI input
|
|
#
|
|
ret=self.checkLists(modelList,parmList,cycleList,fcstrList,dateType,
|
|
dayList)
|
|
if ret==0:
|
|
return
|
|
self.startWorking("Working on Grid Display")
|
|
#
|
|
#
|
|
#
|
|
numdisplayed=0
|
|
ret=self.setupGM(parmList,modelList)
|
|
if ret==1:
|
|
self.stopWorking()
|
|
return
|
|
errpat=re.compile("^(.*?)(spd|dir)Err")
|
|
totaliters=len(parmList)*len(modelList)
|
|
iter=0
|
|
self.VU.logMsg("going into parmList loop",10)
|
|
for parm in parmList:
|
|
self.VU.logMsg("in ShowGrids working on %s"%parm,5)
|
|
datatype=self.VU.getVerParmType(parm)
|
|
verType=self.VU.getVerType(parm)
|
|
errColor=self.VU.getVerErrColor(parm)
|
|
(parmUnits,parmPrecision,parmMinval,parmMaxval,parmRateFlag,parmColorTable,
|
|
parmDisplayMinval,parmDisplayMaxval)=self.getParmInfo(self.mutableID(),parm)
|
|
logkey="%s_LogFactor"%parm
|
|
logfactor = getFloatPref(logkey, -1)
|
|
#
|
|
# get info on what that verifies the current parm
|
|
#
|
|
obsParm=self.VU.getObsParm(parm)
|
|
(obsParmUnits,obsParmPrecision,obsParmMinval,obsParmMaxval,obsParmRateFlag,obsParmColorTable,
|
|
obsParmDisplayMinval,obsParmDisplayMaxval)=self.getParmInfo(obsmodel,obsParm)
|
|
logkey="%s_LogFactor"%obsParm
|
|
obslogfactor = getFloatPref(logkey, -1)
|
|
|
|
obsGridMode=self.getReadMode(obsmodel,obsParm,0)
|
|
#
|
|
# Get case times/records for all models - but don't require
|
|
# observations...since we might want to display just the
|
|
# forecasts in the future without a verifying observation
|
|
#
|
|
caseInfo=self.VU.getCases(parm,modelList,obsParm,obsmodel,
|
|
dateStyle,dateType,fromDay=fromDay,
|
|
numDays=numDays,dayList=dayList,
|
|
fcstrs=fcstrList,cycles=cycleList,
|
|
fhrStart=fhrStart,fhrEnd=fhrEnd,
|
|
accumHours=accumHours,accumFreq=accumFreq,
|
|
requireObs=0,commonCases=commonCases,
|
|
basetimeOffsets=1,
|
|
callbackMethod=self.workingCommon)
|
|
if self.checkWorking()==1:
|
|
self.stopWorking()
|
|
return
|
|
#
|
|
#
|
|
#
|
|
parmnames=[]
|
|
obsnames=[]
|
|
for model in modelList:
|
|
iter+=1
|
|
if (totaliters>1):
|
|
firstString="Getting (%d of %d) %s %s grids"%(iter,totaliters,model,parm)
|
|
else:
|
|
firstString="Getting %s %s grids"%(model,parm)
|
|
self.setWorking(firstString)
|
|
|
|
tomorrow=time.time()+self.DAYSECS
|
|
if self.setAndCheckWorking("%s:"%firstString)==1:
|
|
self.stopWorking()
|
|
return
|
|
count=0
|
|
okeys=[]
|
|
fcstGridMode=self.getReadMode(model,parm)
|
|
cases=caseInfo[model]
|
|
casekeys=cases.keys()
|
|
casekeys.sort()
|
|
totalcount=len(casekeys)
|
|
self.VU.logMsg("%d cases for %s"%(len(casekeys),model),1)
|
|
for key in casekeys:
|
|
count+=1
|
|
if self.setAndCheckWorking("%s: %d of %d"%(firstString,count,totalcount))==1:
|
|
self.stopWorking()
|
|
return
|
|
(basetimestr,stimestr,etimestr)=key.split(",")
|
|
basetime=int(basetimestr)
|
|
stime=int(stimestr)
|
|
etime=int(etimestr)
|
|
(frecList,orecList)=cases[key]
|
|
tr=self.VU.getVerTimeRange(stime, etime)
|
|
#
|
|
# If there is an observed grid - read it and display it
|
|
#
|
|
obsavailable=0
|
|
if len(orecList)>0:
|
|
okey="%s,%s"%(stimestr,etimestr)
|
|
obsname="%s0bs"%(parm) # zero instead of O so that is sorts before others
|
|
if accumFreq<accumHours:
|
|
shr=(stime%86400)/3600
|
|
obsname+="For%2.2d"%shr
|
|
obsdata=self.VU.getVerGrids(obsmodel,basetime,obsParm,
|
|
stime,etime,mode=obsGridMode,
|
|
recList=orecList)
|
|
if obsdata is not None:
|
|
obsavailable=1
|
|
if datatype!=1:
|
|
if scale>0:
|
|
obsdata=self.VU.smoothpm(obsdata,scale)
|
|
if okey not in okeys:
|
|
obsclip=clip(obsdata,parmMinval,parmMaxval)
|
|
self.createGrid("Ver",obsname,"SCALAR",obsclip,tr,"Observed",
|
|
None,obsParmPrecision,obsParmMinval,obsParmMaxval,
|
|
obsParmUnits)
|
|
okeys.append(okey)
|
|
numdisplayed+=1
|
|
else:
|
|
(obsmag,obsdirec)=obsdata
|
|
if scale>0:
|
|
(u,v)=self.MagDirToUV(obsmag,obsdirec)
|
|
u=self.VU.smoothpm(u,scale)
|
|
v=self.VU.smoothpm(v,scale)
|
|
(obsmag,obsdirec)=self.UVToMagDir(u,v)
|
|
obsdata=(obsmag,obsdirec)
|
|
if okey not in okeys:
|
|
obsmag=clip(obsmag,parmMinval,parmMaxval)
|
|
obsdirec=clip(obsdirec,0.0,360.0)
|
|
obsclip=(obsmag,obsdirec)
|
|
self.createGrid("Ver",obsname,"VECTOR",obsclip,tr,"Observed",
|
|
None,obsParmPrecision,obsParmMinval,obsParmMaxval,
|
|
obsParmUnits)
|
|
okeys.append(okey)
|
|
numdisplayed+=1
|
|
if obsname not in obsnames:
|
|
obsnames.append(obsname)
|
|
#
|
|
# Make forecast grid
|
|
#
|
|
fcstdata=self.VU.getVerGrids(model,basetime,parm,
|
|
stime,etime,mode=fcstGridMode,
|
|
recList=frecList)
|
|
if fcstdata is not None:
|
|
if datatype!=1:
|
|
if scale>0:
|
|
fcstdata=self.VU.smoothpm(fcstdata,scale)
|
|
else:
|
|
(fcstmag,fcstdirec)=fcstdata
|
|
if scale>0:
|
|
(u,v)=self.MagDirToUV(fcstmag,fcstdirec)
|
|
u=self.VU.smoothpm(u,scale)
|
|
v=self.VU.smoothpm(v,scale)
|
|
(fcstmag,fcstdirec)=self.UVToMagDir(u,v)
|
|
fcstdata=(fcstmag,fcstdirec)
|
|
#
|
|
# part of name based on grouping method - and model name
|
|
#
|
|
if groupBy=="Run Time":
|
|
basetuple=time.gmtime(basetime)
|
|
runTime="%4.4d%2.2d%2.2d%2.2d"%(basetuple[0],basetuple[1],basetuple[2],basetuple[3])
|
|
runHours=self.VU.getFcstHour(basetime,tomorrow)
|
|
run=(runHours/6)+1
|
|
runname="run%3.3dfrom%s"%(run,runTime[6:10])
|
|
else:
|
|
fhr=self.VU.getFcstHour(basetime,stime)
|
|
if fhr<0:
|
|
self.VU.logMsg("%d-hour forecasts not shown"%fhr,1)
|
|
continue
|
|
runname="f%3.3dHr"%(fhr)
|
|
if model!="Official":
|
|
runname+=model
|
|
#
|
|
# calculate errors (if needed) and clip to twice the 'bigerr' range
|
|
#
|
|
if display=="Errors":
|
|
if obsavailable==1:
|
|
ep=max(self.errPrecision,parmPrecision)
|
|
if datatype!=1:
|
|
parmname=parm+"Err"+runname
|
|
if accumFreq<accumHours:
|
|
shr=(stime%86400)/3600
|
|
parmname+="For%2.2d"%shr
|
|
bigerr=self.VU.getVerBigErr(parm)
|
|
clipval=bigerr*2.0
|
|
#
|
|
# use obsgrid instead of obsdata...and for
|
|
# probability forecasts - calculate the verifying
|
|
# obsgrid grid from the condition
|
|
#
|
|
obsgrid=obsdata
|
|
if verType==1:
|
|
obsgrid=self.getProbVerGrid(parm,obsdata)*100
|
|
newgrid=fcstdata-obsgrid
|
|
newgrid=clip(newgrid,-clipval,clipval)
|
|
self.createGrid("Ver",parmname,"SCALAR",newgrid,tr,
|
|
"Forecast",None,ep,-clipval,
|
|
clipval,self.errUnits)
|
|
numdisplayed+=1
|
|
if parmname not in parmnames:
|
|
parmnames.append(parmname)
|
|
else:
|
|
(fcstmag,fcstdir)=fcstdata
|
|
(bigerrmag,bigerrdir)=self.VU.getVerBigErr(parm)
|
|
(obsmag,obsdir)=obsdata
|
|
#print "bigerrmag:%d bigerrdir=%d"%(bigerrmag,bigerrdir)
|
|
errmag=fcstmag-obsmag
|
|
errdir=fcstdir-obsdir
|
|
errdir=where(greater(errdir,180.0),360.0-errdir,errdir)
|
|
errdir=where(less(errdir,-180.0),-(360.0+errdir),errdir)
|
|
errmag=clip(errmag,-(2*bigerrmag),(2*bigerrmag))
|
|
parmname=parm+"spdErr"+runname
|
|
if accumFreq<accumHours:
|
|
shr=(stime%86400)/3600
|
|
parmname+="For%2.2d"%shr
|
|
self.createGrid("Ver",parmname,"SCALAR",errmag,tr,"Forecast",
|
|
None,ep,-(2*bigerrmag),(2*bigerrmag),
|
|
self.errUnits)
|
|
numdisplayed+=1
|
|
#
|
|
# direction Error
|
|
#
|
|
if parmname not in parmnames:
|
|
parmnames.append(parmname)
|
|
parmname=parm+"dirErr"+runname
|
|
if accumFreq<accumHours:
|
|
shr=(stime%86400)/3600
|
|
parmname+="For%2.2d"%shr
|
|
self.createGrid("Ver",parmname,"SCALAR",errdir,tr,"Forecast",
|
|
None,self.errPrecision,-180,180,
|
|
self.errUnits)
|
|
numdisplayed+=1
|
|
if parmname not in parmnames:
|
|
parmnames.append(parmname)
|
|
#
|
|
# Vector Error
|
|
#
|
|
(fu,fv)=self.MagDirToUV(fcstmag,fcstdir)
|
|
(ou,ov)=self.MagDirToUV(obsmag,obsdir)
|
|
eu=fu-ou
|
|
ev=fv-ov
|
|
(errmag,errdir)=self.UVToMagDir(eu,ev)
|
|
vecerr=(errmag,errdir)
|
|
parmname=parm+"Err"+runname
|
|
if accumFreq<accumHours:
|
|
shr=(stime%86400)/3600
|
|
parmname+="For%2.2d"%shr
|
|
self.createGrid("Ver",parmname,"VECTOR",vecerr,tr,"Forecast",
|
|
None,parmPrecision,parmMinval,parmMaxval,
|
|
parmUnits)
|
|
numdisplayed+=1
|
|
if parmname not in parmnames:
|
|
parmnames.append(parmname)
|
|
else:
|
|
parmname=parm+runname
|
|
if accumFreq<accumHours:
|
|
shr=(stime%86400)/3600
|
|
parmname+="For%2.2d"%shr
|
|
if datatype!=1:
|
|
newgrid=clip(fcstdata,parmMinval,parmMaxval)
|
|
self.createGrid("Ver",parmname,"SCALAR",newgrid,tr,
|
|
"Forecast",None,parmPrecision,parmMinval,
|
|
parmMaxval,parmUnits)
|
|
numdisplayed+=1
|
|
else:
|
|
(fcstmag,fcstdir)=fcstdata
|
|
fcstmag=clip(fcstmag,parmMinval,parmMaxval)
|
|
self.createGrid("Ver",parmname,"VECTOR",(fcstmag,fcstdir),tr,
|
|
"Forecast",None,parmPrecision,parmMinval,
|
|
parmMaxval,parmUnits)
|
|
numdisplayed+=1
|
|
if parmname not in parmnames:
|
|
parmnames.append(parmname)
|
|
#
|
|
# Set the colorTables for each unique parm added.
|
|
#
|
|
totalcount=len(parmnames)+len(obsnames)
|
|
count=0
|
|
|
|
for obsname in obsnames:
|
|
count+=1
|
|
self.setWorking("Setting colorcurves: %d of %d"%(count,totalcount))
|
|
if obslogfactor>-1:
|
|
keyname="%s_LogFactor"%obsname
|
|
setFloatPref(keyname, obslogfactor)
|
|
parmOb=self.getParm("Ver",obsname,"SFC")
|
|
self.setColorTableAndRange(parmOb,obsParmColorTable,obsParmDisplayMinval,obsParmDisplayMaxval)
|
|
for parmname in parmnames:
|
|
count+=1
|
|
self.setWorking("Setting colorcurves: %d of %d"%(count,totalcount))
|
|
parmOb=self.getParm("Ver",parmname,"SFC")
|
|
if display=="Errors":
|
|
if datatype!=1:
|
|
self.setColorTableAndRange(parmOb,errColor,-bigerr,bigerr)
|
|
else:
|
|
(errColorMag,errColorDir)=errColor
|
|
matchObj=errpat.search(parmname)
|
|
if matchObj is not None:
|
|
type=matchObj.group(2)
|
|
if type=="spd":
|
|
self.setColorTableAndRange(parmOb,errColorMag,-bigerrmag,bigerrmag)
|
|
else:
|
|
self.setColorTableAndRange(parmOb,errColorDir,-bigerrdir,bigerrdir)
|
|
else:
|
|
self.setColorTableAndRange(parmOb,parmColorTable,parmDisplayMinval,parmDisplayMaxval)
|
|
else:
|
|
if logfactor>=-1:
|
|
keyname="%s_LogFactor"%parmname
|
|
setFloatPref(keyname, logfactor)
|
|
self.setColorTableAndRange(parmOb,parmColorTable,parmDisplayMinval,parmDisplayMaxval)
|
|
#
|
|
if numdisplayed==0:
|
|
self.stopWorking()
|
|
self.statusBarMsg("No grids match your selected models/times/parms","U")
|
|
return
|
|
self.finishWorking()
|
|
return
|
|
|
|
|
|
#==================================================================
|
|
# setColorTableAndRange - Set the color table
|
|
#
|
|
#
|
|
def setColorTableAndRange(self,parm,colorTable,displayMinval,displayMaxval):
|
|
spatialMgr = self._dbss.getSpatialDisplayManager()
|
|
if displayMinval or displayMaxval or colorTable:
|
|
rsc = spatialMgr.getResourcePair(parm).getResource()
|
|
from com.raytheon.uf.viz.core.rsc.capabilities import ColorMapCapability
|
|
params = rsc.getCapability(ColorMapCapability).getColorMapParameters()
|
|
|
|
if colorTable:
|
|
if self.__colorMapParams.has_key(colorTable):
|
|
colorMap = self.__colorMapParams[colorTable].getColorMap()
|
|
else:
|
|
from com.raytheon.uf.viz.core.drawables import ColorMapLoader
|
|
if "GFE/" not in colorTable:
|
|
colorTable = "GFE/" + colorTable
|
|
colorMap = ColorMapLoader.loadColorMap(colorTable)
|
|
elemType = parm.getGridInfo().getGridType().toString()
|
|
if ('DISCRETE' == elemType):
|
|
from com.raytheon.viz.gfe.rsc import DiscreteDisplayUtil
|
|
DiscreteDisplayUtil.deleteParmColorMap(parm)
|
|
params.setColorMap(colorMap)
|
|
params.setColorMapName(colorTable)
|
|
logfactor = getFloatPref(parm.getParmID().getParmName()+"_LogFactor", None)
|
|
if logfactor is not None:
|
|
params.setLogFactor(logfactor)
|
|
rsc.issueRefresh()
|
|
|
|
if displayMinval or displayMaxval:
|
|
if (displayMinval != displayMaxval):
|
|
params.setColorMapMax(float(displayMaxval))
|
|
params.setColorMapMin(float(displayMinval))
|
|
|
|
parm.getListeners().fireColorTableModified(parm)
|
|
|
|
return
|
|
|
|
#==================================================================
|
|
# showGridsStats - display grid statistics
|
|
#
|
|
#
|
|
def ShowGridsStats(self,DialogDict):
|
|
self.VU.logMsg("running ShowGridsStats:")
|
|
parmList=[]
|
|
parm=DialogDict["Parm"]
|
|
parmList.append(parm)
|
|
display=DialogDict["Display"]
|
|
threshold=DialogDict["Threshold"]
|
|
cycleList=DialogDict["cycleList"]
|
|
modelList=DialogDict["Models"]
|
|
obsmodel=DialogDict["ObsModel"]
|
|
fcstrList=DialogDict["fcstrList"]
|
|
fhrStart=DialogDict["fhrStart"]
|
|
fhrEnd=DialogDict["fhrEnd"]
|
|
dateType=DialogDict["dateType"]
|
|
numDays=DialogDict["numDays"]
|
|
fromDay=DialogDict["fromDay"]
|
|
dayList=DialogDict["dayList"]
|
|
scale=DialogDict["scale"]
|
|
dateStyle=DialogDict["dateStyle"]
|
|
commonCases=DialogDict["commonCases"]
|
|
accumHours=DialogDict["accumHours"]
|
|
accumFreq=DialogDict["accumFreq"]
|
|
TwoCatType=DialogDict["TwoCatType"]
|
|
TwoCatCond=DialogDict["TwoCatCond"]
|
|
TwoCatValue=DialogDict["TwoCatValue"]
|
|
TwoCatValueString=DialogDict["TwoCatValueString"]
|
|
#
|
|
# Check for good GUI input
|
|
#
|
|
ret=self.checkLists(modelList,parmList,cycleList,fcstrList,dateType,
|
|
dayList)
|
|
if ret==0:
|
|
return
|
|
#
|
|
# If a TwoCat stat - check to see that TwoCatType is OK
|
|
# and setup statID
|
|
#
|
|
if display=="TwoCat":
|
|
statName=TwoCatType
|
|
statCond=TwoCatCond
|
|
statVal=TwoCatValue
|
|
statID=self.VU.getStatID(statName)
|
|
if statID is None:
|
|
self.statusBarMsg("Invalid Statistic Name","U")
|
|
return
|
|
else:
|
|
statID="xxxx"
|
|
#
|
|
#
|
|
#
|
|
self.startWorking("Working on Grid Stats")
|
|
ret=self.setupGM(parmList,modelList)
|
|
if ret==1:
|
|
self.stopWorking()
|
|
return
|
|
casesInfo=[]
|
|
numdisplayed=0
|
|
pctColor=self.VU.getCFG('PERCENT_COLOR')
|
|
#
|
|
# Loop over parm and model
|
|
#
|
|
totaliters=len(parmList)*len(modelList)
|
|
iter=0
|
|
for parm in parmList:
|
|
readParm=parm
|
|
last3="xxx"
|
|
if len(parm)>3:
|
|
last3=parm[-3:]
|
|
if ((last3=="Spd")or(last3=="Dir")):
|
|
readParm=parm[:-3]
|
|
obsParm=self.VU.getObsParm(readParm)
|
|
verType=self.VU.getVerType(readParm)
|
|
datatype=self.VU.getVerParmType(readParm)
|
|
errColor=self.VU.getVerErrColor(readParm)
|
|
bigerr=self.VU.getVerBigErr(readParm)
|
|
thresholds=self.VU.getVerThresholds(readParm)
|
|
if datatype==1:
|
|
(errColorMag,errColorDir)=errColor
|
|
(bigerrmag,bigerrdir)=bigerr
|
|
(threshmag,threshdir)=thresholds
|
|
if last3=="Dir":
|
|
errColor=errColorDir
|
|
bigerr=bigerrdir
|
|
thresholdValue=threshdir[threshold]
|
|
clipval=180
|
|
else: #Spd or vector err magnitude
|
|
errColor=errColorMag
|
|
bigerr=bigerrmag
|
|
thresholdValue=threshmag[threshold]
|
|
clipval=bigerr*2
|
|
else:
|
|
thresholdValue=thresholds[threshold]
|
|
clipval=bigerr*2
|
|
#
|
|
# Get mode for reading obs grids
|
|
#
|
|
obsGridMode=self.getReadMode(obsmodel,obsParm,0)
|
|
#
|
|
# Get case times/records for all models
|
|
#
|
|
caseInfo=self.VU.getCases(readParm,modelList,obsParm,obsmodel,
|
|
dateStyle,dateType,fromDay=fromDay,numDays=numDays,
|
|
dayList=dayList,fcstrs=fcstrList,cycles=cycleList,
|
|
fhrStart=fhrStart,fhrEnd=fhrEnd,
|
|
accumHours=accumHours,accumFreq=accumFreq,
|
|
requireObs=1,commonCases=commonCases,
|
|
basetimeOffsets=1,
|
|
callbackMethod=self.workingCommon)
|
|
if self.checkWorking()==1:
|
|
self.stopWorking()
|
|
return
|
|
#
|
|
#
|
|
#
|
|
for model in modelList:
|
|
iter+=1
|
|
if (totaliters>1):
|
|
firstString="Calculating (%d of %d) %s %s stats"%(iter,totaliters,model,parm)
|
|
else:
|
|
firstString="Calculating %s %s stats"%(model,parm)
|
|
#
|
|
#
|
|
#
|
|
fcstGridMode=self.getReadMode(model,readParm)
|
|
(parmUnits,parmPrecision,parmMinval,parmMaxval,parmRateFlag,parmColorTable,
|
|
parmDisplayMinval,parmDisplayMaxval)=self.getParmInfo(model,readParm)
|
|
#
|
|
#
|
|
#
|
|
parmnames=[]
|
|
gridsave={}
|
|
gridcount={}
|
|
hitssave={}
|
|
misssave={}
|
|
falrsave={}
|
|
cornsave={}
|
|
maxcases=0
|
|
self.setWorking("%s:finding matches"%firstString)
|
|
#
|
|
# Get all the cases for this model
|
|
#
|
|
cases=caseInfo[model]
|
|
#
|
|
# Sort them by the start/end time, not the basetime
|
|
#
|
|
casekeys=cases.keys()
|
|
casekeys.sort(lambda x,y: cmp(x.split(",",1)[1],y.split(",",1)[1]))
|
|
totalcount=len(casekeys)
|
|
self.VU.logMsg("%d cases for %s"%(len(casekeys),model),1)
|
|
count=0
|
|
lastobs=""
|
|
for key in casekeys:
|
|
count+=1
|
|
self.VU.logMsg("%s : %s"%(model,key),10)
|
|
if self.setAndCheckWorking("%s: %d of %d"%(firstString,count,totalcount))==1:
|
|
self.stopWorking()
|
|
return
|
|
(basetimestr,stimestr,etimestr)=key.split(",")
|
|
basetime=int(basetimestr)
|
|
stime=int(stimestr)
|
|
etime=int(etimestr)
|
|
(frecList,orecList)=cases[key]
|
|
#
|
|
# Dont make stats for obs not yet complete
|
|
#
|
|
if etime>time.time(): # dont make stats for obs not yet complete
|
|
count+=1
|
|
continue
|
|
#
|
|
# check to make sure it is a forecast
|
|
# string to store grid under depends on forecast and end hour
|
|
#
|
|
fhr=self.VU.getFcstHour(basetime,stime)
|
|
if fhr<0:
|
|
count+=1
|
|
continue
|
|
#
|
|
# If a new and different obs time - read the obs data
|
|
#
|
|
obskey=key.split(",",1)[1]
|
|
if obskey!=lastobs:
|
|
obsdata=self.VU.getVerGrids(obsmodel,basetime,obsParm,
|
|
stime,etime,obsGridMode,
|
|
orecList)
|
|
#
|
|
# Smooth observed grid...
|
|
# unless a TwoCat "areal" type
|
|
# and smooth vectors in U/V space...
|
|
#
|
|
if scale>0:
|
|
if ((display!="TwoCat")or(statID[0:1]!="a")):
|
|
if datatype==1:
|
|
(obsmag,obsdir)=obsdata
|
|
(u,v)=self.MagDirToUV(obsmag,obsdir)
|
|
us=self.VU.smoothpm(u,scale)
|
|
vs=self.VU.smoothpm(v,scale)
|
|
(obsmag,obsdir)=self.UVToMagDir(us,vs)
|
|
obsdata=(obsmag,obsdir)
|
|
else:
|
|
obsdata=self.VU.smoothpm(obsdata,scale)
|
|
#
|
|
# For probability types...calculate an obs grid of
|
|
# 1 or 0, based on whether the observed threshold
|
|
# is met.
|
|
#
|
|
if verType==1:
|
|
obsdata=self.getProbVerGrid(readParm,obsdata)
|
|
#
|
|
# Save the 'key' for this obs grid - so that we
|
|
# don't have to read and calculate it again every
|
|
# time...only when a new obs time is encountered
|
|
#
|
|
lastobs=obskey
|
|
#
|
|
# get parmname to save as...from cycle/fhr/ehr/model
|
|
#
|
|
ehr=self.VU.getFcstHour(basetime,etime)
|
|
basetuple=time.gmtime(basetime)
|
|
fcstcycle=basetuple[3]
|
|
parmname="%2.2d%3.3d%3.3d%s"%(fcstcycle,fhr,ehr,model)
|
|
#
|
|
# Read forecast grid
|
|
#
|
|
fcstdata=self.VU.getVerGrids(model,basetime,readParm,
|
|
stime,etime,fcstGridMode,
|
|
frecList)
|
|
#
|
|
# Smooth forecast grid...
|
|
# unless a TwoCat "areal" type
|
|
# and smooth vectors in U/V space...
|
|
#
|
|
if scale>0:
|
|
if ((display!="TwoCat")or(statID[0:1]!="a")):
|
|
if datatype==1:
|
|
(fcstmag,fcstdir)=fcstdata
|
|
(u,v)=self.MagDirToUV(fcstmag,fcstdir)
|
|
us=self.VU.smoothpm(u,scale)
|
|
vs=self.VU.smoothpm(v,scale)
|
|
(fcstmag,fcstdir)=self.UVToMagDir(us,vs)
|
|
fcstdata=(fcstmag,fcstdir)
|
|
else:
|
|
fcstdata=self.VU.smoothpm(fcstdata,scale)
|
|
#
|
|
# For TwoCat stats...calculate hits/misses/falsealarms/etc.
|
|
#
|
|
if display=="TwoCat":
|
|
#
|
|
# get the forecast/observed grids into fcstGrid/obsGrid
|
|
# Normally this is what is in fcstdata/obsdata - but for
|
|
# vectors...need to pick the right component and for
|
|
# probabilities - need to divide by 100.
|
|
#
|
|
if datatype==1:
|
|
if last3!="Dir":
|
|
fcstGrid=fcstdata[0]
|
|
obsGrid=obsdata[0]
|
|
else:
|
|
fcstGrid=fcstdata[1]
|
|
obsGrid=obsdata[1]
|
|
else:
|
|
if verType!=0:
|
|
fcstGrid=fcstdata/100.0
|
|
else:
|
|
fcstGrid=fcstdata
|
|
obsGrid=obsdata
|
|
#
|
|
# Now get yes/no of forecast/observed occurrence
|
|
#
|
|
if statCond==">":
|
|
obsOccur=greater(obsGrid,statVal)
|
|
fcstOccur=greater(fcstGrid,statVal)
|
|
elif statCond==">=":
|
|
obsOccur=greater_equal(obsGrid,statVal)
|
|
fcstOccur=greater_equal(fcstGrid,statVal)
|
|
elif statCond=="<":
|
|
obsOccur=less(obsGrid,statVal)
|
|
fcstOccur=less(fcstGrid,statVal)
|
|
elif statCond=="<=":
|
|
obsOccur=less_equal(obsGrid,statVal)
|
|
fcstOccur=less_equal(fcstGrid,statVal)
|
|
#
|
|
# do neighborhood look here
|
|
#
|
|
if statID[0:1]=="a":
|
|
if scale>0:
|
|
obsOccur=self.VU.arealOccur(obsOccur,scale)
|
|
fcstOccur=self.VU.arealOccur(fcstOccur,scale)
|
|
#
|
|
# Make grids of hits, misses, false alarms, correct negatives
|
|
#
|
|
notFcst=logical_not(fcstOccur)
|
|
notObs=logical_not(obsOccur)
|
|
hitsgrid=logical_and(fcstOccur,obsOccur)
|
|
missgrid=logical_and(notFcst,obsOccur)
|
|
falrgrid=logical_and(fcstOccur,notObs)
|
|
corngrid=logical_and(notFcst,notObs)
|
|
#
|
|
# Make space to store these results - if first one
|
|
#
|
|
if parmname not in parmnames:
|
|
parmnames.append(parmname)
|
|
hitssave[parmname]=self.empty()
|
|
misssave[parmname]=self.empty()
|
|
falrsave[parmname]=self.empty()
|
|
cornsave[parmname]=self.empty()
|
|
#
|
|
# Add to the hits/miss/falr/corn values
|
|
#
|
|
hitssave[parmname]+=hitsgrid
|
|
misssave[parmname]+=missgrid
|
|
falrsave[parmname]+=falrgrid
|
|
cornsave[parmname]+=corngrid
|
|
#
|
|
# For non-TwoCat displays...calculate the errors
|
|
#
|
|
else:
|
|
if datatype!=1:
|
|
if verType==0:
|
|
errgrid=fcstdata-obsdata
|
|
else:
|
|
errgrid=(fcstdata/100.0)-obsdata
|
|
else:
|
|
last3=parm[-3:]
|
|
if (last3=="Spd"):
|
|
errgrid=fcstdata[0]-obsdata[0]
|
|
elif (last3=="Dir"):
|
|
errgrid=fcstdata[1]-obsdata[1]
|
|
errgrid=where(greater(errgrid,180.0),360.0-errgrid,errgrid)
|
|
errgrid=where(less(errgrid,-180.0),-(360.0+errgrid),errgrid)
|
|
else:
|
|
(fu,fv)=self.MagDirToUV(fcstdata[0],fcstdata[1])
|
|
(ou,ov)=self.MagDirToUV(obsdata[0],obsdata[1])
|
|
eu=fu-ou
|
|
ev=fv-ov
|
|
(errmag,errdir)=self.UVToMagDir(eu,ev)
|
|
errgrid=errmag
|
|
#
|
|
# change to different scores
|
|
#
|
|
if display=="Mean Abs Error":
|
|
errgrid=where(less(errgrid,0.0),-errgrid,errgrid)
|
|
if display in ["RMS Error","Mean Squared Error"]:
|
|
errgrid*=errgrid
|
|
if display=="Percent Err <":
|
|
errgrid=where(less(errgrid,0.0),-errgrid,errgrid)
|
|
errgrid=less(errgrid,thresholdValue)
|
|
#
|
|
# save list of unique parm names being created
|
|
#
|
|
if parmname not in parmnames:
|
|
parmnames.append(parmname)
|
|
gridsave[parmname]=self.empty()
|
|
gridcount[parmname]=0
|
|
#
|
|
# if doing average errors, add errors to sums
|
|
# otherwise...display the grid
|
|
#
|
|
gridsave[parmname]+=errgrid
|
|
gridcount[parmname]+=1
|
|
#
|
|
# Calculate the statistics grids for this parm/model
|
|
# and display them
|
|
#
|
|
self.VU.logMsg("Creating stat grids")
|
|
pnames=[]
|
|
totalcount=len(parmnames)
|
|
count=0
|
|
for parmname in parmnames:
|
|
#
|
|
# If they want to stop - stop adding more grids
|
|
# but break out to set the color tables correctly
|
|
#
|
|
count+=1
|
|
self.setWorking("%s:%d of %d"%(firstString,count,totalcount))
|
|
if self.checkWorking()==1:
|
|
break
|
|
#
|
|
# Get timerange to save the final grid into
|
|
#
|
|
cyc=int(parmname[0:2])
|
|
f1=int(parmname[2:5])
|
|
f2=int(parmname[5:8])
|
|
tr=self.createTimeRange(f1+cyc,f2+cyc,"Zulu")
|
|
#
|
|
# Make name that will be used in grid manager
|
|
#
|
|
mod=parmname[8:]
|
|
pname="%s%2.2dZ%s"%(parm,cyc,mod)
|
|
#
|
|
# For TwoCat stats
|
|
#
|
|
if display=="TwoCat":
|
|
hitsgrid=hitssave[parmname]
|
|
missgrid=misssave[parmname]
|
|
falrgrid=falrsave[parmname]
|
|
corngrid=cornsave[parmname]
|
|
statgrid=self.VU.getGridBinaryStat(statID,hitsgrid,missgrid,
|
|
falrgrid,corngrid)
|
|
#
|
|
# get case number - for table of cases
|
|
#
|
|
totgrid=hitsgrid+missgrid+falrgrid+corngrid
|
|
n=maximum.reduce(maximum.reduce(totgrid))
|
|
maxcases=max(n,maxcases)
|
|
#
|
|
# Different stats have different limits
|
|
#
|
|
minlim=-1.0
|
|
maxlim=1.0
|
|
res=2
|
|
if statID in ["hits","ahits","miss","amiss","fals","afals",
|
|
"corn","acorn"]:
|
|
minlim=0.0
|
|
maxlim=float(n)
|
|
res=0
|
|
elif statID in ["freqo","freqf","fc","afc","pod","apod","far","afar",
|
|
"pofd","apofd","ts","ats"]:
|
|
minlim=0.0
|
|
maxlim=1.0
|
|
#
|
|
# Ones that range from 0 to Infinity : clip at +5.0
|
|
#
|
|
elif statID in ["freqbias","afreqbias","oddratio","aoddsratio"]:
|
|
minlim=0.0
|
|
maxlim=5.0
|
|
#
|
|
# Equitable Threat clips at -0.333 and 1.0
|
|
#
|
|
elif statID in ["ets","aets"]:
|
|
minlim=-0.3333
|
|
maxlim=1.0
|
|
#
|
|
# Hansen Kuipers clips at -1.0 to 1.0
|
|
#
|
|
elif statID in ["hk","ahk"]:
|
|
minlim=-1.0
|
|
maxlim=1.0
|
|
#
|
|
# Heidke ranges from -Infinity to 1, and clips at -5.0
|
|
#
|
|
elif statID in ["hss","ahss"]:
|
|
minlim=-5.0
|
|
maxlim=1.0
|
|
#
|
|
# Clip the grid
|
|
#
|
|
newgrid=clip(statgrid,minlim,maxlim)
|
|
self.createGrid("Ver",pname,"SCALAR",newgrid,tr,
|
|
"Forecast",None,res,minlim,maxlim,
|
|
"units")
|
|
#
|
|
# For normal error displays
|
|
#
|
|
else:
|
|
#
|
|
# If there weren't any sums saved - dont make
|
|
# a grid for it
|
|
#
|
|
n=gridcount[parmname]
|
|
if n<1:
|
|
continue
|
|
#
|
|
# make newgrid the grid to show
|
|
#
|
|
newgrid=gridsave[parmname]/float(n)
|
|
if display=="RMS Error":
|
|
newgrid=newgrid**0.5
|
|
if verType==1:
|
|
newgrid*=100.0
|
|
#
|
|
# clip the newgrid based on the parm clipping value
|
|
#
|
|
newgrid=clip(newgrid,-clipval,clipval)
|
|
#
|
|
# Percent error grids always range from 0 to 100
|
|
#
|
|
if display=="Percent Err <":
|
|
newgrid*=100.0
|
|
newgrid=clip(newgrid,0.0,100.0)
|
|
self.createGrid("Ver",pname,"SCALAR",newgrid,tr,
|
|
"Forecast",None,0,0.0,100.0,"%")
|
|
#
|
|
# Others can have variable ranges. We clip the
|
|
# values at 2 times the 'bigerr' value
|
|
#
|
|
else:
|
|
ep=max(self.errPrecision,parmPrecision)
|
|
if datatype!=1:
|
|
bigerr=self.VU.getVerBigErr(parm)
|
|
clipval=bigerr*2.0
|
|
self.createGrid("Ver",pname,"SCALAR",newgrid,tr,
|
|
"Forecast",None,ep,-clipval,
|
|
clipval,self.errUnits)
|
|
#
|
|
# Keep track of grids actually put in grid manager
|
|
#
|
|
if pname not in pnames:
|
|
pnames.append(pname)
|
|
numdisplayed+=1
|
|
casesInfo.append("%-25s|%3.3d|%d"%(pname,f1,n))
|
|
#
|
|
# Set the colorTables for each unique parm added.
|
|
#
|
|
self.VU.logMsg("Setting color tables",2)
|
|
for pname in pnames:
|
|
parmOb=self.getParm("Ver",pname,"SFC")
|
|
if display=="TwoCat":
|
|
if res==0:
|
|
self.setColorTableAndRange(parmOb,pctColor,0,maxcases)
|
|
else:
|
|
self.setColorTableAndRange(parmOb,pctColor,minlim,maxlim)
|
|
else:
|
|
if display=="Percent Err <":
|
|
self.setColorTableAndRange(parmOb,pctColor,0,100)
|
|
else:
|
|
self.setColorTableAndRange(parmOb,errColor,-bigerr,bigerr)
|
|
if self.checkWorking()==1:
|
|
self.stopWorking()
|
|
return
|
|
#
|
|
#
|
|
#
|
|
if numdisplayed==0:
|
|
self.stopWorking()
|
|
self.statusBarMsg("No grids match your selected models/times/parms","U")
|
|
return
|
|
#
|
|
self.finishWorking()
|
|
#
|
|
# Make text with case info
|
|
#
|
|
casesInfo.sort()
|
|
casesText="Number of Cases:\n"
|
|
lastmod=""
|
|
for info in casesInfo:
|
|
(modlong,fhr,num)=info.split("|")
|
|
mod=modlong.strip()
|
|
if mod!=lastmod:
|
|
casesText+="\n %s:\n"%mod
|
|
lastmod=mod
|
|
casesText+=" %3d-hr: %5d\n"%(int(fhr),int(num))
|
|
#
|
|
# Make the case info pop up
|
|
#
|
|
self.cases.updateText(casesText)
|
|
self.miniCases.deiconify()
|
|
self.miniCases.lift()
|
|
self.miniCases.update_idletasks()
|
|
self.VU.logMsg("Done making stat grids")
|
|
return
|
|
#==================================================================
|
|
# getProbVerGrid - get grid for probability verification, based
|
|
# on the obsdata, and the condition/threshold for
|
|
# the specified parmName
|
|
#
|
|
def getProbVerGrid(self,parmName,obsdata):
|
|
outdata=obsdata*0
|
|
obsCondition=self.VU.getObsCondition(parmName)
|
|
obsThreshold=self.VU.getObsThreshold(parmName)
|
|
if obsCondition==">":
|
|
outdata=greater(obsdata,obsThreshold)
|
|
elif obsCondition==">=":
|
|
outdata=greater_equal(obsdata,obsThreshold)
|
|
elif obsCondition=="<":
|
|
outdata=less(obsdata,obsThreshold)
|
|
elif obsCondition=="<=":
|
|
outdata=less_equal(obsdata,obsThreshold)
|
|
return outdata
|
|
#==================================================================
|
|
# showGridsDists - display histograms/scatterplots
|
|
#
|
|
#
|
|
def ShowDists(self,DialogDict):
|
|
self.VU.logMsg("running ShowDists:")
|
|
parmList=[]
|
|
parm=DialogDict["Parm"]
|
|
parmList.append(parm)
|
|
display=DialogDict["Display"]
|
|
cycleList=DialogDict["cycleList"]
|
|
modelList=DialogDict["Models"]
|
|
obsmodel=DialogDict["ObsModel"]
|
|
fcstrList=DialogDict["fcstrList"]
|
|
fhrStart=DialogDict["fhrStart"]
|
|
fhrEnd=DialogDict["fhrEnd"]
|
|
dateType=DialogDict["dateType"]
|
|
numDays=DialogDict["numDays"]
|
|
fromDay=DialogDict["fromDay"]
|
|
dayList=DialogDict["dayList"]
|
|
dateStyle=DialogDict["dateStyle"]
|
|
scale=DialogDict["scale"]
|
|
commonCases=DialogDict["commonCases"]
|
|
accumHours=DialogDict["accumHours"]
|
|
accumFreq=DialogDict["accumFreq"]
|
|
#
|
|
# Check for good GUI input
|
|
#
|
|
ret=self.checkLists(modelList,parmList,cycleList,fcstrList,dateType,
|
|
dayList)
|
|
if ret==0:
|
|
return
|
|
#
|
|
# Do seperate processing for each type
|
|
#
|
|
if display=="Error Histogram":
|
|
self.errorHistogram(parmList,cycleList,modelList,obsmodel,fcstrList,
|
|
fhrStart,fhrEnd,dateType,numDays,fromDay,dayList,
|
|
dateStyle,scale,commonCases,accumHours,accumFreq)
|
|
elif display=="Value Histogram":
|
|
self.valueHistogram(parmList,cycleList,modelList,obsmodel,fcstrList,
|
|
fhrStart,fhrEnd,dateType,numDays,fromDay,dayList,
|
|
dateStyle,scale,commonCases,accumHours,accumFreq)
|
|
elif display=="Expected Value":
|
|
self.expectedValue(parmList,cycleList,modelList,obsmodel,fcstrList,
|
|
fhrStart,fhrEnd,dateType,numDays,fromDay,dayList,
|
|
dateStyle,scale,commonCases,accumHours,accumFreq)
|
|
elif display=="Scatterplot":
|
|
self.scatterPlot(parmList,cycleList,modelList,obsmodel,fcstrList,
|
|
fhrStart,fhrEnd,dateType,numDays,fromDay,dayList,
|
|
dateStyle,scale,commonCases,accumHours,accumFreq)
|
|
return
|
|
#==================================================================
|
|
# errorHistogram - display error histogram
|
|
#
|
|
#
|
|
def errorHistogram(self,parmList,cycleList,modelList,obsmodel,
|
|
fcstrList,fhrStart,fhrEnd,dateType,numDays,fromDay,
|
|
dayList,dateStyle,scale,commonCases,accumHours,
|
|
accumFreq):
|
|
#
|
|
#
|
|
# Clear display - setup title
|
|
#
|
|
parm=parmList[0]
|
|
self.cd.canvas.delete(Tkinter.ALL)
|
|
self.cd.title("Error Histogram - %s"%parm)
|
|
#
|
|
# Start 'working' display
|
|
#
|
|
workStart="Working on error histogram"
|
|
self.startWorking(workStart,optionRemove=0)
|
|
#
|
|
#
|
|
#
|
|
NUMTBUTTONS=12 # normal number of time buttons on a row - configure
|
|
NUMMBUTTONS=6 # normal number of model buttons on a row - configure
|
|
#
|
|
# get the active EditArea into ea. If the active edit area is
|
|
# None - then assume they want to run it over the entire grid
|
|
#
|
|
editArea=self.getActiveEditArea()
|
|
editAreaMask=self.encodeEditArea(editArea)
|
|
npts=add.reduce(add.reduce(editAreaMask))
|
|
if (npts==0):
|
|
editArea.invert()
|
|
ea=self.encodeEditArea(editArea)
|
|
eaflat=ravel(ea)
|
|
totalpoints=add.reduce(eaflat)
|
|
#
|
|
# make space for saving data
|
|
#
|
|
self.histograms={} # storage for histograms for each model/forecast hour
|
|
self.histoWorseLow={}
|
|
self.histoWorseHigh={}
|
|
self.numCases={}
|
|
self.errSums={}
|
|
self.errSumSquareds={}
|
|
self.errSumAbs={}
|
|
#
|
|
#
|
|
#
|
|
totaliters=len(modelList)
|
|
iter=0
|
|
#
|
|
# For vectors...the parm to read might be different than
|
|
# the name of the parm
|
|
#
|
|
readParm=parm
|
|
last3="xxx"
|
|
if len(parm)>3:
|
|
last3=parm[-3:]
|
|
if ((last3=="Spd")or(last3=="Dir")):
|
|
readParm=parm[:-3]
|
|
#
|
|
# Get information about the parm we are reading
|
|
#
|
|
(parmUnits,parmPrecision,parmMinval,parmMaxval,parmRateFlag,parmColorTable,
|
|
parmDisplayMinval,parmDisplayMaxval)=self.getParmInfo(self.mutableID(),parm)
|
|
obsParm=self.VU.getObsParm(readParm)
|
|
verType=self.VU.getVerType(readParm)
|
|
datatype=self.VU.getVerParmType(readParm)
|
|
#
|
|
# get binwidth and bigerr for parm...but for vectors its
|
|
# complicated by dir/mag/vecerr options
|
|
#
|
|
binwidth=self.VU.getVerBinWidth(readParm)
|
|
bigerr=self.VU.getVerBigErr(readParm)
|
|
if datatype==1:
|
|
(bwMag,bwDir)=binwidth
|
|
(beMag,beDir)=bigerr
|
|
if last3=="Dir":
|
|
binwidth=bwDir
|
|
bigerr=beDir
|
|
else:
|
|
binwidth=bwMag
|
|
bigerr=beMag
|
|
(binmin,binmax)=self.getBins(binwidth,bigerr)
|
|
self.histosetup(-bigerr,bigerr,binwidth)
|
|
|
|
nbin=len(binmin)
|
|
nbins=reshape(arange(nbin),(nbin,1))
|
|
abinmin=reshape(array(binmin),(nbin,1))
|
|
abinmax=reshape(array(binmax),(nbin,1))
|
|
#
|
|
# Get mode for reading obs grids
|
|
#
|
|
obsGridMode=self.getReadMode(obsmodel,obsParm,0)
|
|
#
|
|
# Get case times/records for all models
|
|
#
|
|
caseInfo=self.VU.getCases(readParm,modelList,obsParm,obsmodel,
|
|
dateStyle,dateType,fromDay=fromDay,
|
|
numDays=numDays,dayList=dayList,
|
|
fcstrs=fcstrList,cycles=cycleList,
|
|
fhrStart=fhrStart,fhrEnd=fhrEnd,
|
|
accumHours=accumHours,accumFreq=accumFreq,
|
|
commonCases=commonCases,basetimeOffsets=1,
|
|
callbackMethod=self.workingCommon)
|
|
if self.checkWorking()==1:
|
|
self.stopWorking()
|
|
return
|
|
#
|
|
# Loop over each model
|
|
#
|
|
for model in modelList:
|
|
iter+=1
|
|
workNow=workStart+":%s (%d of %d)"%(model,iter,totaliters)
|
|
#
|
|
fcstGridMode=self.getReadMode(model,readParm)
|
|
#
|
|
# Get all the cases for this model
|
|
#
|
|
cases=caseInfo[model]
|
|
#
|
|
# Sort cases by the start time, not the basetime
|
|
#
|
|
casekeys=cases.keys()
|
|
casekeys.sort(lambda x,y: cmp(x.split(",",1)[1],y.split(",",1)[1]))
|
|
totalcount=len(casekeys)
|
|
self.VU.logMsg("reading %d cases for %s"%(totalcount,model),1)
|
|
count=0
|
|
lastobs=""
|
|
for key in casekeys:
|
|
count+=1
|
|
self.VU.logMsg("%s : %s"%(model,key),10)
|
|
if self.setAndCheckWorking("%s: %d of %d"%(workNow,count,totalcount))==1:
|
|
self.stopWorking()
|
|
return
|
|
(basetimestr,stimestr,etimestr)=key.split(",")
|
|
basetime=int(basetimestr)
|
|
stime=int(stimestr)
|
|
etime=int(etimestr)
|
|
(frecList,orecList)=cases[key]
|
|
#
|
|
# Dont make stats for obs not yet complete
|
|
#
|
|
if etime>time.time():
|
|
continue
|
|
#
|
|
# Dont include negative forecast hours
|
|
#
|
|
fhr=self.VU.getFcstHour(basetime,stime)
|
|
if fhr<0:
|
|
continue
|
|
#
|
|
# string to store grid under depends on model and forecast hour
|
|
#
|
|
savekey="%s-%3.3d"%(model,fhr)
|
|
#
|
|
# If a new and different obs time - read the obs data
|
|
#
|
|
obskey=key.split(",",1)[1]
|
|
if obskey!=lastobs:
|
|
self.VU.logMsg("new Obs grid",10)
|
|
obsdata=self.VU.getVerGrids(obsmodel,basetime,obsParm,
|
|
stime,etime,mode=obsGridMode,
|
|
recList=orecList)
|
|
obsdata=self.scaleGrid(obsdata,scale,datatype)
|
|
#
|
|
# For probabilistic variables...calculate the
|
|
# observed 'yes/no' value
|
|
#
|
|
if verType==1:
|
|
obsdata=self.getProbVerGrid(readParm,obsdata)
|
|
#
|
|
#
|
|
#
|
|
if ((datatype!=1)or(last3 in ["Spd","Dir"])):
|
|
if last3=="Spd":
|
|
obsgrid=obsdata[0]
|
|
elif last3=="Dir":
|
|
obsgrid=obsdata[1]
|
|
else:
|
|
obsgrid=obsdata
|
|
obsonly=compress(eaflat,ravel(obsgrid))
|
|
else:
|
|
(u,v)=self.MagDirToUV(obsdata[0],obsdata[1])
|
|
obsuonly=compress(eaflat,ravel(u))
|
|
obsvonly=compress(eaflat,ravel(v))
|
|
#
|
|
# save the last obskey that we have read so that
|
|
# we don't read it again many times
|
|
#
|
|
lastobs=obskey
|
|
#
|
|
# Read forecast grid
|
|
#
|
|
fcstdata=self.VU.getVerGrids(model,basetime,readParm,
|
|
stime,etime,mode=fcstGridMode,
|
|
recList=frecList)
|
|
fcstdata=self.scaleGrid(fcstdata,scale,datatype)
|
|
#
|
|
# Get the error, handling vector error, etc.
|
|
#
|
|
if ((datatype!=1)or(last3 in ["Spd","Dir"])):
|
|
if last3=="Spd":
|
|
fcstgrid=fcstdata[0]
|
|
elif last3=="Dir":
|
|
fcstgrid=fcstdata[1]
|
|
else:
|
|
fcstgrid=fcstdata
|
|
fcstonly=compress(eaflat,ravel(fcstgrid))
|
|
erronly=fcstonly-obsonly
|
|
if last3=="Dir":
|
|
erronly=where(greater(erronly,180.0),360.0-erronly,erronly)
|
|
erronly=where(less(erronly,-180.0),-(360.0+erronly),erronly)
|
|
else:
|
|
(fcstmag,fcstdir)=fcstdata
|
|
(u,v)=self.MagDirToUV(fcstmag,fcstdir)
|
|
uonly=compress(eaflat,ravel(u))
|
|
vonly=compress(eaflat,ravel(v))
|
|
uerr=uonly-obsuonly
|
|
verr=vonly-obsvonly
|
|
(mag,direc)=self.UVToMagDir(uerr,verr)
|
|
erronly=mag
|
|
#
|
|
# make histograms
|
|
#
|
|
(errCount,worseLow,worseHigh)=self.histo(erronly)
|
|
errSum=add.reduce(erronly)
|
|
errSumSquared=add.reduce(erronly*erronly)
|
|
errabs=abs(erronly)
|
|
errSumAb=add.reduce(errabs)
|
|
if self.histograms.has_key(savekey):
|
|
self.histograms[savekey]+=errCount
|
|
self.histoWorseLow[savekey]+=worseLow
|
|
self.histoWorseHigh[savekey]+=worseHigh
|
|
self.errSums[savekey]+=errSum
|
|
self.errSumSquareds[savekey]+=errSumSquared
|
|
self.errSumAbs[savekey]+=errSumAb
|
|
self.numCases[savekey]+=1
|
|
else:
|
|
self.histograms[savekey]=errCount
|
|
self.histoWorseLow[savekey]=worseLow
|
|
self.histoWorseHigh[savekey]=worseHigh
|
|
self.errSums[savekey]=errSum
|
|
self.errSumSquareds[savekey]=errSumSquared
|
|
self.errSumAbs[savekey]=errSumAb
|
|
self.numCases[savekey]=1
|
|
#
|
|
# Get all "model-fhr" keys we saved
|
|
#
|
|
fullkeys=self.histograms.keys()
|
|
#
|
|
# if no data could be read - stop here
|
|
#
|
|
if len(fullkeys)<1:
|
|
self.stopWorking()
|
|
msg="No verification data could be found matching those criteria"
|
|
self.statusBarMsg(msg,"U")
|
|
return
|
|
#
|
|
# For buttons...get models/forecasthours actually in the data
|
|
#
|
|
fullkeys.sort()
|
|
fhrstrs=[]
|
|
modkeys=[]
|
|
for fullkey in fullkeys:
|
|
(mod,fhrstr)=fullkey.split("-")
|
|
if fhrstr not in fhrstrs:
|
|
fhrstrs.append(fhrstr)
|
|
if mod not in modkeys:
|
|
modkeys.append(mod)
|
|
#
|
|
# Change fhrstrs (sorted on 3-character 000-999) into
|
|
# smaller fhrkeys that are NOT all 3-characters wide
|
|
#
|
|
fhrstrs.sort()
|
|
fhrkeys=[]
|
|
for fhrstr in fhrstrs:
|
|
fhrkeys.append("%d"%int(fhrstr))
|
|
#
|
|
# If an Official button is in there...make it first
|
|
#
|
|
modkeys.sort()
|
|
if "Official" in modkeys:
|
|
idx=modkeys.index("Official")
|
|
del modkeys[idx]
|
|
modkeys.insert(0,"Official")
|
|
#
|
|
# set colors for each model
|
|
#
|
|
self.colornames={}
|
|
index=0
|
|
for mod in modkeys:
|
|
self.colornames[mod]=self.COLORLIST[index]
|
|
index+=1
|
|
if index==len(self.COLORLIST):
|
|
index=0
|
|
#
|
|
# Setup first row of buttons (forecast hours)
|
|
#
|
|
self.setupBut1(fhrkeys,numbuttons=NUMTBUTTONS,arrows=1,width=3)
|
|
#
|
|
# Setup second row of buttons (models)
|
|
#
|
|
self.setupBut2(modkeys,numbuttons=NUMMBUTTONS,arrows=1)
|
|
#
|
|
# find max number in any bin in any of the histograms
|
|
#
|
|
histkey1=self.histograms.keys()[0]
|
|
maxHist=zeros_like(self.histograms[histkey1])
|
|
for histkey in self.histograms.keys():
|
|
self.histograms[histkey]/=float(self.numCases[histkey])
|
|
maxHist=maximum(maxHist,self.histograms[histkey])
|
|
fullmax=maximum.reduce(maxHist)
|
|
#
|
|
# Find good tickmark interval for vertical axis and set the
|
|
# vertical range to be one tick mark above the fullmax (max
|
|
# number in any histogram)
|
|
#
|
|
numticks=10
|
|
tickInterval=self.niceNumDec(fullmax/(numticks-1),1)
|
|
graphmax=(int(fullmax/tickInterval)+1)*tickInterval
|
|
#
|
|
#
|
|
# Setup graphing coordinates
|
|
#
|
|
minx=-bigerr
|
|
maxx=bigerr
|
|
maxscore=maxx/2.0
|
|
left=self.cd.curwidth*(50.0/700.0)
|
|
right=self.cd.curwidth*(650.0/700.0)
|
|
bot=self.cd.curheight*(100.0/530.0)
|
|
top=self.cd.curheight*(480.0/530.0)
|
|
self.setgraph(minx,maxx,0.0,graphmax,left,right,bot,top)
|
|
self.histoaxes(graphmax,-bigerr,bigerr,binwidth,tickInterval)
|
|
#
|
|
# Draw each histogram
|
|
#
|
|
totalcount=len(self.histograms.keys())
|
|
count=0
|
|
for key in self.histograms.keys():
|
|
count+=1
|
|
if self.setAndCheckWorking("%s: drawing histogram %d of %d"%(workStart,count,totalcount))==1:
|
|
self.stopWorking()
|
|
return
|
|
tagbase=key.split("-")
|
|
mod=tagbase[0]
|
|
fhr=int(tagbase[1])
|
|
fhrstr="f%d"%fhr
|
|
tagtuple=(mod,fhrstr)
|
|
flabel="%d-hr forecast"%fhr
|
|
self.labelLine(flabel,3,justify="right",tags=tagtuple)
|
|
|
|
colorname=self.colornames[mod]
|
|
bins=self.histograms[key]
|
|
nbin=bins.shape[0]
|
|
for i in xrange(nbin):
|
|
y=bins[i]
|
|
x1=self.histomin+(i*self.histowidth)
|
|
x2=x1+self.histowidth
|
|
if y>0:
|
|
if i==0:
|
|
(sx1,sy1)=self.graphcoord(x1,0)
|
|
else:
|
|
(sx1,sy1)=self.graphcoord(x1,bins[i-1])
|
|
(sx2,sy2)=self.graphcoord(x1,y)
|
|
(sx3,sy3)=self.graphcoord(x2,y)
|
|
if ((i+1)==nbin):
|
|
(sx4,sy4)=self.graphcoord(x2,0)
|
|
self.cd.canvas.create_line(sx1,sy1,sx2,sy2,sx3,sy3,sx4,sy4,fill=colorname,tags=tagtuple)
|
|
elif bins[i+1]==0:
|
|
(sx4,sy4)=self.graphcoord(x2,0)
|
|
self.cd.canvas.create_line(sx1,sy1,sx2,sy2,sx3,sy3,sx4,sy4,fill=colorname,tags=tagtuple)
|
|
else:
|
|
self.cd.canvas.create_line(sx1,sy1,sx2,sy2,sx3,sy3,fill=colorname,tags=tagtuple)
|
|
self.but2state[mod]=1
|
|
self.but1state[fhrstr]=1
|
|
lowfcst=self.histoWorseLow[key]
|
|
highfcst=self.histoWorseHigh[key]
|
|
self.showWorse(lowfcst,highfcst,bigerr,15,colorname,tagtuple)
|
|
allpts=self.numCases[key]*totalpoints
|
|
avg=self.errSums[key]/allpts
|
|
mae=self.errSumAbs[key]/allpts
|
|
std=sqrt((self.errSumSquareds[key]/allpts)-(avg*avg))
|
|
rms=sqrt(self.errSumSquareds[key]/allpts)
|
|
self.showAvg(avg,colorname,tagtuple)
|
|
modnum=modkeys.index(mod)
|
|
self.showScores(modnum,mod,self.numCases[key],avg,std,mae,rms,colorname,tagtuple)
|
|
score=100.0-(self.errSumSquareds[key]/allpts)
|
|
self.showScore(score,mod,colorname,tagtuple)
|
|
#
|
|
# Show first time/model
|
|
#
|
|
startBut1(self)
|
|
startBut2(self)
|
|
#
|
|
# Label top of graph
|
|
#
|
|
(x,y)=self.graphcoord(0,graphmax)
|
|
self.cd.canvas.create_text(x,y-5,text="Gridpoints per case",fill="black",anchor=Tkinter.S)
|
|
#
|
|
# Labels
|
|
#
|
|
ul1="Histogram - %s"%parm
|
|
self.cdLabels(ul1,totalpoints,dateStyle,dateType,numDays,fromDay,dayList,cycleList)
|
|
#
|
|
# Bin width
|
|
#
|
|
if binwidth<1.0:
|
|
str="Bin width: %3.1f"%binwidth
|
|
else:
|
|
str="Bin width: %d"%binwidth
|
|
self.labelLine(str,3,justify="left")
|
|
#
|
|
# table labels
|
|
#
|
|
x=self.cd.curwidth*(80.0/700.0)
|
|
y=self.cd.curheight*(130.0/530.0)
|
|
self.cd.canvas.create_text(x,y,text="Model",anchor=Tkinter.E,fill="black")
|
|
x=self.cd.curwidth*(130.0/700.0)
|
|
y=self.cd.curheight*(130.0/530.0)
|
|
self.cd.canvas.create_text(x,y,text="Cases",anchor=Tkinter.E,fill="black")
|
|
x=self.cd.curwidth*(170.0/700.0)
|
|
y=self.cd.curheight*(130.0/530.0)
|
|
self.cd.canvas.create_text(x,y,text="Avg",anchor=Tkinter.E,fill="black")
|
|
x=self.cd.curwidth*(210.0/700.0)
|
|
y=self.cd.curheight*(130.0/530.0)
|
|
self.cd.canvas.create_text(x,y,text="Std",anchor=Tkinter.E,fill="black")
|
|
x=self.cd.curwidth*(250.0/700.0)
|
|
y=self.cd.curheight*(130.0/530.0)
|
|
self.cd.canvas.create_text(x,y,text="MAE",anchor=Tkinter.E,fill="black")
|
|
x=self.cd.curwidth*(290.0/700.0)
|
|
y=self.cd.curheight*(130.0/530.0)
|
|
self.cd.canvas.create_text(x,y,text="RMS",anchor=Tkinter.E,fill="black")
|
|
#
|
|
# Color Bar
|
|
#
|
|
midx=self.cd.curwidth/2.0
|
|
for i in xrange(0,256):
|
|
x=midx-128+i
|
|
y=50
|
|
colorstr="#%02x%02x00"%(255-i,i)
|
|
self.cd.canvas.create_line(x,y-3,x,y+3,fill=colorstr)
|
|
self.cd.canvas.create_text(midx-128-5,50,text="Bad",anchor=Tkinter.E)
|
|
self.cd.canvas.create_text(midx+128+5,50,text="Good",anchor=Tkinter.W)
|
|
|
|
self.stopWorking()
|
|
self.moveCD()
|
|
self.cd.deiconify()
|
|
self.cd.lift()
|
|
return
|
|
#==================================================================
|
|
# valueHistogram - display value histogram
|
|
#
|
|
#
|
|
def valueHistogram(self,parmList,cycleList,modelList,obsmodel,
|
|
fcstrList,fhrStart,fhrEnd,dateType,numDays,fromDay,
|
|
dayList,dateStyle,scale,commonCases,accumHours,
|
|
accumFreq):
|
|
#
|
|
# Clear display - setup title
|
|
#
|
|
parm=parmList[0]
|
|
self.cd.canvas.delete(Tkinter.ALL)
|
|
self.cd.title("Value Histogram - %s"%parm)
|
|
#
|
|
#
|
|
#
|
|
workStart="Working on value histogram"
|
|
self.startWorking(workStart,optionRemove=0)
|
|
#
|
|
#
|
|
#
|
|
NUMTBUTTONS=12 # normal number of time buttons on a row - configure
|
|
NUMMBUTTONS=6 # normal number of model buttons on a row - configure
|
|
#
|
|
# get the active EditArea into ea. If the active edit area is
|
|
# None - then assume they want to run it over the entire grid
|
|
#
|
|
editArea=self.getActiveEditArea()
|
|
editAreaMask=self.encodeEditArea(editArea)
|
|
npts=add.reduce(add.reduce(editAreaMask))
|
|
if (npts==0):
|
|
editArea.invert()
|
|
ea=self.encodeEditArea(editArea)
|
|
eaflat=ravel(ea)
|
|
totalpoints=add.reduce(eaflat)
|
|
#
|
|
# make space for saving data
|
|
#
|
|
self.histograms={} # storage for histograms for each model/forecast hour
|
|
self.numCases={}
|
|
#
|
|
# Loop over parm and model
|
|
#
|
|
totaliters=len(modelList)
|
|
iter=0
|
|
#
|
|
# For vectors...the parm to read might be different than
|
|
# the name of the parm
|
|
#
|
|
readParm=parm
|
|
last3="xxx"
|
|
if len(parm)>3:
|
|
last3=parm[-3:]
|
|
if ((last3=="Spd")or(last3=="Dir")):
|
|
readParm=parm[:-3]
|
|
#
|
|
# Get information about the parm we are reading
|
|
#
|
|
(parmUnits,parmPrecision,parmMinval,parmMaxval,parmRateFlag,parmColorTable,
|
|
parmDisplayMinval,parmDisplayMaxval)=self.getParmInfo(self.mutableID(),parm)
|
|
obsParm=self.VU.getObsParm(readParm)
|
|
verType=self.VU.getVerType(readParm)
|
|
datatype=self.VU.getVerParmType(readParm)
|
|
if ((datatype==1)and(last3=="Dir")):
|
|
parmMinval=0
|
|
parmMaxval=360
|
|
#
|
|
# get binwidth and bigerr for parm...but for vectors its
|
|
# complicated by dir/mag/vecerr options
|
|
#
|
|
binwidth=self.VU.getVerBinWidth(readParm)
|
|
if datatype==1:
|
|
(bwMag,bwDir)=binwidth
|
|
if last3=="Dir":
|
|
binwidth=bwDir
|
|
else:
|
|
binwidth=bwMag
|
|
#
|
|
# Setup histogram binning routines
|
|
#
|
|
self.histosetup(parmMinval,parmMaxval,binwidth)
|
|
#
|
|
# Get mode for reading obs grids
|
|
#
|
|
obsGridMode=self.getReadMode(obsmodel,obsParm,0)
|
|
#
|
|
# Get case times/records for all models
|
|
#
|
|
caseInfo=self.VU.getCases(readParm,modelList,obsParm,obsmodel,
|
|
dateStyle,dateType,fromDay=fromDay,
|
|
numDays=numDays,dayList=dayList,
|
|
fcstrs=fcstrList,cycles=cycleList,
|
|
fhrStart=fhrStart,fhrEnd=fhrEnd,
|
|
accumHours=accumHours,accumFreq=accumFreq,
|
|
commonCases=commonCases,basetimeOffsets=1,
|
|
callbackMethod=self.workingCommon)
|
|
if self.checkWorking()==1:
|
|
self.stopWorking()
|
|
return
|
|
#
|
|
# Loop over each model
|
|
#
|
|
for model in modelList:
|
|
iter+=1
|
|
workNow=workStart+":%s (%d of %d)"%(model,iter,totaliters)
|
|
#
|
|
fcstGridMode=self.getReadMode(model,readParm)
|
|
#
|
|
# Get all the cases for this model
|
|
#
|
|
cases=caseInfo[model]
|
|
#
|
|
# Sort cases by the start/end time, not the basetime
|
|
#
|
|
casekeys=cases.keys()
|
|
casekeys.sort(lambda x,y: cmp(x.split(",",1)[1],y.split(",",1)[1]))
|
|
totalcount=len(casekeys)
|
|
self.VU.logMsg("reading %d cases for %s"%(totalcount,model),1)
|
|
count=0
|
|
lastobs=""
|
|
for key in casekeys:
|
|
count+=1
|
|
self.VU.logMsg("%s : %s"%(model,key),10)
|
|
if self.setAndCheckWorking("%s: %d of %d"%(workNow,count,totalcount))==1:
|
|
self.stopWorking()
|
|
return
|
|
(basetimestr,stimestr,etimestr)=key.split(",")
|
|
basetime=int(basetimestr)
|
|
stime=int(stimestr)
|
|
etime=int(etimestr)
|
|
(frecList,orecList)=cases[key]
|
|
#
|
|
# Dont make stats for obs not yet complete
|
|
#
|
|
if etime>time.time():
|
|
continue
|
|
#
|
|
# Dont include negative forecast hours
|
|
#
|
|
fhr=self.VU.getFcstHour(basetime,stime)
|
|
if fhr<0:
|
|
continue
|
|
#
|
|
# string to store grid under depends on model and forecast hour
|
|
#
|
|
saveKey="%s-%3.3d"%(model,fhr)
|
|
#
|
|
# If a new and different obs time - read the obs data
|
|
#
|
|
obskey=key.split(",",1)[1]
|
|
if obskey!=lastobs:
|
|
self.VU.logMsg("new Obs grid",10)
|
|
obsdata=self.VU.getVerGrids(obsmodel,basetime,obsParm,
|
|
stime,etime,mode=obsGridMode,
|
|
recList=orecList)
|
|
obsdata=self.scaleGrid(obsdata,scale,datatype)
|
|
#
|
|
# For probabilistic variables...calculate the
|
|
# observed 'yes/no' value
|
|
#
|
|
if verType==1:
|
|
obsdata=self.getProbVerGrid(readParm,obsdata)*100.0
|
|
#
|
|
# cant do a value histogram of vector wind
|
|
# errors...so those get changed to windSpd
|
|
#
|
|
if ((datatype!=1)or(last3 in ["Spd","Dir"])):
|
|
if last3=="Spd":
|
|
obsgrid=obsdata[0]
|
|
elif last3=="Dir":
|
|
obsgrid=obsdata[1]
|
|
else:
|
|
obsgrid=obsdata
|
|
obsonly=compress(eaflat,ravel(obsgrid))
|
|
else:
|
|
obsgrid=obsdata[0]
|
|
obsonly=compress(eaflat,ravel(obsgrid))
|
|
(obsCount,below,above)=self.histo(obsonly)
|
|
lastobs=obskey
|
|
#
|
|
# Add observed bin counts to counts for same model/fhr
|
|
#
|
|
obsSaveKey="%s-%3.3d"%(obsmodel,fhr)
|
|
if self.histograms.has_key(obsSaveKey):
|
|
self.histograms[obsSaveKey]+=obsCount
|
|
self.numCases[obsSaveKey]+=1
|
|
else:
|
|
self.histograms[obsSaveKey]=obsCount
|
|
self.numCases[obsSaveKey]=1
|
|
#
|
|
# Read forecast grid and calculate error grid
|
|
#
|
|
fcstdata=self.VU.getVerGrids(model,basetime,readParm,
|
|
stime,etime,mode=fcstGridMode,
|
|
recList=frecList)
|
|
fcstdata=self.scaleGrid(fcstdata,scale,datatype)
|
|
#
|
|
# Get the error, handling vector error, etc.
|
|
#
|
|
if ((datatype!=1)or(last3 in ["Spd","Dir"])):
|
|
if last3=="Spd":
|
|
fcstgrid=fcstdata[0]
|
|
elif last3=="Dir":
|
|
fcstgrid=fcstdata[1]
|
|
else:
|
|
fcstgrid=fcstdata
|
|
fcstonly=compress(eaflat,ravel(fcstgrid))
|
|
else:
|
|
fcstgrid=fcstdata[0]
|
|
fcstonly=compress(eaflat,ravel(fcstgrid))
|
|
#
|
|
# bin the forecast values
|
|
#
|
|
(valCount,below,above)=self.histo(fcstonly)
|
|
#
|
|
# Add bin counts to counts for same model/fhr
|
|
#
|
|
if self.histograms.has_key(saveKey):
|
|
self.histograms[saveKey]+=valCount
|
|
self.numCases[saveKey]+=1
|
|
else:
|
|
self.histograms[saveKey]=valCount
|
|
self.numCases[saveKey]=1
|
|
#
|
|
# Get all the keys that will be displayed - we've been storing in
|
|
# different places for different things
|
|
#
|
|
fullkeys=self.histograms.keys()
|
|
#
|
|
# if no data could be read - stop here
|
|
#
|
|
if len(fullkeys)<1:
|
|
self.stopWorking()
|
|
msg="No verification data could be found matching those criteria"
|
|
self.statusBarMsg(msg,"U")
|
|
return
|
|
#
|
|
# For buttons...get models/forecasthours actually in the data
|
|
#
|
|
fullkeys.sort()
|
|
fhrstrs=[]
|
|
modkeys=[]
|
|
for fullkey in fullkeys:
|
|
(mod,fhrstr)=fullkey.split("-")
|
|
if fhrstr not in fhrstrs:
|
|
fhrstrs.append(fhrstr)
|
|
if mod not in modkeys:
|
|
modkeys.append(mod)
|
|
#
|
|
# Change fhrstrs (sorted on 3-character 000-999) into
|
|
# smaller fhrkeys that are NOT all 3-characters wide
|
|
#
|
|
fhrstrs.sort()
|
|
fhrkeys=[]
|
|
for fhrstr in fhrstrs:
|
|
fhrkeys.append("%d"%int(fhrstr))
|
|
#
|
|
# If an Official button is in there...make it first
|
|
#
|
|
modkeys.sort()
|
|
if "Official" in modkeys:
|
|
idx=modkeys.index("Official")
|
|
del modkeys[idx]
|
|
modkeys.insert(0,"Official")
|
|
#
|
|
# Put the observed one last
|
|
#
|
|
if obsmodel in modkeys:
|
|
idx=modkeys.index(obsmodel)
|
|
del modkeys[idx]
|
|
modkeys.append(obsmodel)
|
|
#
|
|
# set colors for each model
|
|
#
|
|
self.colornames={}
|
|
index=0
|
|
for mod in modkeys:
|
|
self.colornames[mod]=self.COLORLIST[index]
|
|
index+=1
|
|
if index==len(self.COLORLIST):
|
|
index=0
|
|
#
|
|
# Setup first row of buttons (forecast hours)
|
|
#
|
|
self.setupBut1(fhrkeys,numbuttons=NUMTBUTTONS,arrows=1,width=3)
|
|
#
|
|
# Setup second row of buttons (models)
|
|
#
|
|
self.setupBut2(modkeys,numbuttons=NUMMBUTTONS,arrows=1)
|
|
#
|
|
# Get min/max of forecast/observed values that need to be shown
|
|
#
|
|
fullmin=999999.0
|
|
fullmax=-999999.0
|
|
self.setWorking("%s: getting max/min"%(workStart))
|
|
tothisto=zeros((self.histonumbins,))
|
|
maxvalue=zeros((self.histonumbins,))
|
|
minvalue=zeros((self.histonumbins,))+9999999.0
|
|
for key in self.histograms.keys():
|
|
tothisto+=self.histograms[key]
|
|
nums=self.histograms[key]/float(self.numCases[key])
|
|
maxvalue=maximum(maxvalue,nums)
|
|
minvalue=where(greater(nums,0.0),minimum(minvalue,nums),minvalue)
|
|
for i in xrange(self.histonumbins):
|
|
minval=self.histomin+(i*self.histowidth)
|
|
maxval=minval+self.histowidth
|
|
print "%3d %5.3f--%5.3f %d"%(i,minval,maxval,tothisto[i])
|
|
if tothisto[i]>0:
|
|
fullmin=min(minval,fullmin)
|
|
fullmax=max(maxval,fullmax)
|
|
#print " fullmin:",fullmin
|
|
#print " fullmax:",fullmax
|
|
#fullmin=0.025
|
|
#fullmax=2.025
|
|
#print " fullmin:",fullmin
|
|
#print " fullmax:",fullmax
|
|
#
|
|
# If not many bins shown (i.e. nearly constant values)...add bins
|
|
# up and down until we get 15 bins - so our values are 'centered' in
|
|
# a reaonably wide graph
|
|
#
|
|
numbins=float(fullmax-fullmin-self.histowidth)/float(self.histowidth)
|
|
if numbins<15:
|
|
while numbins<15:
|
|
fullmax=minimum(fullmax+self.histowidth,parmMaxval+self.histohalf)
|
|
fullmin=maximum(fullmin-self.histowidth,parmMinval-self.histohalf)
|
|
numbins=float(fullmax-fullmin-self.histowidth)/float(self.histowidth)
|
|
if ((numbins<15)and(fullmin<parmMinval)and(fullmax>parmMaxval)):
|
|
numbins=16
|
|
#print " fullmin:",fullmin
|
|
#print " fullmax:",fullmax
|
|
#
|
|
#
|
|
#
|
|
numticks=25
|
|
tickInterval=self.niceNumDec((fullmax-fullmin)/float(numticks-1),1)
|
|
print "the tickInterval with 25 desired is:",tickInterval
|
|
#
|
|
# Dont let tick interval be smaller than parm precision
|
|
#
|
|
mintick=10**(-parmPrecision)
|
|
tickInterval=max(tickInterval,mintick)
|
|
print "after checking against precision...tickInterval is:",tickInterval
|
|
#
|
|
# Set the minimum graph one tick interval below the minimum...but
|
|
# not below the parm minimum value
|
|
#
|
|
graphmin=(floor(float(fullmin)/float(tickInterval))-1)*tickInterval
|
|
graphmin=maximum(graphmin,parmMinval)
|
|
#
|
|
# Set the maximum graph one tick interval above the maximum...but
|
|
# not above the parm maximum value
|
|
#
|
|
graphmax=(floor(float(fullmax)/float(tickInterval))+2)*tickInterval
|
|
graphmax=minimum(graphmax,parmMaxval)
|
|
print "so final x-coordinate graph from min/max:",graphmin,graphmax
|
|
#
|
|
# Find the maximum Y value for the bins being displayed
|
|
#
|
|
maxnum=0
|
|
minnum=999999
|
|
for i in xrange(self.histonumbins):
|
|
minval=self.histomin+(i*self.histowidth)
|
|
maxval=minval+self.histowidth
|
|
if ((minval>=fullmin)and(maxval<=fullmax)):
|
|
testmax=maxvalue[i]
|
|
maxnum=max(maxnum,testmax)
|
|
testmin=minvalue[i]
|
|
minnum=min(minnum,testmin)
|
|
print "the maximum value to display is:",maxnum
|
|
print "the minimum value to display is:",minnum
|
|
vint=self.niceNumDec(maxnum/20,1)
|
|
print "the vertical tick interval: vint:",vint
|
|
maxnum=(int(float(maxnum)/float(vint))+1)*vint
|
|
print "the maxnumber to graph is:",maxnum
|
|
#
|
|
#
|
|
#
|
|
#
|
|
left=self.cd.curwidth*(175.0/700.0)
|
|
right=self.cd.curwidth*(525.0/700.0)
|
|
bot=self.cd.curheight*(130.0/530.0)
|
|
top=self.cd.curheight*(480.0/530.0)
|
|
if ((verType==1)or(parmRateFlag==1)):
|
|
logflag=1
|
|
logmax=log(maxnum)
|
|
logmin=log(minnum)
|
|
print "old min/max=%f,%f"%(minnum,maxnum)
|
|
print "new log range: %7.3f %7.3f"%(logmin,logmax)
|
|
self.setgraph(graphmin,graphmax,logmin,logmax,left,right,bot,top)
|
|
self.logvalhaxes(graphmin,graphmax,tickInterval,logmin,logmax,parm)
|
|
else:
|
|
logflag=0
|
|
self.setgraph(graphmin,graphmax,0,maxnum,left,right,bot,top)
|
|
self.valhaxes(graphmin,graphmax,tickInterval,maxnum,vint,parm)
|
|
|
|
|
|
ul1="Value Histogram - %s"%parm
|
|
self.cdLabels(ul1,totalpoints,dateStyle,dateType,numDays,fromDay,dayList,cycleList)
|
|
#
|
|
# Draw each histogram
|
|
#
|
|
totalcount=len(self.histograms.keys())
|
|
count=0
|
|
for key in self.histograms.keys():
|
|
count+=1
|
|
if self.setAndCheckWorking("%s: drawing histogram %d of %d"%(workStart,count,totalcount))==1:
|
|
self.stopWorking()
|
|
return
|
|
tagbase=key.split("-")
|
|
mod=tagbase[0]
|
|
fhr=int(tagbase[1])
|
|
fhrstr="f%d"%fhr
|
|
tagtuple=(mod,fhrstr)
|
|
flabel="%d-hr forecast"%fhr
|
|
self.labelLine(flabel,3,justify="right",tags=tagtuple)
|
|
|
|
colorname=self.colornames[mod]
|
|
bins=self.histograms[key]/float(self.numCases[key])
|
|
nbin=bins.shape[0]
|
|
for i in xrange(nbin):
|
|
#
|
|
# get x-coords of bin, and ignore bins outside the range
|
|
# of x-coordinates that we are showing
|
|
#
|
|
x1=max(self.histomin+(i*self.histowidth),graphmin)
|
|
x2=min(self.histomin+((i+1)*self.histowidth),graphmax)
|
|
if x1<fullmin:
|
|
continue
|
|
if x2>fullmax:
|
|
continue
|
|
#
|
|
# Logarithmic y-values a little different
|
|
#
|
|
if logflag==1:
|
|
y=bins[i]
|
|
if y>0.0:
|
|
logy=log(y)
|
|
logy=min(logy,logmax)
|
|
if i==0:
|
|
(sx1,sy1)=self.graphcoord(x1,logmin)
|
|
else:
|
|
yold=bins[i-1]
|
|
if yold>0.0:
|
|
logyold=log(yold)
|
|
else:
|
|
logyold=logmin
|
|
(sx1,sy1)=self.graphcoord(x1,logyold)
|
|
(sx2,sy2)=self.graphcoord(x1,logy)
|
|
(sx3,sy3)=self.graphcoord(x2,logy)
|
|
if ((i+1)==nbin):
|
|
(sx4,sy4)=self.graphcoord(x2,logmin)
|
|
self.cd.canvas.create_line(sx1,sy1,sx2,sy2,sx3,sy3,sx4,sy4,fill=colorname,tags=tagtuple)
|
|
elif bins[i+1]==0:
|
|
(sx4,sy4)=self.graphcoord(x2,logmin)
|
|
self.cd.canvas.create_line(sx1,sy1,sx2,sy2,sx3,sy3,sx4,sy4,fill=colorname,tags=tagtuple)
|
|
else:
|
|
self.cd.canvas.create_line(sx1,sy1,sx2,sy2,sx3,sy3,fill=colorname,tags=tagtuple)
|
|
#
|
|
# Normal graphing for non-logarithmic y-values
|
|
#
|
|
else:
|
|
y=bins[i]
|
|
if y>0:
|
|
y=min(y,maxnum)
|
|
if i==0:
|
|
(sx1,sy1)=self.graphcoord(x1,0)
|
|
else:
|
|
yold=min(bins[i-1],maxnum)
|
|
(sx1,sy1)=self.graphcoord(x1,yold)
|
|
(sx2,sy2)=self.graphcoord(x1,y)
|
|
(sx3,sy3)=self.graphcoord(x2,y)
|
|
if ((i+1)==nbin):
|
|
(sx4,sy4)=self.graphcoord(x2,0)
|
|
self.cd.canvas.create_line(sx1,sy1,sx2,sy2,sx3,sy3,sx4,sy4,fill=colorname,tags=tagtuple)
|
|
elif bins[i+1]==0:
|
|
(sx4,sy4)=self.graphcoord(x2,0)
|
|
self.cd.canvas.create_line(sx1,sy1,sx2,sy2,sx3,sy3,sx4,sy4,fill=colorname,tags=tagtuple)
|
|
else:
|
|
self.cd.canvas.create_line(sx1,sy1,sx2,sy2,sx3,sy3,fill=colorname,tags=tagtuple)
|
|
self.but2state[mod]=1
|
|
self.but1state[fhrstr]=1
|
|
|
|
|
|
startBut1(self)
|
|
startBut2(self)
|
|
|
|
self.stopWorking()
|
|
self.moveCD()
|
|
self.cd.deiconify()
|
|
self.cd.lift()
|
|
return
|
|
#==================================================================
|
|
# expectedValue - display expected value for forecast values
|
|
#
|
|
#
|
|
def expectedValue(self,parmList,cycleList,modelList,obsmodel,
|
|
fcstrList,fhrStart,fhrEnd,dateType,numDays,fromDay,
|
|
dayList,dateStyle,scale,commonCases,accumHours,
|
|
accumFreq):
|
|
#
|
|
# Clear display - setup title
|
|
#
|
|
parm=parmList[0]
|
|
self.cd.canvas.delete(Tkinter.ALL)
|
|
self.cd.title("Expected Value Distribution - %s"%parm)
|
|
#
|
|
#
|
|
#
|
|
workStart="Working on Expected Value Distribution"
|
|
self.startWorking(workStart,optionRemove=0)
|
|
#
|
|
#
|
|
#
|
|
NUMTBUTTONS=12 # normal number of time buttons on a row - configure
|
|
NUMMBUTTONS=6 # normal number of model buttons on a row - configure
|
|
#
|
|
# get the active EditArea into ea. If the active edit area is
|
|
# None - then assume they want to run it over the entire grid
|
|
#
|
|
editArea=self.getActiveEditArea()
|
|
editAreaMask=self.encodeEditArea(editArea)
|
|
npts=add.reduce(add.reduce(editAreaMask))
|
|
if (npts==0):
|
|
editArea.invert()
|
|
editAreaMask=self.encodeEditArea(editArea)
|
|
eaflat=ravel(editAreaMask)
|
|
totalpoints=add.reduce(eaflat)
|
|
#
|
|
# make space for saving data
|
|
#
|
|
self.flists={} # storage for fcst values for each model/forecast hour
|
|
self.olists={} # storage for obs values for each model/forecast hour
|
|
fullmin=999999.0
|
|
fullmax=-999999.0
|
|
#
|
|
# Loop over parm and model
|
|
#
|
|
totaliters=len(modelList)
|
|
iter=0
|
|
#
|
|
# For vectors...the parm to read might be different than
|
|
# the name of the parm
|
|
#
|
|
readParm=parm
|
|
last3="xxx"
|
|
if len(parm)>3:
|
|
last3=parm[-3:]
|
|
if ((last3=="Spd")or(last3=="Dir")):
|
|
readParm=parm[:-3]
|
|
#
|
|
# Get information about the parm we are reading
|
|
#
|
|
(parmUnits,parmPrecision,parmMinval,parmMaxval,parmRateFlag,parmColorTable,
|
|
parmDisplayMinval,parmDisplayMaxval)=self.getParmInfo(self.mutableID(),parm)
|
|
obsParm=self.VU.getObsParm(readParm)
|
|
verType=self.VU.getVerType(readParm)
|
|
datatype=self.VU.getVerParmType(readParm)
|
|
if ((datatype==1)and(last3=="Dir")):
|
|
parmMinval=0
|
|
parmMaxval=360
|
|
#
|
|
# get binwidth and bigerr for parm...but for vectors its
|
|
# complicated by dir/mag/vecerr options
|
|
#
|
|
binwidth=self.VU.getVerBinWidth(readParm)
|
|
if datatype==1:
|
|
(bwMag,bwDir)=binwidth
|
|
if last3=="Dir":
|
|
binwidth=bwDir
|
|
else:
|
|
binwidth=bwMag
|
|
#
|
|
# Setup histogram binning routines
|
|
#
|
|
self.histosetup(parmMinval,parmMaxval,binwidth)
|
|
#
|
|
# Get mode for reading obs grids
|
|
#
|
|
obsGridMode=self.getReadMode(obsmodel,obsParm,0)
|
|
#
|
|
# Get case times/records for all models
|
|
#
|
|
caseInfo=self.VU.getCases(readParm,modelList,obsParm,obsmodel,
|
|
dateStyle,dateType,fromDay=fromDay,
|
|
numDays=numDays,dayList=dayList,
|
|
fcstrs=fcstrList,cycles=cycleList,
|
|
fhrStart=fhrStart,fhrEnd=fhrEnd,
|
|
accumHours=accumHours,accumFreq=accumFreq,
|
|
commonCases=commonCases,basetimeOffsets=1,
|
|
callbackMethod=self.workingCommon)
|
|
if self.checkWorking()==1:
|
|
self.stopWorking()
|
|
return
|
|
#
|
|
# Loop over each model
|
|
#
|
|
for model in modelList:
|
|
iter+=1
|
|
workNow=workStart+":%s (%d of %d)"%(model,iter,totaliters)
|
|
#
|
|
fcstGridMode=self.getReadMode(model,readParm)
|
|
#
|
|
# Get all the cases for this model
|
|
#
|
|
cases=caseInfo[model]
|
|
#
|
|
# Sort cases by the start/end time, not the basetime
|
|
#
|
|
casekeys=cases.keys()
|
|
casekeys.sort(lambda x,y: cmp(x.split(",",1)[1],y.split(",",1)[1]))
|
|
totalcount=len(casekeys)
|
|
self.VU.logMsg("reading %d cases for %s"%(totalcount,model),1)
|
|
count=0
|
|
lastobs=""
|
|
for key in casekeys:
|
|
count+=1
|
|
#self.VU.logMsg("%s : %s memory:%d resident:%d"%(model,key,memory(),resident()))
|
|
if self.setAndCheckWorking("%s: %d of %d"%(workNow,count,totalcount))==1:
|
|
self.stopWorking()
|
|
return
|
|
(basetimestr,stimestr,etimestr)=key.split(",")
|
|
basetime=int(basetimestr)
|
|
stime=int(stimestr)
|
|
etime=int(etimestr)
|
|
(frecList,orecList)=cases[key]
|
|
#
|
|
# Dont make stats for obs not yet complete
|
|
#
|
|
if etime>time.time():
|
|
continue
|
|
#
|
|
# Dont include negative forecast hours
|
|
#
|
|
fhr=self.VU.getFcstHour(basetime,stime)
|
|
if fhr<0:
|
|
continue
|
|
#
|
|
# string to store grid under depends on model and forecast hour
|
|
#
|
|
saveKey="%s-%3.3d"%(model,fhr)
|
|
#
|
|
# If a new and different obs time - read the obs data
|
|
#
|
|
obskey=key.split(",",1)[1]
|
|
#self.VU.logMsg("before getObs: %d %d"%(memory(),resident()))
|
|
|
|
obsdata=self.VU.getVerGrids(obsmodel,basetime,obsParm,
|
|
stime,etime,mode=obsGridMode,
|
|
recList=orecList)
|
|
#obsdata1=copy.copy(obsdata)
|
|
#del obsdata
|
|
obsdata1=obsdata
|
|
obsdata1=self.scaleGrid(obsdata1,scale,datatype)
|
|
#self.VU.logMsg("after scaling: %d %d"%(memory(),resident()))
|
|
#
|
|
# For probabilistic variables...calculate the
|
|
# observed 'yes/no' value
|
|
#
|
|
if verType==1:
|
|
obsdata1=self.getProbVerGrid(readParm,obsdata1)*100.0
|
|
#self.VU.logMsg("after probing: %d %d"%(memory(),resident()))
|
|
#
|
|
# cant do a value histogram of vector wind
|
|
# errors...so those get changed to windSpd
|
|
#
|
|
if ((datatype!=1)or(last3 in ["Spd","Dir"])):
|
|
if last3=="Spd":
|
|
obsgrid=obsdata1[0]
|
|
elif last3=="Dir":
|
|
obsgrid=obsdata1[1]
|
|
else:
|
|
obsgrid=obsdata1
|
|
else:
|
|
obsgrid=obsdata1[0]
|
|
obsonly=compress(eaflat,ravel(obsgrid))
|
|
obsList=list(obsonly)
|
|
del obsonly
|
|
del obsgrid
|
|
del obsdata1
|
|
#self.VU.logMsg("down to obsList: %d %d"%(memory(),resident()))
|
|
minObs=min(obsList)
|
|
maxObs=max(obsList)
|
|
fullmin=min(minObs,fullmin)
|
|
fullmax=max(maxObs,fullmax)
|
|
if self.olists.has_key(saveKey):
|
|
self.olists[saveKey].extend(obsList)
|
|
self.VU.logMsg("extending")
|
|
else:
|
|
self.olists[saveKey]=[]
|
|
self.olists[saveKey].extend(obsList)
|
|
self.VU.logMsg("new key")
|
|
#self.VU.logMsg("after adding: %d %d"%(memory(),resident()))
|
|
del obsList
|
|
#self.VU.logMsg("del of obsList: %d %d"%(memory(),resident()))
|
|
#
|
|
# Read forecast grid and calculate error grid
|
|
#
|
|
#self.VU.logMsg("before getGrids: %d %d"%(memory(),resident()))
|
|
fcstdata=self.VU.getVerGrids(model,basetime,readParm,
|
|
stime,etime,mode=fcstGridMode,
|
|
recList=frecList)
|
|
#self.VU.logMsg("after getGrids: %d %d"%(memory(),resident()))
|
|
#fcstdata1=copy.copy(fcstdata)
|
|
#self.VU.logMsg("after copy: %d %d"%(memory(),resident()))
|
|
#del fcstdata
|
|
#self.VU.logMsg("after del: %d %d"%(memory(),resident()))
|
|
fcstdata1=fcstdata
|
|
fcstdata1=self.scaleGrid(fcstdata1,scale,datatype)
|
|
#
|
|
# Get the error, handling vector error, etc.
|
|
#
|
|
if ((datatype!=1)or(last3 in ["Spd","Dir"])):
|
|
if last3=="Spd":
|
|
fcstgrid=fcstdata1[0]
|
|
elif last3=="Dir":
|
|
fcstgrid=fcstdata1[1]
|
|
else:
|
|
fcstgrid=fcstdata1
|
|
else:
|
|
fcstgrid=fcstdata1[0]
|
|
fcstonly=compress(eaflat,ravel(fcstgrid))
|
|
self.VU.logMsg("pts to save:%s"%fcstonly.shape)
|
|
del fcstgrid
|
|
del fcstdata1
|
|
fcstList=list(fcstonly)
|
|
del fcstonly
|
|
#self.VU.logMsg("after fcstList: %d %d"%(memory(),resident()))
|
|
minFcst=min(fcstList)
|
|
maxFcst=max(fcstList)
|
|
fullmin=min(minFcst,fullmin)
|
|
fullmax=max(maxFcst,fullmax)
|
|
#self.VU.logMsg("after maxmin : %d %d"%(memory(),resident()))
|
|
#
|
|
# Add values forecast lists for same model/fhr
|
|
#
|
|
#
|
|
if self.flists.has_key(saveKey):
|
|
self.flists[saveKey].extend(fcstList)
|
|
self.VU.logMsg("extending")
|
|
else:
|
|
self.flists[saveKey]=[]
|
|
self.flists[saveKey].extend(fcstList)
|
|
self.VU.logMsg("new key")
|
|
#self.VU.logMsg("after saving: %d %d"%(memory(),resident()))
|
|
del fcstList
|
|
#self.VU.logMsg("after del fList: %d %d"%(memory(),resident()))
|
|
#
|
|
# Get all the keys that will be displayed
|
|
#
|
|
fullkeys=self.flists.keys()
|
|
#
|
|
# if no data could be read - stop here
|
|
#
|
|
if len(fullkeys)<1:
|
|
self.stopWorking()
|
|
msg="No verification data could be found matching those criteria"
|
|
self.statusBarMsg(msg,"U")
|
|
return
|
|
#
|
|
# For buttons...get models/forecasthours actually in the data
|
|
#
|
|
fullkeys.sort()
|
|
fhrstrs=[]
|
|
modkeys=[]
|
|
for fullkey in fullkeys:
|
|
(mod,fhrstr)=fullkey.split("-")
|
|
if fhrstr not in fhrstrs:
|
|
fhrstrs.append(fhrstr)
|
|
if mod not in modkeys:
|
|
modkeys.append(mod)
|
|
#
|
|
# Change fhrstrs (sorted on 3-character 000-999) into
|
|
# smaller fhrkeys that are NOT all 3-characters wide
|
|
#
|
|
fhrstrs.sort()
|
|
fhrkeys=[]
|
|
for fhrstr in fhrstrs:
|
|
fhrkeys.append("%d"%int(fhrstr))
|
|
#
|
|
# If an Official button is in there...make it first
|
|
#
|
|
modkeys.sort()
|
|
if "Official" in modkeys:
|
|
idx=modkeys.index("Official")
|
|
del modkeys[idx]
|
|
modkeys.insert(0,"Official")
|
|
#
|
|
# set colors for each model
|
|
#
|
|
self.colornames={}
|
|
index=0
|
|
for mod in modkeys:
|
|
self.colornames[mod]=self.COLORLIST[index]
|
|
index+=1
|
|
if index==len(self.COLORLIST):
|
|
index=0
|
|
#
|
|
# Setup first row of buttons (forecast hours)
|
|
#
|
|
self.setupBut1(fhrkeys,numbuttons=NUMTBUTTONS,arrows=1,width=3)
|
|
#
|
|
# Setup second row of buttons (models)
|
|
#
|
|
self.setupBut2(modkeys,numbuttons=NUMMBUTTONS,arrows=1)
|
|
#
|
|
# If not many bins shown (i.e. nearly constant values)...add bins
|
|
# up and down until we get at least 15 bins - so our values are
|
|
# 'centered' in a reaonably wide graph
|
|
#
|
|
numbins=int(float(fullmax-fullmin)/float(binwidth))+1
|
|
if numbins<15:
|
|
while numbins<15:
|
|
fullmax=min(fullmax+binwidth,parmMaxval)
|
|
fullmin=max(fullmin-binwidth,parmMinval)
|
|
numbins=int(float(fullmax-fullmin)/float(binwidth))+1
|
|
if ((numbins<15)and(fullmin==parmMinval)and(fullmax==parmMaxval)):
|
|
numbins=16
|
|
#
|
|
#
|
|
#
|
|
numticks=25
|
|
tickInterval=self.niceNumDec((fullmax-fullmin)/float(numticks-1),1)
|
|
#
|
|
# Dont let tick interval be smaller than parm precision
|
|
#
|
|
mintick=10**(-parmPrecision)
|
|
tickInterval=max(tickInterval,mintick)
|
|
#
|
|
# Set the minimum graph one tick interval below the minimum...but
|
|
# not below the parm minimum value
|
|
#
|
|
graphmin=(floor(float(fullmin)/float(tickInterval))-1)*tickInterval
|
|
graphmin=maximum(graphmin,parmMinval)
|
|
#
|
|
# Set the maximum graph one tick interval above the maximum...but
|
|
# not above the parm maximum value
|
|
#
|
|
graphmax=(floor(float(fullmax)/float(tickInterval))+2)*tickInterval
|
|
graphmax=minimum(graphmax,parmMaxval)
|
|
#
|
|
#
|
|
#
|
|
numTicks=int(float(graphmax-graphmin)/float(tickInterval))+1
|
|
#
|
|
# Set up the graph axes
|
|
#
|
|
left=self.cd.curwidth*(50.0/700.0)
|
|
right=self.cd.curwidth*(650.0/700.0)
|
|
bot=self.cd.curheight*(100.0/530.0)
|
|
top=self.cd.curheight*(480.0/530.0)
|
|
self.setgraph(graphmin,graphmax,graphmin,graphmax,left,right,bot,top)
|
|
self.expaxes(graphmin,graphmax,tickInterval)
|
|
#
|
|
# Label the top of the graph
|
|
#
|
|
ul1="Expected %s Value for %s Forecast"%(obsmodel,parm)
|
|
self.cdLabels(ul1,totalpoints,dateStyle,dateType,numDays,fromDay,dayList,cycleList)
|
|
#
|
|
# for rateParms, or probability parms, label the length of periods
|
|
#
|
|
if ((verType==1)or(parmRateFlag==1)):
|
|
self.labelLine("%d-hr periods"%accumHours,3)
|
|
#
|
|
# Draw
|
|
#
|
|
totalcount=len(fullkeys)
|
|
count=0
|
|
for key in fullkeys:
|
|
count+=1
|
|
self.VU.logMsg("graph %d memory:%d resident:%d"%(count,memory(),resident()))
|
|
if self.setAndCheckWorking("%s: drawing graph %d of %d"%(workStart,count,totalcount))==1:
|
|
self.stopWorking()
|
|
return
|
|
tagbase=key.split("-")
|
|
mod=tagbase[0]
|
|
modnum=modelList.index(mod)
|
|
fhr=int(tagbase[1])
|
|
fhrstr="f%d"%fhr
|
|
tagtuple=(mod,fhrstr)
|
|
flabel="%d-hr forecast"%fhr
|
|
self.labelLine(flabel,3,justify="right",tags=tagtuple)
|
|
|
|
colorname=self.colornames[mod]
|
|
#
|
|
# Turn lists for this model/time back into arrays
|
|
#
|
|
fcstArray=array(self.flists[key])
|
|
obsArray=array(self.olists[key])
|
|
prevAvg=-99999.9
|
|
for i in xrange(numTicks):
|
|
value=graphmin+(i*tickInterval)
|
|
valuelow=value-(float(tickInterval)/2.0)
|
|
vl1=value-(float(tickInterval)/6.0)
|
|
valuehigh=value+(float(tickInterval)/2.0)
|
|
vh1=value+(float(tickInterval)/6.0)
|
|
pts=logical_and(greater_equal(fcstArray,valuelow),less(fcstArray,valuehigh))
|
|
if sometrue(pts):
|
|
#obsDist=sort(compress(pts,obsArray))
|
|
obsDist=compress(pts,obsArray)
|
|
numCases=obsDist.shape[0]
|
|
#minObs=obsDist[0]
|
|
minObs=minimum.reduce(obsDist)
|
|
#maxObs=obsDist[numCases-1]
|
|
maxObs=maximum.reduce(obsDist)
|
|
avgObs=float(add.reduce(obsDist))/float(numCases)
|
|
avgObsSquared=float(add.reduce(obsDist*obsDist))/float(numCases)
|
|
std=sqrt(avgObsSquared-(avgObs*avgObs))
|
|
#if numCases>1:
|
|
# midObs=obsDist[numCases/2]
|
|
#else:
|
|
# midObs=avgObs
|
|
#if numCases>3:
|
|
# q1Obs=obsDist[numCases/4]
|
|
# q3Obs=obsDist[(3*numCases)/4]
|
|
#else:
|
|
# q1Obs=avgObs
|
|
# q3Obs=avgObs
|
|
#
|
|
# Graph the average
|
|
#
|
|
(x1,y1)=self.graphcoord(valuelow,avgObs)
|
|
(x2,y2)=self.graphcoord(valuehigh,avgObs)
|
|
if prevAvg>-99999.0:
|
|
self.cd.canvas.create_line(x1,prevAvg,x1,y1,x2,y2,fill=colorname,tags=tagtuple)
|
|
else:
|
|
self.cd.canvas.create_line(x1,y1,x2,y2,fill=colorname,tags=tagtuple)
|
|
prevAvg=y1
|
|
#
|
|
# For everything except probability parms...plot min/max/std
|
|
#
|
|
if verType!=1:
|
|
#
|
|
# Plot the min
|
|
#
|
|
(x1,y1)=self.graphcoord(vl1,minObs)
|
|
(x2,y2)=self.graphcoord(vh1,minObs)
|
|
self.cd.canvas.create_line(x1,y1,x2,y2,fill=colorname,tags=tagtuple)
|
|
#
|
|
# Plot the max
|
|
#
|
|
(x1,y1)=self.graphcoord(vl1,maxObs)
|
|
(x2,y2)=self.graphcoord(vh1,maxObs)
|
|
self.cd.canvas.create_line(x1,y1,x2,y2,fill=colorname,tags=tagtuple)
|
|
q1Obs=avgObs-std
|
|
q3Obs=avgObs+std
|
|
(x1,y1)=self.graphcoord(valuelow,q1Obs)
|
|
(x2,y2)=self.graphcoord(valuehigh,q3Obs)
|
|
self.cd.canvas.create_polygon(x1,y1,x1,y2,x2,y2,x2,y1,stipple="gray25",fill=colorname,outline="",tags=tagtuple)
|
|
del pts
|
|
self.but2state[mod]=1
|
|
self.but1state[fhrstr]=1
|
|
del fcstArray
|
|
del obsArray
|
|
|
|
startBut1(self)
|
|
startBut2(self)
|
|
|
|
del self.flists
|
|
del self.olists
|
|
|
|
self.stopWorking()
|
|
self.moveCD()
|
|
self.cd.deiconify()
|
|
self.cd.lift()
|
|
self.VU.logMsg("expected value done memory:%d resident:%d"%(memory(),resident()))
|
|
return
|
|
#==================================================================
|
|
# scatterPlot - display scatterplot
|
|
#
|
|
#
|
|
def scatterPlot(self,parmList,cycleList,modelList,obsmodel,
|
|
fcstrList,fhrStart,fhrEnd,dateType,numDays,fromDay,
|
|
dayList,dateStyle,scale,commonCases,accumHours,
|
|
accumFreq):
|
|
#
|
|
# Clear display - setup title
|
|
#
|
|
parm=parmList[0]
|
|
self.cd.canvas.delete(Tkinter.ALL)
|
|
self.cd.title("Scatterplot - %s"%parm)
|
|
#
|
|
#
|
|
#
|
|
workStart="Working on Verifying Value Distribution"
|
|
self.startWorking(workStart,optionRemove=0)
|
|
#
|
|
#
|
|
#
|
|
NUMTBUTTONS=12 # normal number of time buttons on a row - configure
|
|
NUMMBUTTONS=6 # normal number of model buttons on a row - configure
|
|
#
|
|
# get the active EditArea into ea. If the active edit area is
|
|
# None - then assume they want to run it over the entire grid
|
|
#
|
|
editArea=self.getActiveEditArea()
|
|
editAreaMask=self.encodeEditArea(editArea)
|
|
npts=add.reduce(add.reduce(editAreaMask))
|
|
if (npts==0):
|
|
editArea.invert()
|
|
editAreaMask=self.encodeEditArea(editArea)
|
|
eaflat=ravel(editAreaMask)
|
|
totalpoints=add.reduce(eaflat)
|
|
#
|
|
# make space for saving data
|
|
#
|
|
self.flists={} # storage for fcst values for each model/forecast hour
|
|
self.olists={} # storage for obs values for each model/forecast hour
|
|
fullmin=999999.0
|
|
fullmax=-999999.0
|
|
#
|
|
# Loop over parm and model
|
|
#
|
|
totaliters=len(modelList)
|
|
iter=0
|
|
#
|
|
# For vectors...the parm to read might be different than
|
|
# the name of the parm
|
|
#
|
|
readParm=parm
|
|
last3="xxx"
|
|
if len(parm)>3:
|
|
last3=parm[-3:]
|
|
if ((last3=="Spd")or(last3=="Dir")):
|
|
readParm=parm[:-3]
|
|
#
|
|
# Get information about the parm we are reading
|
|
#
|
|
(parmUnits,parmPrecision,parmMinval,parmMaxval,parmRateFlag,parmColorTable,
|
|
parmDisplayMinval,parmDisplayMaxval)=self.getParmInfo(self.mutableID(),parm)
|
|
obsParm=self.VU.getObsParm(readParm)
|
|
verType=self.VU.getVerType(readParm)
|
|
datatype=self.VU.getVerParmType(readParm)
|
|
if ((datatype==1)and(last3=="Dir")):
|
|
parmMinval=0
|
|
parmMaxval=360
|
|
#
|
|
# get binwidth and bigerr for parm...but for vectors its
|
|
# complicated by dir/mag/vecerr options
|
|
#
|
|
binwidth=self.VU.getVerBinWidth(readParm)
|
|
if datatype==1:
|
|
(bwMag,bwDir)=binwidth
|
|
if last3=="Dir":
|
|
binwidth=bwDir
|
|
else:
|
|
binwidth=bwMag
|
|
#
|
|
# Setup histogram binning routines
|
|
#
|
|
self.histosetup(parmMinval,parmMaxval,binwidth)
|
|
#
|
|
# Get mode for reading obs grids
|
|
#
|
|
obsGridMode=self.getReadMode(obsmodel,obsParm,0)
|
|
#
|
|
# Get case times/records for all models
|
|
#
|
|
caseInfo=self.VU.getCases(readParm,modelList,obsParm,obsmodel,
|
|
dateStyle,dateType,fromDay=fromDay,
|
|
numDays=numDays,dayList=dayList,
|
|
fcstrs=fcstrList,cycles=cycleList,
|
|
fhrStart=fhrStart,fhrEnd=fhrEnd,
|
|
accumHours=accumHours,accumFreq=accumFreq,
|
|
commonCases=commonCases,basetimeOffsets=1,
|
|
callbackMethod=self.workingCommon)
|
|
if self.checkWorking()==1:
|
|
self.stopWorking()
|
|
return
|
|
#
|
|
# Loop over each model
|
|
#
|
|
for model in modelList:
|
|
iter+=1
|
|
workNow=workStart+":%s (%d of %d)"%(model,iter,totaliters)
|
|
#
|
|
fcstGridMode=self.getReadMode(model,readParm)
|
|
#
|
|
# Get all the cases for this model
|
|
#
|
|
cases=caseInfo[model]
|
|
#
|
|
# Sort cases by the start/end time, not the basetime
|
|
#
|
|
casekeys=cases.keys()
|
|
casekeys.sort(lambda x,y: cmp(x.split(",",1)[1],y.split(",",1)[1]))
|
|
totalcount=len(casekeys)
|
|
self.VU.logMsg("reading %d cases for %s"%(totalcount,model),1)
|
|
count=0
|
|
lastobs=""
|
|
for key in casekeys:
|
|
count+=1
|
|
self.VU.logMsg("%s : %s"%(model,key),10)
|
|
if self.setAndCheckWorking("%s: %d of %d"%(workNow,count,totalcount))==1:
|
|
self.stopWorking()
|
|
return
|
|
(basetimestr,stimestr,etimestr)=key.split(",")
|
|
basetime=int(basetimestr)
|
|
stime=int(stimestr)
|
|
etime=int(etimestr)
|
|
(frecList,orecList)=cases[key]
|
|
#
|
|
# Dont make stats for obs not yet complete
|
|
#
|
|
if etime>time.time():
|
|
continue
|
|
#
|
|
# Dont include negative forecast hours
|
|
#
|
|
fhr=self.VU.getFcstHour(basetime,stime)
|
|
if fhr<0:
|
|
continue
|
|
#
|
|
# string to store grid under depends on model and forecast hour
|
|
#
|
|
saveKey="%s-%3.3d"%(model,fhr)
|
|
#
|
|
# If a new and different obs time - read the obs data
|
|
#
|
|
obskey=key.split(",",1)[1]
|
|
obsdata=self.VU.getVerGrids(obsmodel,basetime,obsParm,
|
|
stime,etime,mode=obsGridMode,
|
|
recList=orecList)
|
|
obsdata=self.scaleGrid(obsdata,scale,datatype)
|
|
#
|
|
# For probabilistic variables...calculate the
|
|
# observed 'yes/no' value
|
|
#
|
|
if verType==1:
|
|
obsdata=self.getProbVerGrid(readParm,obsdata)*100.0
|
|
#
|
|
# cant do a value histogram of vector wind
|
|
# errors...so those get changed to windSpd
|
|
#
|
|
if ((datatype!=1)or(last3 in ["Spd","Dir"])):
|
|
if last3=="Spd":
|
|
obsgrid=obsdata[0]
|
|
elif last3=="Dir":
|
|
obsgrid=obsdata[1]
|
|
else:
|
|
obsgrid=obsdata
|
|
obsonly=compress(eaflat,ravel(obsgrid))
|
|
else:
|
|
obsgrid=obsdata[0]
|
|
obsonly=compress(eaflat,ravel(obsgrid))
|
|
obsList=list(obsonly)
|
|
minObs=min(obsList)
|
|
maxObs=max(obsList)
|
|
fullmin=min(minObs,fullmin)
|
|
fullmax=max(maxObs,fullmax)
|
|
if self.olists.has_key(saveKey):
|
|
self.olists[saveKey].extend(obsList)
|
|
else:
|
|
self.olists[saveKey]=obsList
|
|
#
|
|
# Read forecast grid and calculate error grid
|
|
#
|
|
fcstdata=self.VU.getVerGrids(model,basetime,readParm,
|
|
stime,etime,mode=fcstGridMode,
|
|
recList=frecList)
|
|
fcstdata=self.scaleGrid(fcstdata,scale,datatype)
|
|
#
|
|
# Get the error, handling vector error, etc.
|
|
#
|
|
if ((datatype!=1)or(last3 in ["Spd","Dir"])):
|
|
if last3=="Spd":
|
|
fcstgrid=fcstdata[0]
|
|
elif last3=="Dir":
|
|
fcstgrid=fcstdata[1]
|
|
else:
|
|
fcstgrid=fcstdata
|
|
fcstonly=compress(eaflat,ravel(fcstgrid))
|
|
else:
|
|
fcstgrid=fcstdata[0]
|
|
fcstonly=compress(eaflat,ravel(fcstgrid))
|
|
fcstList=list(fcstonly)
|
|
minFcst=min(fcstList)
|
|
maxFcst=max(fcstList)
|
|
fullmin=min(minFcst,fullmin)
|
|
fullmax=max(maxFcst,fullmax)
|
|
#
|
|
# Add values forecast lists for same model/fhr
|
|
#
|
|
#
|
|
if self.flists.has_key(saveKey):
|
|
self.flists[saveKey].extend(fcstList)
|
|
else:
|
|
self.flists[saveKey]=fcstList
|
|
#
|
|
# Get all the keys that will be displayed
|
|
#
|
|
fullkeys=self.flists.keys()
|
|
#
|
|
# if no data could be read - stop here
|
|
#
|
|
if len(fullkeys)<1:
|
|
self.stopWorking()
|
|
msg="No verification data could be found matching those criteria"
|
|
self.statusBarMsg(msg,"U")
|
|
return
|
|
#
|
|
# For buttons...get models/forecasthours actually in the data
|
|
#
|
|
fullkeys.sort()
|
|
fhrstrs=[]
|
|
modkeys=[]
|
|
for fullkey in fullkeys:
|
|
(mod,fhrstr)=fullkey.split("-")
|
|
if fhrstr not in fhrstrs:
|
|
fhrstrs.append(fhrstr)
|
|
if mod not in modkeys:
|
|
modkeys.append(mod)
|
|
#
|
|
# Change fhrstrs (sorted on 3-character 000-999) into
|
|
# smaller fhrkeys that are NOT all 3-characters wide
|
|
#
|
|
fhrstrs.sort()
|
|
fhrkeys=[]
|
|
for fhrstr in fhrstrs:
|
|
fhrkeys.append("%d"%int(fhrstr))
|
|
#
|
|
# If an Official button is in there...make it first
|
|
#
|
|
modkeys.sort()
|
|
if "Official" in modkeys:
|
|
idx=modkeys.index("Official")
|
|
del modkeys[idx]
|
|
modkeys.insert(0,"Official")
|
|
#
|
|
# set colors for each model
|
|
#
|
|
self.colornames={}
|
|
index=0
|
|
for mod in modkeys:
|
|
self.colornames[mod]=self.COLORLIST[index]
|
|
index+=1
|
|
if index==len(self.COLORLIST):
|
|
index=0
|
|
#
|
|
# Setup first row of buttons (forecast hours)
|
|
#
|
|
self.setupBut1(fhrkeys,numbuttons=NUMTBUTTONS,arrows=1,width=3)
|
|
#
|
|
# Setup second row of buttons (models)
|
|
#
|
|
self.setupBut2(modkeys,numbuttons=NUMMBUTTONS,arrows=1)
|
|
#
|
|
# If not many bins shown (i.e. nearly constant values)...add bins
|
|
# up and down until we get at least 15 bins - so our values are
|
|
# 'centered' in a reaonably wide graph
|
|
#
|
|
numbins=int(float(fullmax-fullmin)/float(binwidth))+1
|
|
if numbins<15:
|
|
while numbins<15:
|
|
fullmax=min(fullmax+binwidth,parmMaxval)
|
|
fullmin=max(fullmin-binwidth,parmMinval)
|
|
numbins=int(float(fullmax-fullmin)/float(binwidth))+1
|
|
if ((numbins<15)and(fullmin==parmMinval)and(fullmax==parmMaxval)):
|
|
numbins=16
|
|
#
|
|
#
|
|
#
|
|
numticks=25
|
|
tickInterval=self.niceNumDec((fullmax-fullmin)/float(numticks-1),1)
|
|
#
|
|
# Dont let tick interval be smaller than parm precision
|
|
#
|
|
mintick=10**(-parmPrecision)
|
|
tickInterval=max(tickInterval,mintick)
|
|
#
|
|
# Set the minimum graph one tick interval below the minimum...but
|
|
# not below the parm minimum value
|
|
#
|
|
graphmin=(floor(float(fullmin)/float(tickInterval))-1)*tickInterval
|
|
graphmin=maximum(graphmin,parmMinval)
|
|
#
|
|
# Set the maximum graph one tick interval above the maximum...but
|
|
# not above the parm maximum value
|
|
#
|
|
graphmax=(floor(float(fullmax)/float(tickInterval))+2)*tickInterval
|
|
graphmax=minimum(graphmax,parmMaxval)
|
|
#
|
|
#
|
|
#
|
|
numTicks=int(float(graphmax-graphmin)/float(tickInterval))+1
|
|
#
|
|
# Set up the graph axes
|
|
#
|
|
left=self.cd.curwidth*(50.0/700.0)
|
|
right=self.cd.curwidth*(650.0/700.0)
|
|
bot=self.cd.curheight*(100.0/530.0)
|
|
top=self.cd.curheight*(480.0/530.0)
|
|
self.setgraph(graphmin,graphmax,graphmin,graphmax,left,right,bot,top)
|
|
self.valaxes(graphmin,graphmax,tickInterval)
|
|
#
|
|
numPts=totalpoints
|
|
ul1="Scatterplot - %s"%parm
|
|
self.cdLabels(ul1,numPts,dateStyle,dateType,numDays,fromDay,dayList,cycleList)
|
|
#
|
|
#
|
|
#
|
|
#self.probaxes()
|
|
#
|
|
#
|
|
#
|
|
MaxValuesToShow=1000
|
|
numbins=50
|
|
numPts=totalpoints
|
|
counts={}
|
|
maxcounts={}
|
|
maxnum=0
|
|
self.setWorking("%s: scanning scatterplots"%workStart)
|
|
for key in self.flists.keys():
|
|
maxnum=max(maxnum,len(self.flists[key]))
|
|
if self.checkWorking()==1:
|
|
self.stopWorking()
|
|
return
|
|
if maxnum>MaxValuesToShow:
|
|
binsize=float(graphmax-graphmin)/float(numbins)
|
|
#print "binsize=",binsize
|
|
totaldcount=len(self.flists.keys())
|
|
dcount=0
|
|
for key in self.flists.keys():
|
|
dcount+=1
|
|
self.setWorking("%s: large scatterplot thinning: %d of %d"%(workStart,dcount,totaldcount))
|
|
if self.checkWorking()==1:
|
|
self.stopWorking()
|
|
return
|
|
if len(self.flists[key])>MaxValuesToShow:
|
|
count=zeros((numbins,numbins))
|
|
xpos=minimum(((array(self.olists[key])-graphmin)/binsize).astype(int),numbins-1)
|
|
ypos=minimum(((array(self.flists[key])-graphmin)/binsize).astype(int),numbins-1)
|
|
xl=list(xpos)
|
|
yl=list(ypos)
|
|
for i in xrange(len(xl)):
|
|
x=xl[i]
|
|
y=yl[i]
|
|
count[y,x]+=1
|
|
maxcounts[key]=maximum.reduce(maximum.reduce(count))
|
|
#print "maxcounts[",key,"]=",maxcounts[key]
|
|
counts[key]=count
|
|
#
|
|
# Draw
|
|
#
|
|
totalcount=len(fullkeys)
|
|
count=0
|
|
for key in fullkeys:
|
|
count+=1
|
|
self.setWorking("%s: drawing scatterplot %d of %d"%(workStart,count,totalcount))
|
|
if self.checkWorking()==1:
|
|
self.stopWorking()
|
|
return
|
|
tagbase=key.split("-")
|
|
mod=tagbase[0]
|
|
modnum=modelList.index(mod)
|
|
fhr=int(tagbase[1])
|
|
fhrstr="f%d"%fhr
|
|
tagtuple=(mod,fhrstr)
|
|
flabel="%d-hr forecast"%fhr
|
|
self.labelLine(flabel,3,justify="right",tags=tagtuple)
|
|
|
|
colorname=self.colornames[mod]
|
|
|
|
if key not in maxcounts.keys():
|
|
ylist=self.flists[key]
|
|
xlist=self.olists[key]
|
|
for i in xrange(len(ylist)):
|
|
(x,y)=self.graphcoord(xlist[i],ylist[i])
|
|
self.cd.canvas.create_line(x-2,y,x+2,y,fill=colorname,tags=tagtuple)
|
|
self.cd.canvas.create_line(x,y-2,x,y+2,fill=colorname,tags=tagtuple)
|
|
else:
|
|
for i in xrange(numbins):
|
|
midx=graphmin+((i+0.5)*binsize)
|
|
for j in xrange(numbins):
|
|
midy=graphmin+((j+0.5)*binsize)
|
|
width=(float(counts[key][j,i])/float(maxcounts[key]))*binsize*0.5
|
|
(x0,y0)=self.graphcoord(midx-width,midy-width)
|
|
(x1,y1)=self.graphcoord(midx+width,midy+width)
|
|
if width>0.01:
|
|
self.cd.canvas.create_arc(x0,y0,x1,y1,fill=colorname,outline=colorname,start=0.0,extent=359.9,width=1.0,tags=tagtuple)
|
|
|
|
self.but2state[mod]=1
|
|
self.but1state[fhrstr]=1
|
|
|
|
startBut1(self)
|
|
startBut2(self)
|
|
|
|
self.stopWorking()
|
|
self.moveCD()
|
|
self.cd.deiconify()
|
|
self.cd.lift()
|
|
return
|
|
#==================================================================
|
|
# scaleGrid - smooth a grid by the scale amount. Correctly handles
|
|
# vectors indicated by datatype==1.
|
|
#
|
|
def scaleGrid(self,griddata,scale,datatype):
|
|
if scale>0:
|
|
if datatype!=1:
|
|
griddata=self.VU.smoothpm(griddata,scale)
|
|
else:
|
|
(gridmag,griddir)=griddata
|
|
(u,v)=self.MagDirToUV(gridmag,griddir)
|
|
us=self.VU.smoothpm(u,scale)
|
|
vs=self.VU.smoothpm(v,scale)
|
|
(gridmag,griddir)=self.UVToMagDir(us,vs)
|
|
griddata=(gridmag,griddir)
|
|
return griddata
|
|
#==================================================================
|
|
# moveCD - if the first time self.cd is displayed - move to a good
|
|
# location
|
|
#
|
|
def moveCD(self):
|
|
if self.cd.firstDisplay==1:
|
|
self.cd.update_idletasks()
|
|
geo=self.cd.geometry()
|
|
(mwh,mof)=geo.split("+",1)
|
|
(mw,mh)=mwh.split("x",1)
|
|
parentgeo=self.root.geometry()
|
|
(wh,of)=parentgeo.split("+",1)
|
|
(w,h)=wh.split("x",1)
|
|
(ox,oy)=of.split("+",1)
|
|
xoff=int(ox)+int(w)-int(mw)
|
|
yoff=int((int(h)-int(mh))/2.0)+int(oy)
|
|
newgeo=mwh+"+%d+%d"%(xoff,yoff)
|
|
self.cd.geometry(newgeo)
|
|
self.cd.firstDisplay=0
|
|
return
|
|
#==================================================================
|
|
# showStats - display point/area statistics
|
|
#
|
|
def ShowStats(self,DialogDict):
|
|
self.VU.logMsg("running ShowStats:")
|
|
|
|
plotType=DialogDict["PlotType"]
|
|
if plotType=="vs. Time":
|
|
self.makeTimeSeries(DialogDict)
|
|
if plotType=="vs. Fcst Hour":
|
|
self.makeFhourGraph(DialogDict)
|
|
return
|
|
#==================================================================
|
|
# makeTimeSeries - display time series for point/area
|
|
#
|
|
def makeTimeSeries(self,DialogDict):
|
|
self.VU.logMsg("running makeTimeSeries:")
|
|
display=DialogDict["Display"]
|
|
areaList=DialogDict["areaList"]
|
|
AreaCombine=DialogDict["AreaCombine"]
|
|
parmList=DialogDict["Parms"]
|
|
threshold=DialogDict["Threshold"]
|
|
cycleList=DialogDict["cycleList"]
|
|
modelList=DialogDict["Models"]
|
|
obsmodel=DialogDict["ObsModel"]
|
|
fcstrList=DialogDict["fcstrList"]
|
|
fhrStart=DialogDict["fhrStart"]
|
|
fhrEnd=DialogDict["fhrEnd"]
|
|
dateType=DialogDict["dateType"]
|
|
numDays=DialogDict["numDays"]
|
|
fromDay=DialogDict["fromDay"]
|
|
dayList=DialogDict["dayList"]
|
|
dateStyle=DialogDict["dateStyle"]
|
|
plotType=DialogDict["PlotType"]
|
|
scale=DialogDict["scale"]
|
|
commonCases=DialogDict["commonCases"]
|
|
accumHours=DialogDict["accumHours"]
|
|
accumFreq=DialogDict["accumFreq"]
|
|
TwoCatType=DialogDict["TwoCatType"]
|
|
TwoCatCond=DialogDict["TwoCatCond"]
|
|
TwoCatValue=DialogDict["TwoCatValue"]
|
|
TwoCatValueString=DialogDict["TwoCatValueString"]
|
|
#
|
|
# Check for good GUI input
|
|
#
|
|
ret=self.checkLists(modelList,parmList,cycleList,fcstrList,dateType,
|
|
dayList)
|
|
if ret==0:
|
|
return
|
|
#
|
|
# Check that we do not have too many things varying
|
|
#
|
|
if len(parmList)>1:
|
|
parmVary=1
|
|
else:
|
|
parmVary=0
|
|
if ((len(areaList)>1)and(AreaCombine==0)):
|
|
areaVary=1
|
|
else:
|
|
areaVary=0
|
|
if len(modelList)>1:
|
|
modelVary=1
|
|
else:
|
|
modelVary=0
|
|
totalVary=parmVary+areaVary+modelVary
|
|
if totalVary>1:
|
|
msg="Can only vary one of parm/area/model when doing 'vs. Time' graphs."
|
|
self.statusBarMsg(msg,"U")
|
|
return
|
|
#
|
|
# If nothing varying - pick model
|
|
#
|
|
if totalVary==0:
|
|
modelVary=1
|
|
#
|
|
# get list of names of selected edit areas into areaNames
|
|
#
|
|
areaNames=[]
|
|
nameList=self.VU.listEditAreas()
|
|
descList=self.VU.listEditAreaDescriptions()
|
|
for areaDesc in areaList:
|
|
if areaDesc=="Current":
|
|
areaNames.append("Current")
|
|
elif areaDesc in descList:
|
|
areaNum=descList.index(areaDesc)
|
|
areaNames.append(nameList[areaNum])
|
|
if len(areaNames)<1:
|
|
msg="Invalid Edit Area(s) - contact support"
|
|
self.statusBarMsg(msg,"U")
|
|
return
|
|
print "the areaNames are:",areaNames
|
|
comboArea=self.empty(bool)
|
|
if ((AreaCombine==1)and(len(areaNames)>1)):
|
|
for areaName in areaNames:
|
|
if areaName=="Current":
|
|
areaObject=self.getActiveEditArea()
|
|
mask=self.encodeEditArea(areaObject)
|
|
any=add.reduce(add.reduce(mask))
|
|
if any==0:
|
|
mask=self.newGrid(True, bool)
|
|
elif areaName=="NONE":
|
|
mask=self.newGrid(True, bool)
|
|
else:
|
|
mask=self.encodeEditArea(areaName)
|
|
comboArea=logical_or(comboArea,mask)
|
|
#
|
|
#
|
|
#
|
|
statName=display
|
|
if statName=="TwoCat":
|
|
statName=TwoCatType
|
|
statVal=TwoCatValue
|
|
statCond=TwoCatCond
|
|
#
|
|
# Clear the cd canvas - setup title
|
|
#
|
|
self.cd.canvas.delete(Tkinter.ALL)
|
|
self.cd.title("Statistic Time Series")
|
|
workStart="Working on Statistics"
|
|
self.startWorking(workStart,optionRemove=0)
|
|
#
|
|
#
|
|
#
|
|
outdata={}
|
|
fhrList=[]
|
|
timemin=1e32
|
|
timemax=-1e32
|
|
valmin=1.0e32
|
|
valmax=-1.0e32
|
|
|
|
countimax=len(parmList)*len(modelList)
|
|
counti=0
|
|
for parm in parmList:
|
|
readParm=parm
|
|
vectorType=-1
|
|
last3="xxx"
|
|
if len(parm)>3:
|
|
last3=parm[-3:]
|
|
if ((last3=="Spd")or(last3=="Dir")):
|
|
readParm=parm[:-3]
|
|
if last3=="Spd":
|
|
vectorType==0
|
|
else:
|
|
vectorType==1
|
|
obsParm=self.VU.getObsParm(readParm)
|
|
verType=self.VU.getVerType(readParm)
|
|
datatype=self.VU.getVerParmType(readParm)
|
|
thresholds=self.VU.getVerThresholds(readParm)
|
|
if last3=="Spd":
|
|
thresholds=thresholds[0]
|
|
elif last3=="Dir":
|
|
thresholds=thresholds[1]
|
|
thresholdValue=thresholds[threshold]
|
|
|
|
statsCases=self.VU.getStatCases(parm,modelList,obsmodel,dateStyle,dateType,
|
|
fromDay=fromDay,numDays=numDays,dayList=dayList,
|
|
fcstrs=fcstrList,cycles=cycleList,fhrStart=fhrStart,
|
|
fhrEnd=fhrEnd,accumHours=accumHours,accumFreq=accumFreq,
|
|
commonCases=commonCases,basetimeOffsets=1,
|
|
callbackMethod=self.workingCommon)
|
|
if self.checkWorking()==1:
|
|
self.stopWorking()
|
|
return
|
|
gridsCases=self.VU.getCases(readParm,modelList,obsParm,obsmodel,dateStyle,dateType,
|
|
fromDay=fromDay,numDays=numDays,dayList=dayList,
|
|
fcstrs=fcstrList,cycles=cycleList,fhrStart=fhrStart,
|
|
fhrEnd=fhrEnd,accumHours=accumHours,accumFreq=accumFreq,
|
|
requireObs=1,commonCases=commonCases,basetimeOffsets=1,
|
|
callbackMethod=self.workingCommon)
|
|
if self.checkWorking()==1:
|
|
self.stopWorking()
|
|
return
|
|
for model in modelList:
|
|
counti+=1
|
|
workNow="Reading %s %s (%d of %d)"%(parm,model,counti,countimax)
|
|
scases=statsCases[model]
|
|
if model in gridsCases.keys():
|
|
gcases=gridsCases[model]
|
|
else:
|
|
gcases={}
|
|
#
|
|
# get overall list of keys (both stat and grid)
|
|
#
|
|
skeys=scases.keys()
|
|
gkeys=gcases.keys()
|
|
tkeys=skeys
|
|
for gkey in gkeys:
|
|
if gkey not in tkeys:
|
|
tkeys.append(gkey)
|
|
|
|
#
|
|
# Loop over possible stat or grid cases
|
|
#
|
|
count=0
|
|
totalcount=len(tkeys)
|
|
for key in tkeys:
|
|
count+=1
|
|
self.setWorking("%s: %d of %d"%(workNow,count,totalcount))
|
|
if self.checkWorking()==1:
|
|
self.stopWorking()
|
|
return
|
|
if key in scases:
|
|
srecList=scases[key]
|
|
else:
|
|
srecList=None
|
|
if key in gcases:
|
|
grecList=gcases[key]
|
|
else:
|
|
grecList=None
|
|
#
|
|
#
|
|
#
|
|
(basestr,stimestr,etimestr)=key.split(",")
|
|
basetime=int(basestr)
|
|
starttime=int(stimestr)
|
|
endtime=int(etimestr)
|
|
#
|
|
# Done show results for grids not yet complete
|
|
#
|
|
if endtime>time.time():
|
|
continue
|
|
#
|
|
# Dont show results for grids that start before forecast time
|
|
#
|
|
fhr=(starttime-basetime)/HOURSECS
|
|
if fhr<0:
|
|
continue
|
|
#
|
|
# X-coordinate is 'starttime' when doing "Verifying On" displays
|
|
# and 'basetime' when doing "Forecast on" displays
|
|
#
|
|
if dateStyle=="Verifying on":
|
|
x=starttime
|
|
else:
|
|
x=basetime
|
|
timemin=min(x,timemin)
|
|
timemax=max(x,timemax)
|
|
#
|
|
# Thresholds are different for different variables
|
|
#
|
|
if statName=="Percent Err <":
|
|
statVal=thresholdValue
|
|
#
|
|
# When AreaCombine is on...we already have the combined
|
|
# edit area ready
|
|
#
|
|
if ((AreaCombine==1)and(len(areaNames)>1)):
|
|
eaGrid=comboArea
|
|
|
|
outkey="%s,%s,%3.3d,-01"%(parm,model,fhr)
|
|
if outkey not in outdata.keys():
|
|
outdata[outkey]=[]
|
|
|
|
valx=self.VU.getVerStat(model,basetime,readParm,starttime,endtime,
|
|
obsmodel,statName,statVal=statVal,
|
|
statCond=statCond,editArea=eaGrid,
|
|
smooth=scale,vectorType=vectorType,
|
|
srecList=srecList,grecList=grecList)
|
|
if valx is None:
|
|
print "getVerStat returned None"
|
|
continue
|
|
valmin=min(valx,valmin)
|
|
valmax=max(valx,valmax)
|
|
outdata[outkey].append((x,valx))
|
|
#
|
|
# When AreaCombine is off...loop over editAreas
|
|
#
|
|
else:
|
|
for areaName in areaNames:
|
|
outkey="%s,%s,%3.3d,%s"%(parm,model,fhr,areaName)
|
|
if outkey not in outdata.keys():
|
|
outdata[outkey]=[]
|
|
if areaName=="Current":
|
|
areaObject=self.getActiveEditArea()
|
|
ea=self.encodeEditArea(areaObject)
|
|
any=add.reduce(add.reduce(ea))
|
|
if any==0:
|
|
ea=self.newGrid(True, bool)
|
|
elif areaName=="NONE":
|
|
ea=self.newGrid(True, bool)
|
|
else:
|
|
ea=areaName
|
|
valx=self.VU.getVerStat(model,basetime,readParm,starttime,endtime,
|
|
obsmodel,statName,statVal=statVal,
|
|
statCond=statCond,editArea=ea,
|
|
smooth=scale,vectorType=vectorType,
|
|
srecList=srecList,grecList=grecList)
|
|
if valx is None:
|
|
print "getVerStat returned None"
|
|
continue
|
|
valmin=min(valx,valmin)
|
|
valmax=max(valx,valmax)
|
|
outdata[outkey].append((x,valx))
|
|
#self.VU.setDebug(0)
|
|
#
|
|
#
|
|
#
|
|
if fhr not in fhrList:
|
|
fhrList.append(fhr)
|
|
#
|
|
#
|
|
# If no data read - don't go further
|
|
#
|
|
if len(outdata.keys())<1:
|
|
self.stopWorking()
|
|
msg="No verification data could be found matching those criteria"
|
|
self.statusBarMsg(msg,"U")
|
|
return
|
|
#print "done reading"
|
|
#
|
|
# valmin/valmax usually works - but for bounded stats
|
|
# we always want 0.0 to be the lower bound
|
|
#
|
|
if display in ["RMS Error","Std Dev","Mean Abs Err"]:
|
|
valmin=0.0
|
|
if display=="Percent Err <":
|
|
valmin=0.0
|
|
valmax=1.0
|
|
#print "value range:",valmin,valmax
|
|
#
|
|
# time buttons
|
|
#
|
|
fhrList.sort()
|
|
fList=[]
|
|
for fhr in fhrList:
|
|
fList.append("%d"%fhr)
|
|
self.setupBut1(fList,numbuttons=12,arrows=1,width=3)
|
|
#
|
|
# First part of title line is the type of error
|
|
#
|
|
if display=="TwoCat":
|
|
titleLine="%s Timeseries - "%TwoCatType
|
|
elif display!="Percent Err <":
|
|
titleLine="%s Timeseries - "%display
|
|
else:
|
|
titleLine="%s %d Timeseries - "%(display,threshold)
|
|
#
|
|
# set varList to the thing that varies: model-parm-area
|
|
#
|
|
if parmVary==1:
|
|
varList=parmList[:]
|
|
varButtons=6
|
|
titleLine+=modelList[0]
|
|
elif modelVary==1:
|
|
if "Official" in modelList:
|
|
idx=modelList.index("Official")
|
|
del modelList[idx]
|
|
modelList.insert(0,"Official")
|
|
varList=modelList[:]
|
|
varButtons=6
|
|
titleLine+=parmList[0]
|
|
if display=="TwoCat":
|
|
titleLine+=" %s %s"%(TwoCatCond,TwoCatValueString)
|
|
else:
|
|
varList=areaList[:]
|
|
varButtons=3
|
|
titleLine+="%s %s"%(modelList[0],parmList[0])
|
|
#
|
|
# Associate colors with the varying model/parm/area
|
|
#
|
|
self.colornames={}
|
|
index=0
|
|
for var in varList:
|
|
self.colornames[var]=self.COLORLIST[index]
|
|
index+=1
|
|
if index==len(self.COLORLIST):
|
|
index=0
|
|
#
|
|
# Make buttons
|
|
#
|
|
self.setupBut2(varList,numbuttons=varButtons,arrows=1)
|
|
#
|
|
# Setup graphing coordinates
|
|
#
|
|
numticks=10
|
|
graphrange=valmax-valmin
|
|
print "graphrange=",graphrange
|
|
tickInterval=self.niceNumDec(graphrange/(numticks-1),1)
|
|
#print "tickInterval=",tickInterval
|
|
|
|
left=self.cd.curwidth*(50.0/700.0)
|
|
right=self.cd.curwidth*(650.0/700.0)
|
|
bot=self.cd.curheight*(130.0/530.0)
|
|
top=self.cd.curheight*(480.0/530.0)
|
|
self.setgraph(timemin,timemax,valmin,valmax,left,right,bot,top)
|
|
self.graphaxes(timemin,timemax,valmin,valmax)
|
|
#
|
|
# Draw timeseries lines
|
|
#
|
|
for key in outdata.keys():
|
|
#print "timeseries for ",key
|
|
tagbase=key.split(",")
|
|
fhr="f%d"%int(tagbase[2])
|
|
#
|
|
if parmVary==1:
|
|
varTag=tagbase[0]
|
|
elif modelVary==1:
|
|
varTag=tagbase[1]
|
|
else:
|
|
#varTag=self.VU.EditAreaDescriptions[int(tagbase[3])]
|
|
areaNum=self.VU.getEditAreaNumberFromName(tagbase[3])
|
|
varTag=self.VU.EditAreaDescriptions[areaNum]
|
|
tagtuple=(varTag,fhr)
|
|
|
|
colorname=self.colornames[varTag]
|
|
points=outdata[key]
|
|
points.sort()
|
|
gpoints=[]
|
|
for point in points:
|
|
(xtime,val)=point
|
|
(x,y)=self.graphcoord(xtime,val)
|
|
gpoints.append(x)
|
|
gpoints.append(y)
|
|
if len(gpoints)>3:
|
|
self.cd.canvas.create_line(gpoints,fill=colorname,tags=tagtuple)
|
|
self.but2state[varTag]=1
|
|
self.but1state[fhr]=1
|
|
#
|
|
# Label forecast times
|
|
#
|
|
for fhr in fhrList:
|
|
fhrstr="f%d"%fhr
|
|
labelstr="%d-hr Forecast"%fhr
|
|
self.labelLine(labelstr,3,justify='right',tags=(fhrstr))
|
|
#
|
|
# Turn off all but first
|
|
#
|
|
startBut1(self)
|
|
startBut2(self)
|
|
#
|
|
# Labels at top of graph
|
|
#
|
|
#if len(areaList)>1:
|
|
# if AreaCombine==1:
|
|
# numPts=0
|
|
# for areaNum in areaNums:
|
|
# numPts+=self.pts[areaNum]
|
|
# else:
|
|
# numPts=-1
|
|
#else:
|
|
# numPts=self.pts[areaNums[0]]
|
|
numPts=-1
|
|
self.cdLabels(titleLine,numPts,dateStyle,dateType,numDays,fromDay,
|
|
dayList,cycleList)
|
|
#
|
|
# Done - show the results
|
|
#
|
|
self.stopWorking()
|
|
self.moveCD()
|
|
self.cd.deiconify()
|
|
self.cd.lift()
|
|
|
|
return
|
|
|
|
#==================================================================
|
|
# makeFhourGraph - display graph of average error at various fhrs
|
|
#
|
|
def makeFhourGraph(self,DialogDict):
|
|
self.VU.logMsg("running makeFhourGraph:")
|
|
display=DialogDict["Display"]
|
|
areaList=DialogDict["areaList"]
|
|
AreaCombine=DialogDict["AreaCombine"]
|
|
parmList=DialogDict["Parms"]
|
|
threshold=DialogDict["Threshold"]
|
|
cycleList=DialogDict["cycleList"]
|
|
modelList=DialogDict["Models"]
|
|
obsmodel=DialogDict["ObsModel"]
|
|
fcstrList=DialogDict["fcstrList"]
|
|
fhrStart=DialogDict["fhrStart"]
|
|
fhrEnd=DialogDict["fhrEnd"]
|
|
dateType=DialogDict["dateType"]
|
|
numDays=DialogDict["numDays"]
|
|
fromDay=DialogDict["fromDay"]
|
|
dayList=DialogDict["dayList"]
|
|
dateStyle=DialogDict["dateStyle"]
|
|
plotType=DialogDict["PlotType"]
|
|
scale=DialogDict["scale"]
|
|
commonCases=DialogDict["commonCases"]
|
|
accumHours=DialogDict["accumHours"]
|
|
accumFreq=DialogDict["accumFreq"]
|
|
TwoCatType=DialogDict["TwoCatType"]
|
|
TwoCatCond=DialogDict["TwoCatCond"]
|
|
TwoCatValue=DialogDict["TwoCatValue"]
|
|
TwoCatValueString=DialogDict["TwoCatValueString"]
|
|
#
|
|
# Check for good GUI input
|
|
#
|
|
ret=self.checkLists(modelList,parmList,cycleList,fcstrList,dateType,
|
|
dayList)
|
|
if ret==0:
|
|
return
|
|
#
|
|
# Check that we do not have too many things varying
|
|
#
|
|
if len(parmList)>1:
|
|
parmVary=1
|
|
else:
|
|
parmVary=0
|
|
if ((len(areaList)>1)and(AreaCombine==0)):
|
|
areaVary=1
|
|
else:
|
|
areaVary=0
|
|
if len(modelList)>1:
|
|
modelVary=1
|
|
else:
|
|
modelVary=0
|
|
totalVary=parmVary+areaVary+modelVary
|
|
if totalVary>2:
|
|
msg="Can only vary two of parm/area/model when doing 'vs. Fcst Hour' graphs"
|
|
self.statusBarMsg(msg,"U")
|
|
return
|
|
#
|
|
# If only varying one thing - then set the other to model, unless that
|
|
# is the one already being done - and then set to parm
|
|
#
|
|
if totalVary==1:
|
|
if modelVary==1:
|
|
parmVary=1
|
|
else:
|
|
modelVary=1
|
|
#
|
|
#
|
|
#
|
|
if parmVary==1:
|
|
if modelVary==1:
|
|
varList1=parmList[:]
|
|
varList2=modelList[:]
|
|
else:
|
|
varList1=parmList[:]
|
|
varList2=areaList[:]
|
|
else:
|
|
varList1=areaList[:]
|
|
varList2=modelList[:]
|
|
#
|
|
# Clear the cd canvas - setup title
|
|
#
|
|
self.cd.canvas.delete(Tkinter.ALL)
|
|
self.cd.title("Statistic Graph")
|
|
workStart="Working on Statistics"
|
|
self.startWorking(workStart,optionRemove=0)
|
|
#
|
|
# get names of selected edit areas
|
|
#
|
|
areaNames=[]
|
|
descList=self.VU.listEditAreaDescriptions()
|
|
nameList=self.VU.listEditAreas()
|
|
for areaDesc in areaList:
|
|
if areaDesc=="Current":
|
|
areaNames.append("Current")
|
|
elif areaDesc in descList:
|
|
areaNum=descList.index(areaDesc)
|
|
areaNames.append(nameList[areaNum])
|
|
if len(areaNames)<1:
|
|
msg="Invalid Edit Area(s) - contact support"
|
|
self.statusBarMsg(msg,"U")
|
|
return
|
|
#
|
|
# For 'combined areas' - setup the comboArea just once
|
|
#
|
|
comboArea=self.empty(bool)
|
|
if ((AreaCombine==1)and(len(areaNames)>1)):
|
|
for areaName in areaNames:
|
|
if areaName=="Current":
|
|
areaObject=self.getActiveEditArea()
|
|
mask=self.encodeEditArea(areaObject)
|
|
any=add.reduce(add.reduce(mask))
|
|
if any==0:
|
|
mask=self.newGrid(True, bool)
|
|
elif areaName=="NONE":
|
|
mask=self.newGrid(True, bool)
|
|
else:
|
|
mask=self.encodeEditArea(areaName)
|
|
comboArea=logical_or(comboArea,mask)
|
|
#
|
|
# If any of the TwoCat stats are requested - then get
|
|
# the contingency table entries instead
|
|
#
|
|
statName=display
|
|
if statName=="TwoCat":
|
|
statName="cont"
|
|
if TwoCatType[0:1]=="A":
|
|
statName="acont"
|
|
statVal=TwoCatValue
|
|
statCond=TwoCatCond
|
|
statID=self.VU.getStatID(TwoCatType)
|
|
#
|
|
#
|
|
#
|
|
#
|
|
#
|
|
#
|
|
sumdata={}
|
|
cntdata={}
|
|
hitsdata={}
|
|
missdata={}
|
|
falrdata={}
|
|
corndata={}
|
|
#
|
|
timemin=1e32
|
|
timemax=-1e32
|
|
valmin=1.0e32
|
|
valmax=-1.0e32
|
|
|
|
countimax=len(parmList)*len(modelList)
|
|
counti=0
|
|
for parm in parmList:
|
|
#
|
|
#
|
|
#
|
|
(parmUnits,parmPrecision,parmMinval,parmMaxval,parmRateFlag,parmColorTable,
|
|
parmDisplayMinval,parmDisplayMaxval)=self.getParmInfo(self.mutableID(),parm)
|
|
#
|
|
# setup readParm - which is usually the same as parm, but
|
|
# can be different for the Spd/Dir components of a vector
|
|
#
|
|
readParm=parm
|
|
vectorType=-1
|
|
last3="xxx"
|
|
if len(parm)>3:
|
|
last3=parm[-3:]
|
|
if (last3 in ["Spd","Dir"]):
|
|
readParm=parm[:-3]
|
|
if last3=="Spd":
|
|
vectorType==0
|
|
else:
|
|
vectorType==1
|
|
#
|
|
# Get the observed parm for this parm, the verification type,
|
|
# the data type and the thresholds
|
|
#
|
|
obsParm=self.VU.getObsParm(readParm)
|
|
verType=self.VU.getVerType(readParm)
|
|
datatype=self.VU.getVerParmType(readParm)
|
|
thresholds=self.VU.getVerThresholds(readParm)
|
|
if last3=="Spd":
|
|
thresholds=thresholds[0]
|
|
elif last3=="dir":
|
|
thresholds=thresholds[1]
|
|
thresholdValue=thresholds[threshold]
|
|
#
|
|
# If using the threshold stat - set it now
|
|
#
|
|
if statName=="Percent Err <":
|
|
statVal=thresholdValue
|
|
#
|
|
# Get the statCases for this parm
|
|
#
|
|
statCases=self.VU.getStatCases(parm,modelList,obsmodel,dateStyle,
|
|
dateType,fromDay=fromDay,numDays=numDays,
|
|
dayList=dayList,fcstrs=fcstrList,cycles=cycleList,
|
|
fhrStart=fhrStart,fhrEnd=fhrEnd,accumHours=accumHours,
|
|
accumFreq=accumFreq,commonCases=commonCases,
|
|
basetimeOffsets=1,callbackMethod=self.workingCommon)
|
|
if self.checkWorking()==1:
|
|
self.stopWorking()
|
|
return
|
|
#
|
|
# Get the gridCases for this parm
|
|
#
|
|
gridCases=self.VU.getCases(readParm,modelList,obsParm,obsmodel,
|
|
dateStyle,dateType,fromDay=fromDay,numDays=numDays,
|
|
dayList=dayList,fcstrs=fcstrList,cycles=cycleList,
|
|
fhrStart=fhrStart,fhrEnd=fhrEnd,accumHours=accumHours,
|
|
accumFreq=accumFreq,requireObs=1,commonCases=commonCases,
|
|
basetimeOffsets=1,callbackMethod=self.workingCommon)
|
|
if self.checkWorking()==1:
|
|
self.stopWorking()
|
|
return
|
|
for model in modelList:
|
|
counti+=1
|
|
workStart="Reading %s %s (%d of %d)"%(parm,model,counti,countimax)
|
|
#
|
|
# get cases for this model
|
|
#
|
|
scases=statCases[model]
|
|
if model in gridCases.keys():
|
|
gcases=gridCases[model]
|
|
else:
|
|
gcases={}
|
|
#
|
|
# get overall list of keys (both stat and grid) in tkeys
|
|
#
|
|
skeys=scases.keys()
|
|
gkeys=gcases.keys()
|
|
tkeys=skeys
|
|
for gkey in gkeys:
|
|
if gkey not in tkeys:
|
|
tkeys.append(gkey)
|
|
#
|
|
# Loop over possible stat or grid cases
|
|
#
|
|
count=0
|
|
totalcount=len(tkeys)
|
|
for key in tkeys:
|
|
#
|
|
# Check for user interrupting
|
|
#
|
|
count+=1
|
|
if self.setAndCheckWorking("%s: %d of %d"%(workStart,count,totalcount))==1:
|
|
self.stopWorking()
|
|
return
|
|
#
|
|
# Get times for this case
|
|
#
|
|
(basestr,stimestr,etimestr)=key.split(",")
|
|
basetime=int(basestr)
|
|
starttime=int(stimestr)
|
|
endtime=int(etimestr)
|
|
#
|
|
# Do not use results for grids that are not yet complete
|
|
#
|
|
if endtime>time.time():
|
|
continue
|
|
#
|
|
# Do not show results for grids that start before
|
|
# forecast time
|
|
#
|
|
fhr=(starttime-basetime)/HOURSECS
|
|
if fhr<0:
|
|
continue
|
|
#
|
|
# Get list of records for this cases
|
|
#
|
|
if key in scases:
|
|
srecList=scases[key]
|
|
else:
|
|
srecList=None
|
|
if key in gcases:
|
|
grecList=gcases[key]
|
|
else:
|
|
grecList=None
|
|
#
|
|
#
|
|
#
|
|
#
|
|
# When areaCombine is on...we already have the combined
|
|
# edit area ready
|
|
#
|
|
if ((AreaCombine==1)and(len(areaNames)>1)):
|
|
eaGrid=comboArea
|
|
valx=self.VU.getVerStat(model,basetime,readParm,starttime,
|
|
endtime,obsmodel,statName,statVal=statVal,
|
|
statCond=statCond,editArea=eaGrid,
|
|
smooth=scale,vectorType=vectorType,
|
|
srecList=srecList,grecList=grecList)
|
|
if valx is None:
|
|
print "getVerStat returned None"
|
|
continue
|
|
#
|
|
# store sums in parm,model,fhr,areaName keys
|
|
#
|
|
outkey="%s,%s,%3.3d,-01"%(parm,model,fhr)
|
|
if display!="TwoCat":
|
|
if outkey not in sumdata.keys():
|
|
sumdata[outkey]=0.0
|
|
cntdata[outkey]=0
|
|
sumdata[outkey]+=valx
|
|
cntdata[outkey]+=1
|
|
else:
|
|
if outkey not in hitsdata.keys():
|
|
hitsdata[outkey]=0
|
|
missdata[outkey]=0
|
|
falrdata[outkey]=0
|
|
corndata[outkey]=0
|
|
(hits,miss,falr,corn)=valx
|
|
hitsdata[outkey]+=hits
|
|
missdata[outkey]+=miss
|
|
falrdata[outkey]+=falr
|
|
corndata[outkey]+=corn
|
|
else:
|
|
for areaName in areaNames:
|
|
if areaName=="Current":
|
|
areaObject=self.getActiveEditArea()
|
|
ea=self.encodeEditArea(areaObject)
|
|
any=add.reduce(add.reduce(ea))
|
|
if any==0:
|
|
ea=self.newGrid(True, bool)
|
|
elif areaName=="NONE":
|
|
ea=self.newGrid(True, bool)
|
|
else:
|
|
ea=areaName
|
|
valx=self.VU.getVerStat(model,basetime,readParm,starttime,
|
|
endtime,obsmodel,statName,statVal=statVal,
|
|
statCond=statCond,editArea=ea,
|
|
smooth=scale,vectorType=vectorType,
|
|
srecList=srecList,grecList=grecList)
|
|
if valx is None:
|
|
print "getVerStat returned None"
|
|
continue
|
|
#
|
|
#
|
|
#
|
|
outkey="%s,%s,%3.3d,%s"%(parm,model,fhr,areaName)
|
|
if display!="TwoCat":
|
|
if display=="RMS Error":
|
|
valx=valx**2
|
|
if outkey not in sumdata.keys():
|
|
sumdata[outkey]=0.0
|
|
cntdata[outkey]=0
|
|
sumdata[outkey]+=valx
|
|
cntdata[outkey]+=1
|
|
else:
|
|
if outkey not in hitsdata.keys():
|
|
hitsdata[outkey]=0
|
|
missdata[outkey]=0
|
|
falrdata[outkey]=0
|
|
corndata[outkey]=0
|
|
cntdata[outkey]=0
|
|
(hits,miss,falr,corn)=valx
|
|
hitsdata[outkey]+=hits
|
|
missdata[outkey]+=miss
|
|
falrdata[outkey]+=falr
|
|
corndata[outkey]+=corn
|
|
cntdata[outkey]+=1
|
|
#
|
|
# if no data could be read - stop here
|
|
#
|
|
if len(cntdata.keys())<1:
|
|
self.stopWorking()
|
|
msg="No verification data could be found matching those criteria"
|
|
self.statusBarMsg(msg,"U")
|
|
return
|
|
#
|
|
# We now have the sums...calculate the scores
|
|
#
|
|
fhrList=[]
|
|
valmin=1e32
|
|
valmax=-1e32
|
|
timemin=0
|
|
timemax=0
|
|
outdata={}
|
|
for key in cntdata.keys():
|
|
if cntdata[key]<1:
|
|
continue
|
|
if display!="TwoCat":
|
|
stat=float(sumdata[key])/float(cntdata[key])
|
|
if display=="RMS Error":
|
|
stat=sqrt(stat)
|
|
else:
|
|
hits=hitsdata[key]
|
|
miss=missdata[key]
|
|
falr=falrdata[key]
|
|
corn=corndata[key]
|
|
stat=self.VU.getTwoCatStat(statID,hits,miss,falr,corn)
|
|
#
|
|
#
|
|
#
|
|
valmin=min(valmin,stat)
|
|
valmax=max(valmax,stat)
|
|
(parm,model,fhrstr,areaName)=key.split(",")
|
|
areaNum=self.VU.getEditAreaNumberFromName(areaName)
|
|
fhr=int(fhrstr)
|
|
timemax=max(fhr,timemax)
|
|
if fhr not in fhrList:
|
|
fhrList.append(fhr)
|
|
if parmVary==1:
|
|
if modelVary==1:
|
|
outkey="%s,%s"%(parm,model)
|
|
else:
|
|
#
|
|
# ******** ???????? number or name? What if 'current' or -1?
|
|
#
|
|
outkey="%s,%s"%(parm,self.VU.EditAreaDescriptions[int(areaNum)])
|
|
else:
|
|
outkey="%s,%s"%(self.VU.EditAreaDescriptions[int(areaNum)],model)
|
|
if outkey not in outdata.keys():
|
|
outdata[outkey]=[]
|
|
outdata[outkey].append((fhr,stat))
|
|
#
|
|
# Bounded ones always show 0.0
|
|
#
|
|
if display in ["RMS Error","Std Dev","Mean Abs Error"]:
|
|
valmin=0.0
|
|
#
|
|
# If values are constant (compared to precision of this element)
|
|
# make the graph show slightly more range
|
|
#
|
|
prec1=10**(-parmPrecision)
|
|
minRange=10**(-(parmPrecision+1))
|
|
#minRange=0.1
|
|
graphrange=valmax-valmin
|
|
if graphrange<minRange:
|
|
#valmax=valmax+1.0
|
|
valmax+=(2*prec1)
|
|
#
|
|
#
|
|
allkeys=outdata.keys()
|
|
allkeys.sort()
|
|
#
|
|
# Get lists of the actual buttons we have data for
|
|
#
|
|
varBut1=[]
|
|
varBut2=[]
|
|
for key in outdata.keys():
|
|
(but1,but2)=key.split(",")
|
|
if but1 not in varBut1:
|
|
varBut1.append(but1)
|
|
if but2 not in varBut2:
|
|
varBut2.append(but2)
|
|
varBut1.sort()
|
|
varBut2.sort()
|
|
#
|
|
# In any model list - make sure Official comes first
|
|
#
|
|
if ((parmVary==0)or((parmVary==1)and(modelVary==1))):
|
|
if "Official" in varBut2:
|
|
idx=varBut2.index("Official")
|
|
del varBut2[idx]
|
|
varBut2.insert(0,"Official")
|
|
#
|
|
# Associate colors with the varList2
|
|
#
|
|
self.colornames={}
|
|
index=0
|
|
for var in varBut2:
|
|
self.colornames[var]=self.COLORLIST[index]
|
|
index+=1
|
|
if index==len(self.COLORLIST):
|
|
index=0
|
|
#
|
|
# setup buttons
|
|
#
|
|
self.setupBut1(varBut1,numbuttons=6,arrows=1)
|
|
self.setupBut2(varBut2,numbuttons=6,arrows=1)
|
|
#
|
|
# Setup graphing coordinates
|
|
#
|
|
numticks=10
|
|
graphrange=valmax-valmin
|
|
tickInterval=self.niceNumDec(graphrange/(numticks-1),1)
|
|
|
|
left=self.cd.curwidth*(50.0/700.0)
|
|
right=self.cd.curwidth*(650.0/700.0)
|
|
bot=self.cd.curheight*(130.0/530.0)
|
|
top=self.cd.curheight*(480.0/530.0)
|
|
self.setgraph(timemin,timemax,valmin,valmax,left,right,bot,top)
|
|
self.fhouraxes(timemin,timemax,valmin,valmax)
|
|
#
|
|
# Draw timeseries lines
|
|
#
|
|
for key in outdata.keys():
|
|
(tag1,tag2)=key.split(",")
|
|
tagtuple=(tag1,tag2)
|
|
colorname=self.colornames[tag2]
|
|
points=outdata[key]
|
|
points.sort()
|
|
gpoints=[]
|
|
for point in points:
|
|
(xtime,val)=point
|
|
(x,y)=self.graphcoord(xtime,val)
|
|
gpoints.append(x)
|
|
gpoints.append(y)
|
|
if len(gpoints)>3:
|
|
self.cd.canvas.create_line(gpoints,fill=colorname,tags=tagtuple)
|
|
self.but1state[tag1]=1
|
|
self.but2state[tag2]=1
|
|
#
|
|
# Turn off all but first model and first time
|
|
#
|
|
startBut1(self)
|
|
startBut2(self)
|
|
#
|
|
# Labels
|
|
#
|
|
if len(areaList)>1:
|
|
areaName="Various"
|
|
if AreaCombine==1:
|
|
numPts=0
|
|
for areaNum in areaNums:
|
|
numPts+=self.pts[areaNum]
|
|
else:
|
|
numPts=-1
|
|
else:
|
|
#numPts=self.pts[areaNums[0]]
|
|
numPts=-1
|
|
ul1="Average Error Growth - %s"%parm
|
|
self.cdLabels(ul1,numPts,dateStyle,dateType,numDays,fromDay,dayList,cycleList)
|
|
|
|
self.stopWorking()
|
|
self.moveCD()
|
|
self.cd.deiconify()
|
|
self.cd.lift()
|
|
|
|
return
|
|
#==================================================================
|
|
# getStat - assuming that the statfile is open correctly, get
|
|
# the value for the specified 'display', for the
|
|
# record, area, and threshold number
|
|
#
|
|
def getStat(self,record,areaNum,display,threshold):
|
|
if display=="Bias":
|
|
val=self.VU.sncStats[record,areaNum,0]
|
|
elif display=="Squared Error":
|
|
val=self.VU.sncStats[record,areaNum,1]
|
|
elif display=="RMS Error":
|
|
val=sqrt(self.VU.sncStats[record,areaNum,1])
|
|
elif display=="Std Dev":
|
|
sum=self.VU.sncStats[record,areaNum,0]
|
|
sqr=self.VU.sncStats[record,areaNum,1]
|
|
val=sqrt(sqr-(sum*sum))
|
|
elif display=="Mean Abs Error":
|
|
val=self.VU.sncStats[record,areaNum,2]
|
|
elif display=="Mean Fcst":
|
|
val=self.VU.sncStats[record,areaNum,3]
|
|
elif display=="Mean Squared Fcst":
|
|
val=self.VU.sncStats[record,areaNum,4]
|
|
elif display=="Mean Obs":
|
|
val=self.VU.sncStats[record,areaNum,5]
|
|
elif display=="Mean Squared Obs":
|
|
val=self.VU.sncStats[record,areaNum,6]
|
|
elif display=="Covariance":
|
|
val=self.VU.sncStats[record,areaNum,7]
|
|
elif display=="Percent Err <":
|
|
val=self.VU.sncStats[record,areaNum,8+threshold]
|
|
else:
|
|
print "unknown stat type"
|
|
val=0
|
|
return val
|
|
#==================================================================
|
|
# ShowScaleStats - make graphs of stat vs scale
|
|
#
|
|
def ShowScaleStats(self,DialogDict):
|
|
self.VU.logMsg("running ShowScaleStats:")
|
|
display=DialogDict["Display"]
|
|
areaList=DialogDict["areaList"]
|
|
AreaCombine=DialogDict["AreaCombine"]
|
|
parm=DialogDict["Parm"]
|
|
threshold=DialogDict["Threshold"]
|
|
cycleList=DialogDict["cycleList"]
|
|
modelList=DialogDict["Models"]
|
|
obsmodel=DialogDict["ObsModel"]
|
|
fcstrList=DialogDict["fcstrList"]
|
|
fhrStart=DialogDict["fhrStart"]
|
|
fhrEnd=DialogDict["fhrEnd"]
|
|
dateType=DialogDict["dateType"]
|
|
numDays=DialogDict["numDays"]
|
|
fromDay=DialogDict["fromDay"]
|
|
dayList=DialogDict["dayList"]
|
|
dateStyle=DialogDict["dateStyle"]
|
|
commonCases=DialogDict["commonCases"]
|
|
accumHours=DialogDict["accumHours"]
|
|
accumFreq=DialogDict["accumFreq"]
|
|
TwoCatType=DialogDict["TwoCatType"]
|
|
TwoCatCond=DialogDict["TwoCatCond"]
|
|
TwoCatValue=DialogDict["TwoCatValue"]
|
|
TwoCatValueString=DialogDict["TwoCatValueString"]
|
|
#
|
|
# Check for good GUI input
|
|
#
|
|
parmList=[parm]
|
|
ret=self.checkLists(modelList,parmList,cycleList,fcstrList,dateType,
|
|
dayList)
|
|
if ret==0:
|
|
return
|
|
#
|
|
# Check that we do not have too many things varying
|
|
#
|
|
parmVary=0
|
|
areaVary=0
|
|
modelVary=1
|
|
#
|
|
# Clear the cd canvas - setup title
|
|
#
|
|
self.cd.canvas.delete(Tkinter.ALL)
|
|
self.cd.title("Statistic Graph")
|
|
workStart="Working on Statistics vs. Scale"
|
|
self.startWorking(workStart,optionRemove=0)
|
|
#
|
|
# get names of selected edit areas
|
|
#
|
|
areaNames=[]
|
|
descList=self.VU.listEditAreaDescriptions()
|
|
nameList=self.VU.listEditAreas()
|
|
for areaDesc in areaList:
|
|
if areaDesc=="Current":
|
|
areaNames.append("Current")
|
|
elif areaDesc in descList:
|
|
areaNum=descList.index(areaDesc)
|
|
areaNames.append(nameList[areaNum])
|
|
if len(areaNames)<1:
|
|
msg="Invalid Edit Area(s) - contact support"
|
|
self.statusBarMsg(msg,"U")
|
|
return
|
|
#
|
|
# Setup the combined area
|
|
#
|
|
comboArea=self.empty(bool)
|
|
for areaName in areaNames:
|
|
if areaName=="Current":
|
|
areaObject=self.getActiveEditArea()
|
|
mask=self.encodeEditArea(areaObject)
|
|
any=add.reduce(add.reduce(mask))
|
|
if any==0:
|
|
mask=self.newGrid(True, bool)
|
|
elif areaName=="NONE":
|
|
mask=self.newGrid(True, bool)
|
|
else:
|
|
mask=self.encodeEditArea(areaName)
|
|
comboArea=logical_or(comboArea,mask)
|
|
#
|
|
# If any of the TwoCat stats are requested - then get
|
|
# the contingency table entries instead
|
|
#
|
|
statName=display
|
|
if statName=="TwoCat":
|
|
statName="cont"
|
|
if TwoCatType[0:1]=="A":
|
|
statName="acont"
|
|
statVal=TwoCatValue
|
|
statCond=TwoCatCond
|
|
statID=self.VU.getStatID(TwoCatType)
|
|
#
|
|
#
|
|
#
|
|
sumdata={}
|
|
cntdata={}
|
|
hitsdata={}
|
|
missdata={}
|
|
falrdata={}
|
|
corndata={}
|
|
#
|
|
timemin=1e32
|
|
timemax=-1e32
|
|
valmin=1.0e32
|
|
valmax=-1.0e32
|
|
|
|
countimax=len(parmList)*len(modelList)
|
|
counti=0
|
|
#
|
|
# setup readParm - which is usually the same as parm, but
|
|
# can be different for the Spd/Dir components of a vector
|
|
#
|
|
readParm=parm
|
|
vectorType=-1
|
|
last3="xxx"
|
|
if len(parm)>3:
|
|
last3=parm[-3:]
|
|
if (last3 in ["Spd","Dir"]):
|
|
readParm=parm[:-3]
|
|
if last3=="Spd":
|
|
vectorType==0
|
|
else:
|
|
vectorType==1
|
|
#
|
|
# Get the observed parm for this parm, the verification type,
|
|
# the data type and the thresholds
|
|
#
|
|
obsParm=self.VU.getObsParm(readParm)
|
|
verType=self.VU.getVerType(readParm)
|
|
datatype=self.VU.getVerParmType(readParm)
|
|
thresholds=self.VU.getVerThresholds(readParm)
|
|
if last3=="Spd":
|
|
thresholds=thresholds[0]
|
|
elif last3=="dir":
|
|
thresholds=thresholds[1]
|
|
thresholdValue=thresholds[threshold]
|
|
#
|
|
# If using the threshold stat - set it now
|
|
#
|
|
if statName=="Percent Err <":
|
|
statVal=thresholdValue
|
|
#
|
|
# Get the statCases for this parm
|
|
#
|
|
statCases=self.VU.getStatCases(parm,modelList,obsmodel,dateStyle,
|
|
dateType,fromDay=fromDay,numDays=numDays,
|
|
dayList=dayList,fcstrs=fcstrList,cycles=cycleList,
|
|
fhrStart=fhrStart,fhrEnd=fhrEnd,accumHours=accumHours,
|
|
accumFreq=accumFreq,commonCases=commonCases,
|
|
basetimeOffsets=1,callbackMethod=self.workingCommon)
|
|
if self.checkWorking()==1:
|
|
self.stopWorking()
|
|
return
|
|
#
|
|
# Get the gridCases for this parm
|
|
#
|
|
gridCases=self.VU.getCases(readParm,modelList,obsParm,obsmodel,
|
|
dateStyle,dateType,fromDay=fromDay,numDays=numDays,
|
|
dayList=dayList,fcstrs=fcstrList,cycles=cycleList,
|
|
fhrStart=fhrStart,fhrEnd=fhrEnd,accumHours=accumHours,
|
|
accumFreq=accumFreq,requireObs=1,commonCases=commonCases,
|
|
basetimeOffsets=1,callbackMethod=self.workingCommon)
|
|
if self.checkWorking()==1:
|
|
self.stopWorking()
|
|
return
|
|
for model in modelList:
|
|
counti+=1
|
|
workStart="Reading %s %s (%d of %d)"%(parm,model,counti,countimax)
|
|
#
|
|
# get cases for this model
|
|
#
|
|
scases=statCases[model]
|
|
if model in gridCases.keys():
|
|
gcases=gridCases[model]
|
|
else:
|
|
gcases={}
|
|
#
|
|
# get overall list of keys (both stat and grid) in tkeys
|
|
#
|
|
skeys=scases.keys()
|
|
gkeys=gcases.keys()
|
|
tkeys=skeys
|
|
for gkey in gkeys:
|
|
if gkey not in tkeys:
|
|
tkeys.append(gkey)
|
|
#
|
|
# Loop over possible stat or grid cases
|
|
#
|
|
count=0
|
|
totalcount=len(tkeys)
|
|
for key in tkeys:
|
|
#
|
|
# Check for user interrupting
|
|
#
|
|
count+=1
|
|
if self.setAndCheckWorking("%s: %d of %d"%(workStart,count,totalcount))==1:
|
|
self.stopWorking()
|
|
return
|
|
#
|
|
# Get times for this case
|
|
#
|
|
(basestr,stimestr,etimestr)=key.split(",")
|
|
basetime=int(basestr)
|
|
starttime=int(stimestr)
|
|
endtime=int(etimestr)
|
|
#
|
|
# Do not use results for grids that are not yet complete
|
|
#
|
|
if endtime>time.time():
|
|
continue
|
|
#
|
|
# Do not show results for grids that start before
|
|
# forecast time
|
|
#
|
|
fhr=(starttime-basetime)/HOURSECS
|
|
if fhr<0:
|
|
continue
|
|
#
|
|
# Get list of records for this cases
|
|
#
|
|
if key in scases:
|
|
srecList=scases[key]
|
|
else:
|
|
srecList=None
|
|
if key in gcases:
|
|
grecList=gcases[key]
|
|
else:
|
|
grecList=None
|
|
#
|
|
# Loop over scales
|
|
#
|
|
smoothList=[]
|
|
for (scale,text) in self.scaleList:
|
|
smoothList.append(scale)
|
|
|
|
valx=self.VU.getVerStatScales(model,basetime,readParm,starttime,
|
|
endtime,obsmodel,statName,statVal=statVal,
|
|
statCond=statCond,editArea=comboArea,
|
|
smoothList=smoothList,vectorType=vectorType,
|
|
grecList=grecList)
|
|
if valx is None:
|
|
print "getVerStatScales returned None"
|
|
continue
|
|
if len(valx)<1:
|
|
print "getVerStatScales returned empty list"
|
|
continue
|
|
|
|
for i in xrange(len(smoothList)):
|
|
(scale,text)=self.scaleList[i]
|
|
val=valx[i]
|
|
#
|
|
# store sums in model,fhr,scale keys
|
|
#
|
|
outkey="%s,%3.3d,%4.4d"%(model,fhr,scale)
|
|
if display!="TwoCat":
|
|
if outkey not in sumdata.keys():
|
|
sumdata[outkey]=0.0
|
|
cntdata[outkey]=0
|
|
sumdata[outkey]+=val
|
|
cntdata[outkey]+=1
|
|
else:
|
|
if outkey not in hitsdata.keys():
|
|
hitsdata[outkey]=0
|
|
missdata[outkey]=0
|
|
falrdata[outkey]=0
|
|
corndata[outkey]=0
|
|
(hits,miss,falr,corn)=val
|
|
hitsdata[outkey]+=hits
|
|
missdata[outkey]+=miss
|
|
falrdata[outkey]+=falr
|
|
corndata[outkey]+=corn
|
|
#
|
|
# if no data could be read - stop here
|
|
#
|
|
if len(cntdata.keys())<1:
|
|
self.stopWorking()
|
|
msg="No verification data could be found matching those criteria"
|
|
self.statusBarMsg(msg,"U")
|
|
return
|
|
#
|
|
# We now have the sums...calculate the scores
|
|
#
|
|
fhrList=[]
|
|
valmin=1e32
|
|
valmax=-1e32
|
|
scalemin=0
|
|
scalemax=0
|
|
outdata={}
|
|
for key in cntdata.keys():
|
|
if cntdata[key]<1:
|
|
continue
|
|
if display!="TwoCat":
|
|
stat=float(sumdata[key])/float(cntdata[key])
|
|
if display=="RMS Error":
|
|
stat=sqrt(stat)
|
|
else:
|
|
hits=hitsdata[key]
|
|
miss=missdata[key]
|
|
falr=falrdata[key]
|
|
corn=corndata[key]
|
|
stat=self.VU.getTwoCatStat(statID,hits,miss,falr,corn)
|
|
#
|
|
#
|
|
#
|
|
valmin=min(valmin,stat)
|
|
valmax=max(valmax,stat)
|
|
(model,fhrstr,scalestr)=key.split(",")
|
|
fhr=int(fhrstr)
|
|
scale=int(scalestr)
|
|
scalemax=max(scale,scalemax)
|
|
if fhr not in fhrList:
|
|
fhrList.append(fhr)
|
|
outkey="%s,%s"%(fhrstr,model)
|
|
if outkey not in outdata.keys():
|
|
outdata[outkey]=[]
|
|
outdata[outkey].append((scale,stat))
|
|
#
|
|
# Bounded ones always show 0.0
|
|
#
|
|
if display in ["RMS Error","Std Dev","Mean Abs Error"]:
|
|
valmin=0.0
|
|
#
|
|
# If values are constant - show one up
|
|
#
|
|
graphrange=valmax-valmin
|
|
if graphrange<0.01:
|
|
valmax+=1.0
|
|
#
|
|
#
|
|
allkeys=outdata.keys()
|
|
allkeys.sort()
|
|
#
|
|
# Get lists of the actual buttons we have data for
|
|
#
|
|
varBut1=[]
|
|
varBut2=[]
|
|
for key in outdata.keys():
|
|
(but1,but2)=key.split(",")
|
|
if but1 not in varBut1:
|
|
varBut1.append(but1)
|
|
if but2 not in varBut2:
|
|
varBut2.append(but2)
|
|
varBut1.sort()
|
|
varBut2.sort()
|
|
|
|
#
|
|
# In model list - make sure Official comes first
|
|
#
|
|
if "Official" in varBut2:
|
|
idx=varBut2.index("Official")
|
|
del varBut2[idx]
|
|
varBut2.insert(0,"Official")
|
|
#
|
|
# Associate colors with the varList2
|
|
#
|
|
self.colornames={}
|
|
index=0
|
|
for var in varBut2:
|
|
self.colornames[var]=self.COLORLIST[index]
|
|
index+=1
|
|
if index==len(self.COLORLIST):
|
|
index=0
|
|
#
|
|
# setup buttons
|
|
#
|
|
self.setupBut1(varBut1,numbuttons=6,arrows=1)
|
|
self.setupBut2(varBut2,numbuttons=6,arrows=1)
|
|
#
|
|
# Setup graphing coordinates
|
|
#
|
|
numticks=10
|
|
graphrange=valmax-valmin
|
|
tickInterval=self.niceNumDec(graphrange/(numticks-1),1)
|
|
|
|
left=self.cd.curwidth*(50.0/700.0)
|
|
right=self.cd.curwidth*(650.0/700.0)
|
|
bot=self.cd.curheight*(130.0/530.0)
|
|
top=self.cd.curheight*(480.0/530.0)
|
|
self.setgraph(scalemin,scalemax,valmin,valmax,left,right,bot,top)
|
|
#self.fhouraxes(timemin,timemax,valmin,valmax)
|
|
#
|
|
# Draw timeseries lines
|
|
#
|
|
for key in outdata.keys():
|
|
(tag1,tag2)=key.split(",")
|
|
tagtuple=(tag1,tag2)
|
|
colorname=self.colornames[tag2]
|
|
points=outdata[key]
|
|
points.sort()
|
|
gpoints=[]
|
|
for point in points:
|
|
(scale,val)=point
|
|
(x,y)=self.graphcoord(scale,val)
|
|
gpoints.append(x)
|
|
gpoints.append(y)
|
|
if len(gpoints)>3:
|
|
self.cd.canvas.create_line(gpoints,fill=colorname,tags=tagtuple)
|
|
self.but1state[tag1]=1
|
|
self.but2state[tag2]=1
|
|
#
|
|
# Turn off all but first model and first time
|
|
#
|
|
startBut1(self)
|
|
startBut2(self)
|
|
#
|
|
# Labels
|
|
#
|
|
if len(areaList)>1:
|
|
areaName="Various"
|
|
if AreaCombine==1:
|
|
numPts=0
|
|
for areaNum in areaNums:
|
|
numPts+=self.pts[areaNum]
|
|
else:
|
|
numPts=-1
|
|
else:
|
|
#numPts=self.pts[areaNums[0]]
|
|
numPts=-1
|
|
ul1="Average Error Growth - %s"%parm
|
|
self.cdLabels(ul1,numPts,dateStyle,dateType,numDays,fromDay,dayList,cycleList)
|
|
|
|
self.stopWorking()
|
|
self.moveCD()
|
|
self.cd.deiconify()
|
|
self.cd.lift()
|
|
|
|
return
|
|
#==================================================================
|
|
# getReadMode - figure out if parm is a rateParm...and set mode
|
|
# to "Sum" if it is.
|
|
# If not...and checkProb is set...figure out if the
|
|
# parm is a probability parm and set mode to
|
|
# "Max" if it is (floating PoP).
|
|
# Otherwise set to "Average"
|
|
#
|
|
def getReadMode(self,model,parmName,checkProb=1):
|
|
rateFlag=self.VU.getRateFlag(model,parmName)
|
|
if (rateFlag==1):
|
|
readMode="Sum"
|
|
else:
|
|
readMode="TimeWtAverage"
|
|
if checkProb==1:
|
|
verType=self.VU.getVerType(parmName)
|
|
if verType==1:
|
|
readMode="Max"
|
|
return readMode
|
|
|
|
#==================================================================
|
|
# workingCommon - suitable for a callback that provides a message
|
|
# like (x of y), so that it sets the 'working'
|
|
# display - and returns a 1 if the 'stop' button
|
|
# has been set
|
|
#
|
|
def workingCommon(self,message):
|
|
fullmsg="Finding Common Cases: %s"%message
|
|
return self.setAndCheckWorking(fullmsg)
|
|
#==================================================================
|
|
# setupBut1 - setup button 1 buttons. Names in butList are copied
|
|
# to self.but1names[]. Desired buttons on a row in
|
|
# numbuttons. arrows flag adds 'prev/next' buttons.
|
|
#
|
|
# After setup, self.but1names[] holds names.
|
|
# self.but1{} holds button references
|
|
# self.but1state{} holds button state
|
|
def setupBut1(self,butList,numbuttons=5,arrows=0,width=0):
|
|
#
|
|
# clear old buttons (fbar holds button 1s)
|
|
#
|
|
slaves=self.cd.fbar.pack_slaves()
|
|
if slaves is not None:
|
|
for slave in slaves:
|
|
slave.destroy()
|
|
#
|
|
# put generic 'move left' button on the side
|
|
#
|
|
if ((arrows==1)and(len(butList)>1)):
|
|
cb=GenericCallback(prevBut1,self)
|
|
prev=Tkinter.Button(self.cd.fbar,text="<",padx=2,pady=0,
|
|
fg="black",command=cb)
|
|
prev.pack(side=Tkinter.LEFT,fill=Tkinter.Y)
|
|
#
|
|
# figure number of rows of buttons - and create frames
|
|
#
|
|
numrows=int((float(len(butList))/float(numbuttons))+0.5)
|
|
if numrows<1:
|
|
numrows=1
|
|
self.fbarmodrow=[]
|
|
for i in xrange(numrows):
|
|
self.fbarmodrow.append(Tkinter.Frame(self.cd.fbar))
|
|
numinrow=int(float(len(butList))/float(numrows))+1
|
|
#
|
|
# Make buttons
|
|
#
|
|
self.but1names=butList[:]
|
|
self.but1text=butList[:]
|
|
for i in xrange(len(self.but1names)):
|
|
but=self.but1names[i]
|
|
if but.isdigit():
|
|
self.but1names[i]="f%s"%but
|
|
num=1
|
|
self.but1={}
|
|
self.but1state={}
|
|
for i in xrange(len(self.but1names)):
|
|
but=self.but1names[i]
|
|
buttext=self.but1text[i]
|
|
cb=GenericCallback(showBut1,self,but)
|
|
row=int(float(num)/float(numinrow))
|
|
if width==0:
|
|
self.but1[but]=Tkinter.Button(self.fbarmodrow[row],text=buttext,
|
|
padx=2,pady=0,fg="black",
|
|
command=cb)
|
|
else:
|
|
self.but1[but]=Tkinter.Button(self.fbarmodrow[row],text=buttext,
|
|
width=width,padx=2,pady=0,fg="black",
|
|
command=cb)
|
|
self.but1[but].pack(side=Tkinter.LEFT)
|
|
num+=1
|
|
#
|
|
# put generic 'move right' button on the side
|
|
#
|
|
if ((arrows==1)and(len(butList)>1)):
|
|
cb=GenericCallback(nextBut1,self)
|
|
next=Tkinter.Button(self.cd.fbar,text=">",padx=2,pady=0,
|
|
fg="black",command=cb)
|
|
next.pack(side=Tkinter.RIGHT,fill=Tkinter.Y)
|
|
#
|
|
# pack buttons between possible next/prev buttons
|
|
#
|
|
for i in xrange(numrows):
|
|
self.fbarmodrow[i].pack(side=Tkinter.TOP)
|
|
#
|
|
# Update cd widget - so size of buttonbars don't affect size of
|
|
# the current canvas
|
|
#
|
|
self.cd.update_idletasks()
|
|
hgt1=self.cd.bar.winfo_reqheight()
|
|
hgt2=self.cd.fbar.winfo_reqheight()
|
|
hgt3=28+536 # size of exit button bar + smallest canvas height
|
|
hgt=hgt1+hgt2+hgt3
|
|
self.cd.minsize(706,hgt)
|
|
geo=self.cd.geometry()
|
|
(wh,of)=geo.split("+",1)
|
|
(wid,oldhgt)=wh.split("x",1)
|
|
if hgt>int(oldhgt):
|
|
self.cd.geometry("%sx%d+%s"%(wid,hgt,of))
|
|
self.cd.update_idletasks()
|
|
return
|
|
#==================================================================
|
|
# setupBut2 - setup button 2 buttons. Names in butList are copied
|
|
# to self.but2names[]. Desired buttons on a row in
|
|
# numbuttons. arrows flag adds 'prev/next' buttons.
|
|
#
|
|
# After setup, self.but2names[] holds names.
|
|
# self.but2{} holds button references
|
|
# self.but2state{} holds button state
|
|
#
|
|
def setupBut2(self,butList,numbuttons=5,arrows=0,width=0):
|
|
#
|
|
# remove old buttons (bar holds button 2s)
|
|
#
|
|
slaves=self.cd.bar.pack_slaves()
|
|
if slaves is not None:
|
|
for slave in slaves:
|
|
slave.destroy()
|
|
#
|
|
# put generic 'move left' button on the side
|
|
#
|
|
if ((arrows==1)and(len(butList)>1)):
|
|
cb=GenericCallback(prevBut2,self)
|
|
prev=Tkinter.Button(self.cd.bar,text="<",padx=2,pady=0,
|
|
fg="black",command=cb)
|
|
prev.pack(side=Tkinter.LEFT,fill=Tkinter.Y)
|
|
#
|
|
# figure number of rows of buttons - and create frames
|
|
#
|
|
numrows=int((float(len(butList))/float(numbuttons))+0.5)
|
|
if numrows<1:
|
|
numrows=1
|
|
self.barmodrow=[]
|
|
for i in xrange(numrows):
|
|
self.barmodrow.append(Tkinter.Frame(self.cd.bar))
|
|
numinrow=int(float(len(butList))/float(numrows))+1
|
|
#
|
|
# Make buttons
|
|
#
|
|
self.but2names=butList[:]
|
|
num=1
|
|
self.but2={}
|
|
self.but2state={}
|
|
for i in xrange(len(self.but2names)):
|
|
but=self.but2names[i]
|
|
cb=GenericCallback(showBut2,self,but)
|
|
row=int(float(num)/float(numinrow))
|
|
if width==0:
|
|
self.but2[but]=Tkinter.Button(self.barmodrow[row],text=but,
|
|
padx=2,pady=0,fg=self.colornames[but],
|
|
command=cb)
|
|
else:
|
|
self.but2[but]=Tkinter.Button(self.barmodrow[row],text=but,width=width,
|
|
padx=2,pady=0,fg=self.colornames[but],
|
|
command=cb)
|
|
self.but2[but].pack(side=Tkinter.LEFT)
|
|
num+=1
|
|
#
|
|
# put generic 'move right' button on the side
|
|
#
|
|
if ((arrows==1)and(len(butList)>1)):
|
|
cb=GenericCallback(nextBut2,self)
|
|
next=Tkinter.Button(self.cd.bar,text=">",padx=2,pady=0,
|
|
fg="black",command=cb)
|
|
next.pack(side=Tkinter.RIGHT,fill=Tkinter.Y)
|
|
#
|
|
# pack buttons between possible next/prev buttons
|
|
#
|
|
for i in xrange(numrows):
|
|
self.barmodrow[i].pack(side=Tkinter.TOP)
|
|
#
|
|
# Update cd widget and its minsize - so size of buttonbar
|
|
# doesn't affect size of the current canvas
|
|
#
|
|
self.cd.update_idletasks()
|
|
hgt1=self.cd.bar.winfo_reqheight()
|
|
hgt2=self.cd.fbar.winfo_reqheight()
|
|
hgt3=28+536 # size of exit button bar + smallest canvas height
|
|
hgt=hgt1+hgt2+hgt3
|
|
self.cd.minsize(706,hgt)
|
|
geo=self.cd.geometry()
|
|
(wh,of)=geo.split("+",1)
|
|
(oldwid,oldhgt)=wh.split("x",1)
|
|
if hgt>int(oldhgt):
|
|
self.cd.geometry("%sx%d+%s"%(oldwid,hgt,of))
|
|
self.cd.update_idletasks()
|
|
return
|
|
#==================================================================
|
|
# cdLabels - labels at the top of the canvas
|
|
#
|
|
def cdLabels(self,ul1,numPts,dateStyle,dateType,numDays,fromDay,dayList,cycleList):
|
|
#
|
|
# Upper Left has variable text
|
|
#
|
|
self.labelLine(ul1,1,justify="left")
|
|
#
|
|
str="Gridpoints in editarea: %d"%numPts
|
|
self.labelLine(str,2,justify="left")
|
|
#
|
|
# Dates
|
|
#
|
|
timelabel=dateStyle
|
|
if dateType=="Period Length":
|
|
(gyea,gmon,gday,ghou,gmin,gsec,gwda,gyda,gdst)=time.gmtime(fromDay)
|
|
if numDays==1:
|
|
timelabel+=" %4.4d/%2.2d/%2.2d"%(gyea,gmon,gday)
|
|
else:
|
|
timelabel+=" the %d days ending on %4.4d/%2.2d/%2.2d"%(numDays,gyea,gmon,gday)
|
|
else:
|
|
if len(dayList)==1:
|
|
(gyea,gmon,gday,ghou,gmin,gsec,gwda,gyda,gdst)=time.gmtime(dayList[0])
|
|
timelabel+=" %4.4d/%2.2d/%2.2d"%(gyea,gmon,gday)
|
|
else:
|
|
timelabel+=" several dates"
|
|
self.labelLine(timelabel,1,justify="right")
|
|
#
|
|
# Cycles
|
|
#
|
|
runlabel=""
|
|
if len(cycleList)>1:
|
|
for cyc in cycleList:
|
|
runlabel+="%2.2d+"%cyc
|
|
runlabel=runlabel[:-1]+" UTC Runs"
|
|
else:
|
|
runlabel="%2.2d"%cycleList[0]+" UTC Run ONLY"
|
|
self.labelLine(runlabel,2,justify="right")
|
|
return
|
|
#==================================================================
|
|
# labLine - draw a label
|
|
#
|
|
def labelLine(self,text,lineNum,color="black",justify="left",
|
|
tags=None):
|
|
lineheight=15
|
|
yoff=5
|
|
xoff=5
|
|
|
|
y=((lineNum-1)*lineheight)+yoff
|
|
if justify=="left":
|
|
x=xoff
|
|
anchorType=Tkinter.NW
|
|
else:
|
|
x=self.cd.curwidth-xoff
|
|
anchorType=Tkinter.NE
|
|
self.cd.canvas.create_text(x,y,text=text,fill=color,
|
|
anchor=anchorType,tags=tags)
|
|
return
|
|
#==================================================================
|
|
# checkLists - check lists returned from GUI to make sure at least
|
|
# one is chosen
|
|
#
|
|
def checkLists(self,modelList,parmList,cycleList,fcstrList,dateType,
|
|
dayList):
|
|
if (len(modelList)<1):
|
|
self.statusBarMsg("Must choose at least one model","U")
|
|
return 0
|
|
if (len(parmList)<1):
|
|
self.statusBarMsg("Must choose at least one parm","U")
|
|
return 0
|
|
if (len(cycleList)<1):
|
|
self.statusBarMsg("Must choose at least one cycle","U")
|
|
return 0
|
|
if (len(fcstrList)<1):
|
|
self.statusBarMsg("Must choose at least one forecaster","U")
|
|
return 0
|
|
if dateType=="List of dates":
|
|
if (len(dayList)<1):
|
|
self.statusBarMsg("Must choose at least one date","U")
|
|
return 0
|
|
return 1
|
|
#==================================================================
|
|
#
|
|
# code to scale everything on the canvas so that you always display
|
|
# the same area that you started with
|
|
#
|
|
def resizecanvas(self,event):
|
|
scalex=float(event.width)/self.curwidth
|
|
scaley=float(event.height)/self.curheight
|
|
if ((scalex!=1.0)or(scaley!=1.0)):
|
|
self.canvas.scale("all",0.0,0.0,scalex,scaley)
|
|
self.curwidth=float(event.width)
|
|
self.curheight=float(event.height)
|
|
#bw=2
|
|
#self.canwidth=self.curwidth-((bw+1.0)*2.0)
|
|
#self.canheight=self.canheight-((bw+1.0)*2.0)
|
|
#print "resize canvas gives width/height as: %7.2f,%7.2f"%(self.curwidth,self.curheight)
|
|
return
|
|
#
|
|
# setup graph coordintes
|
|
#
|
|
def setgraph(self,xmin,xmax,ymin,ymax,sxmin,sxmax,symin,symax):
|
|
self.xmin=xmin
|
|
self.xmax=xmax
|
|
self.ymin=ymin
|
|
self.ymax=ymax
|
|
self.xmult=(sxmax-sxmin)/(xmax-xmin)
|
|
self.xoff=sxmin
|
|
self.ymult=(symax-symin)/(ymax-ymin)
|
|
self.yoff=symax
|
|
def graphcoord(self,x,y):
|
|
newx=((x-self.xmin)*self.xmult)+self.xoff
|
|
newy=self.yoff-((y-self.ymin)*self.ymult)
|
|
return newx,newy
|
|
#==================================================================
|
|
#
|
|
# draw histogram axes
|
|
#
|
|
def histoaxes(self,maxheight,minx,maxx,binwidth,htick):
|
|
(sx,sy)=self.graphcoord(0.0,0.0)
|
|
(tx,ty)=self.graphcoord(0.0,maxheight)
|
|
self.cd.canvas.create_line(sx,sy,tx,ty)
|
|
self.vtick(0.0,5,0.0,maxheight,htick,label=1,labeloffset=10,
|
|
skipfirst=1,labelinterval=2)
|
|
#minx=binmin[1]+(binwidth/2.0)
|
|
#maxx=binmax[len(binmax)-2]-(binwidth/2.0)
|
|
(sx,sy)=self.graphcoord(minx,0.0)
|
|
(tx,ty)=self.graphcoord(maxx,0.0)
|
|
self.cd.canvas.create_line(sx,sy,tx,ty)
|
|
numticks=10
|
|
tickInterval=self.niceNumDec(maxx/(numticks-1),1)
|
|
self.htick(0.0,5,0.0,maxx,tickInterval,label=1,
|
|
labeloffset=5,labelinterval=2,skipfirst=1)
|
|
self.htick(0.0,5,0.0,maxx,tickInterval,label=1,
|
|
labeloffset=5,labelinterval=2,skipfirst=1,negative=1)
|
|
#==================================================================
|
|
# probaxes - draw axes for probability reliability diagrams
|
|
#
|
|
def probaxes(self):
|
|
(llx,lly)=self.graphcoord(0,0)
|
|
(urx,ury)=self.graphcoord(100,100)
|
|
self.cd.canvas.create_line(llx,lly,urx,lly,urx,ury,llx,ury,llx,lly,urx,ury)
|
|
self.vtick(0,5,0,100,10,label=1,
|
|
labelinterval=1,labeloffset=-10.0,labelanchor=Tkinter.E)
|
|
self.vtick(100,5,0,100,10,label=1,
|
|
labelinterval=1,labeloffset=10.0,labelanchor=Tkinter.W)
|
|
self.htick(0,5,0,100,10,label=1,
|
|
labelinterval=1,labeloffset=8.0,labelanchor=Tkinter.N)
|
|
self.htick(100,5,0,100,10,label=1,
|
|
labelinterval=1,labeloffset=-8.0,labelanchor=Tkinter.S)
|
|
(midx,ny)=self.graphcoord(50,0)
|
|
self.cd.canvas.create_text(midx,ny+20,anchor=Tkinter.N,text="Forecast Probability")
|
|
(nx,midy)=self.graphcoord(0,50)
|
|
self.cd.canvas.create_text(nx-35,midy,anchor=Tkinter.E,text="O\nb\ns\ne\nr\nv\ne\nd\n \nF\nr\ne\nq\nu\ne\nc\ny")
|
|
#
|
|
#==================================================================
|
|
#
|
|
# draw graph axes
|
|
#
|
|
def graphaxes(self,timemin,timemax,valmin,valmax):
|
|
(llx,lly)=self.graphcoord(timemin,valmin)
|
|
(urx,ury)=self.graphcoord(timemax,valmax)
|
|
self.cd.canvas.create_line(llx,lly,urx,lly,urx,ury,llx,ury,llx,lly)
|
|
zeroline=0
|
|
if ((valmin<0.0)and(valmax>0.0)):
|
|
(lx,zy)=self.graphcoord(timemin,0.0)
|
|
self.cd.canvas.create_line(llx,zy,urx,zy)
|
|
zeroline=1
|
|
numticks=10
|
|
self.timetick(timemin,timemax,numticks,valmin,5,label=1,labeloffset=10)
|
|
self.timetick(timemin,timemax,numticks,valmax,5,label=1,labeloffset=-10,
|
|
labelanchor=Tkinter.S)
|
|
if zeroline==1:
|
|
self.timetick(timemin,timemax,numticks,0.0,5,label=0)
|
|
#
|
|
numticks=10
|
|
valInterval=self.niceNumDec((valmax-valmin)/(numticks-1),1)
|
|
if zeroline==1:
|
|
self.vtick(timemin,5,0,valmax,valInterval,label=1,
|
|
labeloffset=-10,labelanchor=Tkinter.E)
|
|
self.vtick(timemin,5,0,-valmin,valInterval,label=1,
|
|
labeloffset=-10,labelanchor=Tkinter.E,negative=1)
|
|
self.vtick(timemax,5,0,valmax,valInterval,label=1,
|
|
labeloffset=10,labelanchor=Tkinter.W)
|
|
self.vtick(timemax,5,0,-valmin,valInterval,label=1,
|
|
labeloffset=10,labelanchor=Tkinter.W,negative=1)
|
|
else:
|
|
self.vtick(timemin,5,valmin,valmax,valInterval,label=1,
|
|
labeloffset=-10,labelanchor=Tkinter.E)
|
|
self.vtick(timemax,5,valmin,valmax,valInterval,label=1,
|
|
labeloffset=10,labelanchor=Tkinter.W)
|
|
#==================================================================
|
|
#
|
|
# draw fhour axes
|
|
#
|
|
def fhouraxes(self,timemin,timemax,valmin,valmax):
|
|
(llx,lly)=self.graphcoord(timemin,valmin)
|
|
(urx,ury)=self.graphcoord(timemax,valmax)
|
|
self.cd.canvas.create_line(llx,lly,urx,lly,urx,ury,llx,ury,llx,lly)
|
|
zeroline=0
|
|
if ((valmin<0.0)and(valmax>0.0)):
|
|
(lx,zy)=self.graphcoord(timemin,0.0)
|
|
self.cd.canvas.create_line(llx,zy,urx,zy)
|
|
zeroline=1
|
|
finterval=6
|
|
if timemax>120:
|
|
finterval=24
|
|
if timemax>48:
|
|
finterval=12
|
|
#finterval=self.niceNumDec((timemax-timemin)/(numticks-1),1)
|
|
self.htick(valmin,5,timemin,timemax,finterval,label=1,labeloffset=+5,
|
|
labelanchor=Tkinter.N)
|
|
self.htick(valmax,5,timemin,timemax,finterval,label=1,labeloffset=-5,
|
|
labelanchor=Tkinter.S)
|
|
if zeroline==1:
|
|
self.htick(0.0,5,timemin,timemax,finterval,label=0)
|
|
#self.timetick(timemin,timemax,numticks,valmin,5,label=1,labeloffset=10)
|
|
#self.timetick(timemin,timemax,numticks,valmax,5,label=1,labeloffset=-10,
|
|
# labelanchor=Tkinter.S)
|
|
#if zeroline==1:
|
|
# self.timetick(timemin,timemax,numticks,0.0,5,label=0)
|
|
#
|
|
numticks=10
|
|
valInterval=self.niceNumDec((valmax-valmin)/(numticks-1),1)
|
|
if zeroline==1:
|
|
self.vtick(timemin,5,0,valmax,valInterval,label=1,
|
|
labeloffset=-10,labelanchor=Tkinter.E)
|
|
self.vtick(timemin,5,0,-valmin,valInterval,label=1,
|
|
labeloffset=-10,labelanchor=Tkinter.E,negative=1,
|
|
skipfirst=1)
|
|
self.vtick(timemax,5,0,valmax,valInterval,label=1,
|
|
labeloffset=10,labelanchor=Tkinter.W)
|
|
self.vtick(timemax,5,0,-valmin,valInterval,label=1,
|
|
labeloffset=10,labelanchor=Tkinter.W,negative=1,
|
|
skipfirst=1)
|
|
else:
|
|
self.vtick(timemin,5,valmin,valmax,valInterval,label=1,
|
|
labeloffset=-10,labelanchor=Tkinter.E)
|
|
self.vtick(timemax,5,valmin,valmax,valInterval,label=1,
|
|
labeloffset=10,labelanchor=Tkinter.W)
|
|
#==================================================================
|
|
#
|
|
def timetick(self,minsecs,maxsecs,desirednum,yval,ywid,label=1,labeloffset=-10,labelanchor=Tkinter.N):
|
|
#print "in timetick with %d-%d, desired:%d"%(minsecs,maxsecs,desirednum)
|
|
numrange=desirednum*0.75
|
|
minnum=desirednum-numrange
|
|
maxnum=desirednum+numrange
|
|
monString=[" ","JAN","FEB","MAR","APR","MAY","JUN","JUL","AUG","SEP","OCT","NOV","DEC"]
|
|
#print "minnum-maxnum: %d-%d"%(minnum,maxnum)
|
|
HOUR=3600
|
|
DAY=24*HOUR
|
|
MONTH=30*DAY
|
|
YEAR=365*DAY
|
|
tryintervals=[(3,HOUR),
|
|
(6,HOUR),
|
|
(12,HOUR),
|
|
(1,DAY),
|
|
(2,DAY),
|
|
(5,DAY),
|
|
(15,DAY),
|
|
(1,MONTH),
|
|
(2,MONTH),
|
|
(3,MONTH),
|
|
(6,MONTH),
|
|
(1,YEAR)]
|
|
|
|
secondsRange=maxsecs-minsecs
|
|
intervals=[]
|
|
for (interval,base) in tryintervals:
|
|
intervalSeconds=interval*base
|
|
num=int(secondsRange/intervalSeconds)
|
|
if ((num>minnum)and(num<maxnum)):
|
|
#print "potential interval of %d * %d seconds - %d ticks"%(interval,base,num)
|
|
intervals.append((interval,base,num))
|
|
#
|
|
if len(intervals)<1:
|
|
#print "Could not find suitable interval"
|
|
#print "-----"
|
|
return
|
|
#
|
|
mindiff=desirednum
|
|
bestInterval=0
|
|
bestBase=0
|
|
for (interval,base,num) in intervals:
|
|
diff=abs(desirednum-num)
|
|
if diff<mindiff:
|
|
mindiff=diff
|
|
bestInterval=interval
|
|
bestBase=base
|
|
#print "best interval: %d * %d seconds"%(bestInterval,bestBase)
|
|
#
|
|
#
|
|
#
|
|
(gyea,gmon,gday,ghou,gmin,gsec,gwda,gyda,gdst)=time.gmtime(minsecs)
|
|
gmin=0
|
|
gsec=0
|
|
#
|
|
if bestBase==HOUR:
|
|
ghou=0
|
|
elif bestBase==DAY:
|
|
gday=1
|
|
ghou=0
|
|
else:
|
|
gmon=1
|
|
gday=1
|
|
ghou=0
|
|
newTime=calendar.timegm((gyea,gmon,gday,ghou,gmin,gsec,gwda,gyda,gdst))
|
|
|
|
|
|
while (newTime<=maxsecs):
|
|
(gyea,gmon,gday,ghou,gmin,gsec,gwda,gyda,gdst)=time.gmtime(newTime)
|
|
if newTime>minsecs:
|
|
(tx,ty)=self.graphcoord(newTime,yval)
|
|
self.cd.canvas.create_line(tx,ty-ywid,tx,ty+ywid)
|
|
if label==1:
|
|
if bestBase==HOUR:
|
|
if ((maxsecs-minsecs)/HOUR)>24:
|
|
labelstring="%d %2.2dZ"%(gday,ghou)
|
|
else:
|
|
labelstring="%2.2dZ"%ghou
|
|
elif bestBase==DAY:
|
|
if ((maxsecs-minsecs)/DAY)>28:
|
|
labelstring="%d/%d"%(gmon,gday)
|
|
else:
|
|
labelstring="%d"%gday
|
|
else:
|
|
if ((maxsecs-minsecs)/MONTH)>9:
|
|
labelstring="%d/%2.2d"%(gmon,gyea%100)
|
|
else:
|
|
labelstring="%s"%monString[gmon]
|
|
#labelstring="%d/%d %2.2dZ"%(gmon,gday,ghou)
|
|
self.cd.canvas.create_text(tx,ty+labeloffset,anchor=labelanchor,
|
|
text=labelstring)
|
|
|
|
#print "tick at %4.4d/%2.2d/%2.2d %2.2dZ"%(gyea,gmon,gday,ghou)
|
|
if bestBase==HOUR:
|
|
tryTime=calendar.timegm((gyea,gmon,gday,ghou+bestInterval,gmin,gsec,gwda,gyda,gdst))
|
|
elif bestBase==DAY:
|
|
tryTime=calendar.timegm((gyea,gmon,gday+bestInterval,ghou,gmin,gsec,gwda,gyda,gdst))
|
|
else:
|
|
newmon=gmon+bestInterval
|
|
if newmon>12:
|
|
gyea+=1
|
|
newmon=1
|
|
tryTime=calendar.timegm((gyea,newmon,gday,ghou,gmin,gsec,gwda,gyda,gdst))
|
|
(nyea,nmon,nday,nhou,nmin,nsec,nwda,nyda,ndst)=time.gmtime(tryTime)
|
|
if ((nday!=gday)and(bestBase==HOUR)):
|
|
gday+=1
|
|
ghou=0
|
|
tryTime=calendar.timegm((gyea,gmon,gday,ghou,gmin,gsec,gwda,gyda,gdst))
|
|
elif (((nmon!=gmon)or(nday>26))and(bestBase==DAY)):
|
|
gmon+=1
|
|
if gmon>12:
|
|
gmon=1
|
|
gyea+=1
|
|
gday=1
|
|
tryTime=calendar.timegm((gyea,gmon,gday,ghou,gmin,gsec,gwda,gyda,gdst))
|
|
elif ((nyea!=gyea)and(bestBase==MONTH)):
|
|
gyea+=1
|
|
gmon=1
|
|
tryTime=calendar.timegm((gyea,gmon,gday,ghou,gmin,gsec,gwda,gyda,gdst))
|
|
newTime=tryTime
|
|
#print "-----"
|
|
return
|
|
#==================================================================
|
|
# draw ticks on a horizontal axis at y-value:yval. The width of
|
|
# ticks if ywid. Ticks go between minx and maxx with interval
|
|
# tickinterval. If labelinterval is 0, no labels are drawn,
|
|
# if 1, the every label is drawn. If labelinterval is 2 then
|
|
# every 2nd label is drawn. It labelinterval is 3 then every
|
|
# 3rd label is drawn, etc. If skipfirst=1 or skiplast=1 then
|
|
# labelling is skipped for those ticks.
|
|
#
|
|
def htick(self,yval,ywid,minx,maxx,tickInterval,label=0,labeloffset=5,
|
|
labelinterval=1,skipfirst=0,skiplast=0,labelanchor=Tkinter.N,
|
|
negative=0):
|
|
numticks=int((maxx-minx)/tickInterval)+1
|
|
labeldigits=max(-floor(log10(tickInterval)),0)
|
|
neg=1.0
|
|
if negative==1:
|
|
neg=-1.0
|
|
num=0
|
|
for i in xrange(0,numticks):
|
|
x=(minx+(i*tickInterval))*neg
|
|
(tx,ty)=self.graphcoord(x,yval)
|
|
self.cd.canvas.create_line(tx,ty-ywid,tx,ty+ywid)
|
|
if label==1:
|
|
if (i%labelinterval==0):
|
|
if labeldigits==0:
|
|
labelstring="%d"%x
|
|
else:
|
|
format="%%.%df"%labeldigits
|
|
labelstring=format%x
|
|
skip=0
|
|
if ((skipfirst==1)and(i==0))or((skiplast==1)and(i==(numticks-1))):
|
|
skip=1
|
|
if skip==0:
|
|
self.cd.canvas.create_text(tx,ty+labeloffset,anchor=labelanchor,
|
|
text=labelstring)
|
|
def vtick(self,xval,xwid,miny,maxy,tickInterval,label=0,labeloffset=5,
|
|
labelinterval=1,skipfirst=0,skiplast=0,labelanchor=Tkinter.W,
|
|
negative=0):
|
|
numticks=int((maxy-miny)/tickInterval)+1
|
|
labeldigits=max(-floor(log10(tickInterval)),0)
|
|
neg=1.0
|
|
if negative==1:
|
|
neg=-1.0
|
|
num=0
|
|
for i in xrange(0,numticks):
|
|
y=(miny+(i*tickInterval))*neg
|
|
(tx,ty)=self.graphcoord(xval,y)
|
|
self.cd.canvas.create_line(tx-xwid,ty,tx+xwid,ty)
|
|
if label==1:
|
|
if (i%labelinterval==0):
|
|
if labeldigits==0:
|
|
labelstring="%d"%y
|
|
else:
|
|
format="%%.%df"%labeldigits
|
|
labelstring=format%y
|
|
skip=0
|
|
if ((skipfirst==1)and(i==0))or((skiplast==1)and(i==(numticks-1))):
|
|
skip=1
|
|
if skip==0:
|
|
self.cd.canvas.create_text(tx+labeloffset,ty,anchor=labelanchor,
|
|
text=labelstring)
|
|
|
|
#==================================================================
|
|
#
|
|
# make axes for value distributions
|
|
#
|
|
def valaxes(self,minval,maxval,tickInterval):
|
|
(nx,ny)=self.graphcoord(minval,minval)
|
|
(xx,xy)=self.graphcoord(maxval,maxval)
|
|
self.cd.canvas.create_line(nx,ny,xx,ny,xx,xy,nx,xy,nx,ny,xx,xy)
|
|
self.vtick(minval,5,minval,maxval,tickInterval,label=1,
|
|
labelinterval=3,labeloffset=-10.0,labelanchor=Tkinter.E)
|
|
self.vtick(maxval,5,minval,maxval,tickInterval,label=1,
|
|
labelinterval=3,labeloffset=10.0,labelanchor=Tkinter.W)
|
|
self.htick(minval,5,minval,maxval,tickInterval,label=1,
|
|
labelinterval=3,labeloffset=8.0,labelanchor=Tkinter.N)
|
|
self.htick(maxval,5,minval,maxval,tickInterval,label=1,
|
|
labelinterval=3,labeloffset=-8.0,labelanchor=Tkinter.S)
|
|
(midx,dumy)=self.graphcoord((maxval+minval)/2.0,minval)
|
|
self.cd.canvas.create_text(midx,ny+20,anchor=Tkinter.N,text="Observed")
|
|
(dumx,midy)=self.graphcoord(minval,(maxval+minval)/2.0)
|
|
self.cd.canvas.create_text(nx-35,midy,anchor=Tkinter.E,text="F\no\nr\ne\nc\na\ns\nt")
|
|
#==================================================================
|
|
#
|
|
# make axes for expected value distributions
|
|
#
|
|
def expaxes(self,minval,maxval,tickInterval):
|
|
(nx,ny)=self.graphcoord(minval,minval)
|
|
(xx,xy)=self.graphcoord(maxval,maxval)
|
|
self.cd.canvas.create_line(nx,ny,xx,ny,xx,xy,nx,xy,nx,ny,xx,xy)
|
|
self.vtick(minval,5,minval,maxval,tickInterval,label=1,
|
|
labelinterval=3,labeloffset=-10.0,labelanchor=Tkinter.E)
|
|
self.vtick(maxval,5,minval,maxval,tickInterval,label=1,
|
|
labelinterval=3,labeloffset=10.0,labelanchor=Tkinter.W)
|
|
self.htick(minval,5,minval,maxval,tickInterval,label=1,
|
|
labelinterval=3,labeloffset=8.0,labelanchor=Tkinter.N)
|
|
self.htick(maxval,5,minval,maxval,tickInterval,label=1,
|
|
labelinterval=3,labeloffset=-8.0,labelanchor=Tkinter.S)
|
|
(midx,dumy)=self.graphcoord((maxval+minval)/2.0,minval)
|
|
self.cd.canvas.create_text(midx,ny+20,anchor=Tkinter.N,text="Forecast")
|
|
(dumx,midy)=self.graphcoord(minval,(maxval+minval)/2.0)
|
|
self.cd.canvas.create_text(nx-35,midy,anchor=Tkinter.E,text="O\nb\ns\ne\nr\nv\ne\nd")
|
|
#==================================================================
|
|
#
|
|
# make axes for value histograms
|
|
#
|
|
def valhaxes(self,minval,maxval,tickInterval,maxnum,vint,parm):
|
|
(nx,ny)=self.graphcoord(minval,0)
|
|
(xx,xy)=self.graphcoord(maxval,maxnum)
|
|
self.cd.canvas.create_line(nx,ny,xx,ny,xx,xy,nx,xy,nx,ny)
|
|
#vint=self.niceNumDec(maxnum/20,1)
|
|
self.vtick(minval,5,0.0,maxnum,vint,label=1,
|
|
labelinterval=3,labeloffset=-10.0,labelanchor=Tkinter.E)
|
|
self.vtick(maxval,5,0.0,maxnum,vint,label=1,
|
|
labelinterval=3,labeloffset=10.0,labelanchor=Tkinter.W)
|
|
self.htick(0,5,minval,maxval,tickInterval,label=1,
|
|
labelinterval=3,labeloffset=8.0,labelanchor=Tkinter.N)
|
|
self.htick(maxnum,5,minval,maxval,tickInterval,label=1,
|
|
labelinterval=3,labeloffset=-8.0,labelanchor=Tkinter.S)
|
|
(midx,dumy)=self.graphcoord((maxval+minval)/2.0,minval)
|
|
self.cd.canvas.create_text(midx,ny+20,anchor=Tkinter.N,text=parm)
|
|
(dumx,midy)=self.graphcoord(minval,(0+maxnum)/2.0)
|
|
self.cd.canvas.create_text(nx-35,midy,anchor=Tkinter.E,text="N\nu\nm\nb\ne\nr\n \np\ne\nr\n \nc\na\ns\ne")
|
|
#==================================================================
|
|
#
|
|
# make axes for logarithmic value histograms
|
|
#
|
|
def logvalhaxes(self,minval,maxval,tickInterval,logmin,logmax,parm):
|
|
(nx,ny)=self.graphcoord(minval,logmin)
|
|
(xx,xy)=self.graphcoord(maxval,logmax)
|
|
self.cd.canvas.create_line(nx,ny,xx,ny,xx,xy,nx,xy,nx,ny)
|
|
#vint=self.niceNumDec(maxnum/20,1)
|
|
lownum=exp(logmin)
|
|
hignum=exp(logmax)
|
|
print "need ticks from %f to %f"%(lownum,hignum)
|
|
expstart=int(floor(log10(lownum)))
|
|
expend=int(floor(log10(hignum)))
|
|
print " exponents from %d to %d"%(expstart,expend)
|
|
for j in xrange(expstart,expend+1):
|
|
a=10.0**j
|
|
print " loop decade=%f"%a
|
|
for i in xrange(1,10):
|
|
if i==1:
|
|
xwid=5
|
|
else:
|
|
xwid=2
|
|
val=float(i)*a
|
|
if ((val>lownum)and(val<hignum)):
|
|
logy=log(val)
|
|
(nx,ty)=self.graphcoord(minval,logy)
|
|
self.cd.canvas.create_line(nx-xwid,ty,nx+xwid,ty)
|
|
(xx,ty)=self.graphcoord(maxval,logy)
|
|
self.cd.canvas.create_line(xx-xwid,ty,xx+xwid,ty)
|
|
if xwid==5:
|
|
if j>=0:
|
|
labelstring="%d"%val
|
|
else:
|
|
format="%%.%df"%abs(j)
|
|
labelstring=format%val
|
|
self.cd.canvas.create_text(nx-10.0,ty,anchor=Tkinter.E,
|
|
text=labelstring)
|
|
self.cd.canvas.create_text(xx+10.0,ty,anchor=Tkinter.W,
|
|
text=labelstring)
|
|
|
|
|
|
#self.vtick(minval,5,0.0,maxnum,vint,label=1,
|
|
# labelinterval=3,labeloffset=-10.0,labelanchor=Tkinter.E)
|
|
#self.vtick(maxval,5,0.0,maxnum,vint,label=1,
|
|
# labelinterval=3,labeloffset=10.0,labelanchor=Tkinter.W)
|
|
self.htick(logmin,5,minval,maxval,tickInterval,label=1,
|
|
labelinterval=3,labeloffset=8.0,labelanchor=Tkinter.N)
|
|
self.htick(logmax,5,minval,maxval,tickInterval,label=1,
|
|
labelinterval=3,labeloffset=-8.0,labelanchor=Tkinter.S)
|
|
(midx,dumy)=self.graphcoord((maxval+minval)/2.0,logmin)
|
|
self.cd.canvas.create_text(midx,ny+20,anchor=Tkinter.N,text=parm)
|
|
(dumx,midy)=self.graphcoord(minval,(logmin+logmax)/2.0)
|
|
self.cd.canvas.create_text(nx-35,midy,anchor=Tkinter.E,text="N\nu\nm\nb\ne\nr\n \np\ne\nr\n \nc\na\ns\ne")
|
|
#==================================================================
|
|
# niceNumDec - pick a nice decimal number - suitable for tick
|
|
# marks, etc.
|
|
#
|
|
def niceNumDec(self,val,roundit):
|
|
if val==0:
|
|
return 1
|
|
e=floor(log10(val))
|
|
a=10.0**e
|
|
f=val/a
|
|
if roundit>0:
|
|
if f<1.5:
|
|
nf=1
|
|
elif f<3.0:
|
|
nf=2
|
|
elif f<7.0:
|
|
nf=5
|
|
else:
|
|
nf=10
|
|
else:
|
|
if f<=1.0:
|
|
nf=1
|
|
elif f<=2.0:
|
|
nf=2.0
|
|
elif f<=5.0:
|
|
nf=5.0
|
|
else:
|
|
nf=10.0
|
|
return nf*a
|
|
#==================================================================
|
|
# showScore - draw tick on 'colorcurve' with label of 0-100 score
|
|
#
|
|
def showScore(self,fullscore,mod,color,taglabel):
|
|
midx=self.cd.curwidth/2.0
|
|
#x=midx+(128*((-fullscore+0.5)/0.5))
|
|
if fullscore<0:
|
|
fullscore=0.0
|
|
x=midx+(128*((fullscore-50.0)/50.0))
|
|
txt="%d"%int(fullscore)
|
|
if mod=="Official":
|
|
self.cd.canvas.create_line(x,50-8,x,50-3,fill=color,tags=taglabel)
|
|
self.cd.canvas.create_text(x,50-8,text=txt,fill=color,anchor=Tkinter.S,tags=taglabel)
|
|
else:
|
|
self.cd.canvas.create_line(x,50+3,x,50+8,fill=color,tags=taglabel)
|
|
self.cd.canvas.create_text(x,50+8,text=txt,fill=color,anchor=Tkinter.N,tags=taglabel)
|
|
#==================================================================
|
|
# showWorse - show the number in the first/last bins - which are
|
|
# worse than the error limits
|
|
#
|
|
def showWorse(self,low,high,xmax,yoffset,color,taglist):
|
|
x=xmax
|
|
y=0
|
|
(sx,sy)=self.graphcoord(x,y)
|
|
self.cd.canvas.create_text(sx+5,sy,text="Worse",anchor=Tkinter.W)
|
|
textstring="%d"%high
|
|
self.cd.canvas.create_text(sx+5,sy-yoffset,text=textstring,anchor=Tkinter.W,fill=color,tags=taglist)
|
|
x=-xmax
|
|
y=0
|
|
(sx,sy)=self.graphcoord(x,y)
|
|
self.cd.canvas.create_text(sx-5,sy,text="Worse",anchor=Tkinter.E)
|
|
textstring="%d"%low
|
|
self.cd.canvas.create_text(sx-5,sy-yoffset,text=textstring,anchor=Tkinter.E,fill=color,tags=taglist)
|
|
#==================================================================
|
|
# showScores - display modelname,n,avg,std,mae,rms on histogram
|
|
#
|
|
def showScores(self,modnum,mod,num,avg,std,mae,rms,color,taglist):
|
|
font=12
|
|
ystart=self.cd.curheight*(135.0/530.0)
|
|
y=ystart+font+(modnum*font)
|
|
x=self.cd.curwidth*(80.0/700.0)
|
|
self.cd.canvas.create_text(x,y,text=mod,anchor=Tkinter.E,fill=color,tags=taglist)
|
|
textstring="%2d"%num
|
|
x=self.cd.curwidth*(130.0/700.0)
|
|
self.cd.canvas.create_text(x,y,text=textstring,anchor=Tkinter.E,
|
|
fill=color,tags=taglist)
|
|
textstring="%6.2f"%avg
|
|
x=self.cd.curwidth*(170.0/700.0)
|
|
self.cd.canvas.create_text(x,y,text=textstring,anchor=Tkinter.E,
|
|
fill=color,tags=taglist)
|
|
textstring="%5.2f"%std
|
|
x=self.cd.curwidth*(210.0/700.0)
|
|
self.cd.canvas.create_text(x,y,text=textstring,anchor=Tkinter.E,
|
|
fill=color,tags=taglist)
|
|
textstring="%5.2f"%mae
|
|
x=self.cd.curwidth*(250.0/700.0)
|
|
self.cd.canvas.create_text(x,y,text=textstring,anchor=Tkinter.E,
|
|
fill=color,tags=taglist)
|
|
textstring="%5.2f"%rms
|
|
x=self.cd.curwidth*(290.0/700.0)
|
|
self.cd.canvas.create_text(x,y,text=textstring,anchor=Tkinter.E,
|
|
fill=color,tags=taglist)
|
|
return
|
|
#==================================================================
|
|
# showAvg - draw arrow on histogram axis at average value
|
|
#
|
|
def showAvg(self,avg,color,taglist):
|
|
(sx,sy)=self.graphcoord(avg,0)
|
|
self.cd.canvas.create_line(sx,sy+30,sx,sy,fill=color,
|
|
arrow=Tkinter.LAST,tags=taglist)
|
|
textstring="%.2f"%avg
|
|
self.cd.canvas.create_text(sx,sy+30,text=textstring,anchor=Tkinter.N,
|
|
fill=color,tags=taglist)
|
|
#==================================================================
|
|
#
|
|
# getBins - given a bin width and maxerr value, return
|
|
# lists of each bin's min,max, with one of them
|
|
# centerred on zero. Last bins may start up to
|
|
# a half binwidth more than maxerr
|
|
#
|
|
def getBins(self,binwidth,maxerr):
|
|
halfbin=float(binwidth)/2.0
|
|
binmin=[]
|
|
binmax=[]
|
|
|
|
mid=0.0
|
|
while ((mid+halfbin)<=(maxerr+halfbin)):
|
|
maxx=mid+halfbin
|
|
binmin.append(-maxx)
|
|
binmax.append(-maxx+binwidth)
|
|
binmin.append(maxx-binwidth)
|
|
binmax.append(maxx)
|
|
mid+=binwidth
|
|
binmin.append(-900000.0)
|
|
binmax.append(-(mid-halfbin))
|
|
binmin.append(mid-halfbin)
|
|
binmax.append(9000000.0)
|
|
binmin.sort()
|
|
binmax.sort()
|
|
return(binmin,binmax)
|
|
#
|
|
def getProbBins(self,binwidth):
|
|
binmin=[]
|
|
binmax=[]
|
|
halfbin=float(binwidth)/2.0
|
|
num=int(100.0/float(binwidth))
|
|
for i in xrange(num):
|
|
binmid=i*binwidth
|
|
bot=max(binmid-halfbin,0)
|
|
top=min(binmid+halfbin,101)
|
|
binmin.append(bot)
|
|
binmax.append(top)
|
|
return(binmin,binmax)
|
|
#==================================================================
|
|
#
|
|
# binerrs - given a 1-D array of errors, create a 1-D array of
|
|
# the number of points with errors inside each bin
|
|
# described by the binmin and binmax limits
|
|
#
|
|
def binerrs(self,err,abinmin,abinmax):
|
|
bincnt=add.reduce(logical_and(greater(err,abinmin[:,NewAxis]),
|
|
less_equal(err,abinmax[:,NewAxis])),-1)
|
|
return bincnt
|
|
def histosetup(self,minlimit,maxlimit,binwidth):
|
|
self.histowidth=binwidth
|
|
self.histohalf=binwidth/2.0
|
|
self.histomin=minlimit-self.histohalf
|
|
self.histomax=maxlimit+self.histohalf
|
|
self.histonumbins=int(float(self.histomax-self.histomin)/float(self.histowidth))
|
|
self.histobins=resize(arange(self.histonumbins+1),(self.histonumbins+1,1))
|
|
return
|
|
def histo(self,data):
|
|
worseLow=add.reduce(less(data,self.histomin))
|
|
worseHigh=add.reduce(greater(data,self.histomax))
|
|
data=repeat(data,logical_and(less_equal(data,self.histomax),
|
|
greater_equal(data,self.histomin)))
|
|
data=((data-self.histomin)/self.histowidth).astype(int)
|
|
histoData=add.reduce(equal(self.histobins,data),-1)
|
|
histoData[-2]+=histoData[-1]
|
|
return histoData[:-1],worseLow,worseHigh
|
|
def hitcount(self,data,verif):
|
|
numless=add.reduce(less(data,self.histomin))
|
|
numgreater=add.reduce(greater(data,self.histomax))
|
|
data=repeat(data,logical_and(less_equal(data,self.histomax),
|
|
greater_equal(data,self.histomin)))
|
|
data=((data-self.histomin)/self.histowidth).astype(int)
|
|
d1=equal(self.histobins,data)
|
|
self.VU.logMsg("shape of d1=%s"%str(d1.shape))
|
|
self.VU.logMsg("shape of verif=%s"%str(verif.shape))
|
|
histoData=add.reduce(d1,-1)
|
|
self.VU.logMsg("done with histoData reduce")
|
|
#hitCount=add.reduce(where(d1,verif,0),-1)
|
|
a=where(d1,verif,float32(0))
|
|
hitCount=add.reduce(a,-1)
|
|
self.VU.logMsg("done with hitCount reduce")
|
|
histoData[-2]+=histoData[-1]
|
|
hitCount[-2]+=hitCount[-1]
|
|
self.VU.logMsg("returning")
|
|
return histoData[:-2],hitCount[:-2]
|
|
|
|
#=================================================================
|
|
# setupGM - setup Grid Manager - remove all parms from display
|
|
# except for parms in parmList for models in modelList
|
|
# (or in mutableModel) and WG1 for the mutableModel
|
|
# (if available)
|
|
#
|
|
def setupGM(self,parmList,modelList):
|
|
#
|
|
#
|
|
#
|
|
newParmList=[]
|
|
for parm in parmList:
|
|
if (len(parm)>3):
|
|
last3=parm[-3:]
|
|
if ((last3=="Spd")or(last3=="Dir")):
|
|
realname=parm[:-3]
|
|
if realname not in newParmList:
|
|
newParmList.append(realname)
|
|
else:
|
|
newParmList.append(parm)
|
|
else:
|
|
newParmList.append(parm)
|
|
mutableModel=self.mutableID().modelName()
|
|
displayObjList=self._dbss.getParmManager().getDisplayedParms()
|
|
totalcount=len(displayObjList)
|
|
count=0
|
|
for parmObj in displayObjList:
|
|
count+=1
|
|
self.setWorking("Cleaning Grid Manager:%d of %d"%(count,totalcount))
|
|
if self.checkWorking()==1:
|
|
return 1
|
|
pid=parmObj.getParmID()
|
|
pmodel=pid.getDbId().getModelName()
|
|
pname=pid.getParmName()
|
|
plevel=pid.getParmLevel()
|
|
if ((pmodel==mutableModel)and(pname in newParmList)):
|
|
continue
|
|
if ((pmodel==mutableModel)and(pname=="WG1")):
|
|
continue
|
|
if ((pmodel in modelList)and(pname in newParmList)):
|
|
continue
|
|
print pmodel, pname, plevel
|
|
self.unloadWE(pmodel,pname,plevel)
|
|
#
|
|
# if WG1 exists - use that for the units and precision of
|
|
# error grids - otherwise use default values
|
|
#
|
|
(self.errUnits,self.errPrecision,minval,maxval,rateFlag,
|
|
ct,dminval,dmaxval)=self.getParmInfo(mutableModel,"WG1")
|
|
return 0
|
|
#==================================================================
|
|
#
|
|
#
|
|
#
|
|
def getParmInfo(self,mutableModel,parm):
|
|
units="units"
|
|
precision=0
|
|
minval=0
|
|
maxval=100
|
|
rateflag=0
|
|
colorTable="Gridded Data"
|
|
displayMinval=0
|
|
displayMaxval=100
|
|
parm=self.getParm(mutableModel,parm,"SFC")
|
|
if parm is not None:
|
|
parmInfo = parm.getGridInfo()
|
|
units=parmInfo.getUnitString()
|
|
precision=parmInfo.getPrecision()
|
|
minval=parmInfo.getMinValue()
|
|
maxval=parmInfo.getMaxValue()
|
|
rateflag=parmInfo.isRateParm()
|
|
from com.raytheon.viz.gfe.rsc import DiscreteDisplayUtil
|
|
ctInfo = DiscreteDisplayUtil.buildColorMapParameters(parm)
|
|
if ctInfo is not None:
|
|
colorTable = ctInfo.getColorMapName()
|
|
displayMinval = ctInfo.getColorMapMin()
|
|
displayMaxval = ctInfo.getColorMapMax()
|
|
self.__colorMapParams[colorTable] = ctInfo
|
|
return(units,precision,minval,maxval,rateflag,colorTable,displayMinval,displayMaxval)
|
|
#==============================================================================
|
|
#
|
|
# Class for other dialogs.
|
|
#
|
|
"""
|
|
class SimpleDialog(AppDialog.Dialog):
|
|
def __init__(self, parent=None, name="Simple Dialog", callbackMethod=None,
|
|
modal=1):
|
|
self.__parent = parent
|
|
self.__name = name
|
|
self.__modal = modal
|
|
self.__callbackMethod = callbackMethod
|
|
self.__dialog=AppDialog.Dialog.__init__(self,
|
|
parent=self.__parent,
|
|
title=self.__name,
|
|
modal=self.__modal)
|
|
return self.__dialog
|
|
|
|
def buttonbox(self):
|
|
buttonFrame = Tkinter.Frame(self)
|
|
if self.__modal == 1:
|
|
Tkinter.Button(buttonFrame, text="Ok",
|
|
command=self.__okCB, width=10, state=Tkinter.NORMAL).pack(\
|
|
side=Tkinter.LEFT, pady=5, padx=10)
|
|
else:
|
|
Tkinter.Button(buttonFrame, text="Run",
|
|
command=self.__runCB, width=10, state=Tkinter.NORMAL).pack(\
|
|
side=Tkinter.LEFT, pady=5, padx=10)
|
|
Tkinter.Button(buttonFrame, text="Run/Dismiss",
|
|
command=self.__okCB, width=12, state=Tkinter.NORMAL).pack(\
|
|
side=Tkinter.LEFT, pady=5, padx=10)
|
|
Tkinter.Button(buttonFrame, text="Cancel", width=10,
|
|
command=self.cancelCB).pack(side=Tkinter.RIGHT, pady=5, padx=10)
|
|
buttonFrame.pack(side=Tkinter.BOTTOM,expand=0)
|
|
def body(self, master):
|
|
bodylabel=Tkinter.Label(master,text="This is the body")
|
|
bodylabel.pack(side=Tkinter.BOTTOM)
|
|
def __runCB(self):
|
|
self.__callbackMethod("Run")
|
|
def __okCB(self):
|
|
self.withdraw()
|
|
self.__callbackMethod("OK")
|
|
self.ok()
|
|
def cancelCB(self):
|
|
self.__callbackMethod("Cancel")
|
|
self.cancel()
|
|
"""
|
|
#=======================================================================
|
|
class BVDialog(Tkinter.Toplevel):
|
|
def __init__(self,parent,title=None,modal=1,hide=0):
|
|
self.__modal=modal
|
|
Tkinter.Toplevel.__init__(self,parent)
|
|
try:
|
|
if hide==1:
|
|
self.withdraw()
|
|
##self.transient(parent)
|
|
if title:
|
|
self.title(title)
|
|
self.parent=parent
|
|
self.buttonbox()
|
|
bodyFrame=Tkinter.Frame(self)
|
|
self.body(bodyFrame)
|
|
bodyFrame.pack(side=Tkinter.BOTTOM,fill=Tkinter.BOTH,expand=1)
|
|
self.protocol("WM_DELETE_WINDOW",self.cancel)
|
|
if parent is not None:
|
|
self.geometry("+%d+%d"%(parent.winfo_rootx(),parent.winfo_rooty()))
|
|
if self.__modal==1:
|
|
self.deiconify()
|
|
self.wait_window(self)
|
|
except:
|
|
##self.destroy()
|
|
raise Exception
|
|
|
|
def buttonbox(self):
|
|
buttonFrame = Tkinter.Frame(self)
|
|
if self.__modal == 1:
|
|
Tkinter.Button(buttonFrame, text="Ok",
|
|
command=self.ok, width=10, state=Tkinter.NORMAL).pack(\
|
|
side=Tkinter.LEFT, pady=5, padx=10)
|
|
else:
|
|
Tkinter.Button(buttonFrame, text="Run",
|
|
command=self.run, width=10, state=Tkinter.NORMAL).pack(\
|
|
side=Tkinter.LEFT, pady=5, padx=10)
|
|
Tkinter.Button(buttonFrame, text="Run/Dismiss",
|
|
command=self.ok, width=12, state=Tkinter.NORMAL).pack(\
|
|
side=Tkinter.LEFT, pady=5, padx=10)
|
|
Tkinter.Button(buttonFrame, text="Cancel", width=10,
|
|
command=self.cancelCB).pack(side=Tkinter.RIGHT, pady=5, padx=10)
|
|
buttonFrame.pack(side=Tkinter.BOTTOM,expand=0)
|
|
def body(self, master):
|
|
pass
|
|
def ok(self,event=None):
|
|
if not self.validate():
|
|
return
|
|
self.withdraw()
|
|
self.update_idletasks()
|
|
self.apply()
|
|
self.cancel()
|
|
def cancel(self,event=None):
|
|
self.destroy()
|
|
def validate(self):
|
|
return 1
|
|
def apply(self):
|
|
pass
|
|
|
|
#=======================================================================
|
|
# Working - is a dialog to give user info while 'working'. You can
|
|
# set the label for it, or get the value of 'stop', which
|
|
# turns to 1 if they hit 'cancel' while this is displayed.
|
|
# should be 'withdrawn' while not 'working' on something.
|
|
#
|
|
class Working(BVDialog):
|
|
def __init__(self, parent=None, callbackMethod=None):
|
|
self.__parent=parent
|
|
self.__callbackMethod=callbackMethod
|
|
self.stop=Tkinter.IntVar()
|
|
self.label=Tkinter.StringVar()
|
|
BVDialog.__init__(self,parent=self.__parent,
|
|
title="%s Working"%PROGNAME,modal=0,hide=1)
|
|
self.update()
|
|
self.resizable(0,0)
|
|
return
|
|
def buttonbox(self):
|
|
buttonFrame = Tkinter.Frame(self)
|
|
but=Tkinter.Button(buttonFrame,text="Stop",command=self.__callbackMethod)
|
|
but.pack(side=Tkinter.LEFT,expand=0)
|
|
buttonFrame.pack(side=Tkinter.BOTTOM,fill=Tkinter.X,expand=0)
|
|
return
|
|
def body(self,master):
|
|
lab=Tkinter.Label(master,textvariable=self.label,width=60,
|
|
anchor=Tkinter.W)
|
|
lab.pack(side=Tkinter.LEFT)
|
|
self.label.set("Default Text")
|
|
return
|
|
def cancel(self):
|
|
self.__callbackMethod
|
|
return
|
|
#=======================================================================
|
|
# Cases - a dialog with a scrolled text window showing number of cases
|
|
# info. It has one button - a close button.
|
|
#
|
|
class Cases(BVDialog):
|
|
def __init__(self,parent,callbackMethod):
|
|
self.__parent=parent
|
|
self.__callbackMethod=callbackMethod
|
|
BVDialog.__init__(self,parent=self.__parent,title="Number of Cases",modal=0,hide=1)
|
|
self.update()
|
|
geo=self.geometry()
|
|
(wh,of)=geo.split("+",1)
|
|
(w,h)=wh.split("x",1)
|
|
self.minsize(int(w),int(h))
|
|
return
|
|
def buttonbox(self):
|
|
buttonFrame=Tkinter.Frame(self)
|
|
but=Tkinter.Button(self,text="Close",command=self.__callbackMethod)
|
|
but.pack(side=Tkinter.TOP)
|
|
buttonFrame.pack(side=Tkinter.BOTTOM,fill=Tkinter.X,expand=0)
|
|
def body(self,master):
|
|
self.sb=Tkinter.Scrollbar(master)
|
|
self.sb.pack(side=Tkinter.RIGHT,fill=Tkinter.Y)
|
|
self.dataText=Tkinter.Text(master,state=Tkinter.DISABLED,width=25,
|
|
height=10)
|
|
self.sb.configure(command=self.dataText.yview)
|
|
self.dataText.configure(yscrollcommand=self.sb.set)
|
|
self.dataText.pack(side=Tkinter.LEFT,fill=Tkinter.BOTH,expand=1)
|
|
self.updateText("Default Text")
|
|
return
|
|
def cancel(self):
|
|
self.__callbackMethod()
|
|
return
|
|
def updateText(self,updateText):
|
|
self.dataText.configure(state=Tkinter.NORMAL)
|
|
self.dataText.delete(1.0,Tkinter.END)
|
|
self.dataText.insert(Tkinter.END,updateText)
|
|
self.dataText.configure(state=Tkinter.DISABLED)
|
|
return
|
|
#=======================================================================
|
|
# MiniDiag - is a minimized dialog - to save screen real
|
|
# estate. It has one button - to go back to the main
|
|
# dialog.
|
|
#
|
|
class MiniDiag(BVDialog):
|
|
def __init__(self,parent,callbackMethod,title="Title",buttonText="Button",loc="x"):
|
|
self.__parent=parent
|
|
self.__callbackMethod=callbackMethod
|
|
self.__title=title
|
|
self.__buttonText=buttonText
|
|
self.__loc=loc
|
|
BVDialog.__init__(self,parent=self.__parent,title=self.__title,modal=0,hide=1)
|
|
self.update()
|
|
self.resizable(0,0)
|
|
#
|
|
# Set initial location (ul and ur)
|
|
#
|
|
if self.__loc in ("ur","lr") and parent is not None:
|
|
parentgeo=self.__parent.geometry()
|
|
(wh,of)=parentgeo.split("+",1)
|
|
(w,h)=wh.split("x",1)
|
|
(ox,oy)=of.split("+",1)
|
|
self.update_idletasks()
|
|
geo=self.geometry()
|
|
(mwh,mo)=geo.split("+",1)
|
|
(mw,mh)=mwh.split("x",1)
|
|
if self.__loc=="lr":
|
|
newgeo=mwh+"+%d+%d"%(int(ox)+int(w)-int(mw),int(oy)+int(h)-int(mh))
|
|
elif self.__loc=="ur":
|
|
newgeo=mwh+"+%d+%d"%(int(ox)+int(w)-int(mw),int(oy))
|
|
self.geometry(newgeo)
|
|
self.update_idletasks()
|
|
return
|
|
def buttonbox(self):
|
|
buttonFrame=Tkinter.Frame(self)
|
|
but=Tkinter.Button(self,text=self.__buttonText,command=self.__callbackMethod)
|
|
but.pack(side=Tkinter.TOP)
|
|
buttonFrame.pack(side=Tkinter.BOTTOM,fill=Tkinter.X,expand=0)
|
|
def cancel(self):
|
|
self.__callbackMethod()
|
|
return
|
|
#=====================================================================
|
|
class CanvasDisplay(BVDialog):
|
|
def __init__(self, parent, title="Canvas Display", callbackMethod=None):
|
|
self.__parent=parent
|
|
self.__title=title
|
|
self.__callbackMethod=callbackMethod
|
|
self.curwidth=706.0 # initial canvas width
|
|
self.curheight=536.0 # initial canvas height
|
|
BVDialog.__init__(self,parent=self.__parent,
|
|
title=self.__title,modal=0,hide=1)
|
|
self.update()
|
|
self.firstDisplay=1
|
|
geo=self.geometry()
|
|
(mwh,mof)=geo.split("+",1)
|
|
(mw,mh)=mwh.split("x",1)
|
|
self.minsize(int(mw),int(mh))
|
|
return
|
|
def body(self,master):
|
|
self.bar=Tkinter.Frame(master)
|
|
self.bar.pack(side=Tkinter.BOTTOM)
|
|
self.fbar=Tkinter.Frame(master)
|
|
self.fbar.pack(side=Tkinter.BOTTOM)
|
|
#
|
|
borderwidth=2
|
|
canwidth=self.curwidth-((borderwidth+1)*2)
|
|
canheight=self.curheight-((borderwidth+1)*2)
|
|
self.canvas=Tkinter.Canvas(master,width=canwidth,height=canheight,
|
|
borderwidth=borderwidth,relief=Tkinter.SUNKEN)
|
|
self.canvas.bind("<Configure>",self.resizecanvas)
|
|
self.canvas.pack(fill=Tkinter.BOTH,expand=1)
|
|
def buttonbox(self):
|
|
buttonFrame=Tkinter.Frame(self)
|
|
but=Tkinter.Button(buttonFrame,text="Exit",fg="red",command=self.__callbackMethod)
|
|
but.pack(side=Tkinter.LEFT)
|
|
self.label=Tkinter.Label(buttonFrame,text=" ")
|
|
self.label.pack(side=Tkinter.LEFT,fill=Tkinter.X)
|
|
buttonFrame.pack(side=Tkinter.BOTTOM,fill=Tkinter.X)
|
|
def cancel(self):
|
|
self.__callbackMethod()
|
|
|
|
def resizecanvas(self,event):
|
|
w=float(event.width)
|
|
h=float(event.height)
|
|
#print "resizecanvas called: %s %s"%(event.width,event.height)
|
|
scalex=w/self.curwidth
|
|
scaley=h/self.curheight
|
|
if ((scalex!=1.0)or(scaley!=1.0)):
|
|
self.canvas.scale("all",0.0,0.0,scalex,scaley)
|
|
self.curwidth=w
|
|
self.curheight=h
|
|
return
|
|
#=======================================================================
|
|
#
|
|
# Custom dialog that provides selection for verification stuff
|
|
#
|
|
class Verif(BVDialog):
|
|
def __init__(self, VU, userName, scaleList, parent=None, callbackMethod=None):
|
|
self.__VU=VU
|
|
self.__parent=parent
|
|
self.__callbackMethod=callbackMethod
|
|
self.__userName=userName
|
|
self.__scaleList=scaleList
|
|
BVDialog.__init__(self,parent=self.__parent,
|
|
title="%s Options"%PROGNAME,modal=0,hide=1)
|
|
#
|
|
# find minimum size
|
|
#
|
|
self.update()
|
|
maxw=0
|
|
maxh=0
|
|
self.dispGrids()
|
|
self.update_idletasks()
|
|
geo=self.geometry()
|
|
(maxw,maxh)=self.checkMax(geo,maxw,maxh)
|
|
self.dispGridStats()
|
|
self.update_idletasks()
|
|
geo=self.geometry()
|
|
(maxw,maxh)=self.checkMax(geo,maxw,maxh)
|
|
self.dispDists()
|
|
self.update_idletasks()
|
|
geo=self.geometry()
|
|
(maxw,maxh)=self.checkMax(geo,maxw,maxh)
|
|
self.dispStats()
|
|
self.update_idletasks()
|
|
geo=self.geometry()
|
|
(maxw,maxh)=self.checkMax(geo,maxw,maxh)
|
|
self.minsize(maxw,maxh)
|
|
self.dispGrids()
|
|
self.deiconify()
|
|
self.lift()
|
|
self.wait_visibility()
|
|
self.protocol("WM_DELETE_WINDOW",self.__quitCB)
|
|
return
|
|
def checkMax(self,geo,maxw,maxh):
|
|
(wh,of)=geo.split("+",1)
|
|
(w,h)=wh.split("x",1)
|
|
maxw=max(int(w),maxw)
|
|
maxh=max(int(h),maxh)
|
|
return(maxw,maxh)
|
|
def buttonbox(self):
|
|
buttonFrame = Tkinter.Frame(self)
|
|
Tkinter.Button(buttonFrame, text="Run",command=self.__runCB, width=6,
|
|
state=Tkinter.NORMAL).pack(\
|
|
side=Tkinter.LEFT, pady=5, padx=10)
|
|
Tkinter.Button(buttonFrame, text="Hide",
|
|
command=self.__hideCB, width=6, state=Tkinter.NORMAL).pack(\
|
|
side=Tkinter.LEFT, pady=5, padx=10)
|
|
Tkinter.Button(buttonFrame, text="Quit", width=6,
|
|
command=self.__quitCB).pack(side=Tkinter.RIGHT, pady=5, padx=10)
|
|
buttonFrame.pack(side=Tkinter.BOTTOM,expand=0)
|
|
def __runCB(self):
|
|
self.__callbackMethod("Run")
|
|
def __hideCB(self):
|
|
self.__callbackMethod("Hide")
|
|
def __quitCB(self):
|
|
self.cancel()
|
|
def cancel(self):
|
|
self.__callbackMethod("Quit")
|
|
return
|
|
#
|
|
# Custom body that has tabbed frames
|
|
#
|
|
def body(self, master):
|
|
#
|
|
# The "tab" buttons at the top
|
|
#
|
|
tabs=[("Grid Displays",self.dispGrids),
|
|
("Grid Stats",self.dispGridStats),
|
|
("Distributions",self.dispDists),
|
|
("Point/Area Stats",self.dispStats),
|
|
("Stat vs. Scale",self.dispScaleStats),
|
|
]
|
|
tabFrame=Tkinter.Frame(master,relief="sunken",borderwidth=1)
|
|
self.tabSetting=Tkinter.StringVar()
|
|
self.tabSetting.set("Grid Displays")
|
|
for (text,callback) in tabs:
|
|
x=Tkinter.Radiobutton(tabFrame,text=text,indicatoron=0,
|
|
command=callback,variable=self.tabSetting,
|
|
value=text)
|
|
col=x.cget("highlightbackground")
|
|
x.config(selectcolor=col)
|
|
x.pack(side=Tkinter.LEFT)
|
|
tabFrame.pack(side=Tkinter.TOP,anchor=Tkinter.W,fill=Tkinter.X)
|
|
#
|
|
# Big "body" part of dialog
|
|
#
|
|
self.BodyFrame=Tkinter.Frame(master)
|
|
#
|
|
self.col4=Tkinter.Frame(self.BodyFrame)
|
|
self.column4(self.col4)
|
|
self.col4.pack(side=Tkinter.RIGHT,fill=Tkinter.Y,expand=0)
|
|
self.col3=Tkinter.Frame(self.BodyFrame)
|
|
self.column3(self.col3)
|
|
self.col3.pack(side=Tkinter.RIGHT,fill=Tkinter.Y,expand=0)
|
|
self.col2=Tkinter.Frame(self.BodyFrame)
|
|
self.column2(self.col2)
|
|
self.col2.pack(side=Tkinter.RIGHT,fill=Tkinter.Y,expand=0)
|
|
#
|
|
self.Grids=Tkinter.Frame(self.BodyFrame)
|
|
self.OptionsGrids(self.Grids)
|
|
#
|
|
self.GridStats=Tkinter.Frame(self.BodyFrame)
|
|
self.OptionsGridsStats(self.GridStats)
|
|
#
|
|
self.ScaleStats=Tkinter.Frame(self.BodyFrame)
|
|
self.OptionsScaleStats(self.ScaleStats)
|
|
#
|
|
self.Dists=Tkinter.Frame(self.BodyFrame)
|
|
self.OptionsDists(self.Dists)
|
|
#
|
|
self.Stats=Tkinter.Frame(self.BodyFrame)
|
|
self.OptionsStats(self.Stats)
|
|
#
|
|
self.BodyFrame.pack(side=Tkinter.TOP,fill=Tkinter.BOTH,expand=1)
|
|
#
|
|
# setup scales (updating the GridsScale updates all others)
|
|
#
|
|
self.updateGridsScale()
|
|
#
|
|
# pack the default one
|
|
#
|
|
cur=self.tabSetting.get()
|
|
for (text,callback) in tabs:
|
|
if cur==text:
|
|
callback()
|
|
return
|
|
#==================================================================
|
|
#
|
|
# Switch tab frame displayed
|
|
#
|
|
def dispGrids(self):
|
|
self.GridStats.pack_forget()
|
|
self.ScaleStats.pack_forget()
|
|
self.Dists.pack_forget()
|
|
self.Stats.pack_forget()
|
|
self.Grids.pack(side=Tkinter.RIGHT,
|
|
fill=Tkinter.BOTH,expand=1)
|
|
def dispGridStats(self):
|
|
self.Grids.pack_forget()
|
|
self.ScaleStats.pack_forget()
|
|
self.Dists.pack_forget()
|
|
self.Stats.pack_forget()
|
|
self.GridStats.pack(side=Tkinter.RIGHT,
|
|
fill=Tkinter.BOTH,expand=1)
|
|
def dispDists(self):
|
|
self.Grids.pack_forget()
|
|
self.GridStats.pack_forget()
|
|
self.ScaleStats.pack_forget()
|
|
self.Stats.pack_forget()
|
|
self.Dists.pack(side=Tkinter.RIGHT,
|
|
fill=Tkinter.BOTH,expand=1)
|
|
def dispStats(self):
|
|
self.Grids.pack_forget()
|
|
self.GridStats.pack_forget()
|
|
self.ScaleStats.pack_forget()
|
|
self.Dists.pack_forget()
|
|
self.Stats.pack(side=Tkinter.RIGHT,
|
|
fill=Tkinter.BOTH,expand=1)
|
|
def dispScaleStats(self):
|
|
self.Grids.pack_forget()
|
|
self.GridStats.pack_forget()
|
|
self.Dists.pack_forget()
|
|
self.Stats.pack_forget()
|
|
self.ScaleStats.pack(side=Tkinter.RIGHT,
|
|
fill=Tkinter.BOTH,expand=1)
|
|
#
|
|
# Get the values associated with the dialog pieces that
|
|
# are displayed with the current tab
|
|
#
|
|
def getValues(self):
|
|
values={}
|
|
tabtype=self.tabSetting.get()
|
|
values["tab"]=tabtype
|
|
if tabtype=="Grid Displays":
|
|
values=self.getGridsValues(values)
|
|
if tabtype=="Grid Stats":
|
|
values=self.getGridsStatsValues(values)
|
|
if tabtype=="Stat vs. Scale":
|
|
values=self.getScaleStatsValues(values)
|
|
if tabtype=="Distributions":
|
|
values=self.getDistsValues(values)
|
|
if tabtype=="Point/Area Stats":
|
|
values=self.getStatsValues(values)
|
|
return values
|
|
#
|
|
# values on with the Grids tab
|
|
#
|
|
def getGridsValues(self,values):
|
|
values["Display"]=self.GridsDisplay.get()
|
|
values["Parm"]=self.getCheckList(self.GridsParms)
|
|
values["Group"]=self.GridsGroup.get()
|
|
values["Model"]=self.getCheckList(self.Models)
|
|
values["ObsModel"]=self.ObsModel.get()
|
|
values["fcstrList"]=self.getForecasterListbox()
|
|
values["fhrStart"]=self.fhrStart.get()
|
|
values["fhrEnd"]=self.fhrEnd.get()
|
|
values["commonCases"]=self.Common.get()
|
|
values["dateStyle"]=self.Datestyle.get()
|
|
values["dateType"]=self.Datetype.get()
|
|
values["numDays"]=self.Ndays.get()
|
|
values["fromDay"]=self.getFromdayListbox()
|
|
values["dayList"]=self.getDaylistListbox()
|
|
values["cycleList"]=self.getCycleVals()
|
|
values["scale"]=self.GridsScale.get()
|
|
values["accumHours"]=self.accumHours.get()
|
|
values["accumFreq"]=self.accumFreq.get()
|
|
return values
|
|
#
|
|
# values on with the GridsStats tab
|
|
#
|
|
def getGridsStatsValues(self,values):
|
|
values["Display"]=self.GridsStatsDisplay.get()
|
|
#values["Parms"]=self.getCheckList(self.GridsStatsParms)
|
|
values["Parm"]=self.GridsStatsParm.get()
|
|
values["Threshold"]=self.GridsStatsThreshold.get()
|
|
values["Models"]=self.getCheckList(self.Models)
|
|
values["ObsModel"]=self.ObsModel.get()
|
|
values["fcstrList"]=self.getForecasterListbox()
|
|
values["fhrStart"]=self.fhrStart.get()
|
|
values["fhrEnd"]=self.fhrEnd.get()
|
|
values["commonCases"]=self.Common.get()
|
|
values["dateStyle"]=self.Datestyle.get()
|
|
values["dateType"]=self.Datetype.get()
|
|
values["numDays"]=self.Ndays.get()
|
|
values["fromDay"]=self.getFromdayListbox()
|
|
values["dayList"]=self.getDaylistListbox()
|
|
values["cycleList"]=self.getCycleVals()
|
|
values["scale"]=self.GridsStatsScale.get()
|
|
values["accumHours"]=self.accumHours.get()
|
|
values["accumFreq"]=self.accumFreq.get()
|
|
values["TwoCatType"]=self.GridsStatsTwoCatType.get()
|
|
values["TwoCatCond"]=self.GridsStatsTwoCatCond.get()
|
|
str=self.GridsStatsTwoCatValueString.get()
|
|
try:
|
|
val=float(str)
|
|
except:
|
|
val=0.0
|
|
values["TwoCatValue"]=val
|
|
values["TwoCatValueString"]=str
|
|
return values
|
|
#
|
|
# values on with the Dists tab
|
|
#
|
|
def getDistsValues(self,values):
|
|
values["Display"]=self.DistsDisplay.get()
|
|
#values["Parms"]=self.getCheckList(self.DistsParms)
|
|
values["Parm"]=self.DistsParm.get()
|
|
values["Models"]=self.getCheckList(self.Models)
|
|
values["ObsModel"]=self.ObsModel.get()
|
|
values["fcstrList"]=self.getForecasterListbox()
|
|
values["fhrStart"]=self.fhrStart.get()
|
|
values["fhrEnd"]=self.fhrEnd.get()
|
|
values["commonCases"]=self.Common.get()
|
|
values["dateStyle"]=self.Datestyle.get()
|
|
values["dateType"]=self.Datetype.get()
|
|
values["numDays"]=self.Ndays.get()
|
|
values["fromDay"]=self.getFromdayListbox()
|
|
values["dayList"]=self.getDaylistListbox()
|
|
values["cycleList"]=self.getCycleVals()
|
|
values["scale"]=self.DistsScale.get()
|
|
values["accumHours"]=self.accumHours.get()
|
|
values["accumFreq"]=self.accumFreq.get()
|
|
return values
|
|
#
|
|
# values on the Stats tab
|
|
#
|
|
def getStatsValues(self,values):
|
|
values["Display"]=self.StatsDisplay.get()
|
|
values["areaList"]=self.getListbox(self.StatsAreasListbox)
|
|
values["AreaCombine"]=self.StatsAreaCombine.get()
|
|
values["Parms"]=self.getCheckList(self.StatsParms)
|
|
values["Threshold"]=self.StatsThreshold.get()
|
|
values["PlotType"]=self.StatsType.get()
|
|
#values["Parm"]=self.StatsParm.get()
|
|
values["Models"]=self.getCheckList(self.Models)
|
|
values["ObsModel"]=self.ObsModel.get()
|
|
values["fcstrList"]=self.getForecasterListbox()
|
|
values["fhrStart"]=self.fhrStart.get()
|
|
values["fhrEnd"]=self.fhrEnd.get()
|
|
values["commonCases"]=self.Common.get()
|
|
values["dateStyle"]=self.Datestyle.get()
|
|
values["dateType"]=self.Datetype.get()
|
|
values["numDays"]=self.Ndays.get()
|
|
values["fromDay"]=self.getFromdayListbox()
|
|
values["dayList"]=self.getDaylistListbox()
|
|
values["cycleList"]=self.getCycleVals()
|
|
values["scale"]=self.StatsScale.get()
|
|
values["accumHours"]=self.accumHours.get()
|
|
values["accumFreq"]=self.accumFreq.get()
|
|
values["TwoCatType"]=self.statsTwoCatType.get()
|
|
values["TwoCatCond"]=self.statsTwoCatCond.get()
|
|
str=self.statsTwoCatValueString.get()
|
|
try:
|
|
val=float(str)
|
|
except:
|
|
val=0.0
|
|
values["TwoCatValue"]=val
|
|
values["TwoCatValueString"]=str
|
|
return values
|
|
#
|
|
# values on with the ScaleStats tab
|
|
#
|
|
def getScaleStatsValues(self,values):
|
|
values["Display"]=self.ScaleStatsDisplay.get()
|
|
values["areaList"]=self.getListbox(self.ScaleStatsAreasListbox)
|
|
values["AreaCombine"]=self.ScaleStatsAreaCombine.get()
|
|
values["Parm"]=self.ScaleStatsParm.get()
|
|
values["Threshold"]=self.ScaleStatsThreshold.get()
|
|
values["Models"]=self.getCheckList(self.Models)
|
|
values["ObsModel"]=self.ObsModel.get()
|
|
values["fcstrList"]=self.getForecasterListbox()
|
|
values["fhrStart"]=self.fhrStart.get()
|
|
values["fhrEnd"]=self.fhrEnd.get()
|
|
values["commonCases"]=self.Common.get()
|
|
values["dateStyle"]=self.Datestyle.get()
|
|
values["dateType"]=self.Datetype.get()
|
|
values["numDays"]=self.Ndays.get()
|
|
values["fromDay"]=self.getFromdayListbox()
|
|
values["dayList"]=self.getDaylistListbox()
|
|
values["cycleList"]=self.getCycleVals()
|
|
values["scale"]=self.GridsStatsScale.get()
|
|
values["accumHours"]=self.accumHours.get()
|
|
values["accumFreq"]=self.accumFreq.get()
|
|
values["TwoCatType"]=self.scaleStatsTwoCatType.get()
|
|
values["TwoCatCond"]=self.scaleStatsTwoCatCond.get()
|
|
str=self.scaleStatsTwoCatValueString.get()
|
|
try:
|
|
val=float(str)
|
|
except:
|
|
val=0.0
|
|
values["TwoCatValue"]=val
|
|
values["TwoCatValueString"]=str
|
|
return values
|
|
#===============================================================
|
|
#
|
|
# Column 2 - model
|
|
#
|
|
def column2(self,master):
|
|
#
|
|
# At bottom - ObsModel being used
|
|
#
|
|
obsModelFrame=Tkinter.Frame(master,relief=Tkinter.GROOVE,borderwidth=2)
|
|
obsModelLabel=Tkinter.Label(obsModelFrame,text="Observed:")
|
|
obsModelLabel.pack(side=Tkinter.LEFT)
|
|
obsModels=self.__VU.getCFG('OBSMODELS')
|
|
namewidth=0
|
|
for model in obsModels:
|
|
if len(model)>namewidth:
|
|
namewidth=len(model)
|
|
self.ObsModel=Tkinter.StringVar()
|
|
self.ObsModelButton=Tkinter.Menubutton(obsModelFrame,textvariable=self.ObsModel,
|
|
relief=Tkinter.RAISED,indicatoron=1,width=namewidth+1,anchor=Tkinter.W)
|
|
self.ObsModelButton.pack(side=Tkinter.RIGHT)
|
|
self.ObsModelPopup=Tkinter.Menu(self.ObsModelButton,tearoff=0)
|
|
obsModels=self.__VU.getCFG('OBSMODELS')
|
|
for model in obsModels:
|
|
self.ObsModelPopup.add_radiobutton(label=model,indicatoron=0,value=model,
|
|
variable=self.ObsModel)
|
|
self.ObsModel.set(obsModels[0])
|
|
obsModelFrame.pack(side=Tkinter.BOTTOM,fill=Tkinter.X,expand=1)
|
|
self.ObsModelButton.config(menu=self.ObsModelPopup)
|
|
|
|
#
|
|
# common Cases checkbox
|
|
#
|
|
commonFrame=Tkinter.Frame(master,relief=Tkinter.GROOVE,borderwidth=2)
|
|
self.Common=Tkinter.IntVar()
|
|
commonCheck=Tkinter.Checkbutton(commonFrame,text="Common Cases",
|
|
variable=self.Common,
|
|
onvalue=1,offvalue=0)
|
|
self.Common.set(1)
|
|
commonCheck.pack(side=Tkinter.TOP,anchor=Tkinter.W)
|
|
commonFrame.pack(side=Tkinter.BOTTOM,anchor=Tkinter.N,
|
|
fill=Tkinter.X,expand=0)
|
|
#
|
|
# Models checkbox
|
|
#
|
|
self.Models=[]
|
|
models=self.__VU.listModels()
|
|
for model in models:
|
|
self.Models.append(Tkinter.StringVar())
|
|
if "Official" in models:
|
|
defaultModels=["Official",]
|
|
else:
|
|
defaultModels=[models[0],]
|
|
self.checkGroup(master,"Model:",self.Models,models,defaultModels,Tkinter.BOTH,1)
|
|
return
|
|
#===============================================================
|
|
#
|
|
# Column 3 - Forecaster and common cases
|
|
#
|
|
def column3(self,master):
|
|
XHOUR=self.__VU.getCFG('MAXFORECASTHOUR')
|
|
#
|
|
# Accumulation Time Periods:
|
|
#
|
|
accumFrame=Tkinter.Frame(master,relief=Tkinter.GROOVE,borderwidth=2)
|
|
self.accumHours=Tkinter.IntVar()
|
|
self.accumFreq=Tkinter.IntVar()
|
|
freqFrame=Tkinter.Frame(accumFrame)
|
|
flab=Tkinter.Label(freqFrame,text="Every:",width=8)
|
|
flab.pack(side=Tkinter.LEFT,anchor=Tkinter.S)
|
|
flab=Tkinter.Label(freqFrame,text="hrs")
|
|
flab.pack(side=Tkinter.RIGHT,anchor=Tkinter.S)
|
|
scaleFreq=Tkinter.Scale(freqFrame,from_=1,to=24,
|
|
variable=self.accumFreq,
|
|
orient=Tkinter.HORIZONTAL,
|
|
sliderlength=15)
|
|
accumFrequencyDefault=self.__VU.getCFG("ACCUM_FREQUENCY_DEFAULT")
|
|
if accumFrequencyDefault is None:
|
|
accumFrequencyDefault=6
|
|
self.accumFreq.set(accumFrequencyDefault)
|
|
scaleFreq.pack(side=Tkinter.RIGHT,fill=Tkinter.X,expand=1)
|
|
freqFrame.pack(side=Tkinter.BOTTOM,fill=Tkinter.X,expand=1)
|
|
hoursFrame=Tkinter.Frame(accumFrame)
|
|
flab=Tkinter.Label(hoursFrame,text="Length:",width=8)
|
|
flab.pack(side=Tkinter.LEFT,anchor=Tkinter.S)
|
|
flab=Tkinter.Label(hoursFrame,text="hrs")
|
|
flab.pack(side=Tkinter.RIGHT,anchor=Tkinter.S)
|
|
accumResolution=self.__VU.getCFG('ACCUM_RESOLUTION')
|
|
if accumResolution is None:
|
|
accumResolution=6
|
|
scaleHours=Tkinter.Scale(hoursFrame,from_=accumResolution,
|
|
to=XHOUR,resolution=accumResolution,
|
|
variable=self.accumHours,
|
|
orient=Tkinter.HORIZONTAL,
|
|
sliderlength=15)
|
|
accumDefaultLength=self.__VU.getCFG("ACCUM_LENGTH_DEFAULT")
|
|
if accumDefaultLength is None:
|
|
accumDefaultLength=6
|
|
self.accumHours.set(accumDefaultLength)
|
|
scaleHours.pack(side=Tkinter.RIGHT,fill=Tkinter.X,expand=1)
|
|
hoursFrame.pack(side=Tkinter.BOTTOM,fill=Tkinter.X,expand=1)
|
|
flab=Tkinter.Label(accumFrame,text="Accumulation Time Periods:")
|
|
flab.pack(side=Tkinter.BOTTOM,expand=0)
|
|
accumFrame.pack(side=Tkinter.BOTTOM,fill=Tkinter.X,expand=0)
|
|
#
|
|
# Forecast Hours start/stop
|
|
#
|
|
fhrFrame=Tkinter.Frame(master,relief=Tkinter.GROOVE,borderwidth=2)
|
|
self.fhrStart=Tkinter.IntVar()
|
|
self.fhrEnd=Tkinter.IntVar()
|
|
fend=Tkinter.Scale(fhrFrame,from_=0,to=XHOUR,variable=self.fhrEnd,
|
|
orient=Tkinter.HORIZONTAL,command=self.endMove,
|
|
sliderlength=15)
|
|
self.fhrEnd.set(XHOUR)
|
|
fend.pack(side=Tkinter.BOTTOM,fill=Tkinter.X,expand=1)
|
|
fstart=Tkinter.Scale(fhrFrame,from_=0,to=XHOUR,variable=self.fhrStart,
|
|
orient=Tkinter.HORIZONTAL,command=self.startMove,
|
|
sliderlength=15)
|
|
self.fhrStart.set(0)
|
|
fstart.pack(side=Tkinter.BOTTOM,fill=Tkinter.X,expand=1)
|
|
flab=Tkinter.Label(fhrFrame,text="Forecast Hours:")
|
|
flab.pack(side=Tkinter.BOTTOM,expand=0)
|
|
fhrFrame.pack(side=Tkinter.BOTTOM,fill=Tkinter.X,expand=0)
|
|
#
|
|
# Forecaster names to show...
|
|
#
|
|
forecasters=["ALL"]
|
|
self.forecasterNumbers=[-1]
|
|
#
|
|
trimming=self.__VU.getCFG('FORECASTER_LIST_TRIMMING')
|
|
trimADMIN=self.__VU.getCFG('FORECASTER_LIST_TRIMMING_ADMINISTRATORS')
|
|
fFormat=self.__VU.getCFG('FORECASTER_LIST_FORMAT')
|
|
fSort=self.__VU.getCFG('FORECASTER_LIST_SORT')
|
|
labels=[]
|
|
numstrs=self.__VU.getFcstrNums()
|
|
for numstr in numstrs:
|
|
num=int(numstr)
|
|
id=self.__VU.getFcstrID(num)
|
|
if ((trimming==1)and(self.__userName not in trimADMIN)and(self.__userName!=id)and(num!=0)):
|
|
continue
|
|
name=self.__VU.getFcstrName(num)
|
|
sort=numstr #defaults to number
|
|
if fSort=="id":
|
|
sort=id
|
|
elif fSort=="name":
|
|
sort=name
|
|
label=name #defaults to name
|
|
if fFormat=="number":
|
|
label=numstr
|
|
elif fFormat=="id":
|
|
label=id
|
|
elif fFormat=="number-name":
|
|
label="%s - %s"%(numstr,name)
|
|
elif fFormat=="number-id":
|
|
label="%s - %s"%(numstr,id)
|
|
labels.append("%s|%s|%s"%(sort,numstr,label))
|
|
labels.sort()
|
|
for entry in labels:
|
|
(sstr,numstr,label)=entry.split("|")
|
|
forecasters.append(label)
|
|
self.forecasterNumbers.append(int(numstr))
|
|
defaultForecasters=["ALL",]
|
|
maxwid=0
|
|
for forecaster in forecasters:
|
|
wid=len(forecaster)
|
|
if wid>maxwid:
|
|
maxwid=wid
|
|
maxheight=10
|
|
fcstrFrame=Tkinter.Frame(master,relief=Tkinter.GROOVE,borderwidth=2)
|
|
self.ForecasterListbox=self.sListbox(fcstrFrame,"Forecaster:",
|
|
forecasters,defaultForecasters,maxwid+1,maxheight,Tkinter.EXTENDED)
|
|
fcstrFrame.pack(side=Tkinter.BOTTOM,fill=Tkinter.BOTH,expand=1)
|
|
return
|
|
#
|
|
#=================================================================
|
|
# getForecasterListbox - get list of integer forecast numbers for
|
|
# forcasters turned on in ForecasterListbox
|
|
#
|
|
def getForecasterListbox(self):
|
|
outlist=[]
|
|
itemnums=self.ForecasterListbox.curselection()
|
|
try:
|
|
itemnums=map(int,itemnums)
|
|
except ValueError: pass
|
|
for itemnum in itemnums:
|
|
outlist.append(self.forecasterNumbers[itemnum])
|
|
return outlist
|
|
#==================================================================
|
|
#
|
|
# If moving fhrStart...check to make sure that it is not more
|
|
# than fhrEnd...and if it is...move fhrEnd too.
|
|
#
|
|
def startMove(self,event):
|
|
st=self.fhrStart.get()
|
|
en=self.fhrEnd.get()
|
|
if en<st:
|
|
self.fhrEnd.set(st)
|
|
return
|
|
#==================================================================
|
|
#
|
|
# When moving fhrEnd...check to make sure that it is not less
|
|
# than fhrStart...and if it is...move fhrStart too.
|
|
#
|
|
def endMove(self,event):
|
|
st=self.fhrStart.get()
|
|
en=self.fhrEnd.get()
|
|
if en<st:
|
|
self.fhrStart.set(en)
|
|
return
|
|
#===============================================================
|
|
#
|
|
# Column 4 - Date stuff
|
|
#
|
|
def column4(self,master):
|
|
#
|
|
# datestyle
|
|
#
|
|
self.Datestyle=Tkinter.StringVar()
|
|
datestyles=["Verifying on","Forecast on"]
|
|
defaultDatestyle="Forecast on"
|
|
self.radioGroup(master,"Dates:",self.Datestyle,datestyles,defaultDatestyle,Tkinter.X,0)
|
|
|
|
byFrame=Tkinter.Frame(master,relief=Tkinter.GROOVE,borderwidth=2)
|
|
#
|
|
# byPeriod
|
|
#
|
|
self.ByPeriod=Tkinter.Frame(byFrame)
|
|
self.Ndays=Tkinter.IntVar()
|
|
self.Ndays.set(7)
|
|
nFrame=Tkinter.Frame(self.ByPeriod)
|
|
labFrame=Tkinter.Frame(nFrame)
|
|
lab=Tkinter.Label(labFrame,text="Length (days)")
|
|
lab.pack(side=Tkinter.LEFT,anchor=Tkinter.W)
|
|
self.NTog=Tkinter.Button(labFrame,text=">",padx=0,pady=0,command=self.toggleNScale)
|
|
self.NTog.pack(side=Tkinter.RIGHT,anchor=Tkinter.E)
|
|
labFrame.pack(side=Tkinter.TOP,anchor=Tkinter.W,fill=Tkinter.X,expand=1)
|
|
self.NScale=Tkinter.Scale(nFrame,from_=1,to=50,variable=self.Ndays,
|
|
showvalue=1,orient=Tkinter.HORIZONTAL,
|
|
sliderlength=15)
|
|
self.NScale.pack(side=Tkinter.TOP,anchor=Tkinter.N,fill=Tkinter.X)
|
|
nFrame.pack(side=Tkinter.TOP,anchor=Tkinter.N,fill=Tkinter.X)
|
|
|
|
gridDayStrings,self.gridDays=self.getRecentDates(self.__VU.GRIDDAYS)
|
|
defaultDay=[gridDayStrings[0],]
|
|
maxwid=10
|
|
maxheight=5 # number of days to show
|
|
self.FromdayListbox=self.sListbox(self.ByPeriod,"Ending on:",
|
|
gridDayStrings,defaultDay,maxwid,maxheight,Tkinter.BROWSE)
|
|
self.ByPeriod.pack(side=Tkinter.TOP,anchor=Tkinter.N,fill=Tkinter.BOTH,expand=1)
|
|
#
|
|
# byList
|
|
#
|
|
self.ByList=Tkinter.Frame(byFrame)
|
|
#days,daydates=self.getRecentDates(self.__VU.GRIDDAYS)
|
|
defaultDaylist=[]
|
|
for i in xrange(7):
|
|
defaultDaylist.append(gridDayStrings[i])
|
|
maxwid=10
|
|
maxheight=5 #number of days to show
|
|
self.DaylistListbox=self.sListbox(self.ByList,"Include:",
|
|
gridDayStrings,defaultDaylist,maxwid,maxheight,Tkinter.EXTENDED)
|
|
self.ByList.pack(side=Tkinter.TOP,anchor=Tkinter.N,fill=Tkinter.BOTH,expand=1)
|
|
#
|
|
# datetype
|
|
#
|
|
datetypeFrame=Tkinter.Frame(master,relief=Tkinter.GROOVE,borderwidth=2)
|
|
datetypeLabel=Tkinter.Label(datetypeFrame,text="Choose Dates by:")
|
|
datetypeLabel.pack(side=Tkinter.TOP,anchor=Tkinter.W)
|
|
self.Datetype=Tkinter.StringVar()
|
|
datetypeDefault="Period Length"
|
|
datetypes=["Period Length","List of dates"]
|
|
for datetype in datetypes:
|
|
a=Tkinter.Radiobutton(datetypeFrame,text=datetype,command=self.setDatetype,
|
|
variable=self.Datetype,value=datetype)
|
|
if datetype is datetypeDefault:
|
|
a.invoke()
|
|
a.pack(side=Tkinter.TOP,anchor=Tkinter.W)
|
|
datetypeFrame.pack(side=Tkinter.TOP,anchor=Tkinter.N,fill=Tkinter.X)
|
|
#
|
|
# Now pack the frame with the "byPeriod" or "byList"
|
|
#
|
|
byFrame.pack(side=Tkinter.TOP,anchor=Tkinter.NW,fill=Tkinter.BOTH,expand=1)
|
|
#
|
|
# cycle
|
|
#
|
|
cycleFrame=Tkinter.Frame(master,relief=Tkinter.GROOVE,borderwidth=2)
|
|
labFrame=Tkinter.Frame(cycleFrame)
|
|
cycleLabel=Tkinter.Label(labFrame,text="Cycle:")
|
|
cycleLabel.pack(side=Tkinter.LEFT,anchor=Tkinter.W)
|
|
cycleToggle=Tkinter.Button(labFrame,text="ALL",padx=0,pady=0,command=self.toggleCycles)
|
|
cycleToggle.pack(side=Tkinter.RIGHT,anchor=Tkinter.E)
|
|
labFrame.pack(side=Tkinter.TOP,anchor=Tkinter.W,fill=Tkinter.X,expand=1)
|
|
cyclecol1=Tkinter.Frame(cycleFrame)
|
|
cyclecol2=Tkinter.Frame(cycleFrame)
|
|
cycleDefault=self.__VU.getCFG('ALLCYCLES')
|
|
cycles=self.__VU.getCFG('ALLCYCLES')
|
|
self.CycleFlags=[]
|
|
self.CycleVals=[]
|
|
cnt=0
|
|
for cycle in cycles:
|
|
self.CycleFlags.append(Tkinter.IntVar())
|
|
if cnt<int(float(len(cycles))/2.0):
|
|
parent=cyclecol1
|
|
else:
|
|
parent=cyclecol2
|
|
self.CycleVals.append(int(cycle))
|
|
a=Tkinter.Checkbutton(parent,text=cycle,
|
|
variable=self.CycleFlags[cnt],onvalue=1,
|
|
offvalue=0)
|
|
if cycle in cycleDefault:
|
|
self.CycleFlags[cnt].set(1)
|
|
a.pack(side=Tkinter.TOP,anchor=Tkinter.W)
|
|
cnt+=1
|
|
cyclecol1.pack(side=Tkinter.LEFT,anchor=Tkinter.W)
|
|
cyclecol2.pack(side=Tkinter.LEFT,anchor=Tkinter.W)
|
|
cycleFrame.pack(side=Tkinter.TOP,anchor=Tkinter.N,fill=Tkinter.X)
|
|
return
|
|
#================================================================
|
|
#
|
|
def toggleNScale(self):
|
|
curN=self.Ndays.get()
|
|
curSet=self.NTog.cget("text")
|
|
if curSet==">":
|
|
self.NScale.configure(to=self.__VU.STATDAYS)
|
|
self.NTog.configure(text="<")
|
|
else:
|
|
if curN>50:
|
|
self.Ndays.set(50)
|
|
self.NScale.configure(to=50)
|
|
self.NTog.configure(text=">")
|
|
return
|
|
#to=self.__VU.STATDAYS
|
|
#================================================================
|
|
# toggleCycles - toggles all the cycle buttons
|
|
#
|
|
def toggleCycles(self):
|
|
for cycleFlag in self.CycleFlags:
|
|
val=cycleFlag.get()
|
|
val=abs(val-1)
|
|
cycleFlag.set(val)
|
|
return
|
|
#=================================================================
|
|
# getCycleVals - get list of values turned on in Cycles
|
|
#
|
|
def getCycleVals(self):
|
|
outlist=[]
|
|
for i in xrange(len(self.CycleFlags)):
|
|
a=self.CycleFlags[i].get()
|
|
if a!=0:
|
|
outlist.append(self.CycleVals[i])
|
|
return outlist
|
|
#=================================================================
|
|
# getFromdayListbox - get unix date for day listed in Fromday
|
|
# listbox
|
|
#
|
|
def getFromdayListbox(self):
|
|
itemnums=self.FromdayListbox.curselection()
|
|
try:
|
|
itemnums=map(int,itemnums)
|
|
except ValueError: pass
|
|
itemnum=itemnums[0]
|
|
outdate=self.gridDays[itemnum]
|
|
return outdate
|
|
#=================================================================
|
|
# getDaylistListbox - get list of integer forecast numbers for
|
|
# forcasters turned on in ForecasterListbox
|
|
#
|
|
def getDaylistListbox(self):
|
|
outlist=[]
|
|
itemnums=self.DaylistListbox.curselection()
|
|
try:
|
|
itemnums=map(int,itemnums)
|
|
except ValueError: pass
|
|
for itemnum in itemnums:
|
|
outlist.append(self.gridDays[itemnum])
|
|
return outlist
|
|
#==================================================================
|
|
#
|
|
# Frame that specifies the options for the Grids displays
|
|
#
|
|
def OptionsGrids(self,master):
|
|
#
|
|
# parameter
|
|
#
|
|
self.GridsParms=[]
|
|
parms=self.__VU.getVerParms()
|
|
for parm in parms:
|
|
self.GridsParms.append(Tkinter.StringVar())
|
|
defaultParms=[parms[0],]
|
|
self.checkGroup(master,"Parameter:",self.GridsParms,parms,
|
|
defaultParms,Tkinter.BOTH,1)
|
|
#
|
|
# display
|
|
#
|
|
self.GridsDisplay=Tkinter.StringVar()
|
|
displays=["Forecasts","Errors"]
|
|
defaultDisplay="Forecasts"
|
|
gridDisplayFrame=Tkinter.Frame(master,relief=Tkinter.GROOVE,borderwidth=2)
|
|
gdLabelFrame=Tkinter.Frame(gridDisplayFrame)
|
|
gridDisplayLabel=Tkinter.Label(gdLabelFrame,text="Display:")
|
|
gridDisplayLabel.pack(side=Tkinter.LEFT,anchor=Tkinter.W)
|
|
|
|
self.GridsScale=Tkinter.IntVar()
|
|
self.GridsScaleText=Tkinter.StringVar()
|
|
but=Tkinter.Menubutton(gdLabelFrame,textvariable=self.GridsScaleText,
|
|
relief=Tkinter.RAISED,indicatoron=1)
|
|
but.pack(side=Tkinter.RIGHT,anchor=Tkinter.W)
|
|
self.GridsScalePopup=Tkinter.Menu(but,tearoff=0)
|
|
for (value,text) in self.__scaleList:
|
|
self.GridsScalePopup.add_radiobutton(label=text,indicatoron=0,value=value,
|
|
variable=self.GridsScale,
|
|
command=self.updateGridsScale)
|
|
self.GridsScale.set(0)
|
|
#self.updateGridsScale()
|
|
but.config(menu=self.GridsScalePopup)
|
|
|
|
gdLabelFrame.pack(side=Tkinter.TOP,anchor=Tkinter.W,fill=Tkinter.X,expand=0)
|
|
for item in displays:
|
|
a=Tkinter.Radiobutton(gridDisplayFrame,text=item,
|
|
variable=self.GridsDisplay,value=item)
|
|
a.pack(side=Tkinter.TOP,anchor=Tkinter.W)
|
|
if item is defaultDisplay:
|
|
self.GridsDisplay.set(item)
|
|
gridDisplayFrame.pack(side=Tkinter.TOP,anchor=Tkinter.NW,fill=Tkinter.X,expand=0)
|
|
#
|
|
# Group by
|
|
#
|
|
self.GridsGroup=Tkinter.StringVar()
|
|
defaultGroup="Forecast Hour"
|
|
groups=["Forecast Hour","Run Time"]
|
|
self.radioGroup(master,"Group by:",self.GridsGroup,groups,defaultGroup,Tkinter.X,0)
|
|
return
|
|
#
|
|
def updateGridsScale(self):
|
|
value=self.GridsScale.get()
|
|
for i in xrange(len(self.__scaleList)):
|
|
(num,text)=self.__scaleList[i]
|
|
if num==value:
|
|
self.GridsScaleText.set(text)
|
|
self.GridsStatsScale.set(num)
|
|
self.GridsStatsScaleText.set(text)
|
|
self.DistsScale.set(num)
|
|
self.DistsScaleText.set(text)
|
|
self.StatsScale.set(num)
|
|
self.StatsScaleText.set(text)
|
|
break
|
|
#==================================================================
|
|
#
|
|
# Frame that specifies the options for the GridsStats display
|
|
#
|
|
def OptionsGridsStats(self,master):
|
|
#
|
|
# parameter
|
|
#
|
|
self.GridsStatsParm=Tkinter.StringVar()
|
|
parms=self.__VU.getVerParmsVect()
|
|
#for parm in parms:
|
|
# self.GridsStatsParms.append(Tkinter.StringVar())
|
|
defaultParm=parms[0]
|
|
self.radioGroup(master,"Parameter:",self.GridsStatsParm,parms,
|
|
defaultParm,Tkinter.BOTH,1,callback=self.updateGridsStatsThreshold)
|
|
#
|
|
# display
|
|
#
|
|
self.GridsStatsDisplay=Tkinter.StringVar()
|
|
displays=["Bias","Mean Abs Error","RMS Error","Mean Squared Error"]
|
|
defaultDisplay="Bias"
|
|
radioFrame=Tkinter.Frame(master,relief=Tkinter.GROOVE,borderwidth=2)
|
|
scaleFrame=Tkinter.Frame(radioFrame)
|
|
radioLabel=Tkinter.Label(scaleFrame,text="Display:")
|
|
radioLabel.pack(side=Tkinter.LEFT,anchor=Tkinter.W)
|
|
|
|
self.GridsStatsScale=Tkinter.IntVar()
|
|
self.GridsStatsScaleText=Tkinter.StringVar()
|
|
but=Tkinter.Menubutton(scaleFrame,textvariable=self.GridsStatsScaleText,
|
|
relief=Tkinter.RAISED,indicatoron=1)
|
|
but.pack(side=Tkinter.RIGHT,anchor=Tkinter.W)
|
|
self.GridsStatsScalePopup=Tkinter.Menu(but,tearoff=0)
|
|
for (value,text) in self.__scaleList:
|
|
self.GridsStatsScalePopup.add_radiobutton(label=text,indicatoron=0,value=value,
|
|
variable=self.GridsStatsScale,
|
|
command=self.updateGridsStatsScale)
|
|
self.GridsStatsScale.set(0)
|
|
#self.updateGridsStatsScale()
|
|
|
|
but.config(menu=self.GridsStatsScalePopup)
|
|
scaleFrame.pack(side=Tkinter.TOP,anchor=Tkinter.W,fill=Tkinter.X,expand=1)
|
|
|
|
a=Tkinter.Radiobutton(radioFrame,text="Bias",
|
|
variable=self.GridsStatsDisplay,value="Bias")
|
|
a.pack(side=Tkinter.TOP,anchor=Tkinter.W)
|
|
self.GridsStatsDisplay.set("Bias")
|
|
a=Tkinter.Radiobutton(radioFrame,text="Mean Abs Error",
|
|
variable=self.GridsStatsDisplay,value="Mean Abs Error")
|
|
a.pack(side=Tkinter.TOP,anchor=Tkinter.W)
|
|
a=Tkinter.Radiobutton(radioFrame,text="RMS Error",
|
|
variable=self.GridsStatsDisplay,value="RMS Error")
|
|
a.pack(side=Tkinter.TOP,anchor=Tkinter.W)
|
|
a=Tkinter.Radiobutton(radioFrame,text="Mean Squared Error",
|
|
variable=self.GridsStatsDisplay,value="Mean Squared Error")
|
|
a.pack(side=Tkinter.TOP,anchor=Tkinter.W)
|
|
threshFrame=Tkinter.Frame(radioFrame)
|
|
a=Tkinter.Radiobutton(threshFrame,text="Percent Err <",
|
|
variable=self.GridsStatsDisplay,value="Percent Err <")
|
|
a.pack(side=Tkinter.LEFT,anchor=Tkinter.W)
|
|
self.GridsStatsThreshold=Tkinter.IntVar()
|
|
self.GridsStatsThresholdValue=Tkinter.StringVar()
|
|
but=Tkinter.Menubutton(threshFrame,textvariable=self.GridsStatsThresholdValue,
|
|
relief=Tkinter.RAISED,indicatoron=1)
|
|
but.pack(side=Tkinter.LEFT,anchor=Tkinter.W)
|
|
self.GridsStatsThresholdPopup=Tkinter.Menu(but,tearoff=0)
|
|
self.GridsStatsThresholdPopup.add_command(label="stuff")
|
|
for i in xrange(self.__VU.getCFG('NUMTHRESH')):
|
|
self.GridsStatsThresholdPopup.add_radiobutton(label="xxxx",indicatoron=0,value=i,
|
|
variable=self.GridsStatsThreshold,
|
|
command=self.pickGridsStatsThreshold)
|
|
but.config(menu=self.GridsStatsThresholdPopup)
|
|
threshFrame.pack(side=Tkinter.TOP,anchor=Tkinter.W)
|
|
|
|
twocatFrame=Tkinter.Frame(radioFrame)
|
|
a=Tkinter.Radiobutton(twocatFrame,text="",variable=self.GridsStatsDisplay,
|
|
value="TwoCat")
|
|
a.pack(side=Tkinter.LEFT,anchor=Tkinter.W)
|
|
self.GridsStatsTwoCatType=Tkinter.StringVar()
|
|
but=Tkinter.Menubutton(twocatFrame,textvariable=self.GridsStatsTwoCatType,
|
|
relief=Tkinter.RAISED,indicatoron=1)
|
|
but.pack(side=Tkinter.LEFT,anchor=Tkinter.W)
|
|
self.GridsStatsTwoCatTypePopup=Tkinter.Menu(but,tearoff=0)
|
|
for stat in ["Hits","Areal Hits","Misses","Areal Misses",
|
|
"False Alarms","Areal False Alarms","Correct Negatives",
|
|
"Areal Correct Negatives",
|
|
"Frequency Observed",
|
|
"Frequency Forecast",
|
|
"Fraction Correct","Areal Fraction Correct",
|
|
"Frequency Bias","Areal Frequency Bias",
|
|
"POD","Areal POD","FAR","Areal FAR","Threat Score",
|
|
"Areal Threat Score","Equitable Threat Score",
|
|
"Areal Equitable Threat Score","True Skill Score",
|
|
"Areal True Skill Score","Heidke Skill Score",
|
|
"Areal Heidke Skill Score","Odds Ratio","Areal Odds Ratio"]:
|
|
self.GridsStatsTwoCatTypePopup.add_radiobutton(label=stat,
|
|
indicatoron=0,value=stat,
|
|
variable=self.GridsStatsTwoCatType,command=self.updateGridsStatsTwoCatType)
|
|
self.GridsStatsTwoCatType.set("Fraction Correct")
|
|
but.config(menu=self.GridsStatsTwoCatTypePopup)
|
|
self.GridsStatsTwoCatCond=Tkinter.StringVar()
|
|
but=Tkinter.Menubutton(twocatFrame,textvariable=self.GridsStatsTwoCatCond,
|
|
relief=Tkinter.RAISED,indicatoron=1)
|
|
but.pack(side=Tkinter.LEFT,anchor=Tkinter.W)
|
|
self.GridsStatsTwoCatCondPopup=Tkinter.Menu(but,tearoff=0)
|
|
for cond in [">",">=","<=","<"]:
|
|
self.GridsStatsTwoCatCondPopup.add_radiobutton(label=cond,
|
|
indicatoron=0,value=cond,
|
|
variable=self.GridsStatsTwoCatCond,command=self.updateGridsStatsTwoCatType)
|
|
self.GridsStatsTwoCatCond.set(">")
|
|
but.config(menu=self.GridsStatsTwoCatCondPopup)
|
|
self.GridsStatsTwoCatValueString=Tkinter.StringVar()
|
|
ent=Tkinter.Entry(twocatFrame,textvariable=self.GridsStatsTwoCatValueString,
|
|
width=5,relief=Tkinter.SUNKEN)
|
|
self.GridsStatsTwoCatValueString.set("0.0")
|
|
ent.pack(side=Tkinter.LEFT,anchor=Tkinter.W)
|
|
twocatFrame.pack(side=Tkinter.TOP,anchor=Tkinter.W)
|
|
radioFrame.pack(side=Tkinter.TOP,anchor=Tkinter.NW,fill=Tkinter.X,expand=0)
|
|
self.updateGridsStatsThreshold()
|
|
return
|
|
#==================================================================
|
|
#
|
|
def updateGridsStatsScale(self):
|
|
value=self.GridsStatsScale.get()
|
|
for i in xrange(len(self.__scaleList)):
|
|
(num,text)=self.__scaleList[i]
|
|
if num==value:
|
|
self.GridsStatsScaleText.set(text)
|
|
self.GridsScale.set(num)
|
|
self.GridsScaleText.set(text)
|
|
self.DistsScale.set(num)
|
|
self.DistsScaleText.set(text)
|
|
self.StatsScale.set(num)
|
|
self.StatsScaleText.set(text)
|
|
break
|
|
#
|
|
# When user actually picks a threshold - then also set the display to
|
|
# use the Percent Err < display
|
|
#
|
|
def pickGridsStatsThreshold(self):
|
|
self.GridsStatsDisplay.set("Percent Err <")
|
|
self.updateGridsStatsThreshold()
|
|
return
|
|
def updateGridsStatsTwoCatType(self):
|
|
self.GridsStatsDisplay.set("TwoCat")
|
|
return
|
|
#
|
|
# When parm is changed, or when the user picks a threshold - need to
|
|
# update the chosen thresholds.
|
|
#
|
|
def updateGridsStatsThreshold(self):
|
|
#print "in updateGridsStatsThreshold"
|
|
parm=self.GridsStatsParm.get()
|
|
#print " parm=",parm
|
|
parmList=[parm,]
|
|
if len(parmList)<1:
|
|
return
|
|
tlist=[]
|
|
plist=[]
|
|
for parm in parmList:
|
|
readParm=parm
|
|
last3="xxx"
|
|
if len(parm)>3:
|
|
last3=parm[-3:]
|
|
if ((last3=="Spd")or(last3=="Dir")):
|
|
readParm=parm[:-3]
|
|
datatype=self.__VU.getVerParmType(readParm)
|
|
thresholds=self.__VU.getVerThresholds(readParm)
|
|
#print " thresholds for ",parm
|
|
#print " are:",thresholds
|
|
if datatype==1:
|
|
(threshmag,threshdir)=thresholds
|
|
if last3=="Dir":
|
|
thresholds=threshdir
|
|
else:
|
|
thresholds=threshmag
|
|
#if last3 in ("Spd","Dir"):
|
|
# (threshmag,threshdir)=thresholds
|
|
# if last3=="Spd":
|
|
# thresholds=threshmag
|
|
# else:
|
|
# thresholds=threshdir
|
|
if len(tlist)>0:
|
|
same=1
|
|
for j in xrange(len(tlist)):
|
|
thresh=tlist[j]
|
|
parms=plist[j]
|
|
same=1
|
|
for i in xrange(len(thresh)):
|
|
if thresh[i]!=thresholds[i]:
|
|
same=0
|
|
break
|
|
if same==1:
|
|
plist[j]+=",%s"%parm
|
|
break
|
|
if same!=1:
|
|
tlist.append(thresholds)
|
|
plist.append(parm)
|
|
else:
|
|
tlist.append(thresholds)
|
|
plist.append(parm)
|
|
|
|
dthresh=[]
|
|
if len(tlist)>1:
|
|
for j in xrange(len(tlist)):
|
|
thresh=tlist[j]
|
|
parms=plist[j]
|
|
for i in xrange(len(thresh)):
|
|
t=thresh[i]
|
|
str="%d"%(t)
|
|
if len(dthresh)<(i+1):
|
|
dthresh.append(str)
|
|
else:
|
|
dthresh[i]+=" | %s"%str
|
|
else:
|
|
thresh=tlist[0]
|
|
for i in xrange(len(thresh)):
|
|
t=thresh[i]
|
|
str="%d"%t
|
|
dthresh.append(str)
|
|
#
|
|
#
|
|
parmList=" | ".join(plist)
|
|
self.GridsStatsThresholdPopup.entryconfigure(0,label=parmList)
|
|
for i in xrange(len(dthresh)):
|
|
self.GridsStatsThresholdPopup.entryconfigure(i+1,label=dthresh[i])
|
|
#print " ",i,dthresh[i]
|
|
self.GridsStatsThresholdValue.set(dthresh[self.GridsStatsThreshold.get()])
|
|
return
|
|
def updateScaleThreshold(self):
|
|
parm=self.GridsStatsParm.get()
|
|
parmList=[parm,]
|
|
if len(parmList)<1:
|
|
return
|
|
tlist=[]
|
|
plist=[]
|
|
for parm in parmList:
|
|
readParm=parm
|
|
last3="xxx"
|
|
if len(parm)>3:
|
|
last3=parm[-3:]
|
|
if ((last3=="Spd")or(last3=="Dir")):
|
|
readParm=parm[:-3]
|
|
thresholds=self.__VU.getVerThresholds(readParm)
|
|
print " thresholds for ",parm
|
|
print " are:",thresholds
|
|
if last3 in ("Spd","Dir"):
|
|
(threshmag,threshdir)=thresholds
|
|
if last3=="Spd":
|
|
thresholds=threshmag
|
|
else:
|
|
thresholds=threshdir
|
|
if len(tlist)>0:
|
|
same=1
|
|
for j in xrange(len(tlist)):
|
|
thresh=tlist[j]
|
|
parms=plist[j]
|
|
same=1
|
|
for i in xrange(len(thresh)):
|
|
if thresh[i]!=thresholds[i]:
|
|
same=0
|
|
break
|
|
if same==1:
|
|
plist[j]+=",%s"%parm
|
|
break
|
|
if same!=1:
|
|
tlist.append(thresholds)
|
|
plist.append(parm)
|
|
else:
|
|
tlist.append(thresholds)
|
|
plist.append(parm)
|
|
|
|
dthresh=[]
|
|
if len(tlist)>1:
|
|
for j in xrange(len(tlist)):
|
|
thresh=tlist[j]
|
|
parms=plist[j]
|
|
for i in xrange(len(thresh)):
|
|
t=thresh[i]
|
|
str="%d"%(t)
|
|
if len(dthresh)<(i+1):
|
|
dthresh.append(str)
|
|
else:
|
|
dthresh[i]+=" | %s"%str
|
|
else:
|
|
thresh=tlist[0]
|
|
for i in xrange(len(thresh)):
|
|
t=thresh[i]
|
|
str="%d"%t
|
|
dthresh.append(str)
|
|
#
|
|
#
|
|
parmList=" | ".join(plist)
|
|
self.ScaleThresholdPopup.entryconfigure(0,label=parmList)
|
|
for i in xrange(len(dthresh)):
|
|
self.ScaleThresholdPopup.entryconfigure(i+1,label=dthresh[i])
|
|
#print " ",i,dthresh[i]
|
|
self.ScaleThresholdValue.set(dthresh[self.ScaleThreshold.get()])
|
|
return
|
|
#==================================================================
|
|
#
|
|
# Frame that specifies the options for the Dists display
|
|
#
|
|
def OptionsDists(self,master):
|
|
#
|
|
# parameter
|
|
#
|
|
self.DistsParm=Tkinter.StringVar()
|
|
parms=self.__VU.getVerParmsVect()
|
|
defaultParm=parms[0]
|
|
self.radioGroup(master,"Parameter:",self.DistsParm,parms,defaultParm,Tkinter.BOTH,1)
|
|
#
|
|
# display
|
|
#
|
|
self.DistsDisplay=Tkinter.StringVar()
|
|
|
|
radioFrame=Tkinter.Frame(master,relief=Tkinter.GROOVE,borderwidth=2)
|
|
labFrame=Tkinter.Frame(radioFrame)
|
|
|
|
radioLabel=Tkinter.Label(labFrame,text="Display:")
|
|
radioLabel.pack(side=Tkinter.LEFT,anchor=Tkinter.W)
|
|
|
|
self.DistsScale=Tkinter.IntVar()
|
|
self.DistsScaleText=Tkinter.StringVar()
|
|
but=Tkinter.Menubutton(labFrame,textvariable=self.DistsScaleText,
|
|
relief=Tkinter.RAISED,indicatoron=1)
|
|
but.pack(side=Tkinter.RIGHT,anchor=Tkinter.W)
|
|
self.DistsScalePopup=Tkinter.Menu(but,tearoff=0)
|
|
for (value,text) in self.__scaleList:
|
|
self.DistsScalePopup.add_radiobutton(label=text,indicatoron=0,value=value,
|
|
variable=self.DistsScale,
|
|
command=self.updateDistsScale)
|
|
self.DistsScale.set(0)
|
|
#self.updateDistsScale()
|
|
but.config(menu=self.DistsScalePopup)
|
|
labFrame.pack(side=Tkinter.TOP,anchor=Tkinter.W,fill=Tkinter.X,expand=1)
|
|
|
|
a=Tkinter.Radiobutton(radioFrame,text="Error Histogram",
|
|
variable=self.DistsDisplay,value="Error Histogram")
|
|
a.pack(side=Tkinter.TOP,anchor=Tkinter.W)
|
|
self.DistsDisplay.set("Error Histogram")
|
|
a=Tkinter.Radiobutton(radioFrame,text="Value Histogram",
|
|
variable=self.DistsDisplay,value="Value Histogram")
|
|
a.pack(side=Tkinter.TOP,anchor=Tkinter.W)
|
|
a=Tkinter.Radiobutton(radioFrame,text="Expected Value",
|
|
variable=self.DistsDisplay,value="Expected Value")
|
|
a.pack(side=Tkinter.TOP,anchor=Tkinter.W)
|
|
a=Tkinter.Radiobutton(radioFrame,text="Scatterplot",
|
|
variable=self.DistsDisplay,value="Scatterplot")
|
|
a.pack(side=Tkinter.TOP,anchor=Tkinter.W)
|
|
radioFrame.pack(side=Tkinter.TOP,anchor=Tkinter.NW,fill=Tkinter.X,expand=0)
|
|
|
|
#displays=["Error Histogram","Value Histogram","Expected Value","Scatterplot"]
|
|
#defaultDisplay="Error Histogram"
|
|
#self.radioGroup(master,"Display:",self.DistsDisplay,displays,defaultDisplay,Tkinter.X,0)
|
|
return
|
|
#==================================================================
|
|
#
|
|
def updateDistsScale(self):
|
|
value=self.DistsScale.get()
|
|
for i in xrange(len(self.__scaleList)):
|
|
(num,text)=self.__scaleList[i]
|
|
if num==value:
|
|
self.DistsScaleText.set(text)
|
|
self.GridsScale.set(num)
|
|
self.GridsScaleText.set(text)
|
|
self.GridsStatsScale.set(num)
|
|
self.GridsStatsScaleText.set(text)
|
|
self.StatsScale.set(num)
|
|
self.StatsScaleText.set(text)
|
|
break
|
|
#==================================================================
|
|
#
|
|
# Frame that specifies the options for the Stats display
|
|
#
|
|
def OptionsStats(self,master):
|
|
#
|
|
# parameter
|
|
#
|
|
self.StatsParms=[]
|
|
parms=self.__VU.getVerParmsVect()
|
|
for parm in parms:
|
|
self.StatsParms.append(Tkinter.StringVar())
|
|
defaultParms=[parms[0],]
|
|
self.checkGroup(master,"Parameter:",self.StatsParms,parms,
|
|
defaultParms,Tkinter.X,0,callback=self.updateStatsThreshold)
|
|
#
|
|
# Area list
|
|
#
|
|
af=Tkinter.Frame(master,relief=Tkinter.GROOVE,borderwidth=2)
|
|
alist=self.__VU.listEditAreaDescriptions()
|
|
alist[0:0]=["Current"]
|
|
defaultArea=alist[0]
|
|
maxwid=0
|
|
for area in alist:
|
|
if len(area)>maxwid:
|
|
maxwid=len(area)
|
|
if len(alist)>5:
|
|
maxheight=5
|
|
else:
|
|
maxheight=len(alist)
|
|
|
|
acomb=Tkinter.Frame(af)
|
|
self.StatsAreaCombine=Tkinter.IntVar()
|
|
comb=Tkinter.Checkbutton(acomb,text="Combine",variable=self.StatsAreaCombine)
|
|
self.StatsAreaCombine.set(1)
|
|
comb.pack(side=Tkinter.RIGHT,anchor=Tkinter.E)
|
|
sLabel=Tkinter.Label(acomb,text="Edit Area:")
|
|
sLabel.pack(side=Tkinter.LEFT,anchor=Tkinter.W)
|
|
acomb.pack(side=Tkinter.TOP,anchor=Tkinter.W,fill=Tkinter.X)
|
|
sb=Tkinter.Scrollbar(af,orient=Tkinter.VERTICAL)
|
|
self.StatsAreasListbox=Tkinter.Listbox(af,yscrollcommand=sb.set,
|
|
selectmode=Tkinter.EXTENDED,width=maxwid,height=maxheight)
|
|
sb.config(command=self.StatsAreasListbox.yview)
|
|
sb.pack(side=Tkinter.RIGHT,fill=Tkinter.Y)
|
|
self.StatsAreasListbox.pack(side=Tkinter.LEFT,fill=Tkinter.BOTH,expand=1)
|
|
idx=0
|
|
for item in alist:
|
|
self.StatsAreasListbox.insert(Tkinter.END,item)
|
|
if item in defaultArea:
|
|
self.StatsAreasListbox.select_set(idx)
|
|
idx+=1
|
|
|
|
af.pack(side=Tkinter.TOP,anchor=Tkinter.N,fill=Tkinter.BOTH,expand=1)
|
|
#
|
|
# display
|
|
#
|
|
self.StatsDisplay=Tkinter.StringVar()
|
|
radioFrame=Tkinter.Frame(master,relief=Tkinter.GROOVE,borderwidth=2)
|
|
labFrame=Tkinter.Frame(radioFrame)
|
|
|
|
radioLabel=Tkinter.Label(labFrame,text="Display:")
|
|
radioLabel.pack(side=Tkinter.LEFT,anchor=Tkinter.W)
|
|
|
|
self.StatsScale=Tkinter.IntVar()
|
|
self.StatsScaleText=Tkinter.StringVar()
|
|
but=Tkinter.Menubutton(labFrame,textvariable=self.StatsScaleText,
|
|
relief=Tkinter.RAISED,indicatoron=1)
|
|
but.pack(side=Tkinter.RIGHT,anchor=Tkinter.W)
|
|
self.StatsScalePopup=Tkinter.Menu(but,tearoff=0)
|
|
for (value,text) in self.__scaleList:
|
|
self.StatsScalePopup.add_radiobutton(label=text,indicatoron=0,value=value,
|
|
variable=self.StatsScale,
|
|
command=self.updateStatsScale)
|
|
self.StatsScale.set(0)
|
|
#self.updateStatsScale()
|
|
but.config(menu=self.StatsScalePopup)
|
|
labFrame.pack(side=Tkinter.TOP,anchor=Tkinter.W,fill=Tkinter.X,expand=1)
|
|
|
|
a=Tkinter.Radiobutton(radioFrame,text="Bias",
|
|
variable=self.StatsDisplay,value="Bias")
|
|
a.pack(side=Tkinter.TOP,anchor=Tkinter.W)
|
|
self.StatsDisplay.set("Bias")
|
|
a=Tkinter.Radiobutton(radioFrame,text="Mean Abs Error",
|
|
variable=self.StatsDisplay,value="Mean Abs Error")
|
|
a.pack(side=Tkinter.TOP,anchor=Tkinter.W)
|
|
a=Tkinter.Radiobutton(radioFrame,text="RMS Error",
|
|
variable=self.StatsDisplay,value="RMS Error")
|
|
a.pack(side=Tkinter.TOP,anchor=Tkinter.W)
|
|
a=Tkinter.Radiobutton(radioFrame,text="Mean Squared Error",
|
|
variable=self.StatsDisplay,value="Mean Squared Error")
|
|
a.pack(side=Tkinter.TOP,anchor=Tkinter.W)
|
|
|
|
threshFrame=Tkinter.Frame(radioFrame)
|
|
a=Tkinter.Radiobutton(threshFrame,text="Percent Err <",
|
|
variable=self.StatsDisplay,value="Percent Err <")
|
|
a.pack(side=Tkinter.LEFT,anchor=Tkinter.W)
|
|
self.StatsThreshold=Tkinter.IntVar()
|
|
self.StatsThresholdValue=Tkinter.StringVar()
|
|
but=Tkinter.Menubutton(threshFrame,textvariable=self.StatsThresholdValue,
|
|
relief=Tkinter.RAISED,indicatoron=1)
|
|
but.pack(side=Tkinter.LEFT,anchor=Tkinter.W)
|
|
self.thresholdStatsPopup=Tkinter.Menu(but,tearoff=0)
|
|
self.thresholdStatsPopup.add_command(label="stuff")
|
|
for i in xrange(self.__VU.getCFG('NUMTHRESH')):
|
|
self.thresholdStatsPopup.add_radiobutton(label="xxxx",indicatoron=0,value=i,
|
|
variable=self.StatsThreshold,
|
|
command=self.pickStatsThreshold)
|
|
but.config(menu=self.thresholdStatsPopup)
|
|
threshFrame.pack(side=Tkinter.TOP,anchor=Tkinter.W)
|
|
twocatFrame=Tkinter.Frame(radioFrame)
|
|
a=Tkinter.Radiobutton(twocatFrame,text="",variable=self.StatsDisplay,
|
|
value="TwoCat")
|
|
a.pack(side=Tkinter.LEFT,anchor=Tkinter.W)
|
|
self.statsTwoCatType=Tkinter.StringVar()
|
|
but=Tkinter.Menubutton(twocatFrame,textvariable=self.statsTwoCatType,
|
|
relief=Tkinter.RAISED,indicatoron=1)
|
|
but.pack(side=Tkinter.LEFT,anchor=Tkinter.W)
|
|
self.statsTwoCatTypePopup=Tkinter.Menu(but,tearoff=0)
|
|
for stat in ["Hits","Areal Hits","Misses","Areal Misses",
|
|
"False Alarms","Areal False Alarms","Correct Negatives",
|
|
"Areal Correct Negatives",
|
|
"Frequency Observed",
|
|
"Frequency Forecast",
|
|
"Fraction Correct",
|
|
"Areal Fraction Correct",
|
|
"Frequency Bias","Areal Frequency Bias",
|
|
"POD","Areal POD","FAR","Areal FAR","Threat Score",
|
|
"Areal Threat Score","Equitable Threat Score",
|
|
"Areal Equitable Threat Score","True Skill Score",
|
|
"Areal True Skill Score","Heidke Skill Score",
|
|
"Areal Heidke Skill Score","Odds Ratio","Areal Odds Ratio"]:
|
|
self.statsTwoCatTypePopup.add_radiobutton(label=stat,
|
|
indicatoron=0,value=stat,
|
|
variable=self.statsTwoCatType,command=self.updateStatsTwoCatType)
|
|
self.statsTwoCatType.set("Fraction Correct")
|
|
but.config(menu=self.statsTwoCatTypePopup)
|
|
self.statsTwoCatCond=Tkinter.StringVar()
|
|
but=Tkinter.Menubutton(twocatFrame,textvariable=self.statsTwoCatCond,
|
|
relief=Tkinter.RAISED,indicatoron=1)
|
|
but.pack(side=Tkinter.LEFT,anchor=Tkinter.W)
|
|
self.statsTwoCatCondPopup=Tkinter.Menu(but,tearoff=0)
|
|
for cond in [">",">=","<=","<"]:
|
|
self.statsTwoCatCondPopup.add_radiobutton(label=cond,
|
|
indicatoron=0,value=cond,
|
|
variable=self.statsTwoCatCond,command=self.updateStatsTwoCatType)
|
|
self.statsTwoCatCond.set(">")
|
|
but.config(menu=self.statsTwoCatCondPopup)
|
|
self.statsTwoCatValueString=Tkinter.StringVar()
|
|
ent=Tkinter.Entry(twocatFrame,textvariable=self.statsTwoCatValueString,
|
|
width=5,relief=Tkinter.SUNKEN)
|
|
self.statsTwoCatValueString.set("0.0")
|
|
ent.pack(side=Tkinter.LEFT,anchor=Tkinter.W)
|
|
twocatFrame.pack(side=Tkinter.TOP,anchor=Tkinter.W)
|
|
radioFrame.pack(side=Tkinter.TOP,anchor=Tkinter.NW,fill=Tkinter.X,expand=0)
|
|
self.updateStatsThreshold()
|
|
#
|
|
# Stat type
|
|
#
|
|
self.StatsType=Tkinter.StringVar()
|
|
stattypes=["vs. Time","vs. Fcst Hour"]
|
|
defaulttype="vs. Time"
|
|
self.radioGroup(master,"Plot:",self.StatsType,stattypes,defaulttype,Tkinter.X,0)
|
|
return
|
|
#==================================================================
|
|
#
|
|
def updateStatsScale(self):
|
|
value=self.StatsScale.get()
|
|
for i in xrange(len(self.__scaleList)):
|
|
(num,text)=self.__scaleList[i]
|
|
if num==value:
|
|
self.StatsScaleText.set(text)
|
|
self.GridsScale.set(num)
|
|
self.GridsScaleText.set(text)
|
|
self.GridsStatsScale.set(num)
|
|
self.GridsStatsScaleText.set(text)
|
|
self.DistsScale.set(num)
|
|
self.DistsScaleText.set(text)
|
|
break
|
|
def updateStatsTwoCatType(self):
|
|
self.StatsDisplay.set("TwoCat")
|
|
return
|
|
#==================================================================
|
|
#
|
|
def pickStatsThreshold(self):
|
|
self.StatsDisplay.set("Percent Err <")
|
|
self.updateStatsThreshold()
|
|
return
|
|
|
|
def updateStatsThreshold(self):
|
|
parmList=self.getCheckList(self.StatsParms)
|
|
if len(parmList)<1:
|
|
return
|
|
tlist=[]
|
|
plist=[]
|
|
for parm in parmList:
|
|
readParm=parm
|
|
last3="xxx"
|
|
if len(parm)>3:
|
|
last3=parm[-3:]
|
|
if ((last3=="Spd")or(last3=="Dir")):
|
|
readParm=parm[:-3]
|
|
thresholds=self.__VU.getVerThresholds(readParm)
|
|
if last3 in ("Spd","Dir"):
|
|
(threshmag,threshdir)=thresholds
|
|
if last3=="Spd":
|
|
thresholds=threshmag
|
|
else:
|
|
thresholds=threshdir
|
|
if len(tlist)>0:
|
|
same=1
|
|
for j in xrange(len(tlist)):
|
|
thresh=tlist[j]
|
|
parms=plist[j]
|
|
same=1
|
|
for i in xrange(len(thresh)):
|
|
if thresh[i]!=thresholds[i]:
|
|
same=0
|
|
break
|
|
if same==1:
|
|
plist[j]+=",%s"%parm
|
|
break
|
|
if same!=1:
|
|
tlist.append(thresholds)
|
|
plist.append(parm)
|
|
else:
|
|
tlist.append(thresholds)
|
|
plist.append(parm)
|
|
|
|
dthresh=[]
|
|
if len(tlist)>1:
|
|
for j in xrange(len(tlist)):
|
|
thresh=tlist[j]
|
|
parms=plist[j]
|
|
for i in xrange(len(thresh)):
|
|
t=thresh[i]
|
|
str="%d"%(t)
|
|
if len(dthresh)<(i+1):
|
|
dthresh.append(str)
|
|
else:
|
|
dthresh[i]+=" | %s"%str
|
|
else:
|
|
thresh=tlist[0]
|
|
for i in xrange(len(thresh)):
|
|
t=thresh[i]
|
|
str="%d"%t
|
|
dthresh.append(str)
|
|
#
|
|
#
|
|
parmList=" | ".join(plist)
|
|
self.thresholdStatsPopup.entryconfigure(0,label=parmList)
|
|
for i in xrange(len(dthresh)):
|
|
self.thresholdStatsPopup.entryconfigure(i+1,label=dthresh[i])
|
|
#print " ",i,dthresh[i]
|
|
self.StatsThresholdValue.set(dthresh[self.StatsThreshold.get()])
|
|
return
|
|
#==================================================================
|
|
#
|
|
# Frame that specifies the options for the Scale vs Stat display
|
|
#
|
|
def OptionsScaleStats(self,master):
|
|
#
|
|
# parameter
|
|
#
|
|
self.ScaleStatsParm=Tkinter.StringVar()
|
|
parms=self.__VU.getVerParmsVect()
|
|
defaultParm=parms[0]
|
|
self.radioGroup(master,"Parameter:",self.ScaleStatsParm,parms,defaultParm,Tkinter.BOTH,1)
|
|
#
|
|
# Area list
|
|
#
|
|
af=Tkinter.Frame(master,relief=Tkinter.GROOVE,borderwidth=2)
|
|
alist=self.__VU.listEditAreaDescriptions()
|
|
alist[0:0]=["Current"]
|
|
defaultArea=alist[0]
|
|
maxwid=0
|
|
for area in alist:
|
|
if len(area)>maxwid:
|
|
maxwid=len(area)
|
|
if len(alist)>5:
|
|
maxheight=5
|
|
else:
|
|
maxheight=len(alist)
|
|
|
|
acomb=Tkinter.Frame(af)
|
|
self.ScaleStatsAreaCombine=Tkinter.IntVar()
|
|
#comb=Tkinter.Checkbutton(acomb,text="Combine",variable=self.StatsAreaCombine)
|
|
self.ScaleStatsAreaCombine.set(1) # always set
|
|
#comb.pack(side=Tkinter.RIGHT,anchor=Tkinter.E)
|
|
sLabel=Tkinter.Label(acomb,text="Edit Area:")
|
|
sLabel.pack(side=Tkinter.LEFT,anchor=Tkinter.W)
|
|
acomb.pack(side=Tkinter.TOP,anchor=Tkinter.W,fill=Tkinter.X)
|
|
sb=Tkinter.Scrollbar(af,orient=Tkinter.VERTICAL)
|
|
self.ScaleStatsAreasListbox=Tkinter.Listbox(af,yscrollcommand=sb.set,
|
|
selectmode=Tkinter.EXTENDED,width=maxwid,height=maxheight)
|
|
sb.config(command=self.ScaleStatsAreasListbox.yview)
|
|
sb.pack(side=Tkinter.RIGHT,fill=Tkinter.Y)
|
|
self.ScaleStatsAreasListbox.pack(side=Tkinter.LEFT,fill=Tkinter.BOTH,expand=1)
|
|
idx=0
|
|
for item in alist:
|
|
self.ScaleStatsAreasListbox.insert(Tkinter.END,item)
|
|
if item in defaultArea:
|
|
self.ScaleStatsAreasListbox.select_set(idx)
|
|
idx+=1
|
|
|
|
af.pack(side=Tkinter.TOP,anchor=Tkinter.N,fill=Tkinter.BOTH,expand=1)
|
|
#
|
|
# display
|
|
#
|
|
self.ScaleStatsDisplay=Tkinter.StringVar()
|
|
radioFrame=Tkinter.Frame(master,relief=Tkinter.GROOVE,borderwidth=2)
|
|
labFrame=Tkinter.Frame(radioFrame)
|
|
|
|
radioLabel=Tkinter.Label(labFrame,text="Display:")
|
|
radioLabel.pack(side=Tkinter.LEFT,anchor=Tkinter.W)
|
|
labFrame.pack(side=Tkinter.TOP,anchor=Tkinter.W,fill=Tkinter.X,expand=1)
|
|
|
|
a=Tkinter.Radiobutton(radioFrame,text="Bias",
|
|
variable=self.ScaleStatsDisplay,value="Bias")
|
|
a.pack(side=Tkinter.TOP,anchor=Tkinter.W)
|
|
self.ScaleStatsDisplay.set("Bias")
|
|
a=Tkinter.Radiobutton(radioFrame,text="Mean Abs Error",
|
|
variable=self.ScaleStatsDisplay,value="Mean Abs Error")
|
|
a.pack(side=Tkinter.TOP,anchor=Tkinter.W)
|
|
a=Tkinter.Radiobutton(radioFrame,text="RMS Error",
|
|
variable=self.ScaleStatsDisplay,value="RMS Error")
|
|
a.pack(side=Tkinter.TOP,anchor=Tkinter.W)
|
|
a=Tkinter.Radiobutton(radioFrame,text="Mean Squared Error",
|
|
variable=self.ScaleStatsDisplay,value="Mean Squared Error")
|
|
a.pack(side=Tkinter.TOP,anchor=Tkinter.W)
|
|
|
|
threshFrame=Tkinter.Frame(radioFrame)
|
|
a=Tkinter.Radiobutton(threshFrame,text="Percent Err <",
|
|
variable=self.ScaleStatsDisplay,value="Percent Err <")
|
|
a.pack(side=Tkinter.LEFT,anchor=Tkinter.W)
|
|
self.ScaleStatsThreshold=Tkinter.IntVar()
|
|
self.ScaleStatsThresholdValue=Tkinter.StringVar()
|
|
but=Tkinter.Menubutton(threshFrame,textvariable=self.ScaleStatsThresholdValue,
|
|
relief=Tkinter.RAISED,indicatoron=1)
|
|
but.pack(side=Tkinter.LEFT,anchor=Tkinter.W)
|
|
self.thresholdScaleStatsPopup=Tkinter.Menu(but,tearoff=0)
|
|
self.thresholdScaleStatsPopup.add_command(label="stuff")
|
|
for i in xrange(self.__VU.getCFG('NUMTHRESH')):
|
|
self.thresholdScaleStatsPopup.add_radiobutton(label="xxxx",indicatoron=0,value=i,
|
|
variable=self.ScaleStatsThreshold,
|
|
command=self.pickScaleStatsThreshold)
|
|
but.config(menu=self.thresholdScaleStatsPopup)
|
|
threshFrame.pack(side=Tkinter.TOP,anchor=Tkinter.W)
|
|
twocatFrame=Tkinter.Frame(radioFrame)
|
|
a=Tkinter.Radiobutton(twocatFrame,text="",variable=self.ScaleStatsDisplay,
|
|
value="TwoCat")
|
|
a.pack(side=Tkinter.LEFT,anchor=Tkinter.W)
|
|
self.scaleStatsTwoCatType=Tkinter.StringVar()
|
|
but=Tkinter.Menubutton(twocatFrame,textvariable=self.scaleStatsTwoCatType,
|
|
relief=Tkinter.RAISED,indicatoron=1)
|
|
but.pack(side=Tkinter.LEFT,anchor=Tkinter.W)
|
|
self.scaleStatsTwoCatTypePopup=Tkinter.Menu(but,tearoff=0)
|
|
for stat in ["Hits","Areal Hits","Misses","Areal Misses",
|
|
"False Alarms","Areal False Alarms","Correct Negatives",
|
|
"Areal Correct Negatives",
|
|
"Frequency Observed",
|
|
"Frequency Forecast",
|
|
"Fraction Correct",
|
|
"Areal Fraction Correct",
|
|
"Frequency Bias","Areal Frequency Bias",
|
|
"POD","Areal POD","FAR","Areal FAR","Threat Score",
|
|
"Areal Threat Score","Equitable Threat Score",
|
|
"Areal Equitable Threat Score","True Skill Score",
|
|
"Areal True Skill Score","Heidke Skill Score",
|
|
"Areal Heidke Skill Score","Odds Ratio","Areal Odds Ratio"]:
|
|
self.scaleStatsTwoCatTypePopup.add_radiobutton(label=stat,
|
|
indicatoron=0,value=stat,
|
|
variable=self.scaleStatsTwoCatType,command=self.updateScaleStatsTwoCatType)
|
|
self.scaleStatsTwoCatType.set("Fraction Correct")
|
|
but.config(menu=self.scaleStatsTwoCatTypePopup)
|
|
self.scaleStatsTwoCatCond=Tkinter.StringVar()
|
|
but=Tkinter.Menubutton(twocatFrame,textvariable=self.scaleStatsTwoCatCond,
|
|
relief=Tkinter.RAISED,indicatoron=1)
|
|
but.pack(side=Tkinter.LEFT,anchor=Tkinter.W)
|
|
self.scaleStatsTwoCatCondPopup=Tkinter.Menu(but,tearoff=0)
|
|
for cond in [">",">=","<=","<"]:
|
|
self.scaleStatsTwoCatCondPopup.add_radiobutton(label=cond,
|
|
indicatoron=0,value=cond,
|
|
variable=self.scaleStatsTwoCatCond,command=self.updateScaleStatsTwoCatType)
|
|
self.scaleStatsTwoCatCond.set(">")
|
|
but.config(menu=self.scaleStatsTwoCatCondPopup)
|
|
self.scaleStatsTwoCatValueString=Tkinter.StringVar()
|
|
ent=Tkinter.Entry(twocatFrame,textvariable=self.scaleStatsTwoCatValueString,
|
|
width=5,relief=Tkinter.SUNKEN)
|
|
self.scaleStatsTwoCatValueString.set("0.0")
|
|
ent.pack(side=Tkinter.LEFT,anchor=Tkinter.W)
|
|
twocatFrame.pack(side=Tkinter.TOP,anchor=Tkinter.W)
|
|
radioFrame.pack(side=Tkinter.TOP,anchor=Tkinter.NW,fill=Tkinter.X,expand=0)
|
|
self.updateScaleStatsThreshold()
|
|
return
|
|
def updateScaleStatsTwoCatType(self):
|
|
self.ScaleStatsDisplay.set("TwoCat")
|
|
return
|
|
#==================================================================
|
|
#
|
|
def pickScaleStatsThreshold(self):
|
|
self.ScaleStatsDisplay.set("Percent Err <")
|
|
self.updateScaleStatsThreshold()
|
|
return
|
|
|
|
def updateScaleStatsThreshold(self):
|
|
parm=self.ScaleStatsParm.get()
|
|
readParm=parm
|
|
last3="xxx"
|
|
if len(parm)>3:
|
|
last3=parm[-3:]
|
|
if ((last3=="Spd")or(last3=="Dir")):
|
|
readParm=parm[:-3]
|
|
thresholds=self.__VU.getVerThresholds(readParm)
|
|
if last3 in ("Spd","Dir"):
|
|
(threshmag,threshdir)=thresholds
|
|
if last3=="Spd":
|
|
thresholds=threshmag
|
|
else:
|
|
thresholds=threshdir
|
|
|
|
dthresh=[]
|
|
for i in xrange(len(thresholds)):
|
|
t=thresholds[i]
|
|
str="%d"%t
|
|
dthresh.append(str)
|
|
#
|
|
#
|
|
self.thresholdScaleStatsPopup.entryconfigure(0,label=parm)
|
|
for i in xrange(len(dthresh)):
|
|
self.thresholdScaleStatsPopup.entryconfigure(i+1,label=dthresh[i])
|
|
self.ScaleStatsThresholdValue.set(dthresh[self.ScaleStatsThreshold.get()])
|
|
return
|
|
#=================================================================
|
|
# displayGroup - make a group of radio buttons with scale stuff
|
|
#
|
|
def displayGroup(self,master,labeltext,var,valuelist,defaultvalue,filltype,expandflag):
|
|
radioFrame=Tkinter.Frame(master,relief=Tkinter.GROOVE,borderwidth=2)
|
|
radioLabel=Tkinter.Label(radioFrame,text=labeltext)
|
|
radioLabel.pack(side=Tkinter.TOP,anchor=Tkinter.W)
|
|
for item in valuelist:
|
|
a=Tkinter.Radiobutton(radioFrame,text=item,
|
|
variable=var,value=item)
|
|
a.pack(side=Tkinter.TOP,anchor=Tkinter.W)
|
|
if item is defaultvalue:
|
|
var.set(item)
|
|
radioFrame.pack(side=Tkinter.TOP,anchor=Tkinter.NW,fill=filltype,expand=expandflag)
|
|
#=================================================================
|
|
# radioGroup - make a group of radio buttons
|
|
#
|
|
def radioGroup(self,master,labeltext,var,valuelist,defaultvalue,filltype,
|
|
expandflag,callback=None):
|
|
radioFrame=Tkinter.Frame(master,relief=Tkinter.GROOVE,borderwidth=2)
|
|
radioLabel=Tkinter.Label(radioFrame,text=labeltext)
|
|
radioLabel.pack(side=Tkinter.TOP,anchor=Tkinter.W)
|
|
for item in valuelist:
|
|
a=Tkinter.Radiobutton(radioFrame,text=item,
|
|
variable=var,value=item,command=callback)
|
|
a.pack(side=Tkinter.TOP,anchor=Tkinter.W)
|
|
if item is defaultvalue:
|
|
var.set(item)
|
|
radioFrame.pack(side=Tkinter.TOP,anchor=Tkinter.NW,fill=filltype,expand=expandflag)
|
|
#=================================================================
|
|
# checkGroup - make a group of check buttons
|
|
#
|
|
def checkGroup(self,master,labeltext,varlist,valuelist,
|
|
defaultvalues,filltype,expandflag,callback=None,maxRows=30):
|
|
checkFrame=Tkinter.Frame(master,relief=Tkinter.GROOVE,borderwidth=2)
|
|
checkLabel=Tkinter.Label(checkFrame,text=labeltext)
|
|
checkLabel.pack(side=Tkinter.TOP,anchor=Tkinter.W)
|
|
cnt=0
|
|
row=0
|
|
col=0
|
|
f=Tkinter.Frame(checkFrame,relief=Tkinter.FLAT,borderwidth=2)
|
|
f.pack(side=Tkinter.TOP,anchor=Tkinter.W)
|
|
if len(valuelist) > maxRows:
|
|
ncols = (len(valuelist) - 1)/maxRows + 1
|
|
maxRows = (len(valuelist) - 1)/ncols + 1
|
|
for item in valuelist:
|
|
a=Tkinter.Checkbutton(f,text=item,variable=varlist[cnt],
|
|
onvalue=item,offvalue="",command=callback)
|
|
if item in defaultvalues:
|
|
varlist[cnt].set(item)
|
|
|
|
a.grid(row=row,column=col,sticky=Tkinter.NW)
|
|
print "Formatting row %d col %d for %s" % (row,col,item)
|
|
row=row+1
|
|
if row == maxRows:
|
|
row = 0
|
|
col =col + 1
|
|
cnt=cnt+1
|
|
checkFrame.pack(side=Tkinter.TOP,fill=filltype,expand=expandflag)
|
|
return varlist
|
|
#=================================================================
|
|
# getCheckList - get list of values turned on in the checkbutton list
|
|
#
|
|
def getCheckList(self,checklist):
|
|
outlist=[]
|
|
for i in xrange(len(checklist)):
|
|
a=checklist[i].get()
|
|
if a!="":
|
|
outlist.append(a)
|
|
return outlist
|
|
#=================================================================
|
|
# sListbox - make a listbox with a scrollbar
|
|
#
|
|
def sListbox(self,master,labeltext,itemlist,defaultItems,
|
|
maxwid,maxheight,smode,filltype=Tkinter.BOTH,expandflag=1):
|
|
sLabel=Tkinter.Label(master,text=labeltext)
|
|
sLabel.pack(side=Tkinter.TOP,anchor=Tkinter.W)
|
|
sb=Tkinter.Scrollbar(master,orient=Tkinter.VERTICAL)
|
|
slb=Tkinter.Listbox(master,yscrollcommand=sb.set,
|
|
selectmode=smode,width=maxwid,height=maxheight)
|
|
sb.config(command=slb.yview)
|
|
sb.pack(side=Tkinter.RIGHT,fill=Tkinter.Y)
|
|
slb.pack(side=Tkinter.LEFT,fill=filltype,expand=expandflag)
|
|
idx=0
|
|
for item in itemlist:
|
|
slb.insert(Tkinter.END,item)
|
|
if item in defaultItems:
|
|
slb.select_set(idx)
|
|
idx+=1
|
|
return slb
|
|
#=================================================================
|
|
# getListbox - get list of values turned on in the listbox
|
|
#
|
|
def getListbox(self,listbox):
|
|
outlist=[]
|
|
itemnums=listbox.curselection()
|
|
try:
|
|
itemnums=map(int,itemnums)
|
|
except ValueError: pass
|
|
for itemnum in itemnums:
|
|
outlist.append(listbox.get(itemnum))
|
|
return outlist
|
|
#=================================================================
|
|
def setDatetype(self):
|
|
type=self.Datetype.get()
|
|
if type=="Period Length":
|
|
self.ByList.pack_forget()
|
|
self.ByPeriod.pack(side=Tkinter.TOP,anchor=Tkinter.NW,fill=Tkinter.BOTH,expand=1)
|
|
else:
|
|
self.ByPeriod.pack_forget()
|
|
self.ByList.pack(side=Tkinter.TOP,anchor=Tkinter.NW,fill=Tkinter.BOTH,expand=1)
|
|
#==================================================================
|
|
# getRecentDates - gets a list of date strings from today through
|
|
# numdays in the past. Also returns list of
|
|
# unix times for the beginning of each date.
|
|
def getRecentDates(self,numdays):
|
|
recentDateStrings=[]
|
|
recentDates=[]
|
|
(nyea,nmon,nday,nhou,nmin,nsec,nwda,nyda,ndst)=time.gmtime()
|
|
midtoday=calendar.timegm((nyea,nmon,nday,0,0,0,0,0,0))
|
|
for i in xrange(numdays):
|
|
daymid=midtoday-(i*DAYSECS)
|
|
(gyr,gmo,gdy,ghr,gmi,gse,gwd,gyd,gds)=time.gmtime(daymid)
|
|
recentDateStrings.append("%4.4d/%2.2d/%2.2d"%(gyr,gmo,gdy))
|
|
recentDates.append(daymid)
|
|
return recentDateStrings,recentDates
|
|
#
|
|
# Special global routines used in Histogram callback stuff to
|
|
# move data on/off the screen
|
|
#
|
|
def showmodel(self,modname):
|
|
if self.showmod[modname]==1:
|
|
self.cd.canvas.move(modname,0,-self.cd.curheight)
|
|
self.cd.canvas.lower(modname)
|
|
self.showmod[modname]=0
|
|
self.modb[modname].config(fg="grey")
|
|
else:
|
|
self.cd.canvas.move(modname,0,self.cd.curheight)
|
|
self.cd.canvas.lift(modname)
|
|
self.showmod[modname]=1
|
|
self.modb[modname].config(fg=self.colornames[modname])
|
|
return
|
|
#=====================================================================
|
|
#
|
|
# Toggle stuff with "but" tag in the but1 list
|
|
#
|
|
def showBut1(self,but):
|
|
if but.isdigit():
|
|
newbut="f%s"%but
|
|
but=newbut
|
|
if self.but1state.get(but)==1:
|
|
self.cd.canvas.move(but,0,-self.cd.curheight)
|
|
self.cd.canvas.lower(but)
|
|
self.but1state[but]=0
|
|
self.but1[but].config(fg="grey")
|
|
else:
|
|
self.cd.canvas.move(but,0,self.cd.curheight)
|
|
self.cd.canvas.lift(but)
|
|
self.but1state[but]=1
|
|
self.but1[but].config(fg="black")
|
|
#=====================================================================
|
|
#
|
|
# Turn off all but1 tags except first button
|
|
#
|
|
def startBut1(self):
|
|
for but in self.but1names:
|
|
if but!=self.but1names[0]:
|
|
showBut1(self,but)
|
|
#=====================================================================
|
|
#
|
|
# Move toggled but1 buttons - one to the left
|
|
#
|
|
def prevBut1(self):
|
|
newbut=[]
|
|
for but in self.but1names:
|
|
if self.but1state.get(but)==1:
|
|
newbut.append(1)
|
|
else:
|
|
newbut.append(0)
|
|
temp=newbut[0]
|
|
del newbut[0]
|
|
newbut.append(temp)
|
|
for i in xrange(len(self.but1names)):
|
|
but=self.but1names[i]
|
|
now=self.but1state[but]
|
|
after=newbut[i]
|
|
if ((now==1)and(after==0)):
|
|
self.cd.canvas.move(but,0,-self.cd.curheight)
|
|
self.cd.canvas.lower(but)
|
|
self.but1state[but]=0
|
|
self.but1[but].config(fg="grey")
|
|
elif ((now==0)and(after==1)):
|
|
self.cd.canvas.move(but,0,self.cd.curheight)
|
|
self.cd.canvas.lift(but)
|
|
self.but1state[but]=1
|
|
self.but1[but].config(fg="black")
|
|
#=====================================================================
|
|
#
|
|
# Move toggled but1 buttons - one to the right
|
|
#
|
|
def nextBut1(self):
|
|
newbut=[]
|
|
for but in self.but1names:
|
|
if self.but1state.get(but)==1:
|
|
newbut.append(1)
|
|
else:
|
|
newbut.append(0)
|
|
temp=newbut.pop()
|
|
newbut[0:0]=[temp]
|
|
for i in xrange(len(self.but1names)):
|
|
but=self.but1names[i]
|
|
now=self.but1state[but]
|
|
after=newbut[i]
|
|
if ((now==1)and(after==0)):
|
|
self.cd.canvas.move(but,0,-self.cd.curheight)
|
|
self.cd.canvas.lower(but)
|
|
self.but1state[but]=0
|
|
self.but1[but].config(fg="grey")
|
|
elif ((now==0)and(after==1)):
|
|
self.cd.canvas.move(but,0,self.cd.curheight)
|
|
self.cd.canvas.lift(but)
|
|
self.but1state[but]=1
|
|
self.but1[but].config(fg="black")
|
|
#=====================================================================
|
|
#
|
|
# Toggle stuff with "but" tag in the but2 list
|
|
#
|
|
def showBut2(self,but):
|
|
if self.but2state.get(but)==1:
|
|
self.cd.canvas.move(but,-self.cd.curwidth,0)
|
|
self.cd.canvas.lower(but)
|
|
self.but2state[but]=0
|
|
self.but2[but].config(fg="grey")
|
|
else:
|
|
self.cd.canvas.move(but,self.cd.curwidth,0)
|
|
self.cd.canvas.lift(but)
|
|
self.but2state[but]=1
|
|
self.but2[but].config(fg=self.colornames[but])
|
|
#=====================================================================
|
|
#
|
|
# Turn off all but2 tags except first button
|
|
#
|
|
def startBut2(self):
|
|
for but in self.but2names:
|
|
if but!=self.but2names[0]:
|
|
showBut2(self,but)
|
|
#=====================================================================
|
|
#
|
|
# Move toggled but2 buttons - one to the left
|
|
#
|
|
def prevBut2(self):
|
|
newbut=[]
|
|
for but in self.but2names:
|
|
if self.but2state.get(but)==1:
|
|
newbut.append(1)
|
|
else:
|
|
newbut.append(0)
|
|
temp=newbut[0]
|
|
del newbut[0]
|
|
newbut.append(temp)
|
|
for i in xrange(len(self.but2names)):
|
|
but=self.but2names[i]
|
|
now=self.but2state[but]
|
|
after=newbut[i]
|
|
if ((now==1)and(after==0)):
|
|
self.cd.canvas.move(but,-self.cd.curwidth,0)
|
|
self.cd.canvas.lower(but)
|
|
self.but2state[but]=0
|
|
self.but2[but].config(fg="grey")
|
|
elif ((now==0)and(after==1)):
|
|
self.cd.canvas.move(but,self.cd.curwidth,0)
|
|
self.cd.canvas.lift(but)
|
|
self.but2state[but]=1
|
|
self.but2[but].config(fg=self.colornames[but])
|
|
#=====================================================================
|
|
#
|
|
# Move toggled but2 buttons - one to the right
|
|
#
|
|
def nextBut2(self):
|
|
newbut=[]
|
|
for but in self.but2names:
|
|
if self.but2state.get(but)==1:
|
|
newbut.append(1)
|
|
else:
|
|
newbut.append(0)
|
|
temp=newbut.pop()
|
|
newbut[0:0]=[temp]
|
|
for i in xrange(len(self.but2names)):
|
|
but=self.but2names[i]
|
|
now=self.but2state[but]
|
|
after=newbut[i]
|
|
if ((now==1)and(after==0)):
|
|
self.cd.canvas.move(but,-self.cd.curwidth,0)
|
|
self.cd.canvas.lower(but)
|
|
self.but2state[but]=0
|
|
self.but2[but].config(fg="grey")
|
|
elif ((now==0)and(after==1)):
|
|
self.cd.canvas.move(but,self.cd.curwidth,0)
|
|
self.cd.canvas.lift(but)
|
|
self.but2state[but]=1
|
|
self.but2[but].config(fg=self.colornames[but])
|
|
#
|
|
# debug stuff for memory usage
|
|
#
|
|
_proc_status="/proc/%d/status"%os.getpid()
|
|
_scale={'kB':1024.0,'mB':1024.0*1024.0,
|
|
'KB':1024.0,'MB':1024.0*1024.0}
|
|
def _VmB(VmKey):
|
|
try:
|
|
t=open(_proc_status)
|
|
v=t.read()
|
|
t.close()
|
|
except IOError:
|
|
return 0.0
|
|
i=v.index(VmKey)
|
|
v=v[i:].split(None,3)
|
|
if len(v)<3:
|
|
return 0.0
|
|
return float(v[1])*_scale[v[2]]
|
|
def memory():
|
|
return _VmB('VmSize:')
|
|
def resident():
|
|
return _VmB('VmRSS:')
|
|
#
|
|
# stuff to support a callback with a pre-known variable
|
|
#
|
|
def GenericCallback(callback, *firstArgs, **firstKWArgs):
|
|
if firstKWArgs:
|
|
return GC(callback, *firstArgs, **firstKWArgs)
|
|
else:
|
|
return GCNoKWArgs(callback, *firstArgs)
|
|
#
|
|
# Classes for callbacks
|
|
#
|
|
class GC:
|
|
def __init__(self,callback,*firstArgs, **firstKWArgs):
|
|
self.__callback=callback
|
|
self.__firstArgs=firstArgs
|
|
self.__firstKWArgs=firstKWArgs
|
|
def __call__(self, *lastArgs, **kwArgs):
|
|
if kwArgs:
|
|
netKWArgs=self.__firstKWArgs.copy()
|
|
netKWArgs.update(self.__kwArgs)
|
|
else:
|
|
netKWArgs=self.__firstKWArgs
|
|
return self.__callback (*(self.__firstArgs+lastArgs),**netKWArgs)
|
|
class GCNoKWArgs:
|
|
def __init__(self, callback, *firstArgs):
|
|
self.__callback=callback
|
|
self.__firstArgs=firstArgs
|
|
def __call__(self, *args, **kwArgs):
|
|
return self.__callback (*(self.__firstArgs+args),**kwArgs)
|