#------------------------------------------------------------------------- # Description: This produces a Fire Weather Table. The table produces data # needed for the FWF. The FWF is run seperately and the data are merged # by code in SLC_FWF_TextRules and .pl programs in /home/ifps/release #------------------------------------------------------------------------- # Copying: # 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. #------------------------------------------------------------------------- # RPP version 20.2 #------------------------------------------------------------------------- import SLC_FWF_TextRules, TextRules import SampleAnalysis import ForecastSeries import time, string class TextProduct(TextRules.TextRules, SampleAnalysis.SampleAnalysis): Definition = { "type": "smart", #"displayName": "FWFWindTable", "displayName": "None", ## Edit Areas: Create Combinations file with edit area combinations. "defaultEditAreas": [("Fire1Above8000","Fire Area 1 above 8000"), ("Fire2Above8000","Fire Area 2 above 8000"), ("Fire3Above8000","Fire Area 3 above 8000"), ], # product identifiers "productName": "FIRE SLC stuff", # product name "outputFile": "/home/ifps/slcpractice/products/TEXT/FWFWindTable", "fullStationID": "Kxxx", # full station identifier (4letter) "wmoID": "FNUS5x", # WMO ID "pil": "FWFxxx", # Product pil "areaName": "", # Name of state, such as "GEORGIA" -- optional "wfoCity": "WfoCity", # Location of WFO - city name "wfoState": "WfoState", # Location of WFO - state name # Product-specific variables: # To include a TempPopTable for each area in the current Combination: # Set "includeTempPopTable" to 1 # Make sure you are using a Combinations file # in your AreaFcst_Local file.g. # "defaultEditAreas": "Combinations", # Create a CityDictionary TextUtility file and specify # in your AreaFcst_Local file (see examples/textProducts directory) # Create the TempPopTable_for_AreaFcst_Local product from the # examples/textProducts directory "includeTempPopTable": 0, ## "cityDictionary": "CityDictionary", # Area Dictionary -- Descriptive information about zones ## "areaDictionary": "AreaDictionary", # Language "language": "english", # If summaryExtended == 1, then a summary extended forecast will # be generated for the given summaryArea "summaryExtended": 0, ## "summaryArea":"FireArea", # If individualExtended == 1, an extended forecast will be # generated for each individual area # If extendedLabel == 1, a label will be included for each # individual extended "individualExtended": 0, ## "extendedLabel": 0, } def __init__(self): TextRules.TextRules.__init__(self) SampleAnalysis.SampleAnalysis.__init__(self) ######################################################################## # OVERRIDING THRESHOLDS AND VARIABLES ######################################################################## def phrase_descriptor_dict(self, statDict, argDict): dict = TextRules.TextRules().phrase_descriptor_dict(statDict, argDict) dict["highs"] = self.checkProduct, dict["lows"] = self.checkProduct, return dict def checkProduct(self, statDict, argDict, key, element): component = argDict["subType"] if component == "FirePeriod": return "" else: return key # Uncomment any combinations you wish to collapse. # For example, if the first entry is uncommented, # the phrase: scattered rain showers and widespread rain # will collapse to: scattered rain showers. def wxCombinations(self): return [ ("RW", "R"), ("SW", "S"), ## ("T","RW"), ] def minmax_std_deviation(self, parmHisto, timeRange, componentName): # Replaces MINMAX_STD_DEVIATION # Number of standard deviations to compute around the weighted # average for a stdDev_Minmax return 1.4 ######################################################################## # COMPONENT PRODUCT DEFINITIONS ######################################################################## def FirePeriod(self): return { "type": "component", "analysisList": [ ("Wind", self.vectorTextMedianRange), ## ("Wind", self.vectorTextAvg), ], "textRules":[ (self._fireWind_Ridgephrase), ], "autoUpperCase":1, "autoSentence": 0, "combineSentences": 0, "postProcessPhrase": self.postProcessPhrase, "localEffects": { "MinT": [{ "area1": "BelowElev", "area2": "AboveElev", "threshold": 8, "rangeReportingMethod": self.getScalarRange, "simpleValueReportingMethod": self.getTempPhrase, "introWords": "", "area1Words": "", "exceptionWords": "...except ", "area2Words": " above timberline", }], "MaxT": [{ "area1": "BelowElev", "area2": "AboveElev", "threshold": 8, "rangeReportingMethod": self.getScalarRange, "simpleValueReportingMethod": self.getTempPhrase, "introWords": "", "area1Words": "", "exceptionWords": "...except ", "area2Words": " above timberline", }], "MinRH": [{ "area1": "BelowElev", "area2": "AboveElev", "threshold": 8, "rangeReportingMethod": self.getScalarRange, "simpleValueReportingMethod": self.getTempPhrase, "introWords": "", "area1Words": "%", "exceptionWords": "...except ", "area2Words": "% above timberline", }], "MaxRH": [{ "area1": "BelowElev", "area2": "AboveElev", "threshold": 8, "rangeReportingMethod": self.getScalarRange, "simpleValueReportingMethod": self.getTempPhrase, "introWords": "", "area1Words": "%", "exceptionWords": "...except ", "area2Words": "% above timberline", }], }, } def generateForecast(self, argDict): # Generate Text Phrases for a list of edit areas # Get variables error = self._getVariables(argDict) if error is not None: return error # Get the areaList -- derived from defaultEditAreas and # may be solicited at run-time from user if desired self._areaList = self.getAreaList(argDict) # Determine time ranges error = self._determineTimeRanges(argDict) if error is not None: return error # Sample the data error = self._sampleData(argDict) if error is not None: return error # Initialize the output string fcst = "" fcst = self._preProcessProduct(fcst, argDict) # Generate the product for each edit area in the list fraction = 0 fractionOne = 1.0/float(len(self._areaList)) percent = 50.0 self.setProgressPercentage(percent) for editArea, areaLabel in self._areaList: self.progressMessage(fraction, percent, "Making Product for " + areaLabel) fcst = self._preProcessArea(fcst, editArea, areaLabel, argDict) fcst = self._makeProduct(fcst, editArea, areaLabel, argDict) fcst = self._postProcessArea(fcst, editArea, areaLabel, argDict) fraction = fractionOne ## fcst = self._postProcessProduct(fcst, argDict) return fcst def _getVariables(self, argDict): # Get Definition variables self._definition = argDict["forecastDef"] for key in self._definition.keys(): exec "self._" + key + "= self._definition[key]" # DO NOT Display the dialog for the user to choose issuance ## additionalItems = [] ## status, varDict = self.displayIssuanceDialog( ## self._definition["displayName"], self._issuance_list(argDict), additionalItems) ## if status != "Ok": ## return "Product Cancelled" ## argDict["varDict"] = varDict ## Figure out if day or mid shift ZFP package. time1 = time.gmtime() (year, month, day, h, m, s, w, day, dst) = time1 ## print "ZHour: ", h ## Adjust hours as needed in Zulu. if h > 17 or h < 6: # day shift self._productType = "Afternoon" self.totalPeriods = 4 else: self._productType = "Morning" self.totalPeriods = 3 ## self._productType = varDict["Forecast Product"] self._seriesUtil = self._definition["defaultEditAreas"] ## print self._productType return None def _determineTimeRanges(self, argDict): # Set up the Series Definition and initial Time Range self._issuanceInfo = self.getIssuanceInfo( self._productType, self._issuance_list(argDict)) self._timeRange = self._issuanceInfo.timeRange() self._expireTime = self._issuanceInfo.expireTime() self._definition["SeriesDef"] = self._issuanceInfo.seriesDef() self._definition["labelMethod"] = self._createLabel self._definition["priorPeriod"] = 24 # Determine the extended range if self._individualExtended == 1: self._extendedStart = self._timeRange.endTime() - 24*5*3600 else: self._extendedStart = self._timeRange.endTime() self._extendedRange = self.IFP().TimeRange( self._extendedStart, self._extendedStart + 3600) # Calculate current times self._ddhhmmTime = self.getCurrentTime( argDict, "%d%H%M", shiftToLocal=0, stripLeading=0) self._timeLabel = self.getCurrentTime( argDict, "%l%M %p %Z %a %b %e %Y", stripLeading=1) return None def _sampleData(self, argDict): # Sample and analyze the data for the series # This data will be available in argDict["seriesData"] for text rules self._seriesProcessor = ForecastSeries.ForecastSeries() error = self._seriesProcessor.getSeriesData( argDict, self._definition, self._timeRange, self._areaList) if error is not None: return error return None def _preProcessProduct(self, fcst, argDict): # Product header fcst = "template:\n" return fcst def _preProcessArea(self, fcst, editArea, areaLabel, argDict): # This is the header for an edit area combination fcst = fcst + areaLabel + "\n" return fcst def _makeProduct(self, fcst, editArea, areaLabel, argDict): argDict["language"] = self._language # Generate Series Forecast for Edit Area fcst = fcst + self._seriesProcessor.generateForecast( argDict, editArea, areaLabel) if self._includeTempPopTable == 1: fcst = fcst + self._makeTempPopTable(areaLabel, argDict) return fcst def _postProcessArea(self, fcst, editArea, areaLabel, argDict): fcst = fcst + "=\n$$\n\n" return fcst def _postProcessProduct(self, fcst, argDict): # Add one extended if self._summaryExtended == 1: fcst = fcst + "\n.EXTENDED...\n\n" extended = self.generateProduct("ExtendedSeries", argDict, area=self._summaryArea, timeRange=self._extendedRange) fcst = fcst + string.upper(extended) fcst = fcst + "\n.OUTLOOK\n\n=\n$$" self.setProgressPercentage(100) self.progressMessage(0, 100, self._displayName + " Complete") return fcst ######################################################################## # PRODUCT-SPECIFIC METHODS ######################################################################## def _issuance_list(self, argDict): seriesDefAM = [ ("FirePeriod", "period1"), ("FirePeriod", 12), ("FirePeriod", 12), ] seriesDefPM = [ ("FirePeriod", "period1"), ("FirePeriod", 12), ("FirePeriod", 12), ("FirePeriod", 12) ] return [ ("Morning", self.DAY(), self.NIGHT(), self.NIGHT(), ".TODAY...\n", "early in the morning", "late in the afternoon", 1, seriesDefAM), ("Morning Update", "issuanceHour", self.NIGHT(), self.NIGHT(), ".REST OF TODAY...\n", "early in the morning", "late in the afternoon", 1, seriesDefAM), ("Afternoon Update", "issuanceHour", self.NIGHT(), self.NIGHT(), ".REST OF TODAY...\n", "early in the morning","late in the afternoon", 1, seriesDefAM), # End times are tomorrow: ("Afternoon", self.NIGHT(), 24 + self.DAY(), 24 + self.DAY(), ".TONIGHT...\n", "late in the night", "early in the evening", 1, seriesDefPM), ("Evening Update", "issuanceHour", 24 + self.DAY(), 24 + self.DAY(), ".REST OF TONIGHT...\n", "late in the night","early in the evening", 1, seriesDefPM), # For the early morning update, this produces: # REST OF TONIGHT: # MONDAY # MONDAY NIGHT ("Early Morning Update", "issuanceHour", 24 + self.DAY(), 24 + self.DAY(), ".REST OF TONIGHT...\n", "early in the morning","late in the afternoon", 0, seriesDefPM), ] def lateDay_descriptor(self, statDict, argDict, timeRange): # If time range is in the first period, return period1 descriptor for # late day -- default 3pm-6pm if self._issuanceInfo.period1TimeRange().contains_tr(timeRange): return self._issuanceInfo.period1LateDayPhrase() else: return "late in the afternoon" def lateNight_descriptor(self, statDict, argDict, timeRange): # If time range is in the first period, return period1 descriptor for # late night -- default 3am-6am if self._issuanceInfo.period1TimeRange().contains_tr(timeRange): return self._issuanceInfo.period1LateNightPhrase() else: return "early in the morning" def _createLabel(self, timeRange, currentLocalTime, shift, index): # Make a label given the timeRange in GMT and the shift to # convert it to local time. currentLocalTime can be used to # compare to current day. if timeRange.duration() <= 3600: return "" if index == 0: return self._issuanceInfo.period1Label() today = self._issuanceInfo.todayFlag() label = self.getWeekday(timeRange, holidays=1, shiftToLocal=1, labelType="CapitalWithPeriod", today=today, tomorrow=0) #print label, timeRange.startTime(), self._extendedStart if timeRange.startTime() < self._extendedStart: return label + "\n" else: return label def _makeTempPopTable(self, areaLabel, argDict): # For each area in the areaLabel group of Combinations, # For each city # Generate a TempPopTable exec "import " + self._seriesUtil exec "combinations = "+ self._seriesUtil + ".Combinations" for seriesList, label in combinations: if label == areaLabel: break exec "import " + self._cityDictionary exec "cityDict = " + self._cityDictionary + ".CityDictionary" table = "" # Show title only before first city argDict["_showTempPopTitle"]= 1 for area in seriesList: cities = cityDict[area] for city, cityLabel in cities: table = table + self.generateProduct( "TempPopTable_for_AreaFcst_Local", argDict, city, self._timeRange, areaLabel=cityLabel) argDict["_showTempPopTitle"]= 0 table = "\n" + table + "\n\n" return table def HighlightPhrase(self): return { "type": "phrase", "displayName": None, "lineLength": 69, "analysisList":[ ("Highlights", self.avg_byTimeRange), ], "textRules":[ self.highlights_phrase, ], "autoSentence": 1, "autoUpperCase": 0, }