awips2/cave/com.raytheon.viz.gfe/localization/gfe/userPython/procedures/TCStormSurgeThreat.py
2017-04-21 18:33:55 -06:00

771 lines
35 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.
#
# StormSurgeThreat
#
# Author: Tom LeFebvre/Pablo Santos
# April 20, 2012 - To use gridded MSL TO NAVD and MSL to MLLW
# corrections and to get rid of Very Low.
# Last Modified: June 7, 2012 Shannon White - To fix the handling of time
# for A2 so it works for both real time and displaced real time
# Migrated TC Coastal Flood for AWIPS2. Updated 6/22/2012. S.O.
# March 11, 2014 to adapt to new PSURGE 2.0/PHISH and VDATUM Datasets in A1. PS
# May 21, 2014: for new PHISH but in AWIPS 2: PS/SW
# Aug 13, 2014: To rename SurgeHtPlustTide to InundationMax and incorporate InundationTiming. PS
# Sept 17, 2014: To finalize changes and clean up for 2015initial Baseline Check in.
#
# Sept 18, 2014: Added code to pull grids from NHC via ISC if PHISH not
# Available on time. Left inactive (commented out) for the moment until that can be fully tested later
# in 2014 or in 2015.
# LeFebvre/Santos, July 27, 2015: Expanded Manual options to include Replace and Add options.
# This allows sites to specify manually different threat levels across different edit areas and time ranges.
# See 2015HTIUserGuide for details.
#
# Feb 11, 2016 LeFebvre (16.2.1): Added code to create zero grids and manual grids when
# PSURGE not available. Added checks for current guidance for PHISH and ISC options.
#
# April 14, 2016: Lefebvre/Santos: Added multabledb to restore ISC option
#
# Last Modified:
# 6/20/2016 - Santos: Added code to fix issue of old grid not being deleted when running Manual/Add option.
# 7/15/2016 - Lefebvre/Santos: Added Code to improved Manual Options, numpy compatibility and future builds,
# common methods. Fixed Smoothing Algorithm. inundation grid zeroed out where MHHW <=0.
# 9/8/2016 - Santos: Updated copyISC method to better handle when grids missing in ISC db.
# VERSION 17.1.1 = The one checked in.
# 9/26/16 - LeFebvre - Removed commented out code to pass code review.
# 10/20/16 - Santos - Removed code that stops procedure from running when guidance for current
# advisory is not available and instead advises forecaster.
# 11/3/2016: Santos - Addressed Code Review Comments.
# 12/21/2016: Santos - Added option to adjust InundationMax from manually adjusted InundationTiming grid.
# Also when running with PHISH or PETSS option computes InundationMax from comp max of InundationTiming for consistency. Previously
# they were both retrieved indply from model source and with smoothing it would result in minor differences between
# InundationMax and InundationTiming.
# 01/08/2017: Modified BE CAREFUL line when alerting forecaster PSURGE Data is still from a previous cycle.
# 01/09/2017: Renamed UpdateInunMax in GUI for clarity. Also, introduced on Jan 2017 SWiT ability for procedure to force InundationMax that are > 1 and < 1.5 to 1.5.
# This is because TCV rounds to nearest one foot for categorical HTI threat level consistency with inundation graphic. Not doing this would cause TCV to throw away zones that
# might have more than 3% coverage of inundation > 1 but less than 1.5 altogether. Changing TCV to key on anything with InundationMax >= 1 would not
# do because it would then include zones in TCV with inundation forecasts of less than 1 but >= 0.5 overdoing the threat.
# ----------------------------------------------------------------------------
# The MenuItems list defines the GFE menu item(s) under which the
# Procedure is to appear.
# Possible items are: Populate, Edit, Consistency, Verify, Hazards
MenuItems = ["Populate"]
import TropicalUtility, LogStream
import SmartScript
import numpy as np
import TimeRange
import AbsTime
import time
import sys
VariableList = [("DEFAULT: Typical. Should only be changed in coordination with NHC SS Unit", "", "label"),
("Forecast Confidence? - (Applies to PHISH Only)", "Typical (10% Exceedance; for most systems anytime within 48 hours)",
## "radio", ["Low (Prob-only; 10% Exceedance; for ill behaved systems)",
"radio", ["Typical (10% Exceedance; for most systems anytime within 48 hours)",
"Medium (20% Exceedance; for well-behaved systems within 12 hours of event)",
"High (30% Exceedance; for well-behaved systems within 6-12 hours of event)",
"Higher (40% Exceedance; for well-behaved systems within 6 hours of the event)",
"Highest (50% Exceedance; for well-behaved systems at time of the event)"]),
("Grid Smoothing?", "Yes", "radio", ["Yes","No"]),
("Make grids from \nPHISH, ISC, or Manually?", "PHISH", "radio", ["PHISH", "ISC", "Manually Replace", "Manually Add", "UpdateInunMax (Edit Inundation Timing Grids)"]),
# ("Make grids from \nPHISH, PETSS, ISC, or Manually?", "PHISH", "radio", ["PHISH", "PETSS", "ISC", "Manually Replace", "Manually Add", "UpdateInunMax (Edit Inundation Timing Grids)"]),
("Manual Inundation settings: Time ranges below relative to advisory model cycle", "", "label"),
("Inundation Height:", 1.0, "scale", [0.0, 3.0], 0.1),
("Start Hour for Inundation Timing", 0, "scale", [0.0, 72.0], 6.0),
("End Hour for Inundation Timing", 6, "scale", [0.0, 78.0], 6.0),
]
class Procedure (TropicalUtility.TropicalUtility):
def __init__(self, dbss):
TropicalUtility.TropicalUtility.__init__(self, dbss)
# Compute a base time for this guidance
def baseGuidanceTime(self):
startTime = int((self._gmtime().unixTime() - (2 * 3600)) / (6 * 3600)) * (6 * 3600)
return startTime
# Method to get the average topography for each grid point
def getAvgTopoGrid(self, topodb):
siteID = self.getSiteID()
# print "********************\n TOPO IS: ", topodb
dbName = siteID + "_D2D_" + topodb
weName = "avgTopo"
# timeRange = TimeRange.allTimes().toJavaObj()
trList = self.GM_getWEInventory(weName, dbName, "SFC")
#print "NED Topo list is", trList
if len(trList)== 0:
#print "CRAP!!!"
return
for tr in trList:
# print "My time is", tr
topoGrid = self.getGrids(dbName, weName, "SFC", tr, mode="First")
# Convert topography from meters to feet
topoGrid /= 0.3048
min = -16000
max = 16000.0
mask2 = (topoGrid > max)
topoGrid[topoGrid < min] = -80
topoGrid[mask2] = self.getTopo()[mask2]
return topoGrid
# Make a time range of x hours duration from the current time
def makeNewTimeRange(self, hours):
cTime = int(self._gmtime().unixTime()/ 3600) * 3600
startTime = AbsTime.AbsTime(cTime)
endTime = startTime + (hours * 3600)
timeRange = TimeRange.TimeRange(startTime, endTime)
return timeRange
# Method to find all database versions for the specified model
def getModelIDList(self, matchStr):
# Make a list of all available parameters
availParms = self.availableParms()
# Initialize a list of the database identifiers we want to keep
modelList = []
# Look through every parameter, then check the database id
for pName, level, dbID in availParms:
modelId = dbID.modelIdentifier()
if matchStr in modelId:
if modelId not in modelList:
modelList.append(modelId)
return modelList
# Method to get the selected exceedance height data
def getExceedanceHeight(self, modelName, pctStr, level):
ap = self.availableParms()
dbName = self.getSiteID() + "_D2D_" + modelName
modelIDList = self.getModelIDList(modelName)
modelIDList.sort()
if len(modelIDList) == 0:
return None
surgeModel = modelIDList[-1]
weName = "Surge" + pctStr + "Pct"
trList = self.GM_getWEInventory(weName, dbName, level)
if len(trList) == 0: # No grids found for this database
return None
baseTime = self.baseGuidanceTime() + (6 * 3600) # model data will be offset 6 hours
if baseTime > trList[0].startTime().unixTime():
#modelCycle = AbsTime.AbsTime(self.baseGuidanceTime() - (6*3600))
message = "BE CAREFUL: " + modelName + " IS STILL FROM A PREVIOUS ADVISORY/MODEL CYCLE"
self.statusBarMsg(message, "A")
#return None
#print "Retrieving ", weName, " at ", level
# Make a new time range to span all current data
timeRange = self.GM_makeTimeRange(trList[0].startTime().unixTime(),
trList[-1].endTime().unixTime())
grid = self.getGrids(dbName, weName, level, timeRange, mode="Max")
# for tr in trList:
# grid = self.getGrids(dbName, weName, level, tr, mode="Max")
# Convert current surge values from meters to feet
mask = (grid <= -100)
grid /= 0.3048
grid[mask] = -80.0
# grid[mask] = np.where(mask,surgeVal*3.28, np.float32(-80.0))
return grid # convert meters to feet
# Method to create the inundation timing grids
def makeInundationTiming(self, modelName, pctStr, level, smoothThreatGrid, mutableID, ssea, MHHWMask):
dbName = self.getSiteID() + "_D2D_" + modelName
weName = "Surge" + pctStr + "Pctincr"
#print "Attempting to retrieve: ", weName, level
# get the StormSurgeProb inventory
surgeTRList = self.GM_getWEInventory(weName, dbName, level)
if len(surgeTRList) == 0:
self.statusBarMsg("No PHISH grid found.", "U")
return
# Make timeRanges for all 13 grids. Start with the beginning of the first Phish grid
baseTime = int(surgeTRList[0].startTime().unixTime() / (6 * 3600)) * (6 * 3600) #snap to 6 hour period
trList = self.makeTimingTRs(baseTime)
timingGrids = []
n = 1
for tr in trList:
start = tr.startTime().unixTime() - 6*3600
if n == 1:
starttimeghls = tr.startTime().unixTime() - 48*3600
trdelete = TimeRange.TimeRange(AbsTime.AbsTime(starttimeghls - 100*3600),
AbsTime.AbsTime(starttimeghls + 160*3600))
self.deleteCmd(['InundationTiming'], trdelete)
n = n + 1
end = tr.startTime().unixTime()
tr6 = TimeRange.TimeRange(AbsTime.AbsTime(start),
AbsTime.AbsTime(end))
surgeTR = TimeRange.TimeRange(tr.startTime(), AbsTime.AbsTime(tr.startTime().unixTime() + 3600))
if surgeTR in surgeTRList:
phishGrid = self.getGrids(dbName, weName, level, surgeTR)
else:
phishGrid = self.empty()
#
# For consistency we need to add smoothing here too as we do in execute.
#
if phishGrid is None:
self.statusBarMsg("No PHISH grid available for:" + repr(tr), "S")
continue
#print "SmoothThreatGrid:", smoothThreatGrid
if smoothThreatGrid == "Yes":
# mask = np.greater(phishGrid, 0.0) & ssea
# phishGrid = np.where(np.greater(phishGrid, 0.0), self.GM_smoothGrid(phishGrid, 3, mask), phishGrid)
mask = (phishGrid > 0.0) & ssea
smoothedPhish = self.GM_smoothGrid(phishGrid, 3, mask)
phishGrid[mask] = smoothedPhish[mask]
# Convert units from meters to feet
# mask = (phishGrid <= -100)
grid = phishGrid / 0.3048
# grid[mask] = -80.0
# grid = np.where(phishGrid>-100, phishGrid*3.28, np.float32(-80.0)) # Convert units from meters to feet
grid.clip(0.0, 100.0, grid)
grid[~ssea] = 0.0
grid[MHHWMask] = 0.0
timingGrids.append(grid)
self.createGrid(mutableID, "InundationTiming", "SCALAR", grid, tr6, precision=1)
return trList,timingGrids
def makeInundationMaxGrid(self, timingGrids, trList):
itCube = np.array(timingGrids)
maxGrid = np.amax(itCube, axis=0)
now = int(self._gmtime().unixTime() / 3600) * 3600
maxTimeRange = self.GM_makeTimeRange(now, now + 48 * 3600)
self.createGrid(self.mutableID(), "InundationMax", "SCALAR", maxGrid, maxTimeRange)
return maxGrid
#**************************************************************************************
# THis procedure was written to extract MSL to NAVD corrections from the VDATUMS D2D
# Database. It is not yet implemented because the VDATUMS database has not been
# finalized.
def deleteAllGrids(self, weList):
for weName in weList:
trList = self.GM_getWEInventory(weName)
if len(trList) == 0:
continue
start = trList[0].startTime().unixTime()
end = trList[-1].endTime().unixTime()
tr = self.GM_makeTimeRange(start, end)
self.deleteCmd([weName], tr)
return
def getVDATUM(self, weName, limit):
siteID = self.getSiteID()
dbName = siteID + "_D2D_VDATUMS"
grid = self.getGrids(dbName, weName, "SFC", TimeRange.allTimes(),
mode="First")
if grid is None:
msgStr = weName + " does not exist in the VDATUMS model. "
self.statusBarMsg(msgStr, "S")
mask = (grid <= limit)
grid /= 0.3048
grid[mask] = -80.0
# Converted from meters to feet
return grid
# This procedure was written to extract MSL to NAVD88 corrections from the VDATUMS D2D
# Database.
def getMSLtoNAVD(self):
return self.getVDATUM("MSLtoNAVD88", -0.40)
# This procedure was written to extract MSL to MLLW corrections from the VDATUMS D2D
# Database.
def getMSLtoMLLW(self):
return self.getVDATUM("MSLtoMLLW", 0.0)
# This procedure was written to extract MSL to MHHW corrections from the VDATUMS D2D
# Database.
def getMSLtoMHHW(self):
return self.getVDATUM("MSLtoMHHW", -3.09)
# This procedure was written to extract NAVD88 to MLLW corrections from the VDATUMS D2D
# Database.
def getNAVDtoMLLW(self):
return self.getVDATUM("NAVD88toMLLW", -2.20)
# This procedure was written to extract NAVD88 to MLLW corrections from the VDATUMS D2D
# Database.
def getNAVDtoMHHW(self):
return self.getVDATUM("NAVD88toMHHW", -3.40)
# Copies the specified weather elements in elementList into the Fcst database.
def copyISCGridstoFcst(self, elementList, mutableID):
# Initialize all the grids we plan to return
surgePctGrid = None
surgePctGridMSL = None
surgePctGridMLLW = None
surgePctGridMHHW = None
surgePctGridNAVD = None
baseTime = self.baseGuidanceTime()
# Remove all the grids first before replacing them later
self.deleteCmd(elementList, TimeRange.allTimes())
# Amended To distinguish when inundation grids are available but not datum ones.
for weName in elementList:
#print "Processing ISC ", weName
GridsCheck = True
iscWeName = weName + "nc"
# get the inventory for the ISC grids
try:
trList = self.GM_getWEInventory(iscWeName, "ISC", "SFC")
except:
GridsCheck = False
if len(trList) == 0:
GridsCheck = False
if (weName == "InundationMax" or weName == "InundationTiming") and not GridsCheck:
self.statusBarMsg("No inundation grids found in ISC database for " + iscWeName + ". Stopping. Revert Forecast db.", "S")
return None, None, None, None, None
if not GridsCheck:
self.statusBarMsg("No datum grids in ISC database for " + iscWeName + ". Proceeding without it.", "S")
# Make sure that the ISC grids are current
if GridsCheck:
if baseTime > trList[0].startTime().unixTime():
if weName == "InundationMax" or weName == "InundationTiming":
self.statusBarMsg("ISC grids for inundation element " + iscWeName + " are not current. They correspond to a previous cycle. Aborting. Revert Forecast db.", "S")
return None, None, None, None, None
else:
self.statusBarMsg("ISC grids for datum element " + iscWeName + " are not current. They correspond to a previous cycle. Proceeding without it.", "S")
GridsCheck = False
for tr in trList:
grid = self.getGrids("ISC", iscWeName, "SFC", tr)
if iscWeName == "InundationMaxnc" or iscWeName == "InundationTimingnc":
grid.clip(0.0, 100.0, grid)
else:
grid.clip(-30.0, 100.0, grid)
if iscWeName == "InundationTimingnc":
self.createGrid(mutableID, weName, "SCALAR", grid, tr, precision=2)
elif iscWeName == "InundationMaxnc":
surgePctGrid = grid
self.createGrid(mutableID, weName, "SCALAR", grid, tr, precision=2)
elif iscWeName == "SurgeHtPlusTideMSLnc" and GridsCheck:
surgePctGridMSL = grid
elif iscWeName == "SurgeHtPlusTideMLLWnc" and GridsCheck:
surgePctGridMLLW = grid
elif iscWeName == "SurgeHtPlusTideMHHWnc" and GridsCheck:
surgePctGridMHHW = grid
elif iscWeName == "SurgeHtPlusTideNAVDnc" and GridsCheck:
surgePctGridNAVD = grid
return surgePctGrid,surgePctGridMSL,surgePctGridMLLW,surgePctGridMHHW,surgePctGridNAVD
# Make a list of timeRanges that will be used to make InundationTiming grids
def makeTimingTRs(self, baseTime):
# Make the inundation timing grids
trList = []
for t in range(0, 78, 6):
start = baseTime + t * 3600
end = baseTime + (t + 6) * 3600
tr = TimeRange.TimeRange(AbsTime.AbsTime(start), AbsTime.AbsTime(end))
trList.append(tr)
return trList
def getTimingGrids(self):
baseTime = self.baseGuidanceTime()
gridList = []
trList = self.makeTimingTRs(baseTime)
for tr in trList:
timingGrid = self.empty()
gridList.append(timingGrid)
return trList, gridList
def execute(self, varDict, editArea, timeRange):
t0 = time.time()
self._timeRange = timeRange
mutableID = self.mutableID()
# List of elements
# See if we should copy from ISC. If so, do the copy and exit
smoothThreatGrid = varDict["Grid Smoothing?"]
makeOption = varDict["Make grids from \nPHISH, ISC, or Manually?"]
topodb = "NED"
ssea = self.encodeEditArea("StormSurgeWW_EditArea")
Topo = self.getAvgTopoGrid(topodb)
confidenceStr = varDict["Forecast Confidence? - (Applies to PHISH Only)"]
# extract the percent value from this string
pctPos = confidenceStr.find("%")
pctStr = confidenceStr[pctPos - 2:pctPos]
threatWEName = "StormSurgeThreat"
#print "pctStr is: ", pctStr
surgePctGrid = None
surgePctGridMSL = None
surgePctGridMLLW = None
surgePctGridNHHW = None
surgePctGridNAVD = None
if makeOption == "PHISH" or makeOption == "PETSS":
# Now get the psurge
if makeOption == "PHISH":
modelName = "TPCSurgeProb"
else:
modelName = "PETSS"
surgePctGrid = self.getExceedanceHeight(modelName, pctStr, "FHAG0")
if surgePctGrid is None:
message = "No inundation data found for " + modelName
self.statusBarMsg(message, "S")
return
phishMask = ~ssea
surgePctGrid[phishMask] = 0.0
surgePctGridNAVD = self.getExceedanceHeight(modelName, pctStr, "SFC")
if surgePctGridNAVD is None:
message = "No Surge plus Tide NAVD data found for " + modelName
self.statusBarMsg(message, "S")
return
surgePctGridNAVD[phishMask] = -80.0
if surgePctGrid is None or surgePctGridNAVD is None:
return
#
# The following lines are the gridded vdatum corrections.
#
msltonavd = self.getMSLtoNAVD()
msltomllw = self.getMSLtoMLLW()
msltomhhw = self.getMSLtoMHHW()
navdtomllw = self.getNAVDtoMLLW()
navdtomhhw = self.getNAVDtoMHHW()
# Apply 3x3 smooth within the surge zone
# for values greater than 1 as to not underplay areas adjacent to zero value pixels.
# If you apply a smoother, for consistency among storm surge plus tide and derived
# grids, it must be done here.
if smoothThreatGrid == "Yes":
#mask = np.greater(surgePctGrid, 0.0) & ssea
#surgePctGrid = np.where(np.greater(surgePctGrid, 0.0), self.GM_smoothGrid(surgePctGrid,3, mask), surgePctGrid)
# mask = np.greater(surgePctGridNAVD, -10.0) & ssea
# surgePctGridNAVD = np.where(np.greater(surgePctGridNAVD, -10.0), self.GM_smoothGrid(surgePctGridNAVD,3, mask), surgePctGridNAVD)
mask = (surgePctGridNAVD > -10.0) & ssea
surgePctGridNAVD = self.GM_smoothGrid(surgePctGridNAVD, 3, mask)
# surgePctGridMSL= np.where(mask1, surgePctGridNAVD - msltonavd, np.float32(-80.0)) # MSL Grid
navdMask = (surgePctGridNAVD > -80.0)
mask = (msltonavd > -80.0) & navdMask & ssea
# MSL Grid
surgePctGridMSL = surgePctGridNAVD - msltonavd
surgePctGridMSL[~mask] = -80.0
# surgePctGridMLLW = np.where(np.greater(navdtomllw,-80.0) & np.greater(surgePctGridNAVD,-80.0), \
# surgePctGridNAVD + navdtomllw, np.float32(-80.0)) # MLLW Grid
# MLLW Grid
mask = (navdtomllw > -80.0) & navdMask
surgePctGridMLLW = surgePctGridNAVD + navdtomllw
surgePctGridMLLW[~mask] = -80.0
# surgePctGridMHHW = np.where(np.greater(navdtomhhw,-80.0) & np.greater(surgePctGridNAVD,-80.0), \
# surgePctGridNAVD + navdtomhhw, np.float32(-80.0)) # MHHW Grid
# MHHW Grid
mask = (navdtomhhw > -80.0) & navdMask
surgePctGridMHHW = surgePctGridNAVD + navdtomhhw
surgePctGridMHHW[~mask] = -80.0
# surgeDiffMLLWMHHW = np.where(np.greater(surgePctGridMLLW,-80.0) & np.greater(surgePctGridMHHW, -80.0), \
# surgePctGridMLLW-surgePctGridMHHW, np.float32(-80.0)) # Diff Grid Between MLLW and MHHW
# Diff Grid Between MLLW and MHHW (i.e tidal range)
mask = (surgePctGridMLLW > -80.0) & (surgePctGridMHHW > -80.0)
surgeDiffMLLWMHHW = surgePctGridMLLW - surgePctGridMHHW
surgeDiffMLLWMHHW[~mask] = -80.0
# Mask
MHHWMask = surgePctGridMHHW <= 0.0
#surgePctGrid[MHHWMask] = 0.0
trList,timingGrids = self.makeInundationTiming(modelName, pctStr, "FHAG0", smoothThreatGrid, mutableID, ssea, MHHWMask)
#surgePctGrid and InundationMax recomputed from InundationTiming sequence for consistency
surgePctGrid = self.makeInundationMaxGrid(timingGrids, trList)
elif makeOption == "ISC":
elementList = ["InundationMax","InundationTiming", "SurgeHtPlusTideMSL","SurgeHtPlusTideMLLW",
"SurgeHtPlusTideNAVD","SurgeHtPlusTideMHHW"]
surgePctGrid,surgePctGridMSL,surgePctGridMLLW,surgePctGridMHHW,surgePctGridNAVD = self.copyISCGridstoFcst(elementList, mutableID)
# if you look in CopyISC method if either InundationMax or InundationTiming is missing the procedure stops all together and notifies forecaster.
if surgePctGrid is None:
return
elif makeOption == "Manually Replace" or makeOption == "Manually Add":
inundationHeight = float(varDict["Inundation Height:"])
inunStartHour = float(varDict["Start Hour for Inundation Timing"])
inunEndHour = float(varDict["End Hour for Inundation Timing"])
selectedMask = self.encodeEditArea(editArea)
if not selectedMask.any():
self.statusBarMsg("Please define an area over which to assign the inundation values.", "S")
return
modifyMask = selectedMask & ssea
if not modifyMask.any():
self.statusBarMsg("Please define an area that intersects the StormSurgeEditArea to assign the inundation values.", "S")
return # Calculate the intersection of the SSEditArea and selected editAre
if inunStartHour >= inunEndHour:
self.statusBarMsg("Please define the end hour after the start hour.", "S")
return
surgePctGrid = self.empty()
# Fetch the old grids if we're adding
#if varDict["Make grids from \nPHISH, PETSS, ISC, or Manually?"] == "Manually Add":
if varDict["Make grids from \nPHISH, ISC, or Manually?"] == "Manually Add":
imTRList = self.GM_getWEInventory("InundationMax", mutableID, "SFC")
if len(imTRList) > 0:
imTR = imTRList[0]
surgePctGrid = self.getGrids(mutableID, "InundationMax", "SFC", imTR)
surgePctGrid[modifyMask] = inundationHeight
# Make the timing grids
baseTime = self.baseGuidanceTime()
if makeOption == "Manually Replace": # Make new grids and replace all IT grids
trList, timingGrids = self.getTimingGrids()
for i in range(len(trList)):
# only modify grids in the specified time range
start = trList[i].startTime().unixTime()
end = trList[i].endTime().unixTime()
if (start - baseTime) / 3600 >= inunStartHour and (end - baseTime) / 3600 <= inunEndHour:
timingGrids[i] = surgePctGrid # populate only where needed
timeRange = TimeRange.allTimes()
self.deleteCmd(["InundationTiming"], timeRange)
for i in range(len(trList)):
timingGrids[i].clip(0.0, 100.0, timingGrids[i])
self.createGrid(mutableID, "InundationTiming", "SCALAR", timingGrids[i], trList[i])
elif makeOption == "Manually Add": # Just replace the selected grid points over the selected time
# Fetch the existing IT grids
itTRList = self.GM_getWEInventory("InundationTiming", mutableID, "SFC")
if len(itTRList) == 0:
self.statusBarMsg("No InundationTiming grids found at all.", "S")
return
#Fetch the grids
itGrids = []
trList = []
for tr in itTRList:
start = tr.startTime().unixTime()
end = tr.endTime().unixTime()
#print "Checking tr:", tr
if (start - baseTime) / 3600 >= inunStartHour and (end - baseTime) / 3600 <= inunEndHour:
grid = self.getGrids(mutableID, "InundationTiming", "SFC", tr)
itGrids.append(grid)
trList.append(tr)
if len(itGrids) == 0:
self.statusBarMsg("No InundationTiming grids found for selected start and end hours.", "S")
return
# Surgically insert grid values into the InundationTiming grids over the selected hours
for i in range(len(trList)):
itGrids[i][modifyMask] = inundationHeight # poke in the values
self.createGrid(mutableID, "InundationTiming", "SCALAR", itGrids[i], trList[i])
elif makeOption == "UpdateInunMax (Edit Inundation Timing Grids)":
self.deleteAllGrids(["InundationMax","SurgeHtPlusTideMSL", "SurgeHtPlusTideMLLW",
"SurgeHtPlusTideNAVD", "SurgeHtPlusTideMHHW", "SurgeHtPlusTideMLLW"])
itTRList = self.GM_getWEInventory("InundationTiming", mutableID, "SFC")
if len(itTRList) == 0:
self.statusBarMsg("No InundationTiming grids found at all. Inundation grids required to exist when running with this option. Otherwise run with Manual Replace Option.", "S")
return
timingGrids = []
# Fetch all the timing grids
for tr in itTRList:
grid = self.getGrids(self.mutableID(), "InundationTiming", "SFC", tr)
timingGrids.append(grid)
# Finally create the surge grid which will be saved as the InundationMax
try:
surgePctGrid = self.empty(np.float32)
except AttributeError:
surgePctGrid = self.empty()
surgePctGrid = self.makeInundationMaxGrid(timingGrids, itTRList)
#return
# Done with manual options
# Next line introduced on Jan 2017 SWiT. It forces points in InundationMax that are > 1 and < 1.5 to 1.5. This is because TCV rounds to
# nearest one foot for categorical HTI threat level consistency with inundation graphic. Not doing this would cause TCV to throw away zones that
# might have more than 3% coverage of inundation > 1 but less than 1.5 altogether. Changing TCV to key on anything with InundationMax >= 1 would not
# do because it would then include zones in TCV with inundation forecasts of less than 1 but >= 0.5 overdoing the threat.
surgePctGrid[(surgePctGrid > 1.0) & (surgePctGrid < 1.5)] = 1.5
threatKeys = self.getDiscreteKeys(threatWEName)
# Define a mapping between UI names and key names
# keyMap = {"Very Low" :"Very Low",
keyMap = {"Elevated" : "Elevated",
"Moderate" : "Mod",
"High" : "High",
"Extreme" : "Extreme",
}
threshDict = {} # a dict to store thresholds from the UI
for key in keyMap.keys():
if keyMap[key] == "Extreme":
threshDict[keyMap[key]] = 9
elif keyMap[key] == "High":
threshDict[keyMap[key]] = 6
elif keyMap[key] == "Mod":
threshDict[keyMap[key]] = 3
elif keyMap[key] == "Elevated":
threshDict[keyMap[key]] = 1
#print "threshDict[keyMap[key]]: ", keyMap[key], threshDict[keyMap[key]]
# make a timeRange - 6 hours long
elementList = ["StormSurgeThreat","InundationMax","SurgeHtPlusTideMSL","SurgeHtPlusTideMLLW",
"SurgeHtPlusTideNAVD","SurgeHtPlusTideMHHW"]
# make a new timeRange that will be used to create new grids
timeRange = self.makeNewTimeRange(6)
# Remove old guidance grids and replace them with the new grids
# Delete the old grids first
cTime = int(self._gmtime().unixTime()/ 3600) * 3600
startTime = AbsTime.AbsTime(cTime - 48*3600)
endTime = startTime + 240*3600
deleteTimeRange = TimeRange.TimeRange(startTime, endTime)
for elem in elementList:
self.deleteCmd([elem], deleteTimeRange)
if makeOption != "Manually Replace" and makeOption != "Manually Add" and makeOption != "UpdateInunMax (Edit Inundation Timing Grids)":
if surgePctGridMSL is not None:
surgePctGridMSL.clip(-30.0, 100.0, surgePctGridMSL)
self.createGrid(mutableID, "SurgeHtPlusTideMSL", "SCALAR", surgePctGridMSL,
timeRange, precision=2)
if surgePctGridMLLW is not None:
surgePctGridMLLW.clip(-30.0, 100.0, surgePctGridMLLW)
self.createGrid(mutableID, "SurgeHtPlusTideMLLW", "SCALAR", surgePctGridMLLW,
timeRange, precision=2)
if surgePctGridNAVD is not None:
surgePctGridNAVD.clip(-30.0, 100.0, surgePctGridNAVD)
self.createGrid(mutableID, "SurgeHtPlusTideNAVD", "SCALAR", surgePctGridNAVD,
timeRange, precision=2)
if surgePctGridMHHW is not None:
surgePctGridMHHW.clip(-30.0, 100.0, surgePctGridMHHW)
self.createGrid(mutableID, "SurgeHtPlusTideMHHW", "SCALAR", surgePctGridMHHW,
timeRange, precision=2)
# Make the grid. Start with the existing grid if we have one otherwise zeros
coastalThreat = self.empty(np.int8)
surgePctGrid.clip(0.0, 100.0, surgePctGrid)
self.createGrid(mutableID, "InundationMax", "SCALAR", surgePctGrid, timeRange, precision=2)
# Yet another list to define the order in which we set grid values
# This order must be ranked lowest to highest
#keyList = ["Very Low", "Elevated", "Mod", "High", "Extreme"]
keyList = ["Elevated", "Mod", "High", "Extreme"]
# Set the grid values based on the surgePctGrid grid and thresholds
for key in keyList:
#print "THRESHOLD FOR KEY IS: ", key, threshDict[key]
thresh = threshDict[key]
keyIndex = self.getIndex(key, threatKeys)
#coastalMask = ssea & np.greater_equal(surgePctGrid, thresh)
coastalMask = ssea & np.greater(surgePctGrid, thresh)
coastalThreat[coastalMask] = keyIndex
# create the CoastalThreat Grid
self.createGrid(mutableID, threatWEName, "DISCRETE",
(coastalThreat, threatKeys), timeRange,
discreteKeys=threatKeys,
discreteOverlap=0,
discreteAuxDataLength=2,
defaultColorTable="Hazards")
t1 = time.time()
LogStream.logEvent("Finished TCStormSurgeThreat in %f.4 ms" % ((t1-t0) * 1000))
return