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

264 lines
9.7 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.
#
# serpFile - version 2.0
#
# Does the equivalent of the plain 'serp' tool - but gets the location
# and control point information from a file. Thus, this tool needs no
# user input and can be run as part of a cronjob, etc.
#
# The file is a comma delimited file where each data line contains a
# station ID (ignored), a latitude, a longitude, and a data value.
# Typical data lines might look like this:
#
# BOI,43.57,-116.22,50.5
# TWF,42.48,-114.48,43 # comment about this line
#
# To make the file more readable, you can have comment lines which
# start with a # character or are simply whitespace.
#
# Any lines with less than 4 comma delimited values are ignored. Lines
# with more than 4 comma delimited values are potentially used - but
# fields after the first 4 are ignored.
#
# Stations located off the GFE grid are ignored.
#
# Multiple sites lying on the same GFE gridpoint are ignored (only
# the first one is used - and a status bar message is produced
# which tells you that the second (or more) station is being ignored).
#
# No timeRange checking is done - the tool simply operates on the
# current grid, using the values supplied in the file and stores the
# results back into the same grid. Clipping is performed so that the
# values of the new grid do not exceed the allowable values for the
# grid.
#
# This works for SCALAR grids only - not vectors or weather/discrete
# elements
#
# Author: Tim Barker - SOO BOI (serp tool is from Les Colin)
# 2014/06/11 - Modified a couple of things to make it cleaner in A2
# 2010/08/05 - updated to use ObjAnal utility
# 2003/10/16 - original implementation based on serp tool
##
# 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.
##
#=======================================================================
# START OF CONFIGURATION SECTION
#
# The filename to read
#
FILENAME="/tmp/lsrinfo.dat"
#
# If you wish to include elevation adjustment (so that adjustments
# are based on elevation differences as well as horizontal distance
# from the point) then set elevation_factor to a non-zero value.
#
# elevation_factor should be in units of feet/km.
#
# If you set it to 1, then 1 foot of elevation difference is
# equivalent to 1km of horizontal distance (this means
# that elevation is VERY important in the analysis).
#
# if you set it to 1000, then 1000 feet of elevation
# difference is equal to 1 km of horizontal distance
# (this means that elevation is NOT important to the
# analysis).
#
# To turn off elevation completely - set the elevation_factor to zero.
# which is the default
#
# A value of 36 feet/km seems work reasonably well for including SOME
# influence of elevation - but not too much.
#
elevation_factor=0.0
#
# END OF CONFIGURATION SECTION
#=======================================================================
ToolType = "numeric"
WeatherElementEdited = "variableElement"
ScreenList = ["SCALAR"]
import numpy as np
import SmartScript
import ObjAnal
import os,re
class Tool (SmartScript.SmartScript):
def __init__(self, dbss):
SmartScript.SmartScript.__init__(self, dbss)
self._dbss=dbss
def execute(self, Topo, variableElement, variableElement_GridInfo):
"Match specified points to values in file using objective analysis"
print("Tool serp_file starting")
#
# Setup the utility
#
self.OA=ObjAnal.ObjAnal(self._dbss)
#
# value limits for grid being edited
#
self.valmax=variableElement_GridInfo.getMaxValue()
self.valmin=variableElement_GridInfo.getMinValue()
#
# read data lines from file
#
filename=FILENAME
datalines=self.readFILE(filename)
if len(datalines)<1:
msg="No data in file %s, so grid left unchanged"%(filename)
self.statusBarMsg(msg,"S")
print(msg)
self.cancel()
#
# setup data locations from file
#
valuelist=self.getDataLocations(datalines,variableElement,Topo)
if (len(valuelist)<1):
msg="No valid data in file %s, so grid left unchanged"%(filename)
self.statusBarMsg(msg,"S")
print(msg)
self.cancel()
#
#
#
new=self.OA.ObjectiveAnalysis(valuelist,variableElement,"serp",
elevfactor=elevation_factor)
#
# clip to grid min/max
#
newclip=np.clip(new,self.valmin,self.valmax)
print("Tool serp_file complete")
return newclip
#=================================================================
#
# Read data values from the data lines
#
def getDataLocations(self,datalines,variableElement,Topo):
#
# setup storage for location info
#
valuelist=[]
self.xloclist=[]
self.yloclist=[]
#
# decode data lines into location info
#
for line in datalines:
(id,latstr,lonstr,valuestr)=line.split(",",3)
latstr=re.sub('[^-0123456789.]','',latstr)
lonstr=re.sub('[^-0123456789.]','',lonstr)
valuestr=re.sub(',.*$','',valuestr) # get rid of any more comma-delimited things at end of line
valuestr=re.sub('#.*$','',valuestr) # get rid of any inline comments at end of field
valuestr=re.sub('[^-0123456789.]','',valuestr) # get rid of non-numeric characters in remaining value
latf=float(latstr)
lonf=float(lonstr)
if (latf<-90.0)or(latf>90.0)or(lonf<-180.0)or(lonf>180.0):
msg="Invalid lat/lon ignored: %s"%line
self.statusBarMsg(msg,"S")
print(msg)
continue
#
# make sure point is on grid
#
(x,y)=self.getGridCell(latf,lonf)
if ((x is None)or(y is None)):
msg="Data for %s ignored (%6.3f,%8.3f) - location not on GFE grid" % (id,latf,lonf)
self.statusBarMsg(msg,"S")
print(msg)
continue
xint=int(x)
yint=int(y)
#
# Make sure point has not already been specified
#
if len(self.xloclist)>0:
skip=0
for i in range(len(self.xloclist)):
if ((self.xloclist[i]==xint) and (self.yloclist[i]==yint)):
msg="Data for %s ignored - data for this GFE gridpoint already specified"%(id)
self.statusBarMsg(msg,"S")
print(msg)
skip=1
break
if skip==1:
continue
#
# Make sure value is valid
#
valf=float(valuestr)
if (valf<self.valmin):
msg="%s value of %.3f clipped to allowable range of %f-%f"%(id,valf,self.valmin,self.valmax)
self.statusBarMsg(msg,"S")
print(msg)
valf=float(self.valmin)
if (valf>self.valmax):
msg="%s value of %.3f clipped to allowable range of %f-%f"%(id,valf,self.valmin,self.valmax)
self.statusBarMsg(msg,"S")
print(msg)
valf=float(self.valmax)
#
# add it to list
#
valuelist.append((id,xint,yint,Topo[yint,xint],valf))
self.xloclist.append(xint)
self.yloclist.append(yint)
return valuelist
#===================================================================
# readFILE - read specified FILE returning only data lines where
# 4 or more comma delimited values occur
#
def readFILE(self,filename):
datalines=[]
#
# make sure the file exists
#
if (not os.path.exists(filename)):
msg="Could not find file %s" % (filename)
self.statusBarMsg(msg,"S")
print(msg)
return datalines
#
# read the file
#
with open(filename) as f:
lines = f.readlines()
#
# get only data lines
#
for line in lines:
stripline=line.strip() # ignore whitespace at begin/end
if len(stripline)<1:
continue
if line[0:1]=="#": # ignore comment lines
continue
pieces=stripline.split(",",3)
if len(pieces)!=4: # ignore lines with less than 4 comma fields
continue
datalines.append(stripline)
return datalines