## # This software was developed and / or modified by Raytheon Company, # pursuant to Contract DG133W-05-CQ-1067 with the US Government. # # U.S. EXPORT CONTROLLED TECHNICAL DATA # This software product contains export-restricted data whose # export/transfer/disclosure is restricted by U.S. law. Dissemination # to non-U.S. persons whether in the United States or abroad requires # an export license or other authorization. # # Contractor Name: Raytheon Company # Contractor Address: 6825 Pine Street, Suite 340 # Mail Stop B8 # Omaha, NE 68106 # 402.291.0100 # # See the AWIPS II Master Rights File ("Master Rights File.pdf") for # further licensing information. ## ######################################################################## # RecreationFcst # # This product creates a combination of text phrases for consecutive # time periods for a list of edit areas. # # Type: smart # Local product: # RecreationFcst_Local.py (type: smart) # Associated Utility files: # Combinations # To customize this product for your site: # Set up the Combinations file with Edit Areas and labels. # Set up RecreationFcst_Local to override variables, definitions, thresholds, and methods # # Component Product Definitions included as methods in this file: # RecreationPhrases # Extended # Extended Label ## ########################################################################## # Example Output: ## Recreation Statement ## Area 1 ## .TODAY... ## DEW POINTS... IN THE UPPER TEENS. ## Minimum humidity... 18.0 PERCENT. ## WIND CHILL... 35.0 BECOMING 24.0 IN THE NIGHT.. ## WIND... WEST WINDS 25 TO 35 MPH. ## PRECIPITATION... DRY. ## LIGHTENING... 2. ## .WEDNESDAY... ## DEW POINTS... IN THE MID TEENS. ## Minimum humidity... 20.0 PERCENT. ## WIND CHILL... 36.0 BECOMING 36.0 IN THE NIGHT.. ## WIND... WEST WINDS 25 TO 35 MPH. ## PRECIPITATION... WIDESPREAD RAIN AND SNOW. ## LIGHTENING... 2. ## .THURSDAY... ## VERY WINDY. SUNNY. WIDESPREAD SNOW. LOWS IN THE UPPER 30S. HIGHS IN THE MID ## 40S. ## .FRIDAY... ## SUNNY AND DRY. LOWS AROUND 40. HIGHS IN THE LOWER 40S. ## Area 2 ## .TODAY... ## DEW POINTS... IN THE UPPER TEENS. ## Minimum humidity... 18.0 PERCENT. ## WIND CHILL... 35.0 BECOMING 24.0 IN THE NIGHT.. ## WIND... WEST WINDS 25 TO 35 MPH. ## PRECIPITATION... DRY. ## LIGHTENING... 2. ## .WEDNESDAY... ## DEW POINTS... IN THE MID TEENS. ## Minimum humidity... 20.0 PERCENT. ## WIND CHILL... 36.0 BECOMING 36.0 IN THE NIGHT.. ## WIND... WEST WINDS 25 TO 35 MPH. ## PRECIPITATION... WIDESPREAD RAIN AND SNOW. ## LIGHTENING... 2. ## .THURSDAY... ## VERY WINDY. SUNNY. WIDESPREAD SNOW. LOWS IN THE UPPER 30S. HIGHS IN THE MID ## 40S. ## .FRIDAY... ## SUNNY AND DRY. LOWS AROUND 40. HIGHS IN THE LOWER 40S. import TextRules import SampleAnalysis import ForecastNarrative class TextProduct(TextRules.TextRules, SampleAnalysis.SampleAnalysis): VariableList = [ (("Product Title","title"), "Recreation Statement", "alphaNumeric"), (("Choose Starting Time Range:", "timeRangeName"), "Tomorrow", "radio", ["Today", "Tomorrow"]), (("Number of days:", "numPeriods"), 2, "radio", [2, 3]), # Comment out the following line if you do not want to include Extended as an option: (("Extended", "extended"), "With Extended", "radio", ["Without Extended","With Extended"]), ] Definition = { "type": "smart", "displayName": "None", # Name of map background for creating Combinations #"mapNameForCombinations": "Zones_", ## Edit Areas "defaultEditAreas" : [ ("area1","Area 1"), ("area2","Area 2"), ("area3","Area 3"), ], # Product-specific variables: Can be overridden in the Local file "extendedLabel": 1, "lineLimit": 45, "trace":0, } def __init__(self): TextRules.TextRules.__init__(self) SampleAnalysis.SampleAnalysis.__init__(self) 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) if len(self._areaList) == 0: return "WARNING -- No Edit Areas Specified to Generate Product." # 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 for editArea, areaLabel in self._areaList: fcst = self._preProcessArea(fcst, editArea, areaLabel, argDict) fcst = self._makeProduct(fcst, editArea, areaLabel, argDict) fcst = self._postProcessArea(fcst, editArea, areaLabel, argDict) fcst = self._postProcessProduct(fcst, argDict) return fcst def _getVariables(self, argDict): # Make argDict accessible self.__argDict = argDict # Get Definition variables self._definition = argDict["forecastDef"] for (key, value) in self._definition.items(): exec("self._" + key + "= value") # Get VariableList and _issuance_list variables varDict = argDict["varDict"] for (key, value) in varDict.items(): if type(key) is tuple: label, variable = key exec("self._" + variable + "= value") self._language = argDict["language"] return None def _determineTimeRanges(self, argDict): # Set up the Narrative Definition and initial Time Range self._timeRange, self._narrativeDef = self._createNarrativeDef(argDict) self._definition["narrativeDef"] = self._narrativeDef self._definition["methodList"] = [self.assembleChildWords] return None def _sampleData(self, argDict): # Sample and analyze the data for the narrative # This data will be available in argDict["narrativeData"] for text rules self._narrativeProcessor = ForecastNarrative.ForecastNarrative() error = self._narrativeProcessor.getNarrativeData( argDict, self._definition, self._timeRange, self._areaList, None) if error is not None: return error return None def _preProcessProduct(self, fcst, argDict): return fcst def _preProcessArea(self, fcst, editArea, areaLabel, argDict): return fcst def _makeProduct(self, fcst, editArea, areaLabel, argDict): # Generate Narrative Forecast for Edit Area fcst = fcst + self._narrativeProcessor.generateForecast( argDict, editArea, areaLabel) return fcst def _postProcessArea(self, fcst, editArea, areaLabel, argDict): return fcst def _postProcessProduct(self, fcst, argDict): return fcst.upper() ######################################################################## # PRODUCT-SPECIFIC METHODS ######################################################################## def _createNarrativeDef(self, argDict): # Determine the start time for the product and a Narrative Definition timeRange = self.getTimeRange(self._timeRangeName, argDict) if self._numPeriods == 2: recPhrases = [24, 24] else: recPhrases = [24,24,24] extendeds = [24, 24] # Create the NarrativeDef narrativeDef = [] for recPhrase in recPhrases: narrativeDef.append(("RecreationPhrases",recPhrase)) if self._extended == "With Extended": if self._extendedLabel == 1: narrativeDef.append(("ExtendedLabel",0)) for extended in extendeds: narrativeDef.append(("Extended", extended)) return timeRange, narrativeDef def _td_phrase(self): return { "setUpMethod": self._td_setUp, "wordMethod": self._td_words, "phraseMethods": [self.assembleSubPhrases, self.postProcessPhrase], } def _td_setUp(self, tree, node): td = self.ElementInfo("Td", "List") elementInfoList = [td] self.subPhraseSetUp(tree, node, elementInfoList, self.scalarConnector) node.set("descriptor", "") node.set("indentLabel", "DEW POINTS... ") return self.DONE() def _td_words(self, tree, node): statDict = node.getStatDict() stats = self.getStats(statDict,"Td") if stats is None: return self.setWords(node, "") words = self.getTempPhrase(tree, node, stats,"Td") return self.setWords(node, words) def _rh_phrase(self): return { "setUpMethod": self._rh_setUp, "wordMethod": self._rh_words, "phraseMethods": [self.assembleSubPhrases, self.postProcessPhrase], } def _rh_setUp(self, tree, node): rh = self.ElementInfo("RH", "List") elementInfoList = [rh] self.subPhraseSetUp(tree, node, elementInfoList, self.scalarConnector) node.set("descriptor", "") node.set("indentLabel", "Minimum humidity... ") return self.DONE() def _rh_words(self, tree, node): statDict = node.getStatDict() stats = self.getStats(statDict,"RH") if stats is None: return self.setWords(node, "") min, max = stats ten = int(min / 10) * 10 digit = min % 10 if digit <= 3: RH1 = ten if digit > 3 or digit <= 9: RH1 = ten + 5 RH2 = RH1 + 10 words = repr(RH1) + " to " + repr(RH2) + " percent" return self.setWords(node, words) def _windChill_heatIndex_compoundPhrase(self): return { "phraseList": [ self.windChill_phrase, self.heatIndex_phrase, ], "phraseMethods": [ self.assembleSentences, self._windChill_heatIndex_finishUp, ], } def _windChill_heatIndex_finishUp(self, tree, node): words = node.get("words") if words is None: return if words == "": words = "not a factor" node.set("descriptor", "") statsWC = tree.stats.get("WindChill", node.getTimeRange(), node.getAreaLabel(), mergeMethod="Min") if statsWC is not None and \ statsWC < self.windChill_threshold(tree, node): node.set("indentLabel", "WIND CHILL... ") else: statsHI = tree.stats.get("HeatIndex", node.getTimeRange(), node.getAreaLabel(), mergeMethod="Max") if statsHI is not None and \ statsHI > self.heatIndex_threshold(tree, node): node.set("indentLabel", "Heat index... ") else: node.set("indentLabel", "") words = "" node.set("compound", 1) return self.setWords(node, words) def _wind_phrase(self): return { "setUpMethod": self._wind_setUp, "wordMethod": self._wind_words, "phraseMethods": [self.assembleSubPhrases, self.postProcessPhrase], } def _wind_setUp(self, tree, node): wind = self.ElementInfo("Wind", "List") elementInfoList = [wind] self.subPhraseSetUp(tree, node, elementInfoList, self.scalarConnector) node.set("descriptor", "") node.set("indentLabel", "WIND... ") return self.DONE() def _wind_words(self, tree, node): statDict = node.getStatDict() stats = self.getStats(statDict,"Wind") if stats is None: return self.setWords(node, "") elementInfo = node.getAncestor("firstElement") if elementInfo is None: return self.setWords(node, "") words = self.simple_vector_phrase(tree, node, elementInfo) if words == "null": return self.setWords(node, "null") maxWind, dir = self.getValue(stats, "Max", self.VECTOR()) chopphrase = "" if maxWind >= 26.1: chopphrase = "Heavy chop expected on area rivers and lakes" elif maxWind >= 21.7: chopphrase = "Moderate chop expected on area rivers and lakes" elif maxWind >= 17.4: chopphrase = "Light chop expected on area rivers and lakes" if chopphrase != "": words = words + ". " + chopphrase return self.setWords(node, words) def _wx_phrase(self): return { "setUpMethod": self._wx_setUp, "wordMethod": self._wx_words, "phraseMethods": [self.assembleSubPhrases, self.postProcessPhrase], } def _wx_setUp(self, tree, node): wx = self.ElementInfo("Wx", "List") qpf = self.ElementInfo("QPF", "MinMax") elementInfoList = [wx] self.subPhraseSetUp(tree, node, elementInfoList, self.scalarConnector) node.set("descriptor", "") node.set("indentLabel", "PRECIPITATION... ") return self.DONE() def _wx_words(self, tree, node): statDict = node.getStatDict() stats = self.getStats(statDict,"Wx") if stats is None: return self.setWords(node, "") self.weather_words(tree, node) WXwords = node.get("words") statsQ = self.getStats(statDict, "QPF") if statsQ is None: words = WXwords else: QPFrange0 = str(self.round(statsQ[0],2, "Nearest", 1)) QPFrange1 = str(self.round(statsQ[1],2, "Nearest", 1)) #print QPFrange1 if ((QPFrange0 == "0.0" and QPFrange1 == "0.0") or ("dry" in WXwords)): #print "Found dry weather" QPFwords = "\n" words = WXwords elif (QPFrange0 == "0.0"): QPFwords = "Amounts up to " + QPFrange1 + " of an inch" words = WXwords + ". " + QPFwords else: QPFwords = "Amounts between " + QPFrange0 + " and " + QPFrange1 + " of an inch" words = WXwords + ". " + QPFwords return self.setWords(node, words) def _ltng_phrase(self): return { "setUpMethod": self._ltng_setUp, "wordMethod": self._ltng_words, "phraseMethods": [self.assembleSubPhrases, self.postProcessPhrase], } def _ltng_setUp(self, tree, node): wx = self.ElementInfo("Wx", "List") elementInfoList = [wx] self.subPhraseSetUp(tree, node, elementInfoList, self.scalarConnector) node.set("descriptor", "") node.set("indentLabel", "LIGHTNING... ") return self.DONE() def _ltng_words(self, tree, node): statDict = node.getStatDict() stats = self.getStats(statDict,"Wx") if stats is None: return self.setWords(node, "NONE.") words = "" for subkey, rank in stats: wxType = subkey.wxType() if wxType == "T": cov = subkey.coverage() if cov in ["Num", "Wide", "Ocnl", "Brf", "Frq", "Pds", "Inter", "Lkly", "Def"]: words = "likely" elif cov in ["Sct", "Chc"] and words not in ["likely"]: words = "scattered" elif cov in ["Iso", "SChc"] and words not in ["likely", "scattered"]: words = "isolated" elif words not in ["likely", "scattered", "isolated"]: words = "possible" elif words not in ["likely", "scattered", "isolated", "possible"]: words = "none" #print words return self.setWords(node, words) ######################################################################## # OVERRIDING THRESHOLDS AND VARIABLES ######################################################################## # SampleAnalysis overrides def temporalCoverage_percentage(self, parmHisto, timeRange, componentName): return 15.0 def temporalCoverage_dict(self, parmHisto, timeRange, componentName): return { "LAL": 0, "MinRH": 0, "MaxRH": 0, "MinT": 10, "MaxT": 10, "Haines": 0, "PoP" : 50, } # TextRules overrides def pop_wx_lower_threshold(self, tree, node): # Pop-related Wx will not be reported if Pop is below this threshold return 20 def phrase_descriptor_dict(self, tree, node): # Descriptors for phrases dict = TextRules.TextRules.phrase_descriptor_dict(self, tree, node) dict["DEW POINTS... "] = "DEW POINTS.........." dict["Minimum humidity... "] = "Minimum humidity...." dict["WIND CHILL... "] = "WIND CHILL.........." dict["Heat index... "] = "Heat index.........." dict["WIND... "] = "WIND................" dict["PRECIPITATION... "] = "PRECIPITATION......." dict["LIGHTNING... "] = "LIGHTNING..........." dict["HeatIndex"] = "" dict["WindChill"] = "" return dict ######################################################################## # OVERRIDING METHODS ######################################################################## ######################################################################## # COMPONENT PRODUCT DEFINITIONS ######################################################################## def RecreationPhrases(self): return { "type": "component", "methodList": [ self.assembleIndentedPhrases, ], "analysisList": [ ("Td", self.avg), ("RH", self.minMax), ("T", self.minMax), ("WindChill", self.minMax, [12]), ("HeatIndex", self.minMax, [12]), ("Wind", self.vectorMinMax), ("Wx", self.rankedWx), ("QPF", self.accumMinMax), ("PoP", self.binnedPercent), ], "phraseList":[ self._td_phrase, self._rh_phrase, self._windChill_heatIndex_compoundPhrase, self._wind_phrase, self._wx_phrase, self._ltng_phrase, ], } def ExtendedLabel(self): return { "type": "component", "methodList": [self.setLabel], "analysisList": [], "phraseList":[], } def setLabel(self, tree, component): component.set("words", "\n.EXTENDED...\n") return self.DONE() def Extended(self): return { "type": "component", "methodList": [ self.orderPhrases, self.assemblePhrases, self.wordWrap, ], "analysisList": [ ("MinT", self.avg), ("MaxT", self.avg), ("T", self.hourlyTemp), ("Sky", self.minMax), ("Wind", self.vectorMinMax), ("Wx", self.rankedWx), ("PoP", self.binnedPercent), ], "phraseList":[ self.reportTrends, self.wind_summary, self.sky_phrase, self.weather_phrase, self.lows_phrase, self.highs_phrase, self.temp_trends, ], }