From c18fda5d6031480b61f1cd8b0f852bc40a3ef7d5 Mon Sep 17 00:00:00 2001
From: "(no author)" <(no author)@8a9318a1-56ba-4d59-b755-99d26321be01>
Date: Thu, 14 Jan 2010 20:52:41 +0000
Subject: [PATCH] corrected loading of h5 missing values; fixed info variable
 selection crash; invalid pts much more accurately detected and ignored during
 colocation; set up to plot vector data using config file settings; fixed
 remote file access for colocation copies

git-svn-id: https://svn.ssec.wisc.edu/repos/glance/trunk@100 8a9318a1-56ba-4d59-b755-99d26321be01
---
 pyglance/glance/compare.py | 91 +++++++++++++++++++++++++++++++-------
 pyglance/glance/delta.py   | 13 +++---
 pyglance/glance/figures.py |  2 +-
 pyglance/glance/io.py      | 39 +++++++++++++++-
 4 files changed, 121 insertions(+), 24 deletions(-)

diff --git a/pyglance/glance/compare.py b/pyglance/glance/compare.py
index 736169e..6686428 100644
--- a/pyglance/glance/compare.py
+++ b/pyglance/glance/compare.py
@@ -555,7 +555,8 @@ def _handle_lon_lat_info (lon_lat_settings, a_file_object, b_file_object, output
     error_msg = None
     
     # if there is no lon/lat specified, stop now
-    if ('longitude' not in lon_lat_settings) or ('latitude' not in lon_lat_settings) :
+    if ( ('longitude' not in lon_lat_settings) or ('latitude' not in lon_lat_settings)
+        or (('noLonLatVars' in lon_lat_settings) and lon_lat_settings['noLonLatVars']) ) :
         return { }, spatialInfo, error_msg
     
     # if we should not be comparing against the logitude and latitude, stop now
@@ -786,18 +787,45 @@ def _uri_needs_rsync(uri_to_check) :
     """
     return not os.path.exists(uri_to_check)
 
-def rsync_or_copy_files (list_of_files, target_directory='.') :
+def _get_UV_info_from_magnitude_direction_info(fileObject, magnitudeName, directionName, invalidMask=None) :
+    """
+    If there are magnitude and direction names, load that information and calculate the u and v that correspond to it
+    """
+    
+    # if we don't have magnitude and direction, we can't calculate the U and V values
+    if (magnitudeName is None) or (directionName is None) :
+        return None, None
+    
+    # load the magnitude and direction data sets
+    magnitude = _load_variable_data(fileObject, magnitudeName)
+    direction = _load_variable_data(fileObject, directionName)
+    
+    # convert the magnitude and direction data into u and v vectors
+    uData, vData = delta.convert_mag_dir_to_U_V_vector(magnitude, direction, invalidMask=invalidMask)
+    
+    return uData, vData
+
+def rsync_or_copy_files (list_of_files, target_directory='.', additionalFileNameSuffix='') :
     """
     If the files in the list are remote, rsync them, otherwise, just copy
     them to the target directory
     """
+    newPaths = [ ]
+    
     for file_uri in list_of_files :
+        fileName = os.path.split(file_uri)[1]
+        baseFile, ext = os.path.splitext(fileName)
+        newPath = os.path.join(target_directory, baseFile + additionalFileNameSuffix + ext)
+        newPaths.append(newPath)
+        
         if _uri_needs_rsync(file_uri) :
-            cmd = ['rsync', '-Cuav', file_uri, os.path.join(target_directory, os.path.split(file_uri)[1])]
+            cmd = ['rsync', '-Cuav', file_uri, newPath]
         else :
-            cmd = ['cp', os.path.abspath(file_uri), os.path.join(target_directory, os.path.split(file_uri)[1])]
+            cmd = ['cp', os.path.abspath(file_uri), newPath]
         LOG.debug('running ' + ' '.join(cmd)) 
         sh(cmd)
+    
+    return newPaths
 
 def colocateToFile_library_call(a_path, b_path, var_list=[ ],
                                 options_set={ },
@@ -823,10 +851,10 @@ def colocateToFile_library_call(a_path, b_path, var_list=[ ],
         LOG.info("Creating output directory.")
         os.makedirs(pathsTemp['out'])
     
-    # make copies of the input files for colocation
-    rsync_or_copy_files ([pathsTemp['a'], pathsTemp['b']], target_directory=pathsTemp['out'])
-    pathsTemp['a'] = os.path.join(pathsTemp['out'], os.path.split(pathsTemp['a'])[1])
-    pathsTemp['b'] = os.path.join(pathsTemp['out'], os.path.split(pathsTemp['b'])[1])
+    # make copies of the input files for colocation TODO, fix paths
+    [pathsTemp['a'], pathsTemp['b']] = rsync_or_copy_files ([pathsTemp['a'], pathsTemp['b']],
+                                                            target_directory=pathsTemp['out'],
+                                                            additionalFileNameSuffix='-collocated')
     
     # open the files
     LOG.info("Processing File A:")
@@ -905,6 +933,10 @@ def colocateToFile_library_call(a_path, b_path, var_list=[ ],
         # colocate the data for this variable if we have longitude/latitude data
         if (len(lon_lat_data.keys()) > 0) and runInfo['doColocate'] :
             
+            # figure out the invalid masks
+            invalidA = lon_lat_data['a']['inv_mask'] | (aData == varRunInfo['missing_value'])
+            invalidB = lon_lat_data['b']['inv_mask'] | (bData == varRunInfo['missing_value_alt_in_b'])
+            
             # match up our points in A and B
             (aData, bData, (numberOfMultipleMatchesInA, numberOfMultipleMatchesInB)), \
             (aUnmatchedData,             unmatchedALongitude, unmatchedALatitude), \
@@ -915,8 +947,8 @@ def colocateToFile_library_call(a_path, b_path, var_list=[ ],
                                                                         missingData=varRunInfo['missing_value'],
                                                                         altMissingDataInB=varRunInfo['missing_value_alt_in_b'],
                                                                         # TODO, should missing data be considered?
-                                                                        invalidAMask=lon_lat_data['a']['inv_mask'],
-                                                                        invalidBMask=lon_lat_data['b']['inv_mask'])
+                                                                        invalidAMask=invalidA,
+                                                                        invalidBMask=invalidB)
             
             LOG.debug(str(numberOfMultipleMatchesInA) + " data pairs contain A data points used for multiple matches.")
             LOG.debug(str(numberOfMultipleMatchesInB) + " data pairs contain B data points used for multiple matches.")
@@ -924,15 +956,24 @@ def colocateToFile_library_call(a_path, b_path, var_list=[ ],
             LOG.debug(str(len(bUnmatchedData)) + " B data points could not be matched.")
             
             # save the colocated data information in the output files
+            
+            # all the a file information
             aFile.create_new_variable(technical_name + '-colocated', # TODO, how should this suffix be handled?
                                       missingvalue = varRunInfo['missing'] if 'missing' in varRunInfo else None,
                                       data = aData,
                                       variabletocopyattributesfrom = technical_name)
+            aFile.add_attribute_data_to_variable(technical_name + '-colocated', 'number of multiple matches', numberOfMultipleMatchesInA)
+            aFile.add_attribute_data_to_variable(technical_name + '-colocated', 'number of unmatched points', len(aUnmatchedData))
+            
+            # all the b file information
             bFile.create_new_variable(b_variable_technical_name + '-colocated', # TODO, how should this suffix be handled?
                                       missingvalue = varRunInfo['missing_value_alt_in_b'] if 'missing_value_alt_in_b' in varRunInfo else None,
                                       data = bData,
                                       variabletocopyattributesfrom = b_variable_technical_name)
-            # TODO, save the unmatched data and info on multiple matches
+            bFile.add_attribute_data_to_variable(b_variable_technical_name + '-colocated', 'number of multiple matches', numberOfMultipleMatchesInB)
+            bFile.add_attribute_data_to_variable(b_variable_technical_name + '-colocated', 'number of unmatched points', len(bUnmatchedData))
+            
+            # TODO, any additional statistics
             
         else :
             LOG.debug(explanationName + " was not selected for colocation and will be ignored.")
@@ -1061,7 +1102,8 @@ def reportGen_library_call (a_path, b_path, var_list=[ ],
         do_not_test_with_lon_lat = (not include_images_for_this_variable) or (len(lon_lat_data.keys()) <= 0)
         
         # handle vector data
-        isVectorData = False # TODO actually figure out if we have vector data from user inputted settings
+        isVectorData = ( ('magnitudeName' in varRunInfo)  and ('directionName'  in varRunInfo) and
+                         ('magnitudeBName' in varRunInfo) and ('directionBName' in varRunInfo) )
         
         # check if this data can be displayed but
         # don't compare lon/lat sizes if we won't be plotting
@@ -1142,6 +1184,18 @@ def reportGen_library_call (a_path, b_path, var_list=[ ],
                     else :
                         plotFunctionGenerationObjects.append(plotcreate.MappedContourPlotFunctionFactory())
                 
+                # if there's magnitude and direction data, figure out the u and v, otherwise these will be None
+                aUData, aVData = _get_UV_info_from_magnitude_direction_info (aFile,
+                                                                             varRunInfo['magnitudeName'] if ('magnitudeName') in varRunInfo else None,
+                                                                             varRunInfo['directionName'] if ('directionName') in varRunInfo else None,
+                                                                             lon_lat_data['a']['inv_mask']
+                                                                             if ('a' in lon_lat_data) and ('inv_mask' in lon_lat_data['a']) else None)
+                bUData, bVData = _get_UV_info_from_magnitude_direction_info (bFile,
+                                                                             varRunInfo['magnitudeBName'] if ('magnitudeBName') in varRunInfo else None,
+                                                                             varRunInfo['directionBName'] if ('directionBName') in varRunInfo else None,
+                                                                             lon_lat_data['b']['inv_mask']
+                                                                             if ('b' in lon_lat_data) and ('inv_mask' in lon_lat_data['b']) else None)
+                
                 # plot our lon/lat related info
                 image_names['original'], image_names['compared'] = \
                     plot.plot_and_save_comparison_figures \
@@ -1160,7 +1214,9 @@ def reportGen_library_call (a_path, b_path, var_list=[ ],
                              doFork=runInfo['doFork'],
                              shouldClearMemoryWithThreads=runInfo['useThreadsToControlMemory'],
                              shouldUseSharedRangeForOriginal=runInfo['useSharedRangeForOriginal'],
-                             doPlotSettingsDict = varRunInfo)
+                             doPlotSettingsDict = varRunInfo,
+                             aUData=aUData, aVData=aVData,
+                             bUData=bUData, bVData=bVData,)
                 
                 print("\tfinished creating figures for: " + explanationName)
             
@@ -1354,9 +1410,12 @@ python -m glance
         List available variables for comparison.
         """
         for fn in args:
-            lal = list(io.open(fn)())
-            lal.sort()
-            print fn + ': ' + ('\n  ' + ' '*len(fn)).join(lal)
+            try :
+                lal = list(io.open(fn)())
+                lal.sort()
+                print fn + ': ' + ('\n  ' + ' '*len(fn)).join(lal)
+            except KeyError :
+                LOG.warn('Unable to open / process file selection: ' + fn)
     
     def sdr_cris(*args):
         """compare sdr_cris output
diff --git a/pyglance/glance/delta.py b/pyglance/glance/delta.py
index fc14e56..38e5e2c 100644
--- a/pyglance/glance/delta.py
+++ b/pyglance/glance/delta.py
@@ -770,18 +770,19 @@ def create_colocated_data_with_lon_lat_colocation(listOfColocatedALonLat, listOf
         
         [aLon, aLat, aIndex, aMatches] = listOfColocatedALonLat[aIndex]
         tempMatches = 0
+        isInvalidA = invalidAMask[aIndex]
         
         # for each point that matched to a given a point
         for matchIndex in sorted(aMatches) :
             
             [bLon, bLat, bIndex, bMatches] = listOfColocatedBLonLat[matchIndex]
+            isInvalidB = invalidBMask[bIndex]
             
             # if either of our data points is invalid, then the data doesn't match
-            if invalidBMask[matchIndex] or invalidAMask[aIndex] :
+            if isInvalidA or isInvalidB :
                 # fill in missing data in the matches
                 matchedAPoints[currentIndex] = missingData
                 matchedBPoints[currentIndex] = altMissingDataInB
-                
             else: # we have a valid match!
                 tempMatches = tempMatches + 1
                 matchedAPoints[currentIndex] = aData[aIndex]
@@ -792,7 +793,7 @@ def create_colocated_data_with_lon_lat_colocation(listOfColocatedALonLat, listOf
         totalValidMatchedPairs = totalValidMatchedPairs + tempMatches
         if tempMatches > 1 :
             multipleMatchesInA = multipleMatchesInA + tempMatches
-        elif tempMatches <= 0 :
+        elif (tempMatches <= 0) and (not isInvalidA) :
             unmatchedAPoints.append(aData[aIndex])
             unmatchedALongitude.append(aLon)
             unmatchedALatitude.append(aLat)
@@ -803,14 +804,16 @@ def create_colocated_data_with_lon_lat_colocation(listOfColocatedALonLat, listOf
         
         [bLon, bLat, bIndex, bMatches] = listOfColocatedBLonLat[bIndex]
         tempMatches = 0
+        isInvalidB = invalidBMask[bIndex]
         
         # for each point that matched to a given b point
         for matchIndex in sorted(bMatches) :
             
             [aLon, aLat, aIndex, aMatches] = listOfColocatedALonLat[matchIndex]
+            isInvalidA = invalidAMask[aIndex]
             
             # if either of our data points is invalid, then the data doesn't match
-            if invalidAMask[matchIndex] or invalidBMask[bIndex] :
+            if isInvalidB or isInvalidA :
                 # we've already built our matched data, so no need to missing it out
                 pass
             else: # we have a valid match!
@@ -818,7 +821,7 @@ def create_colocated_data_with_lon_lat_colocation(listOfColocatedALonLat, listOf
         
         if tempMatches > 1 :
             multipleMatchesInB = multipleMatchesInB + tempMatches
-        elif tempMatches <= 0 :
+        elif (tempMatches <= 0) and (not isInvalidB) :
             unmatchedBPoints.append(bData[bIndex])
             unmatchedBLongitude.append(bLon)
             unmatchedBLatitude.append(bLat)
diff --git a/pyglance/glance/figures.py b/pyglance/glance/figures.py
index 0a7af9f..896cb1f 100644
--- a/pyglance/glance/figures.py
+++ b/pyglance/glance/figures.py
@@ -402,7 +402,7 @@ def create_quiver_mapped_figure(data, latitude, longitude, baseMapInstance, boun
     
     # draw our data placed on a map
     maps.draw_basic_features(baseMapInstance, boundingAxes)
-    bMap, x, y = maps.show_quiver_plot (longitudeClean, latitudeClean, baseMapInstance, (uData, vData), colordata=colorData)
+    bMap, x, y = maps.show_quiver_plot (longitudeClean, latitudeClean, baseMapInstance, (uDataClean, vDataClean), colordata=colorData)
     
     # show the title
     axes.set_title(title)
diff --git a/pyglance/glance/io.py b/pyglance/glance/io.py
index 41d99ea..cdc1c6e 100644
--- a/pyglance/glance/io.py
+++ b/pyglance/glance/io.py
@@ -12,6 +12,7 @@ import os, sys, logging
 from pyhdf.SD import SD,SDC, SDS, HDF4Error
 try:
     import h5py
+    from h5py import h5d
 except ImportError:
     pass
 from pycdf import CDF, NC, strerror
@@ -248,6 +249,22 @@ class nc(CDF):
             newVariable.put(data.tolist()) 
         
         return newVariable
+    
+    def add_attribute_data_to_variable(self, variableName, newAttributeName, newAttributeValue) :
+        """
+        if the attribute exists for the given variable, set it to the new value
+        if the attribute does not exist for the given variable, create it and set it to the new value
+        """
+        variableObject = self.get_variable_object(variableName)
+        
+        self.redef()
+        
+        variableObject.__setattr__(newAttributeName, newAttributeValue)
+        
+        self.enddef()
+        
+        return
+
 
 nc4 = nc
 cdf = nc
@@ -278,7 +295,7 @@ class h5(object):
                 try :
                     tempType = obj.dtype # this is required to provoke a type error for closed data sets
                     
-                    LOG.debug ('type: ' + str(tempType))
+                    #LOG.debug ('type: ' + str(tempType))
                     variableList.append(name)
                 except TypeError :
                     LOG.debug('TypeError prevents the use of variable ' + name
@@ -299,6 +316,7 @@ class h5(object):
     # for scaling it will be (so the return type may not reflect the
     # type found in the original file)
     def __getitem__(self, name):
+        
         # defaults
         scale_factor = 1.0
         add_offset = 0.0
@@ -308,6 +326,11 @@ class h5(object):
         # get our raw data and scaling info
         variable_object = self.get_variable_object(name)
         raw_data_copy = variable_object[:]
+        
+        #print ('*************************')
+        #print (dir (variable_object.id)) # TODO, is there a way to get the scale and offset through this?
+        #print ('*************************')
+        
         # load the scale factor and add offset
         if ('scale_factor' in variable_object.attrs) :
             scale_factor = variable_object.attrs['scale_factor']
@@ -335,7 +358,19 @@ class h5(object):
         return h5.trav(self._h5, name)
     
     def missing_value(self, name):
-        return None
+        
+        toReturn = None
+        
+        # get the missing value if it has been set
+        variableObject = self.get_variable_object(name)
+        pListObj = variableObject.id.get_create_plist()
+        fillValueStatus = pListObj.fill_value_defined()
+        if (h5d.FILL_VALUE_DEFAULT is fillValueStatus) or (h5d.FILL_VALUE_USER_DEFINED is fillValueStatus) :
+            temp = np.array((1), dtype=variableObject.dtype)
+            pListObj.get_fill_value(temp)
+            toReturn = temp
+        
+        return toReturn
         
 
 def open(pathname, allowWrite=False):
-- 
GitLab