awips2/edexOsgi/com.raytheon.uf.edex.aviation/utility/common_static/base/aviation/TAFXMLEncoder.py

775 lines
30 KiB
Python

#
# TAFXMLEncoder.py
#
# Purpose: Encodes a python dictionary consisting of TAF components into a XML
# document according to the IWXXM 2.1 schema.
#
# Author: Mark Oberfield MDL/OSTI/NWS/NOAA
#
# Date: 09 October 2016
#
#
# SOFTWARE HISTORY
#
# Date Ticket# Engineer Description
# ------------ ---------- ----------- --------------------------
# 04/17/2017 6065 tgurney Use IWXXM 2.1 schema
# 04/18/2017 6065 tgurney Include previous valid time
# for AMD/COR tafs
# 12/11/2017 6551 tgurney Move "NO AMD" statements to
# the end of the document
#
# 12/11/2017 6548 tgurney Fix PROB30 encoding
# 12/13/2017 6550 tgurney Fix XML previous report valid
# period
#
from __future__ import print_function
import difflib, re, time, uuid
import xml.etree.ElementTree as ET
#
NameSpaces = { 'aixm':'http://www.aixm.aero/schema/5.1.1',
'gml':'http://www.opengis.net/gml/3.2',
'':'http://icao.int/iwxxm/2.1',
'om':'http://www.opengis.net/om/2.0',
'sf':'http://www.opengis.net/sampling/2.0',
'sams':'http://www.opengis.net/samplingSpatial/2.0',
'metce':'http://def.wmo.int/metce/2013',
'xlink':'http://www.w3.org/1999/xlink',
'xsi':'http://www.w3.org/2001/XMLSchema-instance' }
#
# For US extension blocks
usTAFAmendmentParameters = {'None':{'href':'http://nws.weather.gov/codes/NWSI10-813/2012/AmendableTAFParameter/NONE/',
'title':'No amendments will be issued'},
'CLD':{'href':'http://nws.weather.gov/codes/NWSI10-813/2012/AmendableTAFParameter/CEILING/',
'title':'Amendments based on cloud ceilings will be issued'},
'VIS':{'href':'http://nws.weather.gov/codes/NWSI10-813/2012/AmendableTAFParameter/VISIBILITY/',
'title':'Amendments based on horizontal visibility will be issued'},
'WIND':{'href':'http://nws.weather.gov/codes/NWSI10-813/2012/AmendableTAFParameter/WIND/',
'title':'Amendments based on wind will be issued'},
'WX':{'href':'http://nws.weather.gov/codes/NWSI10-813/2012/AmendableTAFParameter/WEATHER/',
'title':'Amendments based on weather phenomenon will be issued'},
}
IWXXM_US_URI='http://nws.weather.gov/iwxxm-us/2.0'
IWXXM_US_URL='http://nws.weather.gov/schemas/IWXXM-US/2.0/Release/schemas/usTaf.xsd'
IWXXM_URL='http://icao.int/iwxxm/2.1 http://schemas.wmo.int/iwxxm/2.1/iwxxm.xsd'
_re_cloudLyr = re.compile(r'(?P<AMT>VV|SKC|CLR|FEW|SCT|BKN|OVC)(?P<HGT>(///|\d{3}))?')
#
_changeIndicator = {'BECMG':'BECOMING','TEMPO':'TEMPORARY_FLUCTUATIONS','PROB30':'PROBABILITY_30','PROB40':'PROBABILITY_40',
'PROB30 TEMPO':'PROBABILITY_30_TEMPORARY_FLUCTUATIONS','PROB40 TEMPO':'PROBABILITY_40_TEMPORARY_FLUCTUATIONS'}
_CldCvr = {'SKC':(0,'Clear'),'FEW':(1,'Few'),'SCT':(2,'Scattered'),'BKN':(3,'Broken'),'OVC':(4,'Overcast')}
_bbbCodes = {'A':'AMENDMENT','c':'CANCELLATION','C':'CORRECTION','N':'MISSING'}
def parseAndGetNameSpaces(fname,References=None):
#
if References is None:
References = {}
events = 'start','start-ns'
root = None
ns = {}
requiredNS = References.keys()
allLocations = [x[0] for x in References.values()]
#
for event, elem in ET.iterparse(fname,events):
if event == 'start' and root == None:
root = elem
for prefix, uri in ns.items():
if prefix not in ['','xs']:
try:
elem.set("xmlns:"+prefix,"http://%s" % difflib.get_close_matches(uri,allLocations,1)[0])
except IndexError:
elem.set("xmlns:"+prefix,uri)
try:
requiredNS.pop(requiredNS.index(prefix))
except ValueError:
pass
#
elif event == 'start-ns':
if elem[0] in ns and ns[elem[0]] != elem[1]:
#
# NOTE: It is perfectly valid to have the same prefix refer
# to different URI namespaces in different parts of the
# document. This exception servers as a reminder that
# this solution is not robust.
raise KeyError("Duplicate prefix with different URI found.")
ns[elem[0]] = elem[1]
#
while True:
try:
key = requiredNS.pop()
root.set("xmlns:%s" % key,"http://%s" % References[key][0])
except IndexError:
break
#
return ET.ElementTree(root),ns
def getGeography(geoFile):
fh = open(geoFile,'r')
d = {}
for lne in fh:
if lne.startswith('#'):
continue
key,lat,lon,elev,name,junk,junk=lne.split('|')
d[key.strip()] = ('%s %s %s' % (lat.strip(),lon.strip(),elev.strip()),name.strip())
return d
class XMLEncoder:
def __init__(self,wwCodesFile='./data/ww.xml',tafStationInfoFile='./data/metarStationInfo.txt'):
#
# Populate METAR metadata dictionary
self.tafMetaData = getGeography(tafStationInfoFile)
#
# Populate the precipitation/obstruction and other phenomenon dictionary
root,wwCodeSpaces = parseAndGetNameSpaces(wwCodesFile)
self.wwCodes = {}
for concept in root.iter('{%s}Concept' % wwCodeSpaces.get('skos')):
try:
uri = concept.get('{%s}about' % wwCodeSpaces.get('rdf'))
for elem in concept:
title = elem.text
key = uri[uri.rfind('/')+1:]
self.wwCodes[key] = dict([('uri',uri),('title',title)])
except KeyError:
pass
#
# map several token ids to a single function
setattr(self,'obv',self.pcp)
setattr(self,'vcnty', self.pcp)
def __call__(self,decodedTaf,lastTafRangeStart,lastTafRangeEnd):
self.lastTafRangeStart = lastTafRangeStart
self.lastTafRangeEnd = lastTafRangeEnd
# see if we have the metadata for the observation
self.ident(decodedTaf['ident'])
self.ICAOLatLonElev, self.ICAOName = self.tafMetaData[self.ICAOId]
self.decodedTaf = decodedTaf
self.doingUSTAF = False
# Determine if US TAF.
if self.ICAOId[0] in ['K','P'] or self.ICAOId[:2] == 'TJ':
self.doingUSTAF = True
# Root element
self.XMLDocument = ET.Element('TAF')
for prefix,uri in NameSpaces.items():
if prefix == '':
self.XMLDocument.set('xmlns',uri)
else:
self.XMLDocument.set('xmlns:%s' % prefix,uri)
self.XMLDocument.set('xsi:schemaLocation', IWXXM_URL)
bbb = self.decodedTaf['bbb']
self.XMLDocument.set('status',_bbbCodes.get(bbb[0],'NORMAL'))
self.XMLDocument.set('permissibleUsage','OPERATIONAL')
self.doIt()
xmlstring = ET.tostring(self.XMLDocument,encoding="utf-8",method="xml")
return xmlstring.replace(' />','/>')
def getUUID(self,prefix='uuid.'):
return '%s%s' % (prefix,uuid.uuid4())
def ident(self,ident):
self.ICAOId = ident['str']
def doIt(self):
self.itime(self.XMLDocument,self.decodedTaf['itime'])
#
# No valid time for NIL TAF
try:
self.vtime(ET.SubElement(self.XMLDocument,'validTime'),self.decodedTaf['vtime'])
self.entireValidTimeID = self.validTimeID
except KeyError:
rmv = self.XMLDocument[-1]
self.XMLDocument.remove(rmv)
#
# Set the "base" forecast, which is the initial prevailing condition of the TAF
try:
base = self.decodedTaf['group'].pop(0)
try:
self.baseFcst(self.XMLDocument,base['prev'])
self.changeGroup(self.XMLDocument,base['ocnl'])
except KeyError:
pass
except IndexError:
pass
# Now the rest of the forecast "evolves" from the initial condition
for group in self.decodedTaf['group']:
self.changeGroup(self.XMLDocument,group['prev'])
try:
self.changeGroup(self.XMLDocument,group['ocnl'])
except KeyError:
pass
# If the TAF was canceled (non-domestic TAFs only)
if self.XMLDocument.get('status') == 'CANCELLATION':
indent = ET.SubElement(self.XMLDocument,'previousReportAerodrome')
self.vtime(ET.SubElement(indent,'previousReportValidPeriod'),
- self.decodedTaf['prevtime'])
# If the TAF was amended or corrected, include the valid time
# of the previous TAF issuance.
elif self.XMLDocument.get('status') in ['AMENDMENT','CORRECTION']:
previousTime = ET.SubElement(self.XMLDocument,'previousReportValidPeriod')
try:
if self.lastTafRangeStart and self.lastTafRangeEnd:
validPeriod = {'from': self.lastTafRangeStart, 'to': self.lastTafRangeEnd}
self.vtime(previousTime, validPeriod)
else:
previousTime.set('nilReason','unknown')
except KeyError as e:
previousTime.set('nilReason','unknown')
# Find limits to amendments
if self.doingUSTAF:
try:
extension = ET.Element('extension')
self.amd(extension,self.decodedTaf['amd'])
self.XMLDocument.append(extension)
except KeyError:
pass
def itime(self,parent,token):
value = token['value']
parent.set('gml:id',self.getUUID())
indent1 = ET.SubElement(parent,'issueTime')
indent2 = ET.SubElement(indent1,'gml:TimeInstant')
indent2.set('gml:id',self.getUUID())
self.resultTimeTag = '#%s' % indent2.get('gml:id')
indent3 = ET.SubElement(indent2,'gml:timePosition')
indent3.text = time.strftime('%Y-%m-%dT%H:%M:%SZ',time.gmtime(value))
def vtime(self,parent,token):
indent = ET.SubElement(parent,'gml:TimePeriod')
indent.set('gml:id',self.getUUID())
indent1 = ET.SubElement(indent,'gml:beginPosition')
indent1.text = time.strftime('%Y-%m-%dT%H:%M:%SZ',time.gmtime(token['from']))
indent1 = ET.SubElement(indent,'gml:endPosition')
indent1.text = time.strftime('%Y-%m-%dT%H:%M:%SZ',time.gmtime(token['to']))
self.validTimeID = '#%s' % indent.get('gml:id')
#
def baseFcst(self,parent,token):
#
indent = ET.SubElement(parent,'baseForecast')
indent1 = ET.SubElement(indent,'om:OM_Observation')
indent1.set('gml:id',self.getUUID())
indent2 = ET.SubElement(indent1,'om:type')
indent2.set('xlink:href','http://codes.wmo.int/49-2/observation-type/IWXXM/1.0/MeteorologicalAerodromeForecast')
if self.decodedTaf['bbb'] == 'NIL':
indent2 = ET.SubElement(indent1,'om:phenomenonTime')
indent2.set('xlink:href',self.resultTimeTag)
else:
self.vtime(ET.SubElement(indent1,'om:phenomenonTime'),token['time'])
indent2 = ET.SubElement(indent1,'om:resultTime')
indent2.set('xlink:href',self.resultTimeTag)
if self.decodedTaf['bbb'] != 'NIL':
indent2 = ET.SubElement(indent1,'om:validTime')
indent2.set('xlink:href',self.entireValidTimeID)
indent2 = ET.SubElement(indent1,'om:procedure')
indent3 = ET.SubElement(indent2,'metce:Process')
indent3.set('gml:id',self.getUUID())
self.procedureID = '#%s' % indent3.get('gml:id')
indent4 = ET.SubElement(indent3,'gml:description')
if self.doingUSTAF:
indent4.text = 'United States National Weather Service Instruction 10-813 Terminal Aerodrome Forecasts'
else:
indent4.text = 'WMO No. 49 Volume 2 Meteorological Service for International Air Navigation APPENDIX 5 TECHNICAL SPECIFICATIONS RELATED TO FORECASTS'
indent2 = ET.SubElement(indent1, 'om:observedProperty')
indent2.set('xlink:href','http://codes.wmo.int/49-2/observable-property/MeteorologicalAerodromeForecast')
indent2 = ET.SubElement(indent1, 'om:featureOfInterest')
indent3 = ET.SubElement(indent2,'sams:SF_SpatialSamplingFeature')
indent3.set('gml:id',self.getUUID())
self.featureOfInterestID = '#%s' % indent3.get('gml:id')
indent4 = ET.SubElement(indent3,'sf:type')
indent4.set('xlink:href','http://www.opengis.net/def/samplingFeatureType/OGC-OM/2.0/SF_SamplingPoint')
indent4 = ET.SubElement(indent3,'sf:sampledFeature')
indent5 = ET.SubElement(indent4,'aixm:AirportHeliport')
indent5.set('gml:id',self.getUUID())
indent6 = ET.SubElement(indent5,'aixm:timeSlice')
indent7 = ET.SubElement(indent6,'aixm:AirportHeliportTimeSlice')
indent7.set('gml:id',self.getUUID())
indent8 = ET.SubElement(indent7,'gml:validTime')
indent8.set('xlink:href',self.resultTimeTag)
indent8 = ET.SubElement(indent7,'aixm:interpretation')
indent8.text = 'SNAPSHOT'
indent8 = ET.SubElement(indent7,'aixm:designator')
indent8.text = self.ICAOId
indent8 = ET.SubElement(indent7,'aixm:name')
indent8.text = self.ICAOName
indent8 = ET.SubElement(indent7,'aixm:locationIndicatorICAO')
indent8.text = self.ICAOId
indent4 = ET.SubElement(indent3,'sams:shape')
indent5 = ET.SubElement(indent4,'gml:Point')
indent5.set('gml:id',self.getUUID())
indent5.set('uomLabels','deg deg m')
indent5.set('axisLabels','lat lon altitude')
indent5.set('srsName','urn:ogc:crs:EPSG::4979')
indent6 = ET.SubElement(indent5,'gml:pos')
indent6.text = self.ICAOLatLonElev
#
# Finally the "base" forecast
self.result(indent1,token,True)
def changeGroup(self,parent,fcsts):
#
if type(fcsts) == type({}):
fcsts = [fcsts]
for token in fcsts:
indent = ET.SubElement(parent,'changeForecast')
indent1 = ET.SubElement(indent,'om:OM_Observation')
indent1.set('gml:id',self.getUUID())
indent2 = ET.SubElement(indent1,'om:type')
indent2.set('xlink:href','http://codes.wmo.int/49-2/observation-type/IWXXM/1.0/MeteorologicalAerodromeForecast')
self.vtime(ET.SubElement(indent1,'om:phenomenonTime'),token['time'])
indent2 = ET.SubElement(indent1,'om:resultTime')
indent2.set('xlink:href',self.resultTimeTag)
indent2 = ET.SubElement(indent1,'om:validTime')
indent2.set('xlink:href',self.entireValidTimeID)
indent2 = ET.SubElement(indent1,'om:procedure')
indent2.set('xlink:href',self.procedureID)
indent2 = ET.SubElement(indent1,'om:observedProperty')
indent2.set('xlink:href','http://codes.wmo.int/49-2/observable-property/MeteorologicalAerodromeForecast')
indent2 = ET.SubElement(indent1,'om:featureOfInterest')
indent2.set('xlink:href',self.featureOfInterestID)
self.result(indent1,token)
#
# Do <om:result>
def result(self,parent,token,baseFcst=False):
indent = ET.SubElement(parent,'om:result')
if self.decodedTaf['bbb'] == 'NIL':
indent.set('nilReason','missing')
return
indent = ET.SubElement(indent,'MeteorologicalAerodromeForecastRecord')
indent.set('gml:id',self.getUUID())
#
indent.set('cloudAndVisibilityOK',token['cavok'])
if token['cavok'] == 'true':
self.ForecastResults = ['wind','temp']
else:
self.ForecastResults = ['vsby','wind','pcp','vcnty','obv','nsw','sky','temp']
if not baseFcst:
if token['type'] == 'PROB':
changeToken = token['time']['str'].split()[0]
indent.set('changeIndicator', _changeIndicator.get(changeToken,'PROBABILITY_30'))
else:
indent.set('changeIndicator', _changeIndicator.get(token['type'],'FROM'))
indent.set('gml:id',self.getUUID())
#
for element in self.ForecastResults:
function = getattr(self,element)
try:
function(indent,token[element])
except KeyError:
pass
#
# US NWS and Air Force TAFs may have additional forecast content which is recorded
# in a <extension> block.
#
if self.doingUSTAF:
MAFRE = ET.Element('MeteorologicalAerodromeForecastRecordExtension')
MAFRE.set('xmlns',IWXXM_US_URI)
MAFRE.set('xsi:schemaLocation','%s %s' % (IWXXM_US_URI,IWXXM_US_URL))
for element in ['altim','volcanicAshLayer','peakdd','llws','icng','turb']:
function = getattr(self,element)
try:
function(MAFRE,token[element])
except KeyError:
pass
if len(MAFRE):
extension = ET.SubElement(indent,'extension')
extension.append(MAFRE)
def wind(self,parent,token):
if token['str'][:5] == '/////':
return
indent = ET.SubElement(parent,'surfaceWind')
indent1 = ET.Element('AerodromeSurfaceWindForecast')
if token['str'].startswith('VRB'):
indent1.set('variableWindDirection','true')
else:
try:
indent1.set('variableWindDirection','false')
indent2 = ET.Element('meanWindDirection')
indent2.text = token['dd']
indent2.set('uom','deg')
except KeyError:
pass
indent1.append(indent2)
try:
indent2 = ET.Element('meanWindSpeed')
indent2.text = token['ff']
indent2.set('uom',token['uom'])
except KeyError:
pass
indent1.append(indent2)
try:
indent2 = ET.Element('windGustSpeed')
indent2.text = token['gg']
indent2.set('uom',token['uom'])
indent1.append(indent2)
except (KeyError,ValueError):
pass
if len(indent1):
indent.append(indent1)
def vsby(self,parent,token):
indent = ET.SubElement(parent,'prevailingVisibility')
indent.set('uom',token['uom'])
indent.text = token['value']
#
# Visbility above 6SM (P6SM) or 10KM
if (token['uom'] == '[mi_i]' and token['value'] == '7'):
indent.text = '6'
indent = ET.SubElement(parent,'prevailingVisibilityOperator')
indent.text = 'ABOVE'
elif token['value'] == '10000':
indent = ET.SubElement(parent,'prevailingVisibilityOperator')
indent.text = 'ABOVE'
def pcp(self,parent,token):
for ww in token['str'].split():
#
# Search BUFR table
try:
codes = self.wwCodes[ww]
indent = ET.SubElement(parent,'weather')
indent.set('xlink:href',codes['uri'])
indent.set('xlink:title',codes['title'])
#
# Initial weather phenomenon token not matched
except KeyError:
self.wxrPhenomenonSearch(parent,ww)
def wxrPhenomenonSearch(self,parent,ww):
#
# Split the weather string into two; both pieces must be found
pos=-2
ww1 = ww[:pos]
ww2 = ww[pos:]
while len(ww1) > 1:
try:
codes1 = self.wwCodes[ww1]
codes2 = self.wwCodes[ww2]
indent = ET.SubElement(parent,'weather')
indent.set('xlink:href',codes1['uri'])
indent.set('xlink:title',codes1['title'])
indent = ET.SubElement(parent,'weather')
indent.set('xlink:href',codes2['uri'])
indent.set('xlink:title',codes2['title'])
break
except KeyError:
pos -= 2
ww1 = ww[:pos]
ww2 = ww[pos:]
def nsw(self,parent,ignored):
indent = ET.SubElement(parent,'weather')
indent.set('nilReason','http://codes.wmo.int/common/nil/nothingOfOperationalSignificance')
def sky(self,parent,token):
indent = ET.SubElement(parent,'cloud')
for numberLyr, layer in enumerate(token['str'].split()):
if layer[:2] == 'VV':
try:
indent1 = ET.SubElement(indent,'AerodromeCloudForecast')
indent1.set('gml:id',self.getUUID())
height = int(layer[2:])*100
indent2 = ET.Element('verticalVisibility')
indent2.text = str(height)
indent2.set('uom','[ft_i]')
indent1.append(indent2)
except ValueError:
parent.remove(indent)
elif layer == 'NSC':
indent.set('nilReason','http://codes.wmo.int/common/nil/nothingOfOperationalSignificance')
else:
if numberLyr == 0:
indent1 = ET.SubElement(indent,'AerodromeCloudForecast')
indent1.set('gml:id',self.getUUID())
self.doCloudLayer(indent1,layer)
def doCloudLayer(self,parent,layer):
indent = ET.SubElement(parent,'layer')
indent1 = ET.SubElement(indent,'CloudLayer')
desc = _re_cloudLyr.match(layer)
try:
amount = desc.group('AMT')
indent2 = ET.Element('amount')
indent2.set('xlink:href','http://codes.wmo.int/bufr4/codeflag/0-20-008/%d' % _CldCvr[amount][0])
indent2.set('xlink:title', _CldCvr[amount][1])
indent1.append(indent2)
except TypeError:
return
indent2 = ET.SubElement(indent1,'base')
indent2.set('uom','[ft_i]')
try:
height = int(desc.group('HGT'))*100
indent2.text = str(height)
except TypeError:
if _CldCvr[amount][0] == 0:
indent2.set('uom','N/A')
indent2.set('xsi:nil','true')
indent2.set('nilReason','inapplicable')
if layer.endswith('CB'):
indent2 = ET.SubElement(indent1,'cloudType')
indent2.set('xlink:href','http://codes.wmo.int/bufr4/codeflag/0-20-012/9')
indent2.set('xlink:title','Cumulonimbus')
if layer.endswith('TCU'):
indent2 = ET.SubElement(indent1,'cloudType')
indent2.set('xlink:href','http://codes.wmo.int/bufr4/codeflag/0-20-012/32')
indent2.set('xlink:title','Towering cumulus')
def temp(self,parent,token):
indent = ET.SubElement(parent,'temperature')
indent1 = ET.SubElement(indent,'AerodromeAirTemperatureForecast')
try:
indent2 = ET.Element('maximumAirTemperature')
maxt = token['max']
indent2.text = str(maxt['value'])
indent2.set('uom','Cel')
indent1.append(indent2)
indent2 = ET.Element('maximumAirTemperatureTime')
indent3 = ET.SubElement(indent2,'gml:TimeInstant')
indent3.set('gml:id',self.getUUID())
indent4 = ET.SubElement(indent3,'gml:timePosition')
indent4.text = time.strftime('%Y-%m-%dT%H:%M:%SZ',time.gmtime(maxt['at']))
indent1.append(indent2)
except KeyError:
pass
try:
indent2 = ET.Element('minimumAirTemperature')
mint = token['min']
indent2.text = str(mint['value'])
indent2.set('uom','Cel')
indent1.append(indent2)
indent2 = ET.Element('minimumAirTemperatureTime')
indent3 = ET.SubElement(indent2,'gml:TimeInstant')
indent3.set('gml:id',self.getUUID())
indent4 = ET.SubElement(indent3,'gml:timePosition')
indent4.text = time.strftime('%Y-%m-%dT%H:%M:%SZ',time.gmtime(mint['at']))
indent1.append(indent2)
except KeyError:
pass
#
#
# US extension codes
def altim(self,parent,token):
QNH = ET.SubElement(parent,'qnh')
QNH.set('uom',token['uom'])
QNH.text = token['value']
def volcanicAshLayer(self,parent,token):
self.layerAboveAerodrome(ET.SubElement(parent,'volcanicAshLayer'),token['upper'],token['lower'],token['uom'])
def peakdd(self,parent,token):
peakDD = ET.SubElement(parent,'peakWindGustDirection')
peakDD.set('uom','deg')
peakDD.text = token['value']
def llws(self,parent,token):
#
NonConvectiveLLWS = ET.SubElement(parent,'nonConvectiveLLWS')
LLWSDir = ET.SubElement(NonConvectiveLLWS, 'lowLevelWindShearWindDirection')
LLWSSpd = ET.SubElement(NonConvectiveLLWS, 'lowLevelWindShearWindSpeed')
self.layerAboveAerodrome(NonConvectiveLLWS,token['hgt'],'0','[ft_i]')
LLWSDir.set('uom','deg')
LLWSSpd.set('uom','[kn_i]')
LLWSDir.text = token['dd']
LLWSSpd.text = token['ff']
def icng(self,parent,token):
#
if token['top'] != '0':
IcingAboveAerodrome = ET.SubElement(parent,'icingAboveAerodrome')
icingType = ET.SubElement(IcingAboveAerodrome,'icingType')
icingType.set('xlink:href','http://nws.weather.gov/codes/NWSI10-813/2012/IcingType/%s' % token['type'])
self.layerAboveAerodrome(IcingAboveAerodrome,token['top'],token['base'],token['uom'])
def turb(self,parent,token):
#
if token['top'] != '0':
TurbulenceAboveAerodrome = ET.SubElement(parent,'turbulenceAboveAerodrome')
turbulenceType = ET.SubElement(TurbulenceAboveAerodrome,'turbulenceType')
turbulenceType.set('xlink:href','http://nws.weather.gov/codes/NWSI10-813/2012/TurbulenceType/%s' % token['type'])
self.layerAboveAerodrome(TurbulenceAboveAerodrome,token['top'],token['base'],token['uom'])
def layerAboveAerodrome(self,parent,upper,lower,uom):
#
LayerAboveAerodrome = ET.SubElement(parent,'layerAboveAerodrome')
lowerLimit = ET.SubElement(LayerAboveAerodrome,'lowerLimit')
upperLimit = ET.SubElement(LayerAboveAerodrome,'upperLimit')
lowerLimit.set('uom',uom)
lowerLimit.text = lower
upperLimit.set('uom',uom)
upperLimit.text = upper
def amd(self,parent,limits):
TAFAmendmentLimitations = ET.SubElement(parent,'TAFAmendmentLimitations')
TAFAmendmentLimitations.set('xmlns',IWXXM_US_URI)
TAFAmendmentLimitations.set('xsi:schemaLocation','%s %s' % (IWXXM_US_URI,IWXXM_US_URL))
periodOfLimitation = ET.SubElement(TAFAmendmentLimitations,'periodOfLimitation')
periodOfLimitation.set('gml:id',self.getUUID())
beginPosition = ET.SubElement(periodOfLimitation,'gml:beginPosition')
beginPosition.text = time.strftime('%Y-%m-%dT%H:%M:%SZ',time.gmtime(limits['time']['from']))
endPosition = ET.SubElement(periodOfLimitation,'gml:endPosition')
endPosition.text = time.strftime('%Y-%m-%dT%H:%M:%SZ',time.gmtime(limits['time']['to']))
if limits['str'].find('AMD NOT SKED') == 0:
amdTAFParameter = ET.SubElement(TAFAmendmentLimitations,'amendableTAFParameter')
amdTAFParameter.set('xlink:href',usTAFAmendmentParameters['None']['href'])
amdTAFParameter.set('xlink:title',usTAFAmendmentParameters['None']['title'])
else:
for parameter in ['CLD','VIS','WIND','WX']:
if limits['str'].find(parameter) > 0:
amdTAFParameter = ET.SubElement(TAFAmendmentLimitations,'amendableTAFParameter')
amdTAFParameter.set('xlink:href',usTAFAmendmentParameters[parameter]['href'])
amdTAFParameter.set('xlink:title',usTAFAmendmentParameters[parameter]['title'])
if __name__ == '__main__':
def Usage():
"""Provide instructions and explanation of TAF encoder command line arguments"""
d = argparse.ArgumentParser(description="Run the TAF encoder unit testing",
epilog="""Encoder will wait for input on standard input for TAC.
Pressing ^D (ETX) will send the raw text to the decoder. Corresponding
XML document is written to standard output. Pressing ^C will
end the unit testing.""",
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
d.add_argument("--version", action="version", version="%(prog)s 1.0")
#
d.add_argument('-n','--namespaces', default=False, action='store_true',
help='XML encoder will include namespace declarations in root element')
return d
#
# Process command line
parser = Usage()
cmdlne = parser.parse_args()
decoder = TAFDecoder.Decoder()
encoder = XMLEncoder()
print('Press ^D (End of Text) to de/encode TAF. Press ^C to quit unit test.')
#
# While intr (^C) sequence not pressed . . .
while True:
try:
#
# Read input until ^D (ETX)
taf = ''.join(sys.stdin.readlines())
result = decoder(taf.replace('\n',''))
except tpg.SyntacticError, e:
print(taf)
print(str(e))
except KeyboardInterrupt:
break
#
# Pass results to encoder. The first argument, the python dictionary,
# is required. The other two arguments affect the resulting XML document.
#
try:
encoder(result,nameSpaceDeclarations=cmdlne.namespaces,report=taf)
encoder.printXML(sys.stdout,readable=True)
except KeyError:
print('%s not found in station directory file' % result['ident']['str'])