awips2/cave/com.raytheon.viz.gfe/localization/gfe/userPython/textUtilities/TropicalHazards.py

1292 lines
47 KiB
Python
Raw Normal View History

2022-05-05 12:34:50 -05:00
# 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.
#
# TropicalHazards
#
# Author: Matthew H. Belk WFO BOX
# ----------------------------------------------------------------------------
#
# SOFTWARE HISTORY
#
# Date Ticket# Engineer Description
# ------------- -------- --------- --------------------------------------------
# Jun 22, 2013 mbelk Initial creation
# Jul 14, 2016 mbelk Changes for 2017 season
# Sep 19, 2016 19293 randerso Initial baseline check in
# May 22, 2019 21290 mbelk/ryu Fix error with importing TCVNHC formatters
#
########################################################################
##
# 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 re
import errno
import functools
import JsonSupport, LocalizationSupport
import LogStream
import ModuleAccessor
import pprint as pp
class TropicalHazards:
def __init__(self):
#-----------------------------------------------------------------------
# Make a link to the TropicalAreaDictionary
# Access the information for the breakpoint area(s) if available
self._tropicalAreaDict = \
ModuleAccessor.ModuleAccessor().variable("AreaDictionary",
"AreaDictionary")
# Then make a cache of sorted hash keys to this dictionary
self._tropicalAreaDictKeys = sorted(self._tropicalAreaDict.keys())
################################################################################
# New common utility methods for tropical hazard formatters
################################################################################
def allowedHazards(self):
"""TropicalHazards version of GenericHazards.allowedHazards.
This method defines the allowed hazards for tropical hazard products.
"""
tropicalActions = ["NEW", "EXA", "CAN", "CON"]
return [
('HU.W',tropicalActions,'Hurricane'),
('HU.A',tropicalActions,'Hurricane'),
('SS.W',tropicalActions,'Surge'),
('SS.A',tropicalActions,'Surge'),
('TR.W',tropicalActions,'Tropical'),
('TR.A',tropicalActions,'Tropical'),
]
################################################################################
# New common utility methods for tropical hazard formatters
################################################################################
#===========================================================================
# Define a method to determine which WFOs are impacted based on a list of
# affected edit area ID's
def _getAffectedWFOs(self, idList):
"""TropicalHazards addition of _getAffectedWFOs.
This method will produce a list of NWS WFOs impacted by tropical
hazards with the specified identifiers.
Arguments:
idList -> list TropicalAreaDictionary keys
"""
# Look at each breakpoint segment in this list
for id in idList:
# Get the AreaDictionary entry for this segment
if id in self._tropicalAreaDict:
entry = self._tropicalAreaDict[id]
else:
LogStream.logProblem(\
"AreaDictionary missing definition for [" + id + "].")
continue
# Get the WFO responsible for this segment - always use the WFO of the
# starting point to avoid including WFOs unnecessarily
if 'wfo' in entry:
wfo = entry['wfo'].strip()
else:
LogStream.logProblem(\
"AreaDictionary missing WFO definition for [" + id + "].")
continue
# If we have a valid WFO identifier, and it is not already noted in the
# impacted WFO list
if len(wfo) > 0 and wfo not in self._wfoList:
# Add this WFO to the list of impacted WFOs
self._wfoList.append(wfo)
#===========================================================================
# Define a method to format a breakpoint text line within a TCV segment
def _formatTCVline(self, entry, type="start"):
"""TropicalHazards addition of _formatTCVline.
This method will produce a list of NWS WFOs impacted by tropical
hazards with the specified identifiers.
Arguments:
entry -> TropicalAreaDictionary entry for an edit area
type -> type of breakpoint to produce (optional, defaults to start)
"""
# If this is not an ending point
if type != "end":
# Get the information we need
point = entry["startBreakpoint"].strip()
lat = entry["startLat"].strip()
lon = entry["startLon"].strip()
state = entry["startState"].strip()
# Otherwise - get the end point info for this segment
else:
# Get the information we need
point = entry["endBreakpoint"].strip()
lat = entry["endLat"].strip()
lon = entry["endLon"].strip()
state = entry["endState"].strip()
# Clean up the state so there are no spaces or dashes
state = re.sub("[ _-]+", "", state)
#-----------------------------------------------------------------------
# If this is not the border of a state or country
if re.search("(?i)border", point) is None:
# Add the state/country
point += "-" + state
#-----------------------------------------------------------------------
# Append the appropriate hemisphere of the latitude
if lat.find("-") != -1:
lat += "S"
lat = lat.replace("-", "")
else:
lat += "N"
#-----------------------------------------------------------------------
# Append the appropriate hemisphere of the longitude
if lon.find("-") != -1:
lon += "W"
lon = lon.replace("-", "")
else:
lon += "E"
#-----------------------------------------------------------------------
# Now construct the final formatted line
text = "%-36s%6s%7s\n" % (re.sub("[ _]+", "-", point) + " ", lat, lon)
# Return the text
return text
#===========================================================================
# Define a method to sort breakpoint record keys
@property
def _sortBreakpoints(self):
"""TropicalHazards addition of _sortBreakpoints.
This method will produce a sorted list of breakpoint segments. This
ensures the order of contiguous breakpoint segments
"""
def cmpfunc(a, b):
# Make a list of valid string identifier parts
validTypes = [
"LN", # mainland segments - any country
"KEY", # Florida Keys
"ISL", # islands
"CUBA", # Cuba
"HISP", # Hispaniola
"NAI", # North Atlantic islands
"WTDE", # Deleware Bay
"WTTP", # Tidal Potomac
"WTCP", # Chesapeake Bay
"WTPT", # Generic water points
# Zones used by VTEC
"GYC", # Guyana
"VEC", # Venezuela
"COC", # Colombia
"PAC", # Panama
"CRC", # Costa Rica
"NIC", # Nicaragua
"HNC", # Honduras
"GTC", # Guatemala
"BZC", # Belize
"MXC", # Mexico
"USC", # United States
"CNC", # Canada
"KEC", # Dry Tortugas
"AWC", # Aruba
"CWC", # Curacao
"TTC", # Trinidad and Tobago
"BBC", # Barbados
"LCC", # St. Lucia
"MQC", # France - Caribbean
"AGC", # Antigua and Barbuda
"BSC", # Bahamas
"BMC", # Bermuda
"JMC", # Jamaica
"KYC", # Cayman Islands
"CUC", # Cuba
"DOC", # Dominican Republic
"HTC", # Haiti
"PMC", # France - North Atlantic
"LOC", # Lake_Okeechobee
"FBC", # Florida Bay
"PSC", # Pamlico Sound
"ASC", # Albemarle Sound
"TXZ", # Texas
"LAZ", # Louisiana
"MSZ", # Mississippi
"ALZ", # Alabama
"FLZ", # Florida
"GAZ", # Georgia
"SCZ", # South Carolina
"NCZ", # North Carolina
"VAZ", # Virginia
"MDZ", # Maryland
"DCZ", # District of Columbia
"DEZ", # Deleware
"NJZ", # New Jersey
"NYZ", # New York
"CTZ", # Connecticut
"RIZ", # Rhode Island
"MAZ", # Massachusetts
"NHZ", # New Hampshire
"MEZ", # Maine
"NMZ", # New Mexico
"ARZ", # Arkansas
"OKZ", # Oklahoma
"MOZ", # Missouri
"TNZ", # Tennessee
"WVZ", # West Virginia
"PAZ", # Pennsylvania
"VTZ", # Vermont
"PRZ", # Puerto Rico
"VIZ", # U.S. Virgin Islands
"RE", # General edit area collection
]
# Get the first part of each identifier
aSeg = a.split("_")[0]
bSeg = b.split("_")[0]
# Get ready to split these identifiers into alpha and numeric parts
aSegType = ""
bSegType = ""
aSegNum = ""
bSegNum = ""
# Start with the alpha components
for c in aSeg:
if c.isalpha():
aSegType += c
for c in bSeg:
if c.isalpha():
bSegType += c
# Now get the numeric components
for c in aSeg:
if c.isnumeric():
aSegNum += c
for c in bSeg:
if c.isnumeric():
bSegNum += c
# Determine the order of these areas based on segment type first
aTypeIndex = validTypes.index(aSegType)
try:
bTypeIndex = validTypes.index(bSegType)
except:
bTypeIndex = aTypeIndex
bSegNum = aSegNum
# Try to determine order based on segment type
if aTypeIndex < bTypeIndex:
return -1
elif bTypeIndex < aTypeIndex:
return 1
# If the segment types are the same, use the numeric component last
if int(aSegNum) < int(bSegNum):
return -1
elif int(bSegNum) < int(aSegNum):
return 1
else:
# # print "ERROR!!!!!!! Segment names are equal!!!!!!!"
return 0
return functools.cmp_to_key(cmpfunc)
#===========================================================================
# Define a method to organize breakpoint segment zones by government
def _organizeZonesByGovt(self):
"""TropicalHazards addition of _organizeZonesByGovt.
This method will produce a dictionary of sorted lists where each list
contains all the zone identifiers for which that government is the
responsible entity.
"""
# Create a dictionary to hold all zones managed by a government
zoneDict = {}
# Also make a list to hold governments in the order we find them
govtList = []
# Initialize some lists for handling zone codes
ugcList = []
ugcListUsa = []
#-----------------------------------------------------------------------
# Look at each edit area we have
for key in self._tropicalAreaDictKeys:
# Get the type of this zone
zoneId = key.split("_")[0]
zoneType = ""
# Get all the letters from this zone ID
for c in zoneId:
if not c.isdigit():
zoneType += c
# If this is primary representation of segment zones
# (this is to avoid duplication with the individual zones)
# Make an exception for US zones though
if zoneType in ["LN", "KEY", "CUBA", "HISP", "ISL", "NAI", "WTDE",
"WTTP", "WTCP", "WTPT", "USC"]:
# Get the government responsible for this zone
zoneGovt = self._tropicalAreaDict[key]["hazardIssuer"].strip()
# Handle the case of the USA, which receives no attribution
if zoneGovt.strip() == "":
zoneGovt = "US"
# If we do not already know about this government
if zoneGovt not in govtList:
# Add it now
govtList.append(zoneGovt)
#---------------------------------------------------------------
# Get the impacted zones for this segment
ugcCode = self._tropicalAreaDict[key]["ugcCode"].strip()
#---------------------------------------------------------------
# Expand UGC code if there is more than one zone represented
if len(ugcCode.split("-")) > 1 or ugcCode.find(">") != -1:
ugcList = self.expandComplexUgc(ugcCode)
# Otherwise, just use this single zone
else:
ugcList = [ugcCode]
#---------------------------------------------------------------
# Expand UGC code if there is more than one zone represented
try:
ugcCodeUsa = self._tropicalAreaDict[key]["ugcCodeUsa"].strip()
if len(ugcCodeUsa.split("-")) > 1 or ugcCodeUsa.find(">") != -1:
ugcListUsa = self.expandComplexUgc(ugcCodeUsa)
# Otherwise, just use this single zone
else:
ugcListUsa = [ugcCodeUsa]
except:
ugcListUsa = []
#---------------------------------------------------------------
# If we already have an entry for this government
if zoneGovt in zoneDict:
# Get the zones already associated
curZoneList = zoneDict[zoneGovt]
# Otherwise make a new list for this governement
else:
curZoneList = []
#---------------------------------------------------------------
# Now add all the new zones
for ugc in ugcList + ugcListUsa:
# If we don't already have this ugc
if ugc not in curZoneList and len(ugc.strip()) > 0:
# Add it
curZoneList.append(ugc)
## print curZoneList
# Sort the UGC list
curZoneList.sort()
# Store the list of zones for this government
zoneDict[zoneGovt] = curZoneList
# Always ensure the USA comes first
if "US" in govtList:
govtList.remove("US")
finalGovtList = ["US"] + govtList
# Return the completed dictionary
return zoneDict, finalGovtList
#===========================================================================
# Define a method to filter a segment list by government
def _filterAreaListByGovernment(self, govtList, areaList):
"""TropicalHazards addition of _filterAreaListByGovernment.
This method will produce a list of all zones managed by a particular
government contained within the specified area list.
Arguments:
govtList -> list of identifiers managed by a government
areaList -> list of edit area identifiers to process
"""
# Initialize a new list
newList = []
# Look through each edit area
for area in areaList:
# If this edit area is managed by this government
if area in govtList:
# Add it to the filtered list
newList.append(area)
# Return the filtered list
return newList
#===========================================================================
# Define a method to filter a segment list by government
def _organizeAreasByType(self, areaList):
"""TropicalHazards addition of _organizeAreasByType.
This method will separate a list of areas into one of four types:
mainland segments, UGC zones, zones and islands. These will be stored
in the processed hazard dictionary for easier access later.
Arguments:
areaList -> list of edit area identifiers to process
"""
# Initialize both lists
segmentList = []
ugcZoneList = []
zoneList = []
islandList = []
waterList = []
# Look through each edit area
for area in areaList:
# Assume this is a "land" area
areaType = "land"
# If the TropicalAreaDictionary has a record for this area
if area in self._tropicalAreaDict:
# Get the type of this area - if we can
if "segmentType" in self._tropicalAreaDict[area]:
areaType = self._tropicalAreaDict[area]["segmentType"]
# Get the type of this area
try:
usaZoneList = self.expandComplexUgc(
self._tropicalAreaDict[area]["ugcCodeUsa"])
except:
usaZoneList = []
#---------------------------------------------------------------
# If this is an island
if areaType == "island":
# If we do not already have a record for this area
if area not in islandList:
# Add it to the list of islands
islandList.append(area)
#---------------------------------------------------------------
# Otherwise, if this is a water area
elif areaType == "water":
# If we do not already have a record for this area
if area not in waterList:
# Add it to the list of islands
waterList.append(area)
#---------------------------------------------------------------
# Otherwise, organize the land-based areas
else:
# If this is a zone-based identifier
if len(area) == 6 and area[2] in ["Z", "C"]:
# Place this zone into the proper list
if area[2] == "Z":
# If this area has not already been recorded
if area not in ugcZoneList:
# Add it to the UGC zone list
ugcZoneList.append(area)
else:
# If this area has not already been recorded
if area not in zoneList:
# Add it to the zone list
zoneList.append(area)
# If there any zones associated with this segment
if len(usaZoneList) > 0:
for usZone in usaZoneList:
if usZone not in ugcZoneList:
# Add it to the UGC zone list
ugcZoneList.append(usZone)
# Otherwise, this is a breakpoint segment
elif area not in segmentList:
segmentList.append(area)
# Get any UGC codes associated with this segment
areaUgc = self._tropicalAreaDict[area]["ugcCode"]
# If there is more than 1 zone associated with segment
if len(areaUgc) > 7:
# Expand the UGC codes
ugcList = self.expandComplexUgc(areaUgc)
# Otherwise, make a simpler list so we can proceed
else:
ugcList = [areaUgc]
#-------------------------------------------------------
# Add each zone code into the list as needed
for ugcCode in ugcList:
# Clean up any extra characters
ugcCode = ugcCode.replace("-", "")
# If this is a zone-based identifier
if len(ugcCode) >= 6 and ugcCode[2] in ["Z", "C"]:
# Place this zone into the proper list
if ugcCode[2] == "Z":
# If this area has not already been recorded
if ugcCode not in ugcZoneList:
# Add it to the UGC zone list
ugcZoneList.append(ugcCode)
else:
# If this area has not already been recorded
if ugcCode not in zoneList:
# Add it to the zone list
zoneList.append(ugcCode)
#-----------------------------------------------------------------------
# Sort all lists to keep them ordered - as needed
if len(segmentList) > 1:
segmentList.sort(key=self._sortBreakpoints)
if len(ugcZoneList) > 1:
ugcZoneList.sort(key=self._sortBreakpoints)
if len(zoneList) > 1:
zoneList.sort(key=self._sortBreakpoints)
if len(islandList) > 1:
islandList.sort(key=self._sortBreakpoints)
if len(waterList) > 1:
waterList.sort(key=self._sortBreakpoints)
# Return the compiled lists
return (segmentList, ugcZoneList, zoneList, islandList, waterList)
#===========================================================================
# Define a method to construct a processed hazard dictionary
def _constructHazardDict(self, hazardPhenSig, filterEtn):
"""TropicalHazards addition of _constructHazardDict.
This method will produce a processed dictionary of tropical hazards
for easier use later on.
Arguments:
hazardPhenSig -> dictionary of hazards keyed by phenomenon and
significance. Values are a list of all hazards
which share that same phenomenon and significance.
filterEtn -> Event Tracking Number of interest which will be used
to filter hazards for a particular product.
"""
#-----------------------------------------------------------------------
# Get ready to populate the hazard dictionary for this storm
hazardAreaDict = {}
hazardAreaDictKeyList = []
# Assume this is going to be the last product we issue for this storm
self._allCAN = True
#=======================================================================
# Look for each of the tropical hazards in order
for phenSig in [("SS","W"), ("HU","W"), ("SS","A"), ("HU","A"),
("TR","W"), ("TR","A")]:
if phenSig in hazardPhenSig:
print("="*90)
print("\n\tConstructing -> %s" % (repr(phenSig)))
print(len(hazardPhenSig[phenSig]), hazardPhenSig[phenSig])
# Look through all the sampled hazards
for phen in hazardPhenSig[phenSig]:
print("-"*90)
print("phen = %s" % (phen))
# Set aside the headline for each action in this area
NEW = []
CAN = []
UPG = []
EXA = []
CON = []
#-----------------------------------------------------------
# If we have items for this particular phen.sig
# combination, and this is the storm we are after
if phen["etn"] != filterEtn:
print("\tWrong storm!", phen)
# Move on to the next one
continue
# Get the full VTEC code for this phenomena
# curHazardKey = (phen["act"], phen["key"])
curHazardKey = (phen["act"], phen["phensig"])
print("+++++ %s" % (repr(curHazardKey)))
# If this action is anything other than "CAN", indicate it
# so we don't delete the JSON file for this storm at end
if phen["act"] != "CAN":
self._allCAN = False
# If we do not have the ETN of this hazard
if re.search(":\d{4}$", curHazardKey[1]) is None:
newHazardType = curHazardKey[1] + ":%d" % \
(phen["etn"])
else:
newHazardType = ""
# If we need to adjust the hazard key
if len(newHazardType) > 0:
# Make the changes
newCurHazardKey = (curHazardKey[0], newHazardType)
curHazardKey = newCurHazardKey
# See if there are upgrades or replacements for this area
areaHazardList = self._hazards.getHazardList(phen["id"])
#-----------------------------------------------------------
# Construct a hazard key which incorporates all hazards
# and actions for this area
tempHazardList = [curHazardKey]
for areaHazard in areaHazardList:
#-------------------------------------------------------
# Record headline for each action we find
if areaHazard["act"] == "NEW" and \
areaHazard["hdln"] not in NEW:
NEW.append(areaHazard["hdln"])
elif areaHazard["act"] == "CAN" and \
areaHazard["hdln"] not in CAN:
CAN.append(areaHazard["hdln"])
elif areaHazard["act"] == "UPG" and \
areaHazard["hdln"] not in UPG:
UPG.append(areaHazard["hdln"])
elif areaHazard["act"] == "CON" and \
areaHazard["hdln"] not in CON:
CON.append(areaHazard["hdln"])
elif areaHazard["act"] == "EXA" and \
areaHazard["hdln"] not in EXA:
EXA.append(areaHazard["hdln"])
#-------------------------------------------------------
# Make a key for this particular hazard/action combo
tempHazardKey = (areaHazard["act"], areaHazard["phensig"])
# If we do not have the ETN of this hazard
if re.search(":\d{4}$", tempHazardKey[1]) is None:
newHazardType = tempHazardKey[1] + ":%d" % \
(areaHazard["etn"])
else:
newHazardType = ""
# If we need to adjust the hazard key
if len(newHazardType) > 0:
# Make the changes
newTempHazardKey = (tempHazardKey[0], newHazardType)
tempHazardKey = newTempHazardKey
# If this is not already part of the hazard key
if tempHazardKey != curHazardKey:
# Add this hazard/action combo to the list
tempHazardList.append(tempHazardKey)
# Sort the keys so we have some consistency in ordering
tempHazardList.sort()
# Convert the list of hazards for this area into a tuple
# so we can use it as a dictionary key
hazardKey = tuple(tempHazardList)
#-----------------------------------------------------------
# Ensure we only group areas associated with same storm
# and hazard/action combos
# If we already have an entry for this storm and hazard
if hazardKey in hazardAreaDict:
# Add to what is already there
tempList = hazardAreaDict[hazardKey]["AREAS"]
tempList.append(phen["id"])
hazardAreaDict[hazardKey]["AREAS"] = tempList
# Otherwise, make a new entry
else:
## "AREAS":[phen["id"]], "HDLN":phen["hdln"],
hazardAreaDict[hazardKey] = {
"AREAS":[phen["id"]], "NEW":NEW, "CAN":CAN,
"CON":CON, "EXA":EXA, "UPG":UPG
}
# Add this key the list
if hazardKey not in hazardAreaDictKeyList:
hazardAreaDictKeyList.append(hazardKey)
print("\n\n", "+"*100)
print("in the middle")
print(pp.pformat(hazardAreaDictKeyList), "\n")
print(pp.pformat(hazardAreaDict))
#-----------------------------------------------------------------------
# Keep track of segments and zones. We will need segments to
# ensure proper grouping and breakpoints. We will need UGC zones
# to get the proper VTEC action
segments = []
ugcZones = []
zones = []
islands = []
water = []
#=======================================================================
# Organize all the impacted areas by type
for key in hazardAreaDictKeyList:
#-------------------------------------------------------------------
# Organize various areas associated with this hazard by type
(segments, ugcZones, zones, islands, water) = \
self._organizeAreasByType(hazardAreaDict[key]["AREAS"])
# Add these organized zones to the dictionary for this hazard
hazardAreaDict[key]["SEGMENTS"] = segments
hazardAreaDict[key]["UGCZONES"] = ugcZones
hazardAreaDict[key]["ZONES"] = zones
hazardAreaDict[key]["ISLANDS"] = islands
hazardAreaDict[key]["WATER"] = water
print("+"*90 + "\nFinally!")
print("found -> %s" % (repr(hazardAreaDictKeyList)))
print("allCAN = %s" % (self._allCAN))
for key in hazardAreaDictKeyList:
print("-"*60)
print("%s\n\t%s" % (key, pp.pformat(hazardAreaDict[key])))
# Return the completed hazard dictionary and sorted keys
return (hazardAreaDict, hazardAreaDictKeyList)
#===========================================================================
# Process the shortcut hazard dictionary and organize a list of segments
# which are organized by similar type.
def _constructSegmentList(self, hazardAreaDict, hazardAreaDictKeyList,
UStcv=0):
"""TropicalHazards addition of _constructSegmentList.
This method will produce create all the appropriate segments which
should go into a tropical hazard product, particularly a TCV.
Arguments:
hazardAreaDict -> processed dictionary of hazards
hazardAreaDictKeyList -> list of keys of active hazards to process.
UStcv -> (optional) toggle to group islands within a single segment
{1 = Yes (default - USA TCV) / 0 = No}
"""
# Initialize a list to hold the final group of segments
finalSegmentList = []
segmentDict = {"CUC": [], "HTC":[], "DOC":[], "USC":[]}
#-----------------------------------------------------------------------
# Look at every hazard we found
for hazardKey in hazardAreaDictKeyList:
segmentList = []
zoneList = []
#-------------------------------------------------------------------
# Double check to be sure Cuba and Hispaniola are not part of
# the mainland segment lists
for curId in hazardAreaDict[hazardKey]["SEGMENTS"]:
# If this is a Cuba or Hispaniola code
if curId[:3] in segmentDict:
## print "Found segment -> curId =", curId
# Add this identifier to the dictionary
tempList = segmentDict[curId[:3]]
# If this identifier is not already in the list
if curId not in tempList:
tempList.append(curId)
# Store the updated list
segmentDict[curId[:3]] = tempList
# Otherwise,
elif curId not in segmentList:
segmentList.append(curId)
#-------------------------------------------------------------------
# Double check to be sure Cuba and Hispaniola are not part of
# the mainland zone lists
for curId in hazardAreaDict[hazardKey]["ZONES"]:
# If this is a Cuba or Hispaniola code
if curId[:3] in segmentDict:
## print "Found zone -> curId =", curId
# Add this identifier to the dictionary
tempList = segmentDict[curId[:3]]
# If this identifier is not already in the list
if curId not in tempList:
tempList.append(curId)
# Store the updated list
segmentDict[curId[:3]] = tempList
# Otherwise,
elif curId not in segmentList:
zoneList.append(curId)
#-------------------------------------------------------------------
# Make a separate segment for Cuba
if len(segmentDict["CUC"]) > 0:
sortedZones = segmentDict["CUC"]
sortedZones.sort()
record = ([], # Segments
[], # UGC zones
sortedZones, # Zones
[], # Islands
[]) # Water
# If we do not already have a record for these particular zones
if record not in finalSegmentList:
print("\n\nCuba record", record)
# Add it now
finalSegmentList.append(record)
#-------------------------------------------------------------------
# Make a separate segment for Hispaniola
if len(segmentDict["HTC"]) > 0 or len(segmentDict["DOC"]) > 0:
# Get a compined list of zones
combinedZones = segmentDict["HTC"] + segmentDict["DOC"]
# Sort these zones
combinedZones.sort(key=self._sortBreakpoints)
record = ([], # Segments
[], # UGC zones
combinedZones, # Zones
[], # Islands
[]) # Water
# If we do not already have a record for these particular zones
if record not in finalSegmentList:
print("\n\nHispaniola record", record)
# Add it now
finalSegmentList.append(record)
## #-------------------------------------------------------------------
## # Make a separate segment for the United States
##
## if len(segmentDict["USC"]) > 0:
##
## sortedZones = segmentDict["USC"]
## sortedZones.sort()
##
## record = ([], # Segments
## [], # UGC zones
## sortedZones, # Zones
## [], # Islands
## []) # Water
##
## # If we do not already have a record for these particular zones
## if record not in finalSegmentList:
##
## print "\n\nCuba record", record
##
## # Add it now
## finalSegmentList.append(record)
#-------------------------------------------------------------------
# If we have filtered segments or zones
if len(segmentList) != 0 and \
len(segmentList) != len(hazardAreaDict[hazardKey]["SEGMENTS"]):
# Update the record for this segment
curSegments = segmentList
# Otherwise, keep segments we already have
else:
curSegments = hazardAreaDict[hazardKey]["SEGMENTS"]
if len(zoneList) != 0 and \
len(zoneList) != len(hazardAreaDict[hazardKey]["ZONES"]):
# Update the record for this segment
curZones = zoneList
# Otherwise, keep segments we already have
else:
curZones = hazardAreaDict[hazardKey]["ZONES"]
#-------------------------------------------------------------------
# Create a new segment for each island which was found - if we
# need to
islandList = []
# USA TCV
if UStcv:
# Make a new record for this island
record = ([], # Segments
[hazardAreaDict[hazardKey]["ISLANDS"]], # UGC zones
[], # Zones
[hazardAreaDict[hazardKey]["ISLANDS"]], # Islands
[]) # Water
# If we do not already have a record for this island
if record not in finalSegmentList:
print("\n\nUSA Island record -> %s" % (record))
# Add it now
finalSegmentList.append(record)
# International TCV
else:
for island in hazardAreaDict[hazardKey]["ISLANDS"]:
# Make a new record for this island
record = ([], # Segments
[], # UGC zones
[island], # Zones
[island], # Islands
[]) # Water
# If we do not already have a record for this island
if record not in finalSegmentList:
# print "\n\nIntl Island record -> %s" % (record)
# Add it now
finalSegmentList.append(record)
#===================================================================
# Make a record to group all the various areas associated with a
# particular hazard
record = (curSegments,
hazardAreaDict[hazardKey]["UGCZONES"],
curZones,
hazardAreaDict[hazardKey]["ISLANDS"],
hazardAreaDict[hazardKey]["WATER"])
# If we do not already have a record for these particular zones
if record not in finalSegmentList:
# Add it now
finalSegmentList.append(record)
print("\n\n", "*"*90)
print("finalSegmentList =", pp.pformat(finalSegmentList))
# Return the final organized list
return finalSegmentList
#===========================================================================
# Define a method to find missing zone codes for breakpoint segments,
# islands and water.
def _findZoneCodes(self, areaList):
# Get ready to find the zone codes
ugcZones = []
zones = []
# Look at each area in the list
for areaId in areaList:
# If there is an entry in the TropicalAreaDictionary for this area
if areaId in self._tropicalAreaDict:
# Get the 'ugcCode' for this area
ugcCode = self._tropicalAreaDict[areaId]["ugcCode"]
# If this is a generic zone code
if len(ugcCode) > 3 and ugcCode[2] == "C":
# If we do not already have it in the zone list
if ugcCode not in zones:
# Add it now
zones.append(ugcCode)
# Otherwise, this must be a UGC code
else:
# See if we need to expand it
if len(ugcCode) > 7:
# Expand the UGC code into individual zones
expandUgcList = self.expandComplexUgc(ugcCode)
# Add each UGC code to the list - if not already there
for ugc in expandUgcList:
if ugc not in ugcZones:
ugcZones.append(ugc)
# Otherwise, just add this UGC zone if it is not already there
elif ugcCode not in ugcZones:
ugcZones.append(ugc)
# Sort these lists as needed
if len(ugcZones) > 1:
ugcZones.sort(key=self._sortBreakpoints)
if len(zones) > 1:
zones.sort(key=self._sortBreakpoints)
if len(ugcZones) == 0:
return "There are no areas in this TCV"
# Return the zones we found
return (ugcZones, zones)
#===============================================================================
# Code to process StormInfo files -
# same as HLSTCV_Common
def _synchronizeAdvisories(self):
# Retrieving a directory causes synching to occur.
# This code can throw an exception but don't catch it
# so that forecasters can be made aware of the issue.
file = LocalizationSupport.getLocalizationFile(
LocalizationSupport.CAVE_STATIC,
LocalizationSupport.SITE, self._site,
self._getAdvisoryPath()).getFile()
return file
# same as HLSTCV_Common
def _getLocalAdvisoryDirectoryPath(self):
file = self._synchronizeAdvisories()
path = file.getPath()
print("\n\nLooking for JSON files in '%s'" % (path))
try:
os.makedirs(path)
except OSError as exception:
if exception.errno != errno.EEXIST:
raise
return path
def _getStormAdvisoryNames(self):
advisoryDirectoryPath = self._getLocalAdvisoryDirectoryPath()
filenames = os.listdir(advisoryDirectoryPath)
allAdvisories = [filename for filename in filenames if filename[-5:] == ".json"]
stormAdvisories = [filename for filename in allAdvisories if filename[:2] == "AT"]
return stormAdvisories
def _loadAdvisory(self, advisoryName):
self._synchronizeAdvisories()
fileName = self._getAdvisoryFilename(advisoryName)
try:
pythonDict = JsonSupport.loadFromJson(LocalizationSupport.CAVE_STATIC,
self._site, fileName)
statFileName = os.path.join(os.environ["HOME"], "caveData", "etc",
"site", self._site, fileName)
lastModified = os.stat(statFileName).st_mtime
pythonDict["lastModified"] = lastModified
print("File contents for %s:" % (fileName))
print(pp.pformat(pythonDict))
return pythonDict
except Exception as e:
print("Load Exception for %s : %s" % (fileName, e))
return None
def _saveAdvisory(self, advisoryName, advisoryDict):
self._synchronizeAdvisories()
fileName = self._getAdvisoryFilename(advisoryName)
print("Saving %s to %s" % (advisoryName, fileName))
print("advisoryDict: %s" % (pp.pformat(advisoryDict)))
try:
JsonSupport.saveToJson(LocalizationSupport.CAVE_STATIC,
self._site, fileName, advisoryDict)
# os.system('chmod 664 %s' % (fileName))
except Exception as e:
print("Save Exception for %s : %s" % (fileName, e))
else: # No exceptions occurred
print("Wrote file contents for: %s" % (fileName))
# Purposely allow this to throw
self._synchronizeAdvisories()
def _deleteAdvisory(self):
# Sync the CAVE localization store
self._synchronizeAdvisories()
fileName = self._getAdvisoryFilename(self._advisoryFileName)
print("\n\nDeleting -> " + fileName)
try:
LocalizationSupport.deleteFile(LocalizationSupport.CAVE_STATIC,
LocalizationSupport.SITE, self._site,
fileName)
except Exception as e:
print("Delete Exception for %s : %s" % (fileName, e))
return None
# same as HLSTCV_Common
def _getAdvisoryPath(self):
dataMgr = self._argDict["dataMgr"]
gfeMode = dataMgr.getOpMode().name()
if gfeMode == "PRACTICE":
return os.path.join("gfe", "tcvAdvisories", "practice")
else:
return os.path.join("gfe", "tcvAdvisories")
def _getAdvisoryFilename(self, advisoryName):
advisoryFilename = os.path.join(self._getAdvisoryPath(), advisoryName)
if not advisoryFilename.endswith(".json"):
advisoryFilename += ".json"
return advisoryFilename