# ---------------------------------------------------------------------------- # 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. # # FWF_Local # Local customizations for FWF as Base class # # Author: Gibson # # NEw version for RPP21.1 8/03 # #------------------------------------------------------------------------- # Edit Areas Needed: # Be sure to define these additional areas to run the FWF: # For local effects: AboveElev, BelowElev # Define edit areas with an appropriate elevation (e.g. in CO, it is 11000 for timberline). # This will be used to report local effects for temperature and RH. # FireArea: An edit area for the summary extended forecast. #------------------------------------------------------------------------- import FWF import TextRules import string, time, re, os, types, copy, AFPS class TextProduct(FWF.TextProduct): Definition = copy.deepcopy(FWF.TextProduct.Definition) # REQUIRED CONFIGURATION ITEMS Definition['displayName'] = "None" ## Definition['displayName'] = "FWF" Definition["outputFile"] = "/ifps/rpp-21/products/TEXT/FWF.txt" Definition["defaultEditAreas"] = [ ("Fire1Near5000", "FIRE AREA 1"), ("Fire2Near5000", "FIRE AREA 2"), ("Fire3Near3000", "FIRE AREA 3"), ] # Header configuration items Definition["productName"] = "FIRE WEATHER FORECAST" # name of product Definition["fullStationID"] = "KSLC" # full station identifier (4letter) Definition["wmoID"] = "FNUS55" # WMO ID Definition["pil"] = "WRKFWF" # product pil Definition["zoneCode"] = "UTZ001>021" # Zone Code, such as "GAZ025-056" Definition["stateName"] = "UTAH" # Name of state, such as "GEORGIA" Definition["wfoCity"] = "SALT LAKE CITY" # Location of WFO - city name Definition["wfoState"] = "UT" # Location of WFO - state name # OPTIONAL CONFIGURATION ITEMS Definition["awipsProductID"] = "" # Product ID for storing to AWIPS. Definition["includeHeader"] = 1 # If 1, include the product header. Definition["useRegionLabel"] = 0 # If 1, use region label for headers. Definition["includeIssuanceType"] = 0 # If 1, an issuance type button will appear on dialog # to handle updates and corrections. #Definition["periodCombining"] = 1 # If 1, do period combining Definition["useRH"] = 0 # Use RH grids instead of MaxRH, MinRH Definition["summaryExtended"] = 0 Definition["summaryArea"] = "CWA" Definition["individualExtended"] = 1 Definition["extendedLabel"] = 1 Definition["useHolidays"] = 0 # Will use holidays in time period labels Definition["includeTrends"] = 0 # Set to 1 to include Temp and RH trends # Set the following variable to 1 if you want Lightning Activity # reported with phrases like "1-8 STRIKES", "9-15 STRIKES", etc. #Definition["lightningPhrases"] = 1 # The following variable sets a wind adjustment factor for surface # (20 ft) winds. Wind speeds will be multiplied by this factor. # Winds reported by RAWS sites are frequently lower than ASOS winds # due to the fact that they use a 10-min average. A common adjustment # factor is 80% (0.80). If you want no adjustment to the winds # then set this variable to 1.00 Definition["windAdjustmentFactor"] = 1.00 Definition["includeMultipleElementTable"] = 0 # Will include a MultipleElementTable Definition["includeMultipleElementTable_perPeriod"] = 0 # Will include a MultipleElementTable # per area per period. # ("singleValueFormat" must be 1) # Uncomment just one elementList below #Definition["elementList"] = ["Temp", "PoP"] # Default #Definition["elementList"] = ["Temp", "Humidity", "PoP"] #Definition["singleValueFormat"] = 1 # Default is 0 #Definition["areaDictionary"] = "AreaDictionary_FWF" # For product headers #Definition["language"] = "english" # Sampling Performance #Definition["sampleFromServer"] = 1 # If 1, sample directly from server # Trouble-shooting items #Definition["passLimit"] = 20 # Limit on passes allowed through # Narrative Tree #Definition["trace"] = 1 # Set to 1 to turn on trace through # Narrative Tree for trouble-shooting def __init__(self): FWF.TextProduct.__init__(self) wordsArray = [] TempRHwordsArray = [] noTrendsArray = [] def null_nlValue_dict(self, tree, node): dict = TextRules.TextRules.null_nlValue_dict(self, tree, node) dict["Wind"] = 15 ## no specific winds below 15 mph. return dict # NULL value phrases def first_null_phrase_dict(self, tree, node): # Phrase to use if values THROUGHOUT the period or # in the first period are Null (i.e. below threshold OR NoWx) # E.g. LIGHT WINDS. or LIGHT WINDS BECOMING N 5 MPH. dict = TextRules.TextRules.first_null_phrase_dict(self, tree, node) dayNight = self.getPeriod(node.getTimeRange(),1) phrase = "light winds" if dayNight == self.DAYTIME(): phrase = "upslope/upvalley 5-10 mph" else: phrase = "downslope/downvalley 5-10 mph" componentName = node.getComponent().get("name") if componentName == "FireExtendedShortTerm": phrase = "winds generally less than 15 mph" dict["Wind"] = phrase return dict def null_phrase_dict(self, tree, node): # Phrase to use for null values in subPhrases other than the first # Can be an empty string # E.g. "NORTH WINDS 20 to 25 KNOTS BECOMING LIGHT" dict = TextRules.TextRules.null_phrase_dict(self, tree, node) dayNight = self.getPeriod(node.getTimeRange(),1) phrase = "" if dayNight == self.DAYTIME(): phrase = "upslope/upvalley 5-10 mph" else: phrase = "downslope/downvalley 5-10 mph" dict["Wind"] = phrase return dict def value_connector_dict(self, tree, node): dict = TextRules.TextRules.value_connector_dict(self, tree, node) dict["Wind"] = "-" return dict def units_descriptor_dict(self, tree, node): # Threshold for reporting null values # Units depend on the element and product dict = TextRules.TextRules.units_descriptor_dict(self, tree, node) unitsDict = dict["units"] unitDict = dict["unit"] # Uncomment these lines if you want the "F" label on the temperatures #unitsDict["F"] = "F" #unitDict["F"] = "F" #dict["units"] = unitsDict #dict["unit"] = unitDict return dict def includeSkyRanges_flag(self, tree, node): # Set to 0 if you do not want ranges reported with sky phrases: # Partly cloudy (35-40 PERCENT) return 1 def untilPhrasing_flag_dict(self, tree, node): # If set to 1, "until" time descriptor phrasing will be used. # E.g. "NORTH WINDS 20 MPH UNTIL 10 AM...THEN 35 MPH" # # NOTE: Be sure to increase the temporal resolution by # overriding "getFirePeriod_analysisList" from the FWF standard file. # E.g. ("MixHgt", self.minMax, [0]), dict = TextRules.TextRules.untilPhrasing_flag_dict(self, tree, node) dict["LAL"] = 1 componentName = node.getComponent().get("name") if componentName == "FirePeriod": dict["Sky"] = 1 dict["Wx"] = 1 return dict def ridgeValleyAreas(self, tree, node): # List of edit area names for which we want # ridge/valley winds reported: # # 20-FOOT WINDS... # VALLEYS/LWR SLOPES... # RIDGES/UPR SLOPES.... # # e.g. #return ["Area1"] return [] def getFirePeriod_intersectAreas(self): return [] # Configurable Weather Values def wxCoverageDescriptors(self): list = TextRules.TextRules.wxCoverageDescriptors(self) #list.append(("Chc", "*", "*", "a chance")) return list def wxTypeDescriptors(self): list = TextRules.TextRules.wxTypeDescriptors(self) #list.append( ("*", "T", "*", "Dry", "dry thunderstorms") ) #list.append( ("*", "RW", "*", "*", "rain showers") ) return list def wxAttributeDescriptors(self): list = TextRules.TextRules.wxAttributeDescriptors(self) #list.append( ("*", "T", "*", "Dry", "") ) return list def wxIntensityDescriptors(self): list = TextRules.TextRules.wxIntensityDescriptors(self) #list.append(("*", "RW", "--", "*", "light")) return list def wxCombinations(self): # This is the list of which wxTypes should be combined into one. # For example, if ("RW", "R") appears, then wxTypes of "RW" and "R" will # be combined into one key and the key with the dominant coverage will # be used as the combined key. # You may also specify a method which will be # -- given arguments subkey1 and subkey2 and # -- should return # -- a flag = 1 if they are to be combined, 0 otherwise # -- the combined key to be used # Note: The method will be called twice, once with (subkey1, subkey2) # and once with (subkey2, subkey1) so you can assume one ordering. # See the example below, "combine_T_RW" # return [ ("RW", "R"), ("SW", "S"), ## self.combine_T_RW, ] def combine_T_RW(self, subkey1, subkey2): # Combine T and RW only if the coverage of T # is dominant over the coverage of RW wxType1 = subkey1.wxType() wxType2 = subkey2.wxType() if wxType1 == "T" and wxType2 == "RW": order = self.dominantCoverageOrder(subkey1, subkey2) if order == -1 or order == 0: return 1, subkey1 return 0, None def _PoP_analysisMethod(self, componentName): # Alternative PoP analysis methods for consistency between PoP and Wx return self.stdDevMaxAvg #return maximum # Use "mode" if you have non-continuous PoP values #return mode ### Above is FWF_Local ### Below are additions and overrides. def getFirePeriod_additionalAreas(self): return [ ("Sky", ["Fire1Near8000","Fire2Near8000","Fire3Near8000"]), ("Wx", ["Fire1Near8000","Fire2Near8000","Fire3Near8000"]), ("LAL", ["Fire1Near8000","Fire2Near8000","Fire3Near8000"]), ("CWR", ["Fire1Near8000","Fire2Near8000","Fire3Near8000"]), ] def FireExtendedShortTerm_additionalAreas(self): return [ ("Sky", ["Fire1Near8000","Fire2Near8000","Fire3Near8000"]), ("Wx", ["Fire1Near8000","Fire2Near8000","Fire3Near8000"]), ("MaxT", ["Fire1Near8000","Fire2Near8000","Fire3Near8000"]), ("MinT", ["Fire1Near8000","Fire2Near8000","Fire3Near8000"]), ("Wind", ["Fire1Near8000","Fire2Near8000","Fire3Near8000"]), ] def FireExtended_additionalAreas(self): return [ ("Sky", ["Fire1Near8000","Fire2Near8000","Fire3Near8000"]), ("Wx", ["Fire1Near8000","Fire2Near8000","Fire3Near8000"]), ("MaxT", ["Fire1Near8000","Fire2Near8000","Fire3Near8000"]), ("MinT", ["Fire1Near8000","Fire2Near8000","Fire3Near8000"]), ] ### added "additionalAreas" def FirePeriod(self): phraseList = self.getFirePeriod_phraseList() analysisList = self.getFirePeriod_analysisList() intersectAreas = self.getFirePeriod_intersectAreas() additionalAreas = self.getFirePeriod_additionalAreas() return { "type": "component", "methodList": [ self._assembleIndentedPhrases, ], "analysisList": analysisList, "phraseList": phraseList, "intersectAreas": intersectAreas, "additionalAreas": additionalAreas, } ## removed unneeded elements def getFirePeriod_analysisList(self): analysisList = [ ("Headlines", self.discreteTimeRangesByKey), ("Sky", self.minMax, [0]), ("PoP", self._PoP_analysisMethod("FirePeriod")), ("Wx", self.dominantWx, [0]), ("LAL", self.maximum, [0]), ("MaxT", self.stdDevMinMax), ("MinT", self.stdDevMinMax), ("MaxRH", self.stdDevMinMax), ("MinRH", self.stdDevMinMax), ("MaxT", self.mode), # for trends ("MinT", self.mode), # for trends ("MaxRH", self.mode), # for trends ("MinRH", self.mode), # for trends ("Ttrend", self.stdDevMinMax), # for trends ("RHtrend", self.stdDevMinMax), # for trends ("Wind", self.vectorMedianRange, [6]), ("Haines", self.avg), ("CWR", self.maximum, [0]), ] return analysisList def getFirePeriod_phraseList(self): if self._useRH: dayRH = "RH" nightRH = "RH" else: dayRH = "MinRH" nightRH = "MaxRH" phraseList = [ self.wind_summary, self.skyWeather_byTimeRange_compoundPhrase, (self.dayOrNight_phrase, ["MaxT", "MinT", 1, 1], self._tempLocalEffects_list()), (self.dayOrNight_phrase, [dayRH, nightRH, 1, 1], self._rhLocalEffects_list()), self.fireWind_compoundPhrase, self.fireWind_label_phrase, self.fireWind_Ridge_compoundPhrase, (self.lal_phrase,self._lal_localeffects_list), (self.cwr_phrase,self._cwr_localeffects_list), self.haines_phrase, ] # Remove trend methods if self._includeTrends != 1: newList = [] for phrase in phraseList: if type(phrase) is types.TupleType: phraseMethod = phrase[0] if phraseMethod == self.trend_DayOrNight_phrase: continue newList.append(phrase) phraseList = newList # Add multipleElementTable if self._includeMultipleElementTable_perPeriod: phraseList.append(self.multipleElementTable_perPeriod_phrase) return phraseList #### modified for compund local effects for sky/weather def skyWeather_byTimeRange_compoundPhrase(self): return { "phraseList": [ (self.fireSky_phrase,self._sky_localeffects_list), (self.weather_phrase,self._wx_localeffects_list), ], "phraseMethods": [ self.assembleSentences, self.skyWeather_finishUp, ], } ### reduce detail in extended weather def FireExtendedShortTerm(self): return { "type": "component", "methodList": [ self.assemblePhrases, self.wordWrap, ], "analysisList": [ ("MinT", self.firstAvg), ("MaxT", self.avg), ("T", self.hourlyTemp), ("Sky", self.avg, [24]), ## ("Wind", self.vectorAvg), ("Wind",self.vectorMax), ## ("Wind20ft", self.vectorAvg), ("Wx", self.dominantWx, [24]), ("PoP", self._PoP_analysisMethod("FireExtendedShortTerm")), ], "phraseList":[ self.reportTrends, self.wind_summary, ## self.skyPopWx_phrase, (self.sky_phrase,self._xtnd_sky_localeffects_list), (self.weather_phrase,self._xtnd_wx_localeffects_list), (self.lows_phrase, self._T_localeffects_list), (self.highs_phrase, self._T_localeffects_list), (self.wind_phrase, self._xtnd_Wind_localeffects_list), ], "additionalAreas": self.FireExtendedShortTerm_additionalAreas(), } ### removed skyPopWx phrase def FireExtended(self): return { "type": "component", "methodList": [ self.assemblePhrases, self.wordWrap, ], "analysisList": [ ("MinT", self.firstAvg), ("MaxT", self.avg), ("T", self.hourlyTemp), ("Sky", self.avg, [24]), ("Wx", self.dominantWx, [24]), ("PoP", self._PoP_analysisMethod("FireExtended")), ], "phraseList":[ self.reportTrends, ## self.skyPopWx_phrase, (self.sky_phrase,self._xtnd_sky_localeffects_list), (self.weather_phrase,self._xtnd_wx_localeffects_list), (self.lows_phrase,self._T_localeffects_list), (self.highs_phrase,self._T_localeffects_list), ], "additionalAreas": self.FireExtended_additionalAreas(), } ##### Short term Local effects def _wx_localeffects_list(self,tree,node): if self.currentAreaContains(tree, ["Fire1Near5000"]): leArea1 = self.LocalEffectArea("__Current__", "in the valleys", intersectFlag=0) leArea2 = self.LocalEffectArea("Fire1Near8000", " in the mountains", intersectFlag=0) return [self.LocalEffect([leArea1,leArea2],0," and ")] elif self.currentAreaContains(tree, ["Fire2Near5000"]): leArea1 = self.LocalEffectArea("__Current__", "in the valleys", intersectFlag=0) leArea2 = self.LocalEffectArea("Fire2Near8000", " in the mountains", intersectFlag=0) return [self.LocalEffect([leArea1,leArea2],0," and ")] elif self.currentAreaContains(tree, ["Fire3Near3000"]): leArea1 = self.LocalEffectArea("__Current__", "in the valleys", intersectFlag=0) leArea2 = self.LocalEffectArea("Fire3Near8000", " in the mountains", intersectFlag=0) return [self.LocalEffect([leArea1,leArea2],0," and ")] else: return[] def _sky_localeffects_list(self,tree,node): if self.currentAreaContains(tree, ["Fire1Near5000"]): leArea1 = self.LocalEffectArea("__Current__", "in the valleys", intersectFlag=0) leArea2 = self.LocalEffectArea("Fire1Near8000", " in the mountains", intersectFlag=0) return [self.LocalEffect([leArea1,leArea2],30," and ")] elif self.currentAreaContains(tree, ["Fire2Near5000"]): leArea1 = self.LocalEffectArea("__Current__", "in the valleys", intersectFlag=0) leArea2 = self.LocalEffectArea("Fire2Near8000", " in the mountains", intersectFlag=0) return [self.LocalEffect([leArea1,leArea2],30," and ")] elif self.currentAreaContains(tree, ["Fire3Near3000"]): leArea1 = self.LocalEffectArea("__Current__", "in the valleys", intersectFlag=0) leArea2 = self.LocalEffectArea("Fire3Near8000", " in the mountains", intersectFlag=0) return [self.LocalEffect([leArea1,leArea2],30," and ")] else: return[] def _lal_localeffects_list(self,tree,node): if self.currentAreaContains(tree, ["Fire1Near5000"]): leArea1 = self.LocalEffectArea("__Current__", "in the valleys", intersectFlag=0) leArea2 = self.LocalEffectArea("Fire1Near8000", " in the mountains", intersectFlag=0) return [self.LocalEffect([leArea1,leArea2],0," and ")] elif self.currentAreaContains(tree, ["Fire2Near5000"]): leArea1 = self.LocalEffectArea("__Current__", "in the valleys", intersectFlag=0) leArea2 = self.LocalEffectArea("Fire2Near8000", " in the mountains", intersectFlag=0) return [self.LocalEffect([leArea1,leArea2],0," and ")] elif self.currentAreaContains(tree, ["Fire3Near3000"]): leArea1 = self.LocalEffectArea("__Current__", "in the valleys", intersectFlag=0) leArea2 = self.LocalEffectArea("Fire3Near8000", " in the mountains", intersectFlag=0) return [self.LocalEffect([leArea1,leArea2],0," and ")] else: return[] def _cwr_localeffects_list(self,tree,node): if self.currentAreaContains(tree, ["Fire1Near5000"]): leArea1 = self.LocalEffectArea("__Current__", "in the valleys", intersectFlag=0) leArea2 = self.LocalEffectArea("Fire1Near8000", " in the mountains", intersectFlag=0) return [self.LocalEffect([leArea1,leArea2],10," and ")] elif self.currentAreaContains(tree, ["Fire2Near5000"]): leArea1 = self.LocalEffectArea("__Current__", "in the valleys", intersectFlag=0) leArea2 = self.LocalEffectArea("Fire2Near8000", " in the mountains", intersectFlag=0) return [self.LocalEffect([leArea1,leArea2],10," and ")] elif self.currentAreaContains(tree, ["Fire3Near3000"]): leArea1 = self.LocalEffectArea("__Current__", "in the valleys", intersectFlag=0) leArea2 = self.LocalEffectArea("Fire3Near8000", " in the mountains", intersectFlag=0) return [self.LocalEffect([leArea1,leArea2],10," and ")] else: return[] def _Wind_localeffects_list(self,tree,node): if self.currentAreaContains(tree, ["Fire1Near5000"]): leArea1 = self.LocalEffectArea("__Current__", "in the valleys", intersectFlag=0) leArea2 = self.LocalEffectArea("Fire1Near8000", " in the mountains", intersectFlag=0) return [self.LocalEffect([leArea1,leArea2],5," and ")] elif self.currentAreaContains(tree, ["Fire2Near5000"]): leArea1 = self.LocalEffectArea("__Current__", "in the valleys", intersectFlag=0) leArea2 = self.LocalEffectArea("Fire2Near8000", " in the mountains", intersectFlag=0) return [self.LocalEffect([leArea1,leArea2],5," and ")] elif self.currentAreaContains(tree, ["Fire3Near3000"]): leArea1 = self.LocalEffectArea("__Current__", "in the valleys", intersectFlag=0) leArea2 = self.LocalEffectArea("Fire3Near8000", " in the mountains", intersectFlag=0) return [self.LocalEffect([leArea1,leArea2],5," and ")] else: return[] ### Extended local effects - Only need for minor spacing issues when the phrases are assembled. def _xtnd_wx_localeffects_list(self,tree,node): if self.currentAreaContains(tree, ["Fire1Near5000"]): leArea1 = self.LocalEffectArea("__Current__", "in the valleys", intersectFlag=0) leArea2 = self.LocalEffectArea("Fire1Near8000", "in the mountains", intersectFlag=0) return [self.LocalEffect([leArea1,leArea2],0," and ")] elif self.currentAreaContains(tree, ["Fire2Near5000"]): leArea1 = self.LocalEffectArea("__Current__", "in the valleys", intersectFlag=0) leArea2 = self.LocalEffectArea("Fire2Near8000", "in the mountains", intersectFlag=0) return [self.LocalEffect([leArea1,leArea2],0," and ")] elif self.currentAreaContains(tree, ["Fire3Near3000"]): leArea1 = self.LocalEffectArea("__Current__", "in the valleys", intersectFlag=0) leArea2 = self.LocalEffectArea("Fire3Near8000", "in the mountains", intersectFlag=0) return [self.LocalEffect([leArea1,leArea2],0," and ")] else: return[] def _xtnd_sky_localeffects_list(self,tree,node): if self.currentAreaContains(tree, ["Fire1Near5000"]): leArea1 = self.LocalEffectArea("__Current__", "in the valleys", intersectFlag=0) leArea2 = self.LocalEffectArea("Fire1Near8000", "in the mountains", intersectFlag=0) return [self.LocalEffect([leArea1,leArea2],self.Sky_test," and ")] elif self.currentAreaContains(tree, ["Fire2Near5000"]): leArea1 = self.LocalEffectArea("__Current__", "in the valleys", intersectFlag=0) leArea2 = self.LocalEffectArea("Fire2Near8000", "in the mountains", intersectFlag=0) return [self.LocalEffect([leArea1,leArea2],self.Sky_test," and ")] elif self.currentAreaContains(tree, ["Fire3Near3000"]): leArea1 = self.LocalEffectArea("__Current__", "in the valleys", intersectFlag=0) leArea2 = self.LocalEffectArea("Fire3Near8000", "in the mountains", intersectFlag=0) return [self.LocalEffect([leArea1,leArea2],self.Sky_test," and ")] else: return[] def _xtnd_Wind_localeffects_list(self,tree,node): if self.currentAreaContains(tree, ["Fire1Near5000"]): leArea1 = self.LocalEffectArea("__Current__", "in the valleys", intersectFlag=0) leArea2 = self.LocalEffectArea("Fire1Near8000", "in the mountains", intersectFlag=0) return [self.LocalEffect([leArea1,leArea2],5," and ")] elif self.currentAreaContains(tree, ["Fire2Near5000"]): leArea1 = self.LocalEffectArea("__Current__", "in the valleys", intersectFlag=0) leArea2 = self.LocalEffectArea("Fire2Near8000", "in the mountains", intersectFlag=0) return [self.LocalEffect([leArea1,leArea2],5," and ")] elif self.currentAreaContains(tree, ["Fire3Near3000"]): leArea1 = self.LocalEffectArea("__Current__", "in the valleys", intersectFlag=0) leArea2 = self.LocalEffectArea("Fire3Near8000", "in the mountains", intersectFlag=0) return [self.LocalEffect([leArea1,leArea2],5," and ")] else: return[] #### T local effects are only needed in the extended def _T_localeffects_list(self,tree,node): if self.currentAreaContains(tree, ["Fire1Near5000"]): leArea1 = self.LocalEffectArea("__Current__", "in the valleys", intersectFlag=0) leArea2 = self.LocalEffectArea("Fire1Near8000", "in the mountains", intersectFlag=0) return [self.LocalEffect([leArea1,leArea2],0," and ")] elif self.currentAreaContains(tree, ["Fire2Near5000"]): leArea1 = self.LocalEffectArea("__Current__", "in the valleys", intersectFlag=0) leArea2 = self.LocalEffectArea("Fire2Near8000", "in the mountains", intersectFlag=0) return [self.LocalEffect([leArea1,leArea2],0," and ")] elif self.currentAreaContains(tree, ["Fire3Near3000"]): leArea1 = self.LocalEffectArea("__Current__", "in the lower valleys", intersectFlag=0) leArea2 = self.LocalEffectArea("Fire3Near8000", "in the mountains", intersectFlag=0) return [self.LocalEffect([leArea1,leArea2],0," and ")] else: return[] def Sky_test(self, tree, node, localEffect, leArea1Label, leArea2Label): ## checks the current area and local effect sky condition WORDS to ## decide wether to fire local effects. Only works for fire extended which ## averages over entire 24 hours. ## background info statDict = node.getStatDict() timeRange = node.parent.getTimeRange() dayNight = self.getPeriod(timeRange, 1) ## get _current_ area sky words currentAreaStats = self.getStats(statDict, "Sky") currentAreasky = self.getValue(currentAreaStats) ## round to nearest 5 currentAreasky = self.round(currentAreasky, "Nearest",5) currentSkyWords = self.sky_value(tree, node, currentAreasky, dayNight) ## get LE area sky words LEAreaStats = tree.stats.get("Sky", timeRange, leArea1Label) LEAreaskytuple = LEAreaStats[0] LESky = LEAreaskytuple[0] LESky = self.round(LESky, "Nearest",5) LEAreaWords = self.sky_value(tree, node, LESky, dayNight) ## print "curr sky/words: ", currentAreasky, currentSkyWords ## print "LE sky/words: ", LESky, LEAreaWords if currentSkyWords == LEAreaWords: return 0 else: return 1 ##################### end local effects setup def hainesDict(self): return { 0:"..very low", 1:"..very low", 2:"..very low", 3:"..very low", 4:"..low", 5:"..moderate", 6:"..high", } ## section below overridden from ConfigVariables def units_descriptor_dict(self, tree, node): # Dictionary of descriptors for various units return { "units": { "percent":"%", "%":"%", ## changed this "mph":"mph", }, "unit": { "percent":"%", "%":"%", # and this "mph":"mph", }, } def maximum_range_nlValue_dict(self, tree, node): # Maximum range to be reported within a phrase # e.g. 5 to 10 mph # Units depend on the product dict = TextRules.TextRules.maximum_range_nlValue_dict(self,tree, node) ## return {} dict["Wind"] = { (0, 5) : 0, (5, 13) : 5, (13, 30) : 10, "default" : 15, } return dict # Descriptors: Phrase, Unit, Time periods # Connecting phrases def phrase_descriptor_dict(self, tree, node): ## # Dictionary of descriptors for various weather elements in phrases ## # The value for an element may be a phrase or a method ## # If a method, it will be called with arguments: ## # tree, node, key, element ## dict = TextRules.TextRules.phrase_descriptor_dict(self, tree, node) componentName = node.getComponentName() if componentName == "FirePeriod": dict["Wind"]="" else: dict["Wind"]="winds" #### dict["longTermWind"]="Winds" dict["20-FOOT WINDS......."]="20-FOOT WINDS.......SLOPE/VALLEY..." dict["RIDGE WINDS......."]=" RIDGETOPS......" dict["unchanged"]="no change" dict["CWR................."]="CWR................." dict["MinT_FireWx"]="" dict["MaxT_FireWx"]="" dict["MinRH_FireWx"]="" dict["MaxRH_FireWx"]="" return dict def cwr_words(self, tree, node) : "Create phrase Probability of Precipitation" statDict = node.getStatDict() cwr = self.getStats(statDict, "CWR") if cwr is None: return self.setWords(node.parent, "MISSING") cwr = self.getValue(cwr) cwr = int(self.round(cwr, "Nearest",10)) threshold = self.nlValue(self.null_nlValue( tree, node, "CWR", "CWR"), cwr) if int(cwr) < threshold: return self.setWords(node, "null") else: words = `int(cwr)` + "%" ##################### words = `int(cwr)` + " percent" return self.setWords(node, words) def wxCoverageDescriptors(self): # This is the list of coverages, wxTypes, intensities for which special # weather coverage wording is desired. Wildcards (*) can be used to match any value. # If a weather subkey is not found in this list, default wording # will be used from the Weather Definition in the server. # The format of each tuple is: # (coverage, wxType, intensity, weatherType descriptor) # For example: #return [ # ("Chc", "*", "*", "a chance"), # ] return [("Chc", "*", "*", "*", "a chance of"), ("SChc", "*", "*", "*","a slight chance of")] def addSkyRange(self, tree, node, words, avg): ### added "cloud cover" # Add range if desired if self.includeSkyRanges_flag(tree, node): roundAvg = int(self.round(avg, "Nearest", 5)) if roundAvg < 5: min = 0 else: min = roundAvg-5 if roundAvg >= 100: max = 100 else: max = roundAvg + 5 units = self.units_descriptor(tree, node, "units", "%") words = words + " (" + `min` + "-" + `max` + "% cloud cover)" return words ################################# Patch 3056 ############ pathc for RPP21.1 def reportTrends_valueStr(self, tree, node, diff): # Given a difference between current and 24-hour prior # MaxT or MinT grids, report a trend. if diff > 15 and diff < 25: return "warmer" elif diff >= 25: return "much warmer" elif diff < -15 and diff > -25: return "cooler" elif diff <= -25: return "much colder" else: return "" ########### end patch ########################## patch 3118 def nlValue(self, nlValue, lookupValue): # Apply a non-linear value to the given value # nlValue might be a dictionary to be applied to value # OR it could be a simple constant if type(nlValue) is types.DictType: # Dictionary lookup result = None if nlValue.has_key('default'): result = nlValue['default'] dictkeys = nlValue.keys() for key in dictkeys: if((lookupValue >= key[0]) and (lookupValue < key[1])): return nlValue[key] if result is None: msgString = """ILLEGAL NON-LINEAR THRESHOLD dictionary. No dictionary entry for value: """ + `lookupValue` + """ Make sure your non-linear threshold dictionaries do not have "gaps" in the ranges. For example, your dictionary should look like this: def maximum_range_nlValue_dict(self, tree, node): ### ConfigVariables # Maximum range to be reported within a phrase # e.g. 5 to 10 mph # Units depend on the product dict = TextRules.TextRules.maximum_range_nlValue_dict(self,tree, node) dict["Wind"] = { (0, 5) : 0, (5, 13) : 5, (13, 28) : 10, "default" : 15, } return dict NOT this: def maximum_range_nlValue_dict(self, tree, node): ### ConfigVariables # Maximum range to be reported within a phrase # e.g. 5 to 10 mph # Units depend on the product dict = TextRules.TextRules.maximum_range_nlValue_dict(self,tree, node) dict["Wind"] = { (0, 4) : 0, (5, 12) : 5, (13, 27) : 10, "default" : 15, } return dict """ raise ValueError, msgString return result elif type(nlValue) is types.MethodType: return nlValue(lookupValue) else: # Constant value return nlValue ##################### end patch ############### override gen forecast to reset period counter to zero each pass 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: ###### added next 2 lines self.AreaLabel = areaLabel self.period = 0 ## counter for getting ridge winds self.otherperiod = 0 ## counter for other data from FWFTable 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 ###### adding a ghost period since chapter defines fire weather "outlook" ###### periods as midnight to midnight. def _issuance_list(self, argDict): narrativeDefAM = [ ("FirePeriod", "period1"), ("FirePeriod", 12), ("FirePeriod", 12),("Phantom", 6) ] narrativeDefPM = [ ("FirePeriod", "period1"), ("FirePeriod", 12), ("FirePeriod", 12), ("FirePeriod", 12), ("Phantom", 6) ] extended = [ ("FireExtendedShortTerm", 24), ("FireExtendedShortTerm", 24), ("FireExtendedShortTerm", 24), ("FireExtended", 24), ("FireExtended", 24), ] if self._individualExtended == 1: if self._extendedLabel == 1: narrativeDefAM.append(("ExtendedLabel",0)) narrativeDefPM.append(("ExtendedLabel",0)) narrativeDefAM = narrativeDefAM + extended narrativeDefPM = narrativeDefPM + extended return [ ("Morning", self.DAY(), self.NIGHT(), self.NIGHT(), ".TODAY...", "early in the morning", "late in the afternoon", 1, narrativeDefAM), ("Morning Update", "issuanceHour", self.NIGHT(), self.NIGHT(), ".REST OF TODAY...", "early in the morning", "late in the afternoon", 1, narrativeDefAM), ("Afternoon Update", "issuanceHour", self.NIGHT(), self.NIGHT(), ".REST OF TODAY...", "early in the morning","late in the afternoon", 1, narrativeDefAM), # End times are tomorrow: ("Afternoon", self.NIGHT(), 24 + self.DAY(), 24 + self.DAY(), ".TONIGHT...", "late in the night", "early in the evening", 1, narrativeDefPM), ("Evening Update", "issuanceHour", 24 + self.DAY(), 24 + self.DAY(), ".REST OF TONIGHT...", "late in the night","early in the evening", 1, narrativeDefPM), # For the early morning update, this produces: # REST OF TONIGHT: # MONDAY # MONDAY NIGHT ("Early Morning Update", "issuanceHour", self.DAY(), self.DAY(), ".REST OF TONIGHT...", "early in the morning","late in the afternoon", 0, narrativeDefPM), # Alternative # For the early morning update, this produces: # EARLY THIS MORNING: # TODAY # TONIGHT #("Evening Update", "issuanceHour", 24 + self.DAY(), 24 + self.DAY(), # ".REST OF TONIGHT...", "late in the night", "early in the evening", # 1, narrativeDefPM), #("Early Morning Update", "issuanceHour", self.DAY(), self.DAY(), # ".EARLY THIS MORNING...", "early in the morning", "late in the afternoon", # 1, narrativeDefPM), ] ##### I decide issuance type based on system time. def _getVariables(self, argDict): # Make argDict accessible self.__argDict = argDict # Get Definition variables self._definition = argDict["forecastDef"] for key in self._definition.keys(): exec "self._" + key + "= self._definition[key]" # Display the dialog for the user to choose issuance additionalItems = [] if self._includeIssuanceType == 1: issuanceTypeEntry = ("Issuance Type" , "Routine", "radio", ["Routine", "Update", "Correction"]) additionalItems = [issuanceTypeEntry] ## status, varDict = self.displayIssuanceDialog( ## self._definition["displayName"], self._issuance_list(argDict), additionalItems, ## argDict) ## if status != "Ok": ## return "Product Cancelled" ## argDict["varDict"] = varDict self._issuanceType = "Routine" if self._includeIssuanceType == 1: self._issuanceType = varDict["Issuance Type"] ## added this section 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 = "Morning" ## self.totalPeriods = 3 ## print "product type: ", self._productType self._narrativeUtil = self._definition["defaultEditAreas"] return None ##### Section for our bullets at the top of the product. def _preProcessProduct(self, fcst, argDict): # Product header self.synopsis = self._getSynopsis() if self._areaName != "": self._areaName = "FOR " + self._areaName if self._issuanceType == "Update": self._areaName = self._areaName + "...UPDATED" elif self._issuanceType == "Correction": self._areaName = self._areaName + "...CORRECTED" fcst = fcst + self._wmoID + " " + self._fullStationID + " " + \ self._ddhhmmTime + "\n" + self._pil + "\n\n" +\ self._productName + self._areaName + "\n" +\ "NATIONAL WEATHER SERVICE " + self._wfoCity + " " + \ self._wfoState + "\n" + self._timeLabel + "\n\n"+ \ "...headline..." + "\n\n" + \ ".BROADCAST DISCUSSION..." + self.synopsis + "\n"+ \ ".DISCUSSION..." + "\n\n" + \ "***THUNDERSTORMS IMPLY STRONG GUSTY AND ERRATIC WINDS***" + "\n" + \ "***ALL WINDS ARE 20 FOOT...10-MINUTE AVERAGES***" + "\n\n" return fcst ##### Make SLC's 3 area headers. def _preProcessArea(self, fcst, editArea, areaLabel, argDict): # This is the header for an edit area combination ## _makeFireAreaHeader defined below special for SLC. areaHeader = self._makeFireAreaHeader( argDict, areaLabel, self._issueTime, self._expireTime, self._areaDictionary, self._defaultEditAreas) fcst = fcst + areaHeader if self._issuanceType == "Update": fcst = fcst + "UPDATED\n\n" elif self._issuanceType == "Correction": fcst = fcst + "CORRECTED\n\n" # Headlines headlines = self.generateProduct("Headlines", argDict, area = editArea, areaLabel=areaLabel, timeRange = self._timeRange) fcst = fcst + headlines return fcst def _makeFireAreaHeader(self, argDict, areaLabel, issueTime, expireTime, areaDictName, defaultEditAreas, cityDescriptor ="INCLUDING THE CITIES OF"): # Make a UGC area header for the given areaLabel # Determine list of areas (there could be more than one if we are using a combination) if areaDictName == None or areaDictName == "None": return areaLabel + "\n" areaList = self.getCurrentAreaNames(argDict, areaLabel) # Access the UGC information for the area(s) if available ## print "areaList: ", areaList expireTimeRange = AFPS.TimeRange(expireTime, expireTime + 3600) expireTime = string.upper(self.timeDisplay(expireTimeRange, "", "", "%d%H%M", "")) #codeString = self.endline(codeString + "-" + expireTime + "-", breakStr="-") codeString = "" nameString = "" header = "" if areaList == ['FIRE AREA 1']: codeString = "UTZ001>011-" codeString = self.endline(codeString + "-" + expireTime + "-", breakStr="-") nameString = "FORECAST AREA 1...NORTHERN UTAH\nZONE 420-GREAT SALT LAKE DESERT " + \ "AND MOUNTAINS...ZONE 421-CACHE\nVALLEY...ZONE 422-NORTHERN WASATCH " + \ "FRONT...ZONE 423-SALT LAKE AND\nTOOELE VALLEYS...ZONE 424-SOUTHERN " + \ "WASATCH FRONT...ZONE 425-WASATCH\nMOUNTAIN VALLEYS...ZONE 426-WASATCH " + \ "MOUNTAINS NORTH OF I-80...ZONE\n427-WASATCH MOUNTAINS SOUTH OF I-80..." + \ "ZONE 428-WESTERN UINTA\nMOUNTAINS...ZONE 429-WEST TAVAPUTS PLATEAU " + \ "AND SURROUNDING\nRANGES...ZONE 430-WESTERN UINTA BASIN" header = codeString + nameString + "\n\n" if areaList == ['FIRE AREA 2']: codeString = "UTZ012>015-" codeString = self.endline(codeString + expireTime + "-", breakStr="-") nameString = "FORECAST AREA 2...CENTRAL UTAH\nZONE 431-CASTLE VALLEY...ZONE 432-" + \ "SAN RAFAEL SWELL AND DESERT...\nZONE 433-SANPETE AND SEVIER VALLEYS" + \ "...ZONE 434-WEST CENTRAL DESERTS\nAND MOUNTAINS...ZONE 436-CENTRAL " + \ "UTAH MOUNTAINS" header = codeString + nameString + "\n\n" if areaList == ['FIRE AREA 3']: codeString = "UTZ016>021-" codeString = self.endline(codeString + expireTime + "-", breakStr="-") nameString = "FORECAST AREA 3...SOUTHWEST UTAH\nZONE 435-SOUTHWEST DESERTS AND " + \ "MOUNTAINS...ZONE 437-SOUTHWEST AND\nSOUTH CENTRAL MOUNTAINS...ZONE " + \ "438-HENRY MOUNTAINS...ZONE 439-UTAHS\nDIXIE AND ZION CANYON...ZONE " + \ "440-441 SOUTH CENTRAL UTAH" header = codeString + nameString + "\n\n" return header def _postProcessArea(self, fcst, editArea, areaLabel, argDict): fcst = fcst + "=\n\n$$\n\n" ## changed from + "=\n$$\n\n" return fcst def _postProcessProduct(self, fcst, argDict): longRange = self._getlongRange() longRangeLabel = self._longRangeLabel() fcst = fcst + ".8-14 DAY OUTLOOK FOR "+ longRangeLabel + "\n" + longRange + "\n=\n$$\n" fcst = string.upper(fcst) fcst = string.replace(fcst, "MPH..", "MPH.") fcst = string.replace(fcst, " %.", ".") fcst = string.replace(fcst, "MAX TEMPERATURE", "\nMAX TEMPERATURE") fcst = string.replace(fcst, "MIN TEMPERATURE", "\nMIN TEMPERATURE") fcst = string.replace(fcst, "20-FOOT", "\n20-FOOT") self.storeAWIPS(fcst, self._awipsProductID) self.setProgressPercentage(100) self.progressMessage(0, 100, self._displayName + " Complete") return fcst def setLabel(self, tree, component): ## removed extra blank line before extended label. component.set("words", ".EXTENDED...\n") return self.DONE() def _getSynopsis(self): # pull synopsis in for brodcast discussion w,r = os.popen2('/ifps/rpp-21/getSynopsis.pl') w.close() phrase = r.read() return phrase def _getlongRange(self): # pull long range data from PMDMRD w,r = os.popen2('/ifps/rpp-21/getPMDMRD.pl') w.close() phrase = r.read() return phrase def _longRangeLabel(self): time1 = time.gmtime() (year, month, day, h, m, s, w, day, dst) = time1 if self._productType == "Afternoon" and h>=6: timeRange = self.createTimeRange(192, 336, "Zulu") label= self.timeDisplay(timeRange, "Zulu", "","%A %B %d", \ " to %A %B %d") else: timeRange = self.createTimeRange(168, 312, "Zulu") label= self.timeDisplay(timeRange, "Zulu", "","%A %B %d", \ " to %A %B %d") return label ############## removed wind_summary def fireWind_compoundPhrase(self): return { "phraseList": [ ## self.wind_summary, self.wind_phrase, ], "phraseMethods": [ self.assembleSentences, self.fireWind_finishUp ], } ######################## Next section does the ridge winds def fireWind_Ridge_compoundPhrase(self): self.period = self.period + 1 self.ridgeWindWords = self._getRidgeWind(self.period) return { "phraseList": [ ## self.wind_summary, self._ridge_wind_phrase, ], "phraseMethods": [ self.assembleSentences, self.fireWind_Ridge_finishUp, ], } def fireWind_Ridge_finishUp(self, tree, node): self.period = self.period + 1 words = self.ridgeWindWords wordsArray = string.split(words, "#") if words is None: return if words == "": words = "MISSING" node.set("descriptor", "") node.set("indentLabel", "RIDGE WINDS.......") node.set("compound", 1) return self.setWords(node, wordsArray[self.period-4]) def _getRidgeWind(self,period): print "getting ridge wind data" w,r = os.popen2('/ifps/rpp-21/getRidgeWind.pl') numperiod = str(self.totalPeriods) period = str(self.period) w.write(numperiod) w.write(period) w.write(self.AreaLabel) #w.write(element) w.close() phrase = r.read() ## print "ridge wind phrase: ", phrase return phrase def _ridge_wind_phrase(self): ##self.period = self.period + 1 return { "setUpMethod": self.wind_setUp, "wordMethod": self._ridge_vector_words, "phraseMethods": self.standard_vector_phraseMethods(), } def _ridge_vector_words(self, tree, node): # Create words for a vector element elementInfo = node.getAncestor("firstElement") if elementInfo is None: return self.setWords(node, "") words = self.ridgeWindWords wordsArray = string.split(words, "#") if words == "null": return self.setWords(node, "null") gustPhrase = "" if words != "": # Add gusts gustFlag = node.getAncestor("gustFlag") if gustFlag == 1: maxMag, dir = tree.stats.get("Wind", node.getTimeRange(), node.getAreaLabel(), mergeMethod="Max") statDict = node.getStatDict() gustStats = self.getStats(statDict, "WindGust") subRange = node.get("timeRange") gustPhrase = self.embedded_gust_phrase( tree, node, gustStats, maxMag, subRange) if self._productType == "Afternoon": return self.setWords(node, wordsArray[self.period-5] + gustPhrase) else: return self.setWords(node, wordsArray[self.period-4] + gustPhrase) ############## Next section for Temps/RH's and trends def _getOtherPhrase(self, elementName): if elementName == "MaxT" or elementName == "MinT": self.otherperiod = self.otherperiod + 1 w,r = os.popen2('/ifps/rpp-21/getOtherFWFData.pl') numperiod = str(self.totalPeriods) period = str(self.otherperiod) w.write(numperiod) w.write(period) w.write(self.AreaLabel) w.write(elementName) w.close() phrase = r.read() return phrase def fire_dayOrNight_words(self, tree, node): ## get extra data from product/TEXT/FWFTable words = "" elementName = node.getAncestor("elementName") self.maxMinAndTrendWords = self._getOtherPhrase(elementName) ## print elementName ## print self.otherperiod TempRHwordsArray = string.split(self.maxMinAndTrendWords, "#") if self.AreaLabel == "FIRE AREA 1" or self.AreaLabel == "FIRE AREA 2": if self.otherperiod == 1 or self.otherperiod == 2: if elementName == "MaxT": words = "MAX TEMPERATURE.....NEAR 5000 FEET... " + TempRHwordsArray[0] + "." + \ "\n NEAR 8000 FEET... " + TempRHwordsArray[1] if elementName == "MinT": words = "MIN TEMPERATURE.....NEAR 5000 FEET... " + TempRHwordsArray[0] + "." + \ "\n NEAR 8000 FEET... " + TempRHwordsArray[1] if elementName == "MaxRH": words = "MAX HUMIDITY........NEAR 5000 FEET... " + TempRHwordsArray[0] + "." + \ "\n NEAR 8000 FEET... " + TempRHwordsArray[1] if elementName == "MinRH": words = "MIN HUMIDITY........NEAR 5000 FEET... " + TempRHwordsArray[0] + "." + \ "\n NEAR 8000 FEET... " + TempRHwordsArray[1] else: ### no trend phrases for periods 3 or 4. noTrendsArray1 = string.split(TempRHwordsArray[0], "TREND...") noTrendsArray2 = string.split(TempRHwordsArray[1], "TREND...") if elementName == "MaxT": words = "MAX TEMPERATURE.....NEAR 5000 FEET... " + noTrendsArray1[0].rstrip() + "." + \ "\n NEAR 8000 FEET... " + noTrendsArray2[0].rstrip() if elementName == "MinT": words = "MIN TEMPERATURE.....NEAR 5000 FEET... " + noTrendsArray1[0].rstrip() + "." + \ "\n NEAR 8000 FEET... " + noTrendsArray2[0].rstrip() if elementName == "MaxRH": words = "MAX HUMIDITY........NEAR 5000 FEET... " + noTrendsArray1[0].rstrip() + "." + \ "\n NEAR 8000 FEET... " + noTrendsArray2[0].rstrip() if elementName == "MinRH": words = "MIN HUMIDITY........NEAR 5000 FEET... " + noTrendsArray1[0].rstrip() + "." + \ "\n NEAR 8000 FEET... " + noTrendsArray2[0].rstrip() else: if self.otherperiod == 1 or self.otherperiod == 2: if elementName == "MaxT": words = "MAX TEMPERATURE.....NEAR 3000 FEET... " + TempRHwordsArray[0] + "." + \ "\n NEAR 5000 FEET... " + TempRHwordsArray[1] + "." + \ "\n NEAR 8000 FEET... " + TempRHwordsArray[2] if elementName == "MinT": words = "MIN TEMPERATURE.....NEAR 3000 FEET... " + TempRHwordsArray[0] + "." + \ "\n NEAR 5000 FEET... " + TempRHwordsArray[1] + "." + \ "\n NEAR 8000 FEET... " + TempRHwordsArray[2] if elementName == "MaxRH": words = "MAX HUMIDITY........NEAR 3000 FEET... " + TempRHwordsArray[0] + "." + \ "\n NEAR 5000 FEET... " + TempRHwordsArray[1] + "." + \ "\n NEAR 8000 FEET... " + TempRHwordsArray[2] if elementName == "MinRH": words = "MIN HUMIDITY........NEAR 3000 FEET... " + TempRHwordsArray[0] + "." + \ "\n NEAR 5000 FEET... " + TempRHwordsArray[1] + "." + \ "\n NEAR 8000 FEET... " + TempRHwordsArray[2] else: ### no trend phrases for periods 3 or 4. noTrendsArray1 = string.split(TempRHwordsArray[0], "TREND...") noTrendsArray2 = string.split(TempRHwordsArray[1], "TREND...") noTrendsArray3 = string.split(TempRHwordsArray[2], "TREND...") if elementName == "MaxT": words = "MAX TEMPERATURE.....NEAR 3000 FEET... " + noTrendsArray1[0].rstrip() + "." + \ "\n NEAR 5000 FEET... " + noTrendsArray2[0].rstrip() + "." + \ "\n NEAR 8000 FEET... " + noTrendsArray3[0].rstrip() if elementName == "MinT": words = "MIN TEMPERATURE.....NEAR 3000 FEET... " + noTrendsArray1[0].rstrip() + "." + \ "\n NEAR 5000 FEET... " + noTrendsArray2[0].rstrip() + "." + \ "\n NEAR 8000 FEET... " + noTrendsArray3[0].rstrip() if elementName == "MaxRH": words = "MAX HUMIDITY........NEAR 3000 FEET... " + noTrendsArray1[0].rstrip() + "." + \ "\n NEAR 5000 FEET... " + noTrendsArray2[0].rstrip() + "." + \ "\n NEAR 8000 FEET... " + noTrendsArray3[0].rstrip() if elementName == "MinRH": words = "MIN HUMIDITY........NEAR 3000 FEET... " + noTrendsArray1[0].rstrip() + "." + \ "\n NEAR 5000 FEET... " + noTrendsArray2[0].rstrip() + "." + \ "\n NEAR 8000 FEET... " + noTrendsArray3[0].rstrip() ## outUnits = self.element_outUnits(tree, node, elementName, elementName) ## print words ## units = self.units_descriptor(tree, node,"units", outUnits) ## words = words + units ## print "after units: ", words dummy = self.setWords(node, words) return words ## return self.setWords(node, words) def postProcessPhrase(self, tree, node): words = node.get("words") rval = None if words is not None: words = words.replace("rain showers and thunderstorms", "showers and thunderstorms") words = words.replace("of showers and snow showers", "of rain and snow showers") # To handle snow amt local effects words = words.replace("except of", "except") rval = self.setWords(node, words) return rval #### Remove for RPP21.3+ ### begin label patch 3159 ########### Override for max/min phrase don;t remove def _assembleIndentedPhrases(self, tree, component): # Assemble and indent component phrases and add Label # Qualify the phrases with local effect qualifiers # if present. # e.g. "near the coast" for phrase in component.get("childList"): words = phrase.get("words") if words is None: return self.combineConjunctiveLocalEffects(tree, component) self.combineConjunctivePhrases(tree, component) fcst = "" lastQualifier = None lastPhrase = None for phrase in component.get("childList"): words = phrase.get("words") if words is None: return # Handle multiple element table phrase # that appears per period # No need to indent or qualify name = phrase.get("name") if name == "multipleElementTable_perPeriod_phrase": fcst = fcst + words continue if phrase.get("compound"): makeSentence = 0 else: makeSentence = 1 words, lastQualifier = self.qualifyWords( phrase, words, "conjunctiveQualifier", lastQualifier, lastPhrase, makeSentence=makeSentence) lastPhrase = phrase indentLabel = phrase.get("indentLabel") elementLabel = indentLabel indentLabel = self.phrase_descriptor( tree, phrase, indentLabel, indentLabel) if words == "": words = " " ############# added this section to handle Max/Min labels and spacing. if elementLabel == "MaxT_FireWx" or elementLabel == "MinT_FireWx" \ or elementLabel == "MinRH_FireWx" or elementLabel == "MaxRH_FireWx": fcst = fcst + words + "\n" else: words = self.labelIndent(words, indentLabel) fcst = fcst + words # Add label curLocalTime, shift = self.determineTimeShift() issuanceInfo = tree.get("issuanceInfo") index = component.getIndex() label = self.createLabel(tree, component, component.get("timeRange"), issuanceInfo, curLocalTime, shift, index) return self.setWords(component, label + "\n" + fcst + "\n") #### remove for rpp21.3+ def combineConjunctivePhrases(self, tree, component): # Check for Conjunctive Local Effects and make sure # we do not repeat the indented label. # # For example: # LAL.................IN THE VALLEYS ...1. # LAL.................IN THE MOUNTAINS...3 UNTIL 2400...THEN 1. # # Should be: # LAL.................IN THE VALLEYS ...1. # IN THE MOUNTAINS...3 UNTIL 2400...THEN 1. # for phrase in component.get("childList"): words = phrase.get("words") if words is None: return newChildList = [] lastName = "" lastPhrase = None for phrase in component.get("childList"): words = phrase.get("words") if words is None: return curName = phrase.get("name") if lastPhrase is None: lastPhrase = phrase lastName = curName else: # Look for a local effect phrase to be combined with # lastPhrase localEffect = phrase.get("localEffect") #print "phrase", curName, lastName, localEffect if localEffect is not None and curName == lastName: # Combine this phrase words into last one # Add conjunctive qualifier #print "combining" phraseWords = phrase.get("words") qualifier = phrase.get("conjunctiveQualifier") if qualifier is not None and qualifier != "": phraseWords = qualifier + " "+ phraseWords newWords = lastPhrase.get("words") + "." + \ phraseWords lastPhrase.set("words", newWords) else: # Add phrase to new list #print "switching" newChildList.append(lastPhrase) lastPhrase = phrase lastName = curName # Clean up lastPhrase if lastPhrase is not None: newChildList.append(lastPhrase) component.set("childList", newChildList) ##### 3159 END