-
Eva Schiffer authoredEva Schiffer authored
gui_model.py 38.77 KiB
#!/usr/bin/env python
# encoding: utf-8
"""
The model portion of the glance GUI code.
Created by evas Oct 2011.
Copyright (c) 2011 University of Wisconsin SSEC. All rights reserved.
"""
import logging
import numpy as np
from os import path
import glance.data as dataobjects
import glance.io as io
from glance.gui_constants import *
from glance.gui_figuremanager import COLORMAP_NAMES
LOG = logging.getLogger(__name__)
"""
The model handles the data in the Glance GUI. It not only stores data and handles
updates, it's also responsible for caching data and handling some logic related to
what data is used with overrides.
The model allows outside objects to register to listen for data updates or for
errors. It's expected that error handlers will manage either logging or displaying
errors appropriately.
"""
class UnableToReadFile(Exception):
"""
An exception to be used when glance could not read a file
"""
def __init__(self, filePath):
"""
create this exception, giving a message based on the file path
"""
self.message = str("Unable to open file. Glance was not able to determine the file type for "
+ filePath + " or cannot process that type of file.")
def __str__(self):
return self.message
class _FileModelData (object) :
"""
This object is meant to be used internally by the GUI model. The model is going to mess with the
data directly. This is not the best practice ever, but I like it more than using a dictionary straight.
This data object includes the following:
self.file - the FileInfo object representing this file, can be used to load more information later
self.variable - the name of the selected variable
self.var_data_cache - a cache of all the variable data that has been loaded for this file
(stored in dataobjects.DataObject objects), keyed by variable name
self.var_attrs_cache - a cache of variable attributes (keyed by variable name), each set of attributes is a dictionary,
keyed with the attribute names and containing their values
self.ALL_VARIABLES - a list of all the variable names in the file
"""
def __init__(self, file_object=None, variable_selection=None, do_override=False, fill_value=None, default_fill_value=None,
latitude_name=None, longitude_name=None,
variables_list=None, variable_data=None, variable_attributes=None) :
"""
create a set of model data, using the data passed in
Note: the file_object is intended to be a FileInfo object from glance.data
"""
self.file = file_object
self.variable = str(variable_selection)
self.latitude = latitude_name
self.longitude = longitude_name
self.ALL_VARIABLES = variables_list
self.var_data_cache = { }
self.var_attrs_cache = { }
if variable_selection is not None :
self.var_data_cache[variable_selection] = dataobjects.DataObject(variable_data, fillValue= fill_value,
overrideFillValue=do_override,
defaultFillValue=default_fill_value)
self.var_attrs_cache[variable_selection] = variable_attributes
class GlanceGUIModel (object) :
"""
This is the main model that handles the information behind the glance GUI.
It includes:
self.fileData - a dictionary with _FileModelData objects indexed by the two file prefixes
self.epsilon - the epsilon value for comparison
self.epsilonPercent - the epsilon percent value for comparison
self.llEpsilon - the epsilon used for judging the longitude and latitude data
self.imageType - the image type that should be created when files are compared
self.colormap - the colormap to use for plotting (if one is needed)
self.dataForm - the form the data should be considered to be
self.useSharedRange - True if images for the original data should be displayed in a range
that includeds the data from both files, False if not
self.plotGeoTiffAsRGB - True if multi-channel geotiff images should be treated as RGB or RGBA images
self.hideMismatchNav - True if data corresponding to navigation that's more different
than self.llEpsilon should be hidden when plotting, False if not
self.fileSettings - a dictionary of settings specific to a file, in the form:
{
"doRange": True or False for whether or not the range of the data should be restricted,
"minRange": The minimum acceptable value if the range is restricted,
"maxRange": The maximum acceptable value if the range is restricted,
"isAWIPS": True or False for whether or not the data is in AWIPS format
}
self.dataListeners - objects that want to be notified when data changes
self.errorHandlers - objects that want to be notified when there's a serious error
"""
DO_RANGE = "doRange"
MIN_RANGE = "minRange"
MAX_RANGE = "maxRange"
IS_AWIPS = "isAWIPS"
def __init__ (self) :
"""
set up the basic model with our initial default values and empty listener lists
"""
# set up the file related data structures
self.fileData = { }
self.fileData[A_CONST] = _FileModelData( )
self.fileData[B_CONST] = _FileModelData( )
# general settings
self.epsilon = 0.0
self.epsilonPercent = None
self.llEpsilon = None
self.imageType = IMAGE_TYPES[0]
self.colormap = COLORMAP_NAMES[0]
self.dataForm = SIMPLE_2D
self.useSharedRange = False
self.plotGeoTiffAsRGB = False
self.hideMismatchNav = False
# This is obviously only going to work for these two prefixes, would need
# to add a fully formed sub-class to make this more general
self.fileSettings = { }
self.fileSettings[A_CONST] = \
{
GlanceGUIModel.DO_RANGE: False,
GlanceGUIModel.MIN_RANGE: None,
GlanceGUIModel.MAX_RANGE: None,
GlanceGUIModel.IS_AWIPS: False
}
self.fileSettings[B_CONST] = \
{
GlanceGUIModel.DO_RANGE: False,
GlanceGUIModel.MIN_RANGE: None,
GlanceGUIModel.MAX_RANGE: None,
GlanceGUIModel.IS_AWIPS: False
}
# this represents all the people who want to hear about data updates
# these people can register and will get data related messages
self.dataListeners = [ ]
# this represents all the people who want to hear when we have errors
# that the user should know about
# these people can register and will get error related messages
self.errorHandlers = [ ]
_AVOID_LOADING_VARIABLES_PREFIXES = ["nwp_"]
def _pick_variable_to_load_on_file_load (self, all_variable_names, other_file_variable_name,
avoid_prefixes=_AVOID_LOADING_VARIABLES_PREFIXES,) :
"""
When we load a new file, select our best guess at a variable to load by default.
This logic will try to select the same variable as the file that is already loaded if possible.
"""
# start out by sorting our variables, and pick a matching one if we have one to match
variable_list = sorted(all_variable_names)
selected_var = None
found_ok_one = False
if other_file_variable_name in set(variable_list) :
found_ok_one = True
selected_var = other_file_variable_name
# if we don't have a candidate, try to look for one
temp_index = -1
while not found_ok_one :
if selected_var is not None :
found_ok_one = True # tentatively assume this one is ok
# check to see if it has any of the prefixes we don't want
for prefix in avoid_prefixes :
if selected_var.find(prefix) >= 0 :
found_ok_one = False
if not found_ok_one :
temp_index += 1
selected_var = str(variable_list[temp_index])
return selected_var
def loadNewFile (self, filePrefix, newFilePath) :
"""
load up a new file based on the prefix and path given
may raise an UnableToReadFile exception if the given file path is non-null
but the resulting file cannot be parsed by glance
"""
# check to see if we were given a valid file path
if (newFilePath is None) or (len(newFilePath) == 0) :
LOG.debug("No file selected. Aborting load.")
return # if there's no path, we can't load anything
# attempt to open the file
try :
newFile = dataobjects.FileInfo(str(newFilePath))
except KeyError :
raise UnableToReadFile(newFilePath)
# reset our caches
self._resetCaches(filePrefix)
# get the list of variables, and pick one
variableList = sorted(newFile.file_object()) # gets a list of all the variables in the file
other_file_var_name = None
if ((filePrefix == A_CONST) & (self.fileData[B_CONST].ALL_VARIABLES is not None)):
other_file_var_name = self.fileData[B_CONST].variable
if ((filePrefix == B_CONST) & (self.fileData[A_CONST].ALL_VARIABLES is not None)):
other_file_var_name = self.fileData[A_CONST].variable
tempVariable = self._pick_variable_to_load_on_file_load(variableList, other_file_var_name,)
LOG.debug ("selected variable: " + str(tempVariable))
# try to load up our variable, and pick a different one if we can't
no_var_loaded = True
temp_vars_set = set(variableList)
io_error = None
while no_var_loaded :
try :
# save all of the data related to this file for later use
self.fileData[filePrefix].file = newFile
self.fileData[filePrefix].variable = tempVariable
self.fileData[filePrefix].ALL_VARIABLES = variableList
# set the longitude and latitude names, using the defaults if they exist
self.fileData[filePrefix].latitude = tempVariable
self.fileData[filePrefix].longitude = tempVariable
if DEFAULT_LATITUDE in variableList :
self.fileData[filePrefix].latitude = DEFAULT_LATITUDE
if DEFAULT_LONGITUDE in variableList :
self.fileData[filePrefix].longitude = DEFAULT_LONGITUDE
# make sure the longitude and latitude are loaded into our local cache
# TODO, it would be good to background this task at some point
_ = self._load_variable_data(filePrefix, self.fileData[filePrefix].latitude)
_ = self._load_variable_data(filePrefix, self.fileData[filePrefix].longitude)
# load info on the current variable
tempDataObj = self._load_variable_data(filePrefix, tempVariable)
# get the variable's attributes
tempAttrs = self._load_variable_attributes (filePrefix, tempVariable)
# if we get here then we loaded the variable ok
no_var_loaded = False
except io.IONonnumericalTypeError as ex :
LOG.warn("Unable to load automatically selected variable. Details: " + str(ex))
io_error = ex
if no_var_loaded :
# try to pick a different variable removing the one we tried from our potential list
temp_vars_set -= set(tempVariable)
if len(temp_vars_set) <= 0 :
LOG.error("Could not load any variables.")
raise io_error
tempVariable = self._pick_variable_to_load_on_file_load(list(temp_vars_set), None,)
io_error = None
# Now tell our data listeners that the file data changed
for dataListener in self.dataListeners :
LOG.debug("Sending update for file " + filePrefix + " with loaded data.")
dataListener.fileDataUpdate(filePrefix, newFile.path, tempVariable,
tempDataObj.override_fill_value,
self._select_fill_value(filePrefix),
tempDataObj.describe_shape(),
variable_list=variableList, attribute_list=tempAttrs)
dataListener.updateSelectedLatLon(filePrefix,
self.fileData[filePrefix].latitude,
self.fileData[filePrefix].longitude,
lonlatList=variableList)
def _load_variable_attributes (self, file_prefix, variable_name) :
"""
Load the attributes for for a given file name, saving them to the
file data structure as appropriate
return the loaded attributes for convenience
"""
variable_name = str(variable_name)
tempAttrs = None
# if we can load the attributes from the cache, do that otherwise get them from the file
if variable_name in self.fileData[file_prefix].var_attrs_cache :
tempAttrs = self.fileData[file_prefix].var_attrs_cache[variable_name]
else :
tempAttrs = self.fileData[file_prefix].file.file_object.get_variable_attributes(variable_name)
# cache these for later use
self.fileData[file_prefix].var_attrs_cache[variable_name] = tempAttrs
return tempAttrs
def _load_variable_data (self, file_prefix, variable_name) :
"""
Load up new variable data, saving it to our fileData structure
return the shape of the data for convenience
TODO, can this be handled as a background task in the future?
"""
variable_name = str(variable_name)
tempData = None
# if we have a cached version of this variable, use that, otherwise, load it from the file
if variable_name in self.fileData[file_prefix].var_data_cache :
LOG.debug ("Loading " + str(file_prefix) + " file cached variable: " + str(variable_name))
tempData = self.fileData[file_prefix].var_data_cache[variable_name]
else :
LOG.debug ("Loading " + str(file_prefix) + " file variable from file: " + str(variable_name))
tempRawData = self.fileData[file_prefix].file.file_object[variable_name]
tempFillVal = self.fileData[file_prefix].file.file_object.missing_value(variable_name)
tempOverride = False
# also save this new data in our cache TODO, this won't save the other data we need?
tempData = dataobjects.DataObject(tempRawData, fillValue=tempFillVal,
overrideFillValue=tempOverride,
defaultFillValue=tempFillVal)
self.fileData[file_prefix].var_data_cache[variable_name] = tempData
return tempData
def _resetCaches (self, file_prefix) :
"""
Clear the two internal caches
"""
self.fileData[file_prefix].var_data_cache = { }
self.fileData[file_prefix].var_attrs_cache = { }
def sendGeneralSettingsData (self) :
"""
send off the general settings data that's not related to the individual files
"""
# let each of our listeners know about the general data
for dataListener in self.dataListeners :
dataListener.updateEpsilon(self.epsilon)
dataListener.updateEpsilonPercent(self.epsilonPercent)
dataListener.updateLLEpsilon(self.llEpsilon)
dataListener.updateImageTypes(self.imageType, list=IMAGE_TYPES)
dataListener.updateColormaps(self.colormap, list=COLORMAP_NAMES)
dataListener.updateDataForms(self.dataForm, list=DATA_FORMS)
dataListener.updateUseSharedRange(self.useSharedRange)
dataListener.updatePlotGeoTiffAsRGB(self.plotGeoTiffAsRGB)
self.sendFileSettings(A_CONST)
self.sendFileSettings(B_CONST)
def sendFileSettings (self, file_prefix) :
"""
send out settings data that's related to the individual files but not data selections
"""
# let our data listeners know about these values
for listener in self.dataListeners :
listener.updateDoRestrictRange (file_prefix, self.fileSettings[file_prefix][GlanceGUIModel.DO_RANGE ])
listener.updateRestrictRangeMin(file_prefix, self.fileSettings[file_prefix][GlanceGUIModel.MIN_RANGE])
listener.updateRestrictRangeMax(file_prefix, self.fileSettings[file_prefix][GlanceGUIModel.MAX_RANGE])
listener.updateIsAWIPS (file_prefix, self.fileSettings[file_prefix][GlanceGUIModel.IS_AWIPS ])
pass
def updateFileDataSelection (self, file_prefix, newVariableText=None, newOverrideValue=None, newFillValue=np.nan) :
"""
someone has updated one or more of the file related data selections
Note: if an input value is left at it's default (None or nan) then it's assumed that it was not externally changed
"""
didUpdate = False # whether or not we updated anything
newBVar = None # None if we don't need to update the B variable to match the A variable, or a variable name if we do
# update the variable selection if needed
io_error = None
if (newVariableText is not None) and (newVariableText != self.fileData[file_prefix].variable) :
if newVariableText in self.fileData[file_prefix].ALL_VARIABLES :
try :
# load the data for this new variable
self._load_variable_data(file_prefix, str(newVariableText))
except io.IONonnumericalTypeError as nne :
LOG.debug("Unable to load requested variable, reverting to previous variable.")
io_error = nne
if io_error is None :
# figure out if we need to update the B file variable selection to match the A file variable selection
# Note: we will only do this if the user already had the same variable selected for both files and
# file B has the new selection available
previous_variable = self.fileData[file_prefix].variable
if ( (file_prefix == A_CONST) & (self.fileData[B_CONST].variable == previous_variable) &
(self.fileData[B_CONST].ALL_VARIABLES is not None) ) :
if (newVariableText in self.fileData[B_CONST].ALL_VARIABLES) :
newBVar = newVariableText
LOG.debug("Setting file " + file_prefix + " variable selection to: " + newVariableText)
self.fileData[file_prefix].variable = str(newVariableText)
didUpdate = True
# get the variable's attributes
self._load_variable_attributes (file_prefix, str(newVariableText))
# for convenience hang on to this
tempVariableName = self.fileData[file_prefix].variable
# update the override selection if needed
if (newOverrideValue is not None) and (tempVariableName in self.fileData[file_prefix].var_data_cache) :
LOG.debug("Setting file " + file_prefix + " override selection to: " + str(newOverrideValue))
self.fileData[file_prefix].var_data_cache[self.fileData[file_prefix].variable].override_fill_value = newOverrideValue
didUpdate = True
# update the fill value if needed
if (newFillValue is not np.nan) and (tempVariableName in self.fileData[file_prefix].var_data_cache) :
LOG.debug("Setting file " + file_prefix + " fill value to: " + str(newFillValue))
self.fileData[file_prefix].var_data_cache[self.fileData[file_prefix].variable].fill_value = newFillValue
didUpdate = True
# let our data listeners know about any changes
if didUpdate or io_error is not None :
tempDataObject = self.fileData[file_prefix].var_data_cache[tempVariableName]
tempAttrsList = self.fileData[file_prefix].var_attrs_cache[tempVariableName]
for listener in self.dataListeners :
listener.fileDataUpdate(file_prefix, self.fileData[file_prefix].file.path, tempVariableName,
tempDataObject.override_fill_value, self._select_fill_value(file_prefix),
tempDataObject.describe_shape(), attribute_list=tempAttrsList)
# if we need to update the B file to keep it in sync with the A file variable, do so
if newBVar is not None :
LOG.debug("Syncing file " + B_CONST + " variable selection to: " + newVariableText)
self.updateFileDataSelection(B_CONST, newVariableText=newVariableText)
# if we had an io issue, raise that
if io_error is not None :
raise io_error
def _select_fill_value (self, file_prefix) :
"""
which fill value should currently be used?
"""
return self.fileData[file_prefix].var_data_cache[self.fileData[file_prefix].variable].select_fill_value()
def updateSettingsDataSelection (self, newEpsilonValue=np.nan, newImageType=None, newDataForm=None,
newEpsilonPercent=np.nan, newllEpsilon=np.nan,
useSharedRangeForOriginals=None, newColormap=None,
doHideDataFromMismatchedNav=None,
doPlotGeoTiffAsRGB=None) :
"""
someone has changed one or more of the general settings related data values
Note: if an input value is left at it's default (None or nan)
then it's assumed that it was not externally changed
"""
didUpdate = False
# update the epsilon if needed
if (newEpsilonValue is not np.nan) and (newEpsilonValue != self.epsilon) :
LOG.debug("Setting epsilon to: " + str(newEpsilonValue))
self.epsilon = newEpsilonValue
didUpdate = True
# update the epsilon %
if (newEpsilonPercent is not np.nan) and (newEpsilonPercent != self.epsilonPercent) :
LOG.debug("Setting epsilon percent to: " + str(newEpsilonPercent))
self.epsilonPercent = newEpsilonPercent
didUpdate = True
# update the lon/lat epsilon if needed
if (newllEpsilon is not np.nan) and (newllEpsilon != self.llEpsilon) :
LOG.debug("Setting lon/lat epsilon to: " + str(newllEpsilon))
self.llEpsilon = newllEpsilon
didUpdate = True
# update the image type if needed
if (newImageType is not None) and (newImageType != self.imageType) :
if newImageType in IMAGE_TYPES :
LOG.debug("Setting image type to: " + newImageType)
self.imageType = str(newImageType)
didUpdate = True
# update the colormap if needed
if (newColormap is not None) and (newColormap != self.colormap) :
if newColormap in COLORMAP_NAMES :
LOG.debug("Setting colormap to: " + newColormap)
self.colormap = str(newColormap)
didUpdate = True
# update the data form if needed
if (newDataForm is not None) and (newDataForm != self.dataForm) :
if newDataForm in DATA_FORMS :
LOG.debug("Setting data form to: " + newDataForm)
self.dataForm = str(newDataForm)
didUpdate = True
# update the shared range settings if needed
if (useSharedRangeForOriginals is not None) and (useSharedRangeForOriginals != self.useSharedRange) :
if useSharedRangeForOriginals is True or useSharedRangeForOriginals is False :
LOG.debug("Setting use shared range for originals to: " + str(useSharedRangeForOriginals))
self.useSharedRange = useSharedRangeForOriginals
didUpdate = True
# update the geotiff plotting settings if needed
if (doPlotGeoTiffAsRGB is not None) and (doPlotGeoTiffAsRGB != self.plotGeoTiffAsRGB) :
if doPlotGeoTiffAsRGB is True or doPlotGeoTiffAsRGB is False :
LOG.debug("Setting plot geoTiff data as RGB images to: " + str(doPlotGeoTiffAsRGB))
self.plotGeoTiffAsRGB = doPlotGeoTiffAsRGB
didUpdate = True
# update whether or not we'll hide data based on the lon/lat comparison
if (doHideDataFromMismatchedNav is not None) and (doHideDataFromMismatchedNav != self.hideMismatchNav) :
if doHideDataFromMismatchedNav is True or doHideDataFromMismatchedNav is False :
LOG.debug("Setting hide data based on mismatched navigation to: " + str(doHideDataFromMismatchedNav))
self.hideMismatchNav = doHideDataFromMismatchedNav
didUpdate = True
# let our data listeners know about any changes
if didUpdate :
for listener in self.dataListeners :
listener.updateEpsilon(self.epsilon)
listener.updateEpsilonPercent(self.epsilonPercent)
listener.updateLLEpsilon(self.llEpsilon)
listener.updateImageTypes(self.imageType)
listener.updateColormaps(self.colormap)
listener.updateDataForms(self.dataForm)
listener.updateUseSharedRange(self.useSharedRange)
listener.updatePlotGeoTiffAsRGB(self.plotGeoTiffAsRGB)
def updateFileSettings (self, file_prefix, doRestrictRange=None,
newRangeMin=np.nan, newRangeMax=np.nan,
doCorrectForAWIPS=None) :
"""
someone has changed one or more of the file specific settings
Note: if an input value is left at it's default (None or nan)
then it's assumed that it was not externally changed
"""
didUpdate = False
if file_prefix not in self.fileSettings :
LOG.warn("Unknown file prefix (" + str(file_prefix) + ") in updateFileSettings.")
return
# update the range restriction setting if needed
if (doRestrictRange is not None) and (self.fileSettings[file_prefix][GlanceGUIModel.DO_RANGE] != doRestrictRange) :
LOG.debug("Setting use range restriction for file " + str(file_prefix) + " to: " + str(doRestrictRange))
self.fileSettings[file_prefix][GlanceGUIModel.DO_RANGE] = doRestrictRange
didUpdate = True
if (newRangeMin is not np.nan) and (self.fileSettings[file_prefix][GlanceGUIModel.MIN_RANGE] != newRangeMin) :
LOG.debug("Setting minimum value for range restriction in file " + str(file_prefix) + " to: " + str(newRangeMin))
self.fileSettings[file_prefix][GlanceGUIModel.MIN_RANGE] = newRangeMin
didUpdate = True
if (newRangeMax is not np.nan) and (self.fileSettings[file_prefix][GlanceGUIModel.MAX_RANGE] != newRangeMax) :
LOG.debug("Setting maximum value for range restriction in file " + str(file_prefix) + " to: " + str(newRangeMax))
self.fileSettings[file_prefix][GlanceGUIModel.MAX_RANGE] = newRangeMax
didUpdate = True
if (doCorrectForAWIPS is not None) and (self.fileSettings[file_prefix][GlanceGUIModel.IS_AWIPS] != doCorrectForAWIPS) :
if (doCorrectForAWIPS is True) or (doCorrectForAWIPS is False) :
LOG.debug("Setting do AWIPS data correction for file " + str(file_prefix) + " to: " + str(doCorrectForAWIPS))
self.fileSettings[file_prefix][GlanceGUIModel.IS_AWIPS] = doCorrectForAWIPS
didUpdate = True
# let our data listeners know about any changes
if didUpdate :
self.sendFileSettings(file_prefix)
def updateLonLatSelections (self, file_prefix, new_latitude_name=None, new_longitude_name=None) :
"""
someone has changed one or more of the file specific longitude/latitude related values
Note: if an input value is left at it's default (None) then it's assumed that it was not externally changed
"""
didUpdate = False
# update the latitude name
if (new_latitude_name is not None) and (new_latitude_name != self.fileData[file_prefix].latitude) :
LOG.debug ("Setting latitude name to: " + new_latitude_name)
self.fileData[file_prefix].latitude = str(new_latitude_name)
# make sure that this variable is in the cache for use later
_ = self._load_variable_data (file_prefix, str(new_latitude_name))
didUpdate = True
# update the longitude name
if new_longitude_name is not None :
LOG.debug ("Setting longitude name to: " + new_longitude_name)
self.fileData[file_prefix].longitude = str(new_longitude_name)
# make sure that this variable is in the cache for use later
_ = self._load_variable_data (file_prefix, str(new_longitude_name))
didUpdate = True
# let our listeners know if we did any updating
if didUpdate :
for listener in self.dataListeners :
listener.updateSelectedLatLon(file_prefix, self.fileData[file_prefix].latitude, self.fileData[file_prefix].longitude)
def getVariableName (self, filePrefix) :
"""
get the name of the variable loaded for the given file prefix
or None if no file is loaded
"""
toReturn = None
if filePrefix in self.fileData :
toReturn = self.fileData[filePrefix].variable
return toReturn
def getLongitudeName (self, filePrefix) :
"""
get the name of the longitude variable selected for the given file prefix
or None if no file is loaded
"""
toReturn = None
if filePrefix in self.fileData :
toReturn = self.fileData[filePrefix].longitude
return toReturn
def getLatitudeName (self, filePrefix) :
"""
get the name of the latitude variable selected for the given file prefix
or None if no file is loaded
"""
toReturn = None
if filePrefix in self.fileData :
toReturn = self.fileData[filePrefix].latitude
return toReturn
def getVariableData (self, filePrefix, variableName, doCorrections=True) :
"""
get the data object for the variable of variableName associated with the file prefix
or None if that variable is not loaded
If doCorrections is True, data filtering for AWIPS and range corrections will be done
by this function based on the currently selected settings for that file.
Note: this is not a copy, but the original object, so any manipulations
done to it will be reflected in the model
"""
toReturn = None
if (filePrefix in self.fileData) and (variableName in self.fileData[filePrefix].var_data_cache) :
toReturn = self.fileData[filePrefix].var_data_cache[variableName].copy()
# if we should do automatic corrections, do those
if doCorrections :
if self.fileSettings[filePrefix][GlanceGUIModel.IS_AWIPS] :
fill_mask = toReturn.data == toReturn.fill_value if toReturn.fill_value is not None else np.zeros(toReturn.data.shape, dtype=np.bool)
toReturn.data = toReturn.data.astype(np.uint8) # TODO, will setting this break anything?
toReturn.data = toReturn.data.astype(np.int32) # make the range larger so we can do comparisons without overflow
toReturn.data[fill_mask] = toReturn.fill_value
if self.fileSettings[filePrefix][GlanceGUIModel.DO_RANGE] :
if self.fileSettings[filePrefix][GlanceGUIModel.MIN_RANGE] is not None :
toReturn.data[toReturn.data < self.fileSettings[filePrefix][GlanceGUIModel.MIN_RANGE]] = toReturn.fill_value
if self.fileSettings[filePrefix][GlanceGUIModel.MAX_RANGE] is not None :
toReturn.data[toReturn.data > self.fileSettings[filePrefix][GlanceGUIModel.MAX_RANGE]] = toReturn.fill_value
return toReturn
def get_global_attributes (self, filePrefix, ) :
"""
get the dictionary of global attributes present in the file corresponding to filePrefix
If that file is not loaded, None will be returned
"""
temp_file = self.fileData[filePrefix].file if filePrefix in self.fileData else None
temp_file_obj = temp_file.file_object if temp_file is not None else None
to_return = temp_file_obj.get_global_attributes(caseInsensitive=False,) if temp_file_obj is not None else None
return to_return
def getUnitsText (self, filePrefix, variableName) :
"""
get the text describing the units of the variable if the variable exists and that
attribute exists, otherwise return None
"""
toReturn = None
if (filePrefix in self.fileData) and (self.fileData[filePrefix].file is not None) :
toReturn = self.fileData[filePrefix].file.file_object.get_attribute(variableName, io.UNITS_CONSTANT)
return toReturn
def getEpsilon (self) :
"""
get the current value of epsilon
"""
return self.epsilon
def getEpsilonPercent (self) :
"""
get the current value of epsilon percent
"""
return self.epsilonPercent
def getLLEpsilon (self) :
"""
get the current value of the lon/lat epsilon
"""
return self.llEpsilon
def getImageType (self) :
"""
get the text string describing the image type currently selected
the return will correspond to one of the constants from this module:
ORIGINAL_A,
ORIGINAL_B,
ABS_DIFF,
RAW_DIFF,
HISTOGRAM,
MISMATCH,
SCATTER,
HEX_PLOT
"""
return self.imageType
def getColormapName (self) :
"""
get the name of the colormap to use
the return will be on of the constants from gui_constants:
COLORMAP_NAMES = [CM_RAINBOW, CM_RAINBOW_REV, CM_RAINBOW_DESAT, CM_GRAY, CM_GRAY_REV, CM_SPECTRAL]
"""
return str(self.colormap)
def getIsAWIPS (self, filePrefix) :
"""
get whether or not the data is in AWIPS format
"""
return self.fileSettings[filePrefix][GlanceGUIModel.IS_AWIPS]
def getDataForm (self) :
"""
get the text string describing the data form currently selected
the return will correspond to one of the constants from glance.gui_constants:
SIMPLE_2D
MAPPED_2D
ONLY_1D
"""
return self.dataForm
def getShouldShowOriginalPlotsInSameRange (self) :
"""
get whether or not the original plots should be shown in a shared range
"""
return self.useSharedRange
def getDoPlotAsRGB (self, filePrefix) :
"""
get whether or not multi-channel geotiffs should be treated as RGB or RGBA
"""
isGeoTiff = False
if ( self.fileData[filePrefix].file is not None ) :
extension_temp = path.splitext(self.fileData[filePrefix].file.path)[-1]
isGeoTiff = (extension_temp == '.tiff') or (extension_temp == '.tif') or (extension_temp == '.tifa')
LOG.debug ("Checking for file " + str(filePrefix) + " geoTiff status... ")
toReturn = self.plotGeoTiffAsRGB and isGeoTiff
if toReturn :
LOG.debug ("file " + str(filePrefix) + " is a geoTiff and should be plotted as an RGB image.")
return toReturn
def getDoHideDataBasedOnMismatchedNavigation (self) :
"""
get whether or not mapped plots should hide data in places where the
navigation is more different than llEpsilon
"""
return self.hideMismatchNav
# FUTURE, use this to do more last minute loading?
def makeSureVariablesAreAvailable (self, filePrefix, listOfVariableNames) :
"""
given a list of variable names, make sure that they're all loaded
and available for use.
If the model is able to load all the requested variables (or they're
already loaded) it will return True, otherwise it will load whichever
ones it can and return False.
"""
couldLoadAll = True
for varName in listOfVariableNames :
varDataObj = self._load_variable_data(filePrefix, varName) if varName in self.fileData[filePrefix].ALL_VARIABLES else None
couldLoadAll = couldLoadAll and (varDataObj is not None)
return couldLoadAll
def registerDataListener (self, objectToRegister) :
"""
add the given object to our list of data listeners
"""
if objectToRegister not in self.dataListeners :
self.dataListeners.append(objectToRegister)
def registerErrorHandler (self, objectToRegister) :
"""
add the given object to our list of error handlers
"""
if objectToRegister not in self.errorHandlers :
self.errorHandlers.append(objectToRegister)
if __name__=='__main__':
import doctest
doctest.testmod()