563 lines
23 KiB
Python
563 lines
23 KiB
Python
##
|
|
# This software was developed and / or modified by Raytheon Company,
|
|
# pursuant to Contract DG133W-05-CQ-1067 with the US Government.
|
|
#
|
|
# U.S. EXPORT CONTROLLED TECHNICAL DATA
|
|
# This software product contains export-restricted data whose
|
|
# export/transfer/disclosure is restricted by U.S. law. Dissemination
|
|
# to non-U.S. persons whether in the United States or abroad requires
|
|
# an export license or other authorization.
|
|
#
|
|
# Contractor Name: Raytheon Company
|
|
# Contractor Address: 6825 Pine Street, Suite 340
|
|
# Mail Stop B8
|
|
# Omaha, NE 68106
|
|
# 402.291.0100
|
|
#
|
|
# See the AWIPS II Master Rights File ("Master Rights File.pdf") for
|
|
# further licensing information.
|
|
##
|
|
# ----------------------------------------------------------------------------
|
|
# This software is in the public domain, furnished "as is", without technical
|
|
# support, and with no warranty, express or implied, as to its usefulness for
|
|
# any purpose.
|
|
#
|
|
# MarinePhrases.py
|
|
# Methods for producing text forecast from SampleAnalysis statistics.
|
|
#
|
|
# Author: hansen
|
|
# ----------------------------------------------------------------------------
|
|
|
|
##
|
|
# This is a base file that is not intended to be overridden.
|
|
##
|
|
|
|
import ScalarPhrases
|
|
import VectorRelatedPhrases
|
|
import WxPhrases
|
|
import DiscretePhrases
|
|
import re
|
|
|
|
class MarinePhrases(ScalarPhrases.ScalarPhrases, VectorRelatedPhrases.VectorRelatedPhrases,
|
|
WxPhrases.WxPhrases, DiscretePhrases.DiscretePhrases):
|
|
def __init__(self):
|
|
ScalarPhrases.ScalarPhrases.__init__(self)
|
|
VectorRelatedPhrases.VectorRelatedPhrases.__init__(self)
|
|
WxPhrases.WxPhrases.__init__(self)
|
|
DiscretePhrases.DiscretePhrases.__init__(self)
|
|
|
|
############################################
|
|
### MARINE PHRASES
|
|
|
|
def marine_wind_flag(self, tree, node):
|
|
# If 1, Wind wording will reflect the
|
|
# crossing of significant thresholds such as gales.
|
|
# E.g. "West gales to 35 knots." instead of "West winds 35 knots."
|
|
return 0
|
|
|
|
def marine_wind_combining_flag(self, tree, node):
|
|
# If 1, Wind combining will reflect the
|
|
# crossing of significant thresholds such as gales.
|
|
# E.g. "HURRICANE FORCE WINDS TO 100 KNOTS." instead of
|
|
# "NORTH HURRICANE FORCE WINDS TO 100 KNOTS EASING TO
|
|
# HURRICANE FORCE WINDS TO 80 KNOTS IN THE AFTERNOON."
|
|
return 0
|
|
|
|
def marine_wind_verbose_flag(self, tree, node):
|
|
# Applies only if marine_wind_flag is 1 and the
|
|
# and marine_wind_combining_flag is 0.
|
|
# If 1, Wind phrasing will repeat special descriptors to produce:
|
|
# "NORTH HURRICANE FORCE WINDS TO 100 KNOTS EASING TO
|
|
# HURRICANCE FORCE WINDS TO 80 KNOTS IN THE AFTERNOON."
|
|
# If 0, will produce:
|
|
# "NORTH HURRICANE FORCE WINDS TO 100 KNOTS EASING TO
|
|
# 80 KNOTS IN THE AFTERNOON."
|
|
#
|
|
return 1
|
|
|
|
def marine_wind_phrase(self):
|
|
return {
|
|
"setUpMethod": self.marine_wind_setUp,
|
|
"wordMethod": self.vector_words,
|
|
"phraseMethods": self.standard_vector_phraseMethods(),
|
|
}
|
|
|
|
def marine_wind_withGusts_phrase(self):
|
|
return {
|
|
"setUpMethod": self.marine_wind_withGusts_setUp,
|
|
"wordMethod": self.vector_words,
|
|
"phraseMethods": self.standard_vector_phraseMethods(),
|
|
}
|
|
|
|
def marine_wind_setUp(self, tree, node):
|
|
return self.wind_setUp(tree, node, gustFlag=0,
|
|
connectorMethod=self.marine_vectorConnector)
|
|
|
|
def marine_wind_withGusts_setUp(self, tree, node):
|
|
return self.wind_setUp(tree, node, gustFlag=1,
|
|
connectorMethod=self.marine_vectorConnector)
|
|
|
|
def noWaveHeight_phrase(self, tree, node, elementName1, elementName2=""):
|
|
if elementName1 != elementName2:
|
|
elementNames = elementName1 + " or " + elementName2
|
|
else:
|
|
elementNames = elementName1
|
|
return "|* Insufficient grids for " + elementNames + " during " + \
|
|
str(node.getTimeRange())+ "*|"
|
|
|
|
def waveHeight_wind_threshold(self, tree, node):
|
|
# Wind value above which waveHeight (combined seas)
|
|
# is reported vs. wind waves.
|
|
# Also, the Swell phrase is omitted if this threshold is exceeded.
|
|
# Unit is knots
|
|
return 34
|
|
|
|
def inlandWatersAreas(self, tree, node):
|
|
# List of edit area names that are inland or bay waters
|
|
# as opposed to "seas"
|
|
# The phrasing for these areas will be treated differently
|
|
# (see the wave_phrase)
|
|
#
|
|
# e.g.
|
|
# return ["TampaBayWaters"]
|
|
return []
|
|
|
|
def inlandWatersWave_element(self, tree, node):
|
|
# Weather element first and second choice to use for reporting inland waters waves
|
|
# If there is incomplete or no data for the first element, the second will be used.
|
|
return ("WindWaveHgt", "WaveHeight")
|
|
|
|
def combinedSeas_threshold(self, tree, node):
|
|
# See wave_phrase
|
|
# If waves and swells are above this threshold,
|
|
# combined seas will be reported AND no Swell phrase will be reported.
|
|
# Units: feet
|
|
return 7
|
|
|
|
def seasWaveHeight_element(self, tree, node):
|
|
# Weather element to use for reporting seas waves
|
|
# IF above wind or swell thresholds
|
|
return "WaveHeight"
|
|
|
|
def seasWindWave_element(self, tree, node):
|
|
# Weather element to use for reporting seas waves
|
|
# IF above wind or swell thresholds
|
|
return "WindWaveHgt"
|
|
|
|
### WaveHeight and WindWaveHgt
|
|
def wave_withPeriods_phrase(self):
|
|
return {
|
|
"setUpMethod": self.wave_withPeriods_setUp,
|
|
"wordMethod": self.wave_words,
|
|
"phraseMethods": self.standard_phraseMethods()
|
|
}
|
|
def wave_phrase(self):
|
|
return {
|
|
"setUpMethod": self.wave_setUp,
|
|
"wordMethod": self.wave_words,
|
|
"phraseMethods": self.standard_phraseMethods()
|
|
}
|
|
|
|
def wave_withPeriods_setUp(self, tree, node):
|
|
return self.wave_setUp(tree, node, periodFlag=1)
|
|
|
|
def wave_setUp(self, tree, node, periodFlag=0):
|
|
areaLabel = node.getAreaLabel()
|
|
timeRange = node.getTimeRange()
|
|
|
|
inlandWaters = self.inlandWatersAreas(tree, node)
|
|
if self.currentAreaContains(tree, inlandWaters) == 1:
|
|
elementName, elementName2 = self.inlandWatersWave_element(tree, node)
|
|
statsByRange = tree.stats.get(elementName, timeRange, areaLabel, mergeMethod="List")
|
|
if statsByRange is None:
|
|
elementName = elementName2
|
|
# Do not report Period for inland waters
|
|
periodFlag = 0
|
|
descriptor = self.phrase_descriptor(tree, node, "inland waters", elementName)
|
|
node.set("descriptor", descriptor)
|
|
elif self.seasFlag(tree, node):
|
|
# Use wave height elementName (default)
|
|
elementName = self.seasWaveHeight_element(tree, node)
|
|
descriptor = self.phrase_descriptor(tree, node, "seas", elementName)
|
|
node.set("descriptor", descriptor)
|
|
else:
|
|
# Use wind waves (default)
|
|
elementName = self.seasWindWave_element(tree, node)
|
|
periodFlag = 0
|
|
descriptor = self.phrase_descriptor(tree, node, "waves", elementName)
|
|
node.set("descriptor", descriptor)
|
|
|
|
wave = self.ElementInfo(elementName, "List")
|
|
elementInfoList = [wave]
|
|
if periodFlag:
|
|
node.set("periodFlag", 1)
|
|
period = self.ElementInfo("Period", "Average", primary=0)
|
|
elementInfoList.append(period)
|
|
self.subPhraseSetUp(tree, node, elementInfoList, self.scalarConnector)
|
|
return self.DONE()
|
|
|
|
def seasFlag(self, tree, node):
|
|
# Return 1 if we are to report combined seas
|
|
timeRange = node.getTimeRange()
|
|
areaLabel = node.getAreaLabel()
|
|
winds = tree.stats.get("Wind", timeRange, areaLabel, mergeMethod="Max")
|
|
if winds is None:
|
|
return 0
|
|
maxWind, dir = winds
|
|
|
|
# Determine if we will report combined seas OR wind waves
|
|
seasFlag = 0
|
|
if maxWind > self.waveHeight_wind_threshold(tree, node):
|
|
seasFlag = 1
|
|
else:
|
|
swell = tree.stats.get("Swell", timeRange, areaLabel, mergeMethod="Max")
|
|
swell2 = tree.stats.get("Swell2", timeRange, areaLabel, mergeMethod="Max")
|
|
maxWave = tree.stats.get("WindWaveHgt", timeRange, areaLabel, mergeMethod="Max")
|
|
if swell is None or maxWave is None:
|
|
pass # Leave seasFlag at zero
|
|
else:
|
|
# We'll decide to report combined seas by looking at
|
|
# the MAX of waves and swells over the entire time period
|
|
swells, dir = swell
|
|
if swell2 is None:
|
|
swells2 = 0
|
|
else:
|
|
swells2, dir = swell2
|
|
threshold = self.combinedSeas_threshold(tree, node)
|
|
if maxWave > threshold and \
|
|
(swells > threshold or swells2 > threshold):
|
|
seasFlag = 1
|
|
return seasFlag
|
|
|
|
def wave_words(self, tree, node):
|
|
# Return a phrase for wave and optionally Period for the given subPhrase
|
|
elementInfo = node.getAncestor("firstElement")
|
|
elementName = elementInfo.name
|
|
statDict = node.getStatDict()
|
|
if statDict is None:
|
|
return self.setWords(node,"")
|
|
wave = self.getStats(statDict, elementName)
|
|
if wave is None:
|
|
return self.setWords(node, "")
|
|
minimum, maximum = self.getValue(wave, "MinMax")
|
|
threshold = self.nlValue(self.null_nlValue(
|
|
tree, node, elementName, elementName), maximum)
|
|
if int(minimum) < threshold and int(maximum) < threshold:
|
|
return self.setWords(node, "null")
|
|
waveStr = self.getScalarRangeStr(tree, node, elementName, minimum, maximum)
|
|
units = self.units_descriptor(tree, node, "units", "ft")
|
|
waveUnit = self.units_descriptor(tree, node, "unit", "ft")
|
|
if int(minimum) == 1 and int(maximum) == 1:
|
|
units = waveUnit
|
|
words = waveStr + " " + units
|
|
if "Period" in statDict:
|
|
period = self.getStats(statDict, "Period")
|
|
if period is not None:
|
|
avg = self.getValue(period, "Average")
|
|
periodUnits = self.units_descriptor(tree, node, "units", "s")
|
|
periodUnit = self.units_descriptor(tree, node, "unit", "s")
|
|
avg = int(avg)
|
|
if avg == 1:
|
|
periodUnits = periodUnit
|
|
periodDescriptor = self.phrase_descriptor(
|
|
tree, node, "dominant period", elementName)
|
|
words = words + " " + periodDescriptor + " " + repr(avg) + " " + periodUnits
|
|
return self.setWords(node, words)
|
|
|
|
def waveHeight_phrase(self):
|
|
return {
|
|
"setUpMethod": self.waveHeight_setUp,
|
|
"wordMethod": self.waveHeight_words,
|
|
"phraseMethods": self.standard_phraseMethods()
|
|
}
|
|
def waveHeight_setUp(self, tree, node):
|
|
elementInfoList = [self.ElementInfo("WaveHeight", "List")]
|
|
self.subPhraseSetUp(tree, node, elementInfoList, self.scalarConnector)
|
|
return self.DONE()
|
|
|
|
def waveHeight_words(self, tree, node):
|
|
"Create phrase for waves"
|
|
statDict = node.getStatDict()
|
|
stats = self.getStats(statDict, "WaveHeight")
|
|
if stats is None:
|
|
nodataPhrase = self.noWaveHeight_phrase(
|
|
tree, node, "WaveHeight", "WaveHeight")
|
|
return self.setWords(node.parent, nodataPhrase)
|
|
|
|
minimum, maximum = self.getValue(stats, "MinMax")
|
|
avg = (minimum + maximum)/2
|
|
words = self.wave_range(avg)
|
|
return self.setWords(node, words)
|
|
|
|
def wave_range(self, avg):
|
|
# Make wave ranges based off the average wave value
|
|
table = ((0, "less than 1 foot"), (1, "1 foot or less"),
|
|
(1.5, "1 to 2 feet"), (2, "1 to 3 feet"),
|
|
(3, "2 to 4 feet"), (4, "3 to 5 feet"),
|
|
(5, "3 to 6 feet"), (6, "4 to 7 feet"),
|
|
(7, "5 to 8 feet"), (8, "6 to 10 feet"),
|
|
(10, "8 to 12 feet"), (12, "10 to 14 feet"),
|
|
(14, "12 to 16 feet"), (18, "14 to 18 feet"),
|
|
(20, "15 to 20 feet"), (100, "over 20 feet"))
|
|
waveRange = ""
|
|
for rangeMax, desc in table:
|
|
if avg <= rangeMax:
|
|
waveRange = desc
|
|
break
|
|
return waveRange
|
|
|
|
### Chop
|
|
def chop_phrase(self):
|
|
return {
|
|
"setUpMethod": self.chop_setUp,
|
|
"wordMethod": self.chop_words,
|
|
"phraseMethods": self.standard_phraseMethods()
|
|
}
|
|
def chop_setUp(self, tree, node):
|
|
# Only generate this phrase for inland waters areas
|
|
inlandWaters = self.inlandWatersAreas(tree, node)
|
|
if self.currentAreaContains(tree, inlandWaters) == 0:
|
|
return self.setWords(node, "")
|
|
|
|
# Set up for only one subPhrase.
|
|
chop = self.ElementInfo("Wind", "Max", self.VECTOR())
|
|
|
|
# Uncomment the following line if you want the chop_phrase to
|
|
# have subPhrases e.g. "A light chop in morning."
|
|
#chop = self.ElementInfo("Wind", "List", self.VECTOR())
|
|
elementInfoList = [chop]
|
|
self.subPhraseSetUp(tree, node, elementInfoList, self.scalarConnector)
|
|
descriptor = self.phrase_descriptor(tree, node, "chop", "chop")
|
|
node.set("descriptor", descriptor)
|
|
return self.DONE()
|
|
|
|
def chop_words(self, tree, node):
|
|
"Create phrase for chop"
|
|
statDict = node.getStatDict()
|
|
stats = self.getStats(statDict, "Wind")
|
|
if stats is None:
|
|
return self.setWords(node, "")
|
|
maxWind, dir = self.getValue(stats, "Max", self.VECTOR())
|
|
if maxWind <= 7:
|
|
value = "smooth"
|
|
elif maxWind > 7 and maxWind <= 12:
|
|
value = "a light chop"
|
|
elif maxWind > 12 and maxWind <= 17:
|
|
value = "a moderate chop"
|
|
elif maxWind > 17 and maxWind <= 22:
|
|
value = "choppy"
|
|
elif maxWind > 22 and maxWind <= 27:
|
|
value = "rough"
|
|
elif maxWind > 27 and maxWind <= 32:
|
|
value = "very rough"
|
|
elif maxWind > 32:
|
|
value = "extremely rough"
|
|
else:
|
|
value = "!!!Chop phrase problem!!!"
|
|
return self.setWords(node, value)
|
|
|
|
### Swell
|
|
def swell_phrase(self):
|
|
return {
|
|
"setUpMethod": self.swell_setUp,
|
|
"wordMethod": self.swell_words,
|
|
"phraseMethods": self.standard_vector_phraseMethods(),
|
|
}
|
|
|
|
def swell_withPeriods_phrase(self):
|
|
return {
|
|
"setUpMethod": self.swell_withPeriods_setUp,
|
|
"wordMethod": self.swell_words,
|
|
"phraseMethods": self.standard_vector_phraseMethods(),
|
|
}
|
|
|
|
def swell_withPeriods_setUp(self, tree, node):
|
|
return self.swell_setUp(tree, node, periodFlag=1)
|
|
|
|
def swell_setUp(self, tree, node, periodFlag=0):
|
|
# Do not report swells for inland waters
|
|
inlandWaters = self.inlandWatersAreas(tree, node)
|
|
if self.currentAreaContains(tree, inlandWaters) == 1:
|
|
return self.setWords(node, "")
|
|
|
|
# Do not report swells if we are reporting combined seas
|
|
if self.seasFlag(tree, node) == 1:
|
|
return self.setWords(node, "")
|
|
|
|
swell = self.ElementInfo("Swell", "List", self.VECTOR())
|
|
elementInfoList = [swell]
|
|
if periodFlag:
|
|
swellPhrase = self.swell_withPeriods_phrase
|
|
else:
|
|
swellPhrase = self.swell_phrase
|
|
swell2 = self.ElementInfo(
|
|
"Swell2", "MinMax", self.VECTOR(), phraseDef=swellPhrase) #, primary=0)
|
|
elementInfoList = [swell, swell2]
|
|
if periodFlag:
|
|
node.set("periodFlag", 1)
|
|
period = self.ElementInfo("Period", "MinMax", primary=0)
|
|
period2 = self.ElementInfo("Period2", "MinMax", primary=0)
|
|
elementInfoList.append(period)
|
|
elementInfoList.append(period2)
|
|
self.subPhraseSetUp(tree, node, elementInfoList, self.vectorConnector)
|
|
return self.DONE()
|
|
|
|
def swell_words(self, tree, node):
|
|
# Create phrase for swell for a given set of stats in statsByRange
|
|
#print "\n in swell words"
|
|
periodFlag = node.getAncestor("periodFlag")
|
|
statDict = node.getStatDict()
|
|
|
|
#Check for Swell alone
|
|
swell2 = self.getStats(statDict, "Swell2")
|
|
if swell2 is None:
|
|
oneSwell = 1
|
|
else:
|
|
oneSwell = 0
|
|
|
|
# Swell and Swell2 subPhrases
|
|
subPhraseParts = []
|
|
elementInfoList = node.getAncestor("elementInfoList")
|
|
for swell, period in [("Swell", "Period"), ("Swell2", "Period2")]:
|
|
if swell == "Swell":
|
|
checkRepeating = 1
|
|
else:
|
|
checkRepeating = 0
|
|
for elementInfo in elementInfoList:
|
|
if elementInfo.name == swell:
|
|
swellInfo = elementInfo
|
|
break
|
|
swellWords = self.simple_vector_phrase(tree, node, swellInfo, checkRepeating)
|
|
if swellWords == "null" or swellWords == "":
|
|
subPhraseParts.append("")
|
|
continue
|
|
# Add Period
|
|
periodPhrase = ""
|
|
if periodFlag == 1:
|
|
periodStats = self.getStats(statDict, period)
|
|
periodPhrase = self.embedded_period_phrase(tree, node, periodStats)
|
|
swellWords = swellWords + periodPhrase
|
|
subPhraseParts.append(swellWords)
|
|
|
|
#print "swell", node.getTimeRange(), subPhraseParts
|
|
if subPhraseParts[0] != "" and subPhraseParts[1] != "":
|
|
words = subPhraseParts[0] + " and " + subPhraseParts[1]
|
|
# Check for mixed swell on first subPhrase
|
|
if node.getIndex() == 0:
|
|
mixedSwell = self.checkMixedSwell(tree, node, statDict)
|
|
if mixedSwell:
|
|
mixedSwellDesc = self.phrase_descriptor(tree, node, "mixed swell", "Swell")
|
|
phrase = node.getParent()
|
|
phrase.set("descriptor", mixedSwellDesc)
|
|
phrase.doneList.append(self.embedDescriptor)
|
|
elif subPhraseParts[0] != "":
|
|
words = subPhraseParts[0]
|
|
elif subPhraseParts[1] != "":
|
|
words = subPhraseParts[1]
|
|
else:
|
|
words = "null"
|
|
|
|
return self.setWords(node, words)
|
|
|
|
def checkMixedSwell(self, tree, node, statDict):
|
|
# Check for mixed swell wording
|
|
# Return mixed swell phrase if appropriate
|
|
# Otherwise, return None
|
|
swell = self.getStats(statDict, "Swell")
|
|
swell2 = self.getStats(statDict, "Swell2")
|
|
if swell is None or swell2 is None:
|
|
return 0
|
|
swellMag, swellDir = swell
|
|
swell2Mag, swell2Dir = swell2
|
|
swellMag = self.getValue(swellMag)
|
|
swell2Mag = self.getValue(swell2Mag)
|
|
if self.direction_difference(swellDir, swell2Dir) >= 90.0 and \
|
|
swellMag > 0 and \
|
|
swell2Mag / swellMag > 0.50:
|
|
return 1
|
|
else:
|
|
return 0
|
|
|
|
### Period
|
|
def embedded_period_phrase(self, tree, node, periodStats):
|
|
# Create a period phrase to be embedded with a Swell phrase
|
|
if periodStats is None:
|
|
return ""
|
|
period = int(self.getValue(periodStats))
|
|
|
|
outUnits = self.element_outUnits(tree, node, "Period", "Period")
|
|
units = self.units_descriptor(tree, node, "units", outUnits)
|
|
unit = self.units_descriptor(tree, node, "unit", outUnits)
|
|
if period == 1:
|
|
units = unit
|
|
return " at " + repr(period) + " " + units
|
|
|
|
def period_phrase(self):
|
|
return {
|
|
"setUpMethod": self.period_setUp,
|
|
"wordMethod": self.period_words,
|
|
"phraseMethods": self.standard_phraseMethods()
|
|
}
|
|
def period_setUp(self, tree, node):
|
|
elementInfoList = [self.ElementInfo("Period", "List")]
|
|
self.subPhraseSetUp(tree, node, elementInfoList, self.scalarConnector)
|
|
return self.DONE()
|
|
|
|
def period_words(self, tree, node):
|
|
# Return a phrase for Period for the given index in statsByRange
|
|
statDict = node.getStatDict()
|
|
stats = self.getStats(statDict, "Period")
|
|
if stats is None:
|
|
return self.setWords(node, "")
|
|
periodValue = int(self.getValue(stats))
|
|
|
|
outUnits = self.element_outUnits(tree, node, "Period", "Period")
|
|
units = self.units_descriptor(tree, node, "units", outUnits)
|
|
unit = self.units_descriptor(tree, node, "unit", outUnits)
|
|
if periodValue == 1:
|
|
units = unit
|
|
return self.setWords(node, repr(periodValue) + " " + units)
|
|
|
|
def marine_abbreviateText(self, fcst):
|
|
#add a space at the beginning to create a word boundary on the first word
|
|
#(space is removed at end of method).
|
|
fcst = " " + fcst
|
|
fcst = re.sub(r'\n', r' ',fcst)
|
|
fcst = re.sub(r'(?i)(\W)NORTH(\W)', r'\1N\2',fcst)
|
|
fcst = re.sub(r'(?i)(\W)SOUTH(\W)', r'\1S\2',fcst)
|
|
fcst = re.sub(r'(?i)(\W)EAST(\W)', r'\1E\2',fcst)
|
|
fcst = re.sub(r'(?i)(\W)WEST(\W)', r'\1W\2',fcst)
|
|
fcst = re.sub(r'(?i)(\W)NORTHEAST(\W)', r'\1NE\2',fcst)
|
|
fcst = re.sub(r'(?i)(\W)SOUTHEAST(\W)', r'\1SE\2',fcst)
|
|
fcst = re.sub(r'(?i)(\W)SOUTHWEST(\W)', r'\1SW\2',fcst)
|
|
fcst = re.sub(r'(?i)(\W)NORTHWEST(\W)', r'\1NW\2',fcst)
|
|
fcst = re.sub(r'(?i)(\W)KNOTS?(\W)', r'\1kt\2',fcst)
|
|
## fcst = re.sub(r'(?i)(\W)FOOT(\W)', r'\1FT\2',fcst)
|
|
fcst = re.sub(r'(?i)(\W)FEET(\W)', r'\1ft\2',fcst)
|
|
fcst = re.sub(r'(\W)Position(\W)', r'\1Psn\2',fcst)
|
|
fcst = re.sub(r'(\W)position(\W)', r'\1psn\2',fcst)
|
|
fcst = re.sub(r'(\W)Visibility(\W)', r'\1Vsby\2',fcst)
|
|
fcst = re.sub(r'(\W)visibility(\W)', r'\1vsby\2',fcst)
|
|
fcst = re.sub(r'(\W)Thunderstorm', r'\1Tstm',fcst)
|
|
fcst = re.sub(r'(\W)thunderstorm', r'\1tstm',fcst)
|
|
fcst = re.sub(r'(\W)Average(\W)', r'\1Avg\2',fcst)
|
|
fcst = re.sub(r'(\W)average(\W)', r'\1avg\2',fcst)
|
|
fcst = re.sub(r'(?i)(\W)NAUTICAL MILES?(\W)', r'\1nm\2',fcst)
|
|
fcst = re.sub(r'(?i)(\W)ATLANTIC(\W)', r'\1Atlc\2',fcst)
|
|
fcst = re.sub(r'(?i)(\W)FATHOMS?(\W)', r'\1fm\2',fcst)
|
|
fcst = re.sub(r'(?i)(\W)LONGITUDE(\W)', r'\1Long\2',fcst)
|
|
fcst = re.sub(r'(?i)(\W)PACIFIC(\W)', r'\1Pac\2',fcst)
|
|
fcst = re.sub(r'(?i)(\W)DEGREES?(\W)', r'\1deg\2',fcst)
|
|
fcst = re.sub(r'(?i)(\W)MILLIBARS?(\W)', r'\1mb\2',fcst)
|
|
fcst = re.sub(r'(\W)Pressure(\W)', r'\1Pres\2',fcst)
|
|
fcst = re.sub(r'(\W)pressure(\W)', r'\1pres\2',fcst)
|
|
fcst = re.sub(r'(?i)(\W)(SUN)DAY(\W)', r'\1\2\3',fcst)
|
|
fcst = re.sub(r'(?i)(\W)(MON)DAY(\W)', r'\1\2\3',fcst)
|
|
fcst = re.sub(r'(?i)(\W)(TUE)SDAY(\W)', r'\1\2\3',fcst)
|
|
fcst = re.sub(r'(?i)(\W)(WED)NESDAY(\W)', r'\1\2\3',fcst)
|
|
fcst = re.sub(r'(?i)(\W)(THU)RSDAY(\W)', r'\1\2\3',fcst)
|
|
fcst = re.sub(r'(?i)(\W)(FRI)DAY(\W)', r'\1\2\3',fcst)
|
|
fcst = re.sub(r'(?i)(\W)(SAT)URDAY(\W)', r'\1\2\3',fcst)
|
|
fcst = re.sub(r'^ ', r'',fcst)
|
|
return fcst
|
|
|