awips2/cave/com.raytheon.viz.gfe/localization/gfe/userPython/procedures/TCImpactGraphics_KML.py
2022-05-05 12:34:50 -05:00

271 lines
12 KiB
Python

# ----------------------------------------------------------------------------
# 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.
#
# TCImpactGraphics_KML
#
# Author: P. Santos/Joe Maloney - 4/19/2011
#
# Last edited: 27 July 2012 - Shannon/R. Anderson - made A2 compatible
# Last Modified 30 July 2012 - J Maloney/P. Santos - made it work with A2
# Modified 09 Sept 2014 - J. Maloney - for 2015 season, removed MarineThreat,
# renamed CoastalThreat -> StormSurgeThreat, InlandThreat -> FloodingRainThreat,
# removed verylow from kml styles in header
# Modified 11 Sept 2014 - J. Maloney/S. White - site ID is now automatically
# retrieved from the environment.
# Modified 16 Sept 2014 - J. Maloney/T. Lefebvre - impact statements are now
# read from TCVDictionary (in Utilities) and formatted on-the-fly!
# Modified 21 Oct 2014 - J. Maloney - products now go in /awips2/GFESuite/hti/
# data.
# Modified 9 June, 2017: Remove old labels for 2018. PS
# Modified 21 June 2017 - JCM - added CDATA tags to Placemark descriptions
# Modified 25 July 2017 - PS/SEW - added EA config and threatPhrase dicts
# ----------------------------------------------------------------------------
##
# This is an absolute override file, indicating that a higher priority version
# of the file will completely replace a lower priority version of the file.
##
import os
import numpy as np
import AbsTime
import SmartScript
import TCVDictionary
import TimeRange
from com.raytheon.uf.common.dataplugin.gfe.reference import ReferenceData
CoordinateType = ReferenceData.CoordinateType
MenuItems = ["Populate"]
### CONFIG section READ ##########
# The kml txt files will be placed in /awips2/GFESuite/hti/data/'threatWEName'.kml.txt.
# From there it is synchronized to the web servers along with the graphics.
# In the servers a php script will convert the # file name so that a browser can properly interpret
# it as a kml file to be open with Google Earth or equivalent application.
# Also, make sure the ownership of the kml.txt files created is fxa:fxalpha with permissions set
# to 666.
# You can test the files created by copying them outside AWIPS and renaming them .kml.
# Then open them with Google Earth.
### Make the edit areas below for Wind, FloodingRain, and Tornado
### your local Land-only CWA edit area
######################################################################
editAreaDict = {
"StormSurgeThreat" : "StormSurgeWW_EditArea_Local", # Leave as-is
"WindThreat" : "MHX", # Land-only EA
"FloodingRainThreat" : "MHX", # Land-only EA
"TornadoThreat" : "MHX" # Land-only EA
}
##### End Config #########
threatPhraseDict = {
"Wind": {
"Extreme": "Potential for wind greater than 110 mph",
"High": "Potential for wind 74 to 110 mph",
"Mod": "Potential for wind 58 to 73 mph",
"Elevated": "Potential for wind 39 to 57 mph",
"None": "Wind less than 39 mph"
},
"Storm Surge": {
"Extreme": "Potential for storm surge flooding greater than 9 feet above ground",
"High": "Potential for storm surge flooding greater than 6 feet above ground",
"Mod": "Potential for storm surge flooding greater than 3 feet above ground",
"Elevated": "Potential for storm surge flooding greater than 1 foot above ground",
"None": "Little to no storm surge flooding"
},
"Flooding Rain": {
"Extreme": "Potential for extreme flooding rain",
"High": "Potential for major flooding rain",
"Mod": "Potential for moderate flooding rain",
"Elevated": "Potential for localized flooding rain",
"None": "Little or no potential for flooding rain"
},
"Tornado": {
"Extreme": "Potential for an outbreak of tornadoes",
"High": "Potential for many tornadoes",
"Mod": "Potential for several tornadoes",
"Elevated": "Potential for a few tornadoes",
"None": "Tornadoes not expected"
}
}
class Procedure (SmartScript.SmartScript):
def __init__(self, dbss):
SmartScript.SmartScript.__init__(self, dbss)
def makeTimeRange(self):
cTime = int(self._gmtime().unixTime()/ 3600) * 3600
startTime = AbsTime.AbsTime(cTime - 12 * 3600)
endTime = AbsTime.AbsTime(cTime + 12 * 3600) # 12 hours
tr = TimeRange.TimeRange(startTime, endTime)
return tr
def makeThreatKML(self,threatWEName,threatKeys,threatGrid_kml):
kml_filename = '/awips2/GFESuite/hti/data/' + threatWEName + '.kml.txt'
kml = open(kml_filename, 'w')
kml.write('<?xml version="1.0" encoding="UTF-8"?>\n')
kml.write('<kml xmlns="http://www.opengis.net/kml/2.2">\n')
kml.write('<Document><name>'+threatWEName+'.kml</name>\n<Style id="none"><PolyStyle><color>99e0e0e0</color><outline>0</outline></PolyStyle></Style>\n')
kml.write('<Style id="low"><PolyStyle><color>9900ffff</color><outline>0</outline></PolyStyle></Style>\n')
kml.write('<Style id="moderate"><PolyStyle><color>990fa7ff</color><outline>0</outline></PolyStyle></Style>\n')
kml.write('<Style id="high"><PolyStyle><color>990000ff</color><outline>0</outline></PolyStyle></Style>\n')
kml.write('<Style id="extreme"><PolyStyle><color>99cc00cc</color><outline>0</outline></PolyStyle></Style>\n')
kml.write('<Folder><name>'+threatWEName+'</name><open>0</open>\n')
kml.write('<ScreenOverlay><name>Product Legend</name><description>Product Legend</description><visibility>1</visibility><Icon>')
# each threatWEName has its own separate legend
# need site id, in lowercase
SiteID = self.getSiteID().lower()
if threatWEName == "StormSurgeThreat":
kml.write('<href>http://www.nws.noaa.gov/images/ghls/' + SiteID + '/stormsurgethreatlegend.png</href>')
elif threatWEName == "WindThreat":
kml.write('<href>http://www.nws.noaa.gov/images/ghls/' + SiteID + '/windthreatlegend.png</href>')
elif threatWEName == "FloodingRainThreat":
kml.write('<href>http://www.nws.noaa.gov/images/ghls/' + SiteID + '/floodingrainthreatlegend.png</href>')
elif threatWEName == "TornadoThreat":
kml.write('<href>http://www.nws.noaa.gov/images/ghls/' + SiteID + '/tornadothreatlegend.png</href>')
# Complete the kml legend
kml.write('</Icon><overlayXY x=".02" y=".01" xunits="fraction" yunits="fraction" /><screenXY x=".02" y=".01" xunits="fraction" yunits="fraction" /><rotationXY x=".02" y=".01" xunits="fraction" yunits="fraction" /><size x="-1" y="-1" xunits="fraction" yunits="fraction" /></ScreenOverlay>')
#threatKeys = self.getDiscreteKeys(threatWEName)
#print "THREATKEYS ARE: ", threatKeys
# initialize a flag. It will only be NO for the first polygon in the file.
flag = 'NO'
for key in threatKeys:
#print "Key:", key
# get index for this key
hazIndex = self.getIndex(key, threatKeys)
#print "hazIndex:", hazIndex
mask = np.equal(threatGrid_kml, hazIndex)
#print "Number of Grid Points: ", sum(sum(mask))
if sum(sum(mask)) == 0:
continue
# make an editArea from the mask
editArea = self.decodeEditArea(mask)
# extract the polygons from the edit area
polygons = editArea.getPolygons(CoordinateType.LATLON)
# pull out the impact statements from the TCVDictionary
# We need to match the threatWEName to the entries found
# in the TCVDictionary
if threatWEName == "TornadoThreat":
threat='Tornado'
elif threatWEName == "StormSurgeThreat":
threat='Storm Surge'
elif threatWEName == "WindThreat":
threat='Wind'
else:
threat='Flooding Rain'
if key =="Extreme":
styleUrl = '#extreme'
elif key == "High":
styleUrl = '#high'
elif key =="Mod":
styleUrl = '#moderate'
elif key =="Elevated":
styleUrl = '#low'
else:
styleUrl = '#none'
# Retrieve the new threat description from the dictionary
threatPhrase = threatPhraseDict[threat][key]
# Extract the appropriate list from the dictionary, join them
# into a string, and make them separate bullets
impactStatement = ""
impactList = TCVDictionary.PotentialImpactStatements[threat][key]
impactStatement = "<br />* ".join(impactList)
impactStatement = "* " + impactStatement
# print "impactList:", impactList
# print "impactStatement:", impactStatement
# Put our kml header together
kmlHeader = '<Placemark><name>Threat Level - ' + threatPhrase + '</name><description><![CDATA[<b>Potential Impacts Include:</b><br />' + impactStatement + ']]></description>\n<styleUrl>' + styleUrl + '</styleUrl>\n'
for i in range(polygons.getNumGeometries()):
poly = polygons.getGeometryN(i)
shell = poly.getExteriorRing()
if shell:
# If shell is true, it's a new polygon
if flag == 'YES':
# If flag is YES, this is not the first polygon we're writing out
# so close the previous polygon before continuing.
kml.write('</Polygon></Placemark>\n')
kml.write(kmlHeader)
kml.write('<Polygon><outerBoundaryIs><LinearRing><coordinates>')
#print "Outer shell coordinates:"
for c in shell.getCoordinates():
#print "x:",c.x,"y:",c.y
line = str(c.x) + ',' + str(c.y) + ',0 \n'
kml.write(line)
kml.write('</coordinates></LinearRing></outerBoundaryIs>')
# Now that we've written at least one polygon, set flag to YES
flag = 'YES'
# CHECK FOR INNER LOOPS (HOLES)
for j in range(poly.getNumInteriorRing()):
hole = poly.getInteriorRingN(j)
#print "Hole",j,"coordinates:"
kml.write('<innerBoundaryIs><LinearRing><coordinates>')
for c in hole.getCoordinates():
#print "x:",c.x,"y:",c.y
line = str(c.x) + ',' + str(c.y) + ',0 \n'
kml.write(line)
kml.write('</coordinates></LinearRing></innerBoundaryIs>')
kmlEnd='</Polygon></Placemark>\n</Folder></Document></kml>\n'
kml.write(kmlEnd)
kml.close()
return
def execute(self):
tr = self.makeTimeRange()
threatlist = ['StormSurgeThreat','WindThreat','FloodingRainThreat','TornadoThreat']
for grid in threatlist:
threatWEName = grid
threatGrid, threatKeys = self.getGrids("Fcst", threatWEName, "SFC", tr)
localEditArea = editAreaDict[threatWEName]
# print "EDIT AREA DICT IS: ", EditArea
editArea = self.getEditArea(localEditArea)
threatEditArea = self.encodeEditArea(editArea)
threatGrid_kml = np.where(threatEditArea, threatGrid, threatGrid-9.0)
self.makeThreatKML(threatWEName,threatKeys,threatGrid_kml)
os.system("/awips2/GFESuite/hti/bin/kml_legend.sh")
return