367 lines
15 KiB
Python
367 lines
15 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.
|
|
#
|
|
# MoveFeatureBySpeed
|
|
#
|
|
# Author: Thomas R. Mazza
|
|
# adapted from EditAreaAdjust tool written by
|
|
# Les Colin
|
|
# Additional Contribution: Todd Lericos
|
|
# Last Updated: Tue 10 Jun 8
|
|
# last Submitted to str: Tue 10 Jun 8
|
|
# ----------------------------------------------------------------------------
|
|
# SOFTWARE HISTORY
|
|
#
|
|
# Date Ticket# Engineer Description
|
|
# ------------- -------- --------- --------------------------------------------
|
|
# Apr 19, 2018 7271 randerso Renamed and/or removed models. Code cleanup.
|
|
#
|
|
##
|
|
|
|
##
|
|
# 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 LogStream, time
|
|
ToolType = "numeric"
|
|
WeatherElementEdited = "variableElement"
|
|
from numpy import *
|
|
ScreenList = ["SCALAR","VECTOR"]
|
|
HideTool = 1
|
|
import AbsTime
|
|
from math import *
|
|
|
|
####### CONFIGURATION SECTION #########################################################################
|
|
#
|
|
# Add or delete models according to whether or not they are available at your office.
|
|
#
|
|
|
|
sourceList = ["NAM", "GFS", "RAP", "wrfnmm", "wrfarw", "WSETA"]
|
|
|
|
threeHour = ["NAM"]
|
|
sixHour = ["GFS"]
|
|
RAP = ["RAP"]
|
|
|
|
resolution = 2.5
|
|
|
|
#
|
|
####### END CONFIGURATION SECTION #####################################################################
|
|
|
|
sourceList.append(("Fcst"))
|
|
sourceList.append(("Observed (enter below)"))
|
|
|
|
|
|
import SmartScript
|
|
## For available commands, see SmartScript
|
|
|
|
VariableList = []
|
|
|
|
VariableList.append(("Source:", "Observed (enter below)", "radio", sourceList))
|
|
VariableList.append(("Wind Level if using model:","MB700","radio",["MB925","MB850","MB700","MB500", "MB925-850", "MB850-700", "MB925-700", "MB925-500", "MB850-500", "MB700-500"]))
|
|
VariableList.append(("Movement Speed (Kts):", "15", "numeric"))
|
|
VariableList.append(("Movement Direction:" , "90", "numeric"))
|
|
VariableList.append(("Backfill upstream edges with:", "Original data", "radio", ["Original data", "Data from very edge\n(Fcst or Model only)", "Zeros"]))
|
|
|
|
|
|
# Set up Class
|
|
|
|
class Tool (SmartScript.SmartScript):
|
|
def __init__(self, dbss):
|
|
SmartScript.SmartScript.__init__(self, dbss)
|
|
|
|
|
|
def execute(self, variableElement, variableElement_GridInfo, GridTimeRange, varDict):
|
|
"Extrapolate features foward or backward in time based on observed or model speed"
|
|
self.logToolUse("Smart Tool execution")
|
|
|
|
modelList = ["NAM", "GFS", "RAP", "wrfnmm", "wrfarw", "WSETA", "Fcst"]
|
|
|
|
if varDict["Source:"] not in modelList and varDict["Source:"] != "Observed (enter below)":
|
|
modelList.append((varDict["Source:"]))
|
|
|
|
Backfill = varDict["Backfill upstream edges with:"]
|
|
|
|
xLimit = len(variableElement[0])
|
|
yLimit = len(variableElement)
|
|
|
|
#########################################################################################
|
|
#
|
|
# Get the source for the speed of motion
|
|
|
|
source = varDict["Source:"]
|
|
levels = []
|
|
if source in modelList:
|
|
site = self.getSiteID()
|
|
|
|
if source == "Fcst":
|
|
level = "SFC"
|
|
msource = source
|
|
wind = "Wind"
|
|
Wind=self.getGrids(msource,wind,level,GridTimeRange)
|
|
(speed, dir) = (Wind[0], Wind[1])
|
|
else:
|
|
#------------------------------------------------------------------------
|
|
# Modification made by CAR to handle gaps in Model grids
|
|
#--------------------------------------------------------------------------
|
|
# This section expands the range of time to look for model data to use.
|
|
# Model data is only available at certain times (e.g. NAM: 00,03,06..etc)
|
|
# Therefore, if the grid to be created is not at a time where model data
|
|
# exists...then this code expands the time range to grab the nearest model
|
|
# data. This range will be different for each model. Therefore...if models
|
|
# are added to the top this section must be changed.
|
|
#
|
|
# Edited: Todd Lericos
|
|
# Date: 3 Aug 2006
|
|
#
|
|
present = AbsTime.current()
|
|
today = AbsTime.absTimeYMD(present.year, present.month, present.day)
|
|
startTimeModel = (GridTimeRange.startTime() - today) // 3600
|
|
# print "over at Move", startTimeModel
|
|
|
|
if source in threeHour:
|
|
self.modelRange = self.createTimeRange(startTimeModel-1, startTimeModel+2, "Zulu")
|
|
elif source in sixHour:
|
|
self.modelRange = self.createTimeRange(startTimeModel-2, startTimeModel+4, "Zulu")
|
|
else:
|
|
self.modelRange = GridTimeRange
|
|
|
|
level = varDict["Wind Level if using model:"]
|
|
msource=site+"_D2D_"+source
|
|
wind = "wind"
|
|
if level in ["MB925","MB850","MB700","MB500"]:
|
|
Wind=self.getGrids(msource,wind,level,self.modelRange)
|
|
(speed, dir) = (Wind[0], Wind[1])
|
|
else:
|
|
if level == "MB925-850":
|
|
levels = self.buildLevels(950, 850, source)
|
|
elif level == "MB850-700":
|
|
levels = self.buildLevels(950, 700, source)
|
|
elif level == "MB925-700":
|
|
levels = self.buildLevels(925, 700, source)
|
|
elif level == "MB925-500":
|
|
levels = self.buildLevels(925, 500, source)
|
|
elif level == "MB850-500":
|
|
levels = self.buildLevels(850, 500, source)
|
|
elif level == "MB700-500":
|
|
levels = self.buildLevels(700, 500, source)
|
|
i = 0
|
|
j = 0
|
|
for k in range(len(levels)):
|
|
Wind=self.getGrids(msource,wind,levels[k],GridTimeRange)
|
|
(u,v)=self.MagDirToUV(Wind[0], Wind[1])
|
|
i += u
|
|
j += v
|
|
u = i / len(levels)
|
|
v = j / len(levels)
|
|
|
|
#
|
|
# convert speed/dir arrays to speed/dir arrays
|
|
#
|
|
(speed, dir) = self.UVToMagDir(u,v)
|
|
#
|
|
# convert from m/s to kts:
|
|
#
|
|
if wind == "wind": ## have model data - need to convert from m/s to kts.
|
|
speed *= 1.94384449244
|
|
|
|
else:
|
|
|
|
speed = varDict["Movement Speed (Kts):"]
|
|
dir = varDict["Movement Direction:"]
|
|
|
|
|
|
########################################################################################
|
|
#
|
|
# OK, we have the two components of motion, in kts. Now convert to kph and compute
|
|
# movement over the grid.
|
|
|
|
if source == "Observed (enter below)":
|
|
|
|
speed *= 1.852 / resolution ### 1 kt = 1.852 kph.
|
|
dir = abs(abs(360 - dir) + 90)
|
|
theta = dir % 360
|
|
rads = pi * theta / 180
|
|
|
|
x = int(self.round(speed * cos(rads), "Nearest", 1))
|
|
y = -int(self.round(speed * sin(rads), "Nearest", 1))
|
|
|
|
if type(variableElement_GridInfo) is not str:
|
|
|
|
newT = zeros(shape(variableElement),dtype=float64) - 80.0 # default value for T
|
|
if Backfill == "Zeros":
|
|
changedMask = zeros(shape(variableElement),dtype=float64) # default value for T
|
|
if x > 0 and y > 0:
|
|
newT[y:, x:] = variableElement[:-y, :-x]
|
|
if Backfill == "Zeros":
|
|
changedMask[y:, x:] = 1
|
|
elif x > 0 and y < 0:
|
|
newT[:y, x:] = variableElement[-y:, :-x]
|
|
if Backfill == "Zeros":
|
|
changedMask[:y, x:] = 1
|
|
elif x < 0 and y > 0:
|
|
newT[y:, :x] = variableElement[:-y, -x:]
|
|
if Backfill == "Zeros":
|
|
changedMask[y:, :x] = 1
|
|
elif x < 0 and y < 0:
|
|
newT[:y, :x] = variableElement[-y:, -x:]
|
|
if Backfill == "Zeros":
|
|
changedMask[:y, :x] = 1
|
|
elif x == 0 and y > 0:
|
|
newT[y:, x:] = variableElement[:-y, :]
|
|
if Backfill == "Zeros":
|
|
changedMask[y:, x:] = 1
|
|
elif x == 0 and y < 0:
|
|
newT[:y, x:] = variableElement[-y:, :]
|
|
if Backfill == "Zeros":
|
|
changedMask[:y, x:] = 1
|
|
elif x > 0 and y == 0:
|
|
newT[y:, x:] = variableElement[:, :-x]
|
|
if Backfill == "Zeros":
|
|
changedMask[y:, x:] = 1
|
|
elif x < 0 and y == 0:
|
|
newT[y:, :x] = variableElement[:, -x:]
|
|
if Backfill == "Zeros":
|
|
changedMask[y:, :x] = 1
|
|
|
|
else:
|
|
|
|
newT0 = zeros(shape(variableElement[0]),dtype=int32) - 80.0
|
|
newT1 = zeros(shape(variableElement[1]),dtype=int32) - 80.0
|
|
oldT0,oldT1 = variableElement
|
|
if Backfill == "Zeros":
|
|
changedMask = zeros(shape(variableElement),dtype=float64) # default value for T
|
|
|
|
if x > 0 and y > 0:
|
|
newT0[y:, x:] = oldT0[:-y, :-x]
|
|
newT1[y:, x:] = oldT1[:-y, :-x]
|
|
elif x > 0 and y < 0:
|
|
newT0[:y, x:] = oldT0[-y:, :-x]
|
|
newT1[:y, x:] = oldT1[-y:, :-x]
|
|
elif x < 0 and y > 0:
|
|
newT0[y:, :x] = oldT0[:-y, -x:]
|
|
newT1[y:, :x] = oldT1[:-y, -x:]
|
|
elif x < 0 and y < 0:
|
|
newT0[:y, :x] = oldT0[-y:, -x:]
|
|
newT1[:y, :x] = oldT1[-y:, -x:]
|
|
elif x == 0 and y > 0:
|
|
newT0[y:, x:] = oldT0[:-y, :]
|
|
newT1[y:, x:] = oldT1[:-y, :]
|
|
elif x == 0 and y < 0:
|
|
newT0[:y, x:] = oldT0[-y:, :]
|
|
newT1[:y, x:] = oldT1[-y:, :]
|
|
elif x > 0 and y == 0:
|
|
newT0[y:, x:] = oldT0[:, :-x]
|
|
newT1[y:, x:] = oldT1[:, :-x]
|
|
elif x < 0 and y == 0:
|
|
newT0[y:, :x] = oldT0[:, -x:]
|
|
newT1[y:, :x] = oldT1[:, -x:]
|
|
|
|
if Backfill == "Zeros":
|
|
changedMask[y:, x:] = 1
|
|
|
|
newT = newT0,newT1
|
|
|
|
else: # source is a model
|
|
speed1 = speed * 1.852 / resolution ### 1 kt = 1.852 kph.
|
|
# print "in move feature tool, missing hours ", missingHours
|
|
theta = dir % 360
|
|
|
|
(u,v)=self.MagDirToUV(speed1,theta)
|
|
u /= resolution
|
|
v /= resolution
|
|
if varDict["Movement Direction:"] < 0:
|
|
u *= -1
|
|
v *= -1
|
|
newVariableElement = zeros(shape(variableElement),dtype=int16)
|
|
if Backfill != "Original data":
|
|
changedMask = zeros(shape(variableElement),dtype=float64) + 1
|
|
|
|
for x in range(len(variableElement[0])):
|
|
|
|
for y in range(len(variableElement)):
|
|
i = u[y,x]
|
|
j = v[y,x]
|
|
a = x - i
|
|
b = y - j
|
|
a = int(clip(a,0,xLimit - 1))
|
|
b = int(clip(b,0,yLimit - 1))
|
|
|
|
newVariableElement[y,x] = variableElement[b,a]
|
|
|
|
if Backfill == "Data from very edge\n(Fcst or Model only)":
|
|
|
|
if u[y,x] > x :
|
|
newVariableElement[y,x] = newVariableElement[y,0]
|
|
if u[y,x] < 0 and u[y,x] > xLimit - x:
|
|
newVariableElement[y,x] = newVariableElement[y,xLimit]
|
|
if v[y,x] > y:
|
|
newVariableElement[y,x] = newVariableElement[0,x]
|
|
if v[y,x] < 0 and v[y,x] > yLimit - y:
|
|
newVariableElement[y,x] = newVariableElement[yLimit,x]
|
|
|
|
elif Backfill == "Zeros":
|
|
|
|
if u[y,x] > x :
|
|
changedMask[y, x] = 0
|
|
if u[y,x] < 0 and abs(u[y,x]) > xLimit - x:
|
|
changedMask[y, x] = 0
|
|
if v[y,x] > y:
|
|
changedMask[y, x] = 0
|
|
if v[y,x] < 0 and abs(v[y,x]) > yLimit - y:
|
|
changedMask[y, x] = 0
|
|
|
|
newT = newVariableElement
|
|
|
|
newT = where(less(newT, -30), variableElement, newT)
|
|
if Backfill == "Zeros":
|
|
newT[(changedMask <= 0)] = 0
|
|
# Return the new value
|
|
|
|
return newT.astype(variableElement.dtype)
|
|
|
|
def buildLevels(self, base, top, model):
|
|
plevels = []
|
|
if model in RAP:
|
|
if base == 925:
|
|
base = 950
|
|
plevel = base
|
|
while plevel >= top:
|
|
plevels.append(("MB" + str(plevel)))
|
|
plevel -= 50
|
|
else:
|
|
plevel = base
|
|
while plevel >= top:
|
|
plevels.append(("MB" + str(plevel)))
|
|
plevel -= 25
|
|
return plevels
|
|
|
|
|
|
|
|
def logToolUse(self,string):
|
|
gtime=time.gmtime()
|
|
ts="%4.4d/%2.2d/%2.2d %2.2d:%2.2d:%2.2d"%(gtime[0],gtime[1],gtime[2],
|
|
gtime[3],gtime[4],gtime[5])
|
|
LogStream.logEvent("%s| %s" % (ts,string))
|