#!/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 for temp_lat_name in DEFAULT_LATITUDES : if temp_lat_name in variableList : self.fileData[filePrefix].latitude = temp_lat_name for temp_lon_name in DEFAULT_LONGITUDES : if temp_lon_name in variableList : self.fileData[filePrefix].longitude = temp_lon_name # 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()