;+ ; NAME: ; NCDF_DATA__DEFINE ; ; PURPOSE: ; ; This program is designed to make it easier to browse and read the ; data and metadata in netCDF and HDF files. The user can browse files, ; and read the data and metadata into main-level IDL variables. New netCDF ; and HDF files can be opened at any time. The user interacts with the ; program via a browser window (GUI) or directly through the methods of ; the object. The program implements an IDL object. ; ; Note that only HDF files with scientific datasets (SD) can be read currently. ; There is no support for VDATA objects or other objects sometimes found in HDF ; files. Also note that when variables are returned from HDF files, they are returned ; in a calibrated form, if calibration information about the variable is present in the ; file. Calibration information is presented as an extra variable attribute in the ; browser. ; ; calibratedData = calData.cal * (uncalibratedData - calData.offset) ; ; AUTHOR: ; ; FANNING SOFTWARE CONSULTING ; David Fanning, Ph.D. ; 1645 Sheely Drive ; Fort Collins, CO 80526 USA ; Phone: 970-221-0438 ; E-mail: david@idlcoyote.com ; Coyote's Guide to IDL Programming: http://www.idlcoyote.com ; ; CATEGORY: ; File I/O ; ; CALLING SEQUENCE: ; ; IDL> nCDFObject = Obj_New('NCDF_DATA', filename) ; ; ARGUMENTS: ; ; filename: The name of a netCDF or HDF file to open and browse. ; ; KEYWORD PARAMETERS: ; ; BROWSE: If this keyword is set, the Browse Window is invoked as soon ; as the object is initiated. ; ; DESTROY_FROM_BROWSER: As with all objects, this object is persistent until ; it is destroyed. However, with this keyword set, the object will ; be destroyed when the user closes the Browse Window. ; ; EXTENSION: In general, netCDF and HDF files use *.nc, *.ncf, *.ncdf of *.hdf file extensions to ; identify themselves as netCDF or HDF files. Some users have their own file extensions. ; You can use this keyword to identify the file extension you wish to use. If ; set here, it will be used as the file filter in place of the normal file ; extensions in DIALOG_PICKFILE. ; ; obj = ('NCDF_DATA', file, EXTENSION='*.bin') ; ; NO_READ_ON_PARSE: Normally, when a file is opened it is parsed for information. ; One piece of information is the minimum and maximum values of the variables. ; This requires actually reading the variables. This can slow things down ; considerably is the variable is large. Setting this keyword will suppress ; the reading of the variables during the parsing of the data file, with the ; result that no minimum or maximum values will be reported. ; ; NOTES: ; ; This program is designed to be flexible in how it is used, so it ; can be used in both interactive and non-interactive (called directly) ; ways. A less flexible way of interacting with the program is via the ; NCDF_BROWSER program, which is a front-end to this object. ; ; The netCDF and HDF file formats are thought to be "standards". And to ; a large extent, they are. But files are not always created to standards, ; and both netCDF and HDF files can be quirky. If you look carefully at the ; code you will see places where I work around quirks in the files I typically ; use on a daily basis. If you find you can't read a particular file, let me know ; about it. I may be able to improve the program in such as way that it can be read. ; ; This program is not meant to be the be-all and end-all of programs. Rather, it is ; a tool I use, and improve upon whenever necessary, in my own work with netCDF and HDF ; files. It will get better for all of us if you report problems to me directly. ; ; METHODS: ; ; The following methods can be used directly. ; ; ncdfObject -> Browse ; Use GUI to browse file data and metadata. ; ncdfObject -> OpenFile, filename ; Opens a new netCDF or HDF file. ; globalAttr = ncdfObject -> ReadGlobalAttr() ; Return a structure containing global attributes. ; attribute = ncdfObject -> ReadAttribute(attrname); Return an attribute, identified by name. ; dim = ncdfObject -> ReadDimension(dimName) ; Return a dimension, identified by name. ; variable = ncdfObject -> ReadVariable(varname) ; Return a variable, identified by name. ; varstruct = ncdfObject -> ReadVariableWithAttr(varname) ; Return a variable, identified by ; ; name, along with its attributes. ; allData = ncdfObject -> ReadFile(filename) ; Read all the data in the file, into structures. ; ; EXAMPLE: ; ; IDL> filename = 'example.nc' ; IDL> ncdfObj = Obj_New('NCDF_DATA', filename) ; IDL> ncdfObj -> Browse ; IDL> Obj_Destroy, ncdfObj ; ; MODIFICATION HISTORY: ; Written by: David W. Fanning, 03 Feb 2008. Used ideas from many ; people, including Chris Torrence, Ken Bowman, Liam Gumely, ; Andrew Slater, and Paul van Delst. ; Added EXTENSION keyword, resizeable TLB, and ability to download ; individual global attibutes. DWF. 04 Feb 2008. ; Added ReadDimension and ReadVariableWithAttr methods. DWF. 05 Feb 2008. ; Ill-formed attribute names giving me fits. Now doing checks with IDL_VALIDNAME ; before creating structures. 06 February 2008. DWF. ; Same problem. Wide use of IDL_VALIDNAME everywhere it seems wise. 06 Feb 2008. DWF. ; Added functionality to read a variable with its attributes from the browser interface, ; and fixed a problem with reading CHAR values. 2 March 2008. DWF. ; Fixed a problem with changing variable name when reading variable plus attributes. 6 March 2008. DWF. ; Fixed a problem with not setting GLOBAL keyword when inquiring about global attribute. 6 March 2008. DWF. ; Made sure file was parsed before attempting to read variables and attributes to avoid errors. 7 March 2008. DWF. ; Small bug with variable attributes fixed. 18 Dec 2008. DWF. ; Added ability to read HDF files containing Scientific Datasets (SD). 21 February 2009. DWF. ; Added error handling and protection for NCDF variables that have a dimension of length zero. 22 April 2009. DWF. ; Added NO_READ_ON_PARSE keyword. 22 April 2009. DWF. ; Now convert NCDF CHAR type variables to strings on output. 22 April 2009. DWF ; Fixed a problem with the directory being correct when file name passed in. 11 May 2009. DWF. ; Added COUNT, OFFSET, and STRIDE keywords to ReadVariable method. 25 June 2009. DWF. ; When reading a netCDF variable by itself (without it's attributes), the program now looks for ; a SCALE_FACTOR and ADD_OFFSET attribute, and if found will apply this to the variable before ; it is returned to the user. 24 August 2009. DWF. ; Added the methods GetAttrNames, GetVarNames, GetVarAttrNames, and ReadVarAttr to retrieve specfic ; information from the data files. 16 November 2009. DWF. ; Modified the ReadVariableWithAttr method to include the number of dimensions (in the NDIMS field, ; and the dimensions (in the DIMS field) in the return structure. For HDF files, the DIMS field ; is a vector of the dimensions of the variable. For netCDF files, the DIMS field is a vector ; of dimension IDs for the dimensions of the variable. 27 Nov 2009. DWF. ; Andy Meigs alerted me to a problem creating a structure when the ncdf variable name ; is ill-formed according to IDL structure tag name rules. Fixed in the ReadFile method. ; 30 November 2009. DWF. ; Added NO_NEW_FILE keyword to the BROWSE method. This keyword will suppress the OPEN FILE ; button on the browse interface. 3 Feb 2010. DWF. ; Made the default browser size a bit larger to accomodate longer variable names. 3 Feb 2010. DWF. ; Add a check for HDF/netCDF file type in the INIT method to better accommodate reading data ; from the file without first parsing the file. 16 March 2010. DWF. ; Changed the ReadVariable for netCDF files to now check for missing data, using either the ; depreciated missing_value attribute or the compliant _FillValue attribute. Missing data ; is now identified via new output keywords MISSINGINDICES and FILLVALUE, and missing data ; is not scaled or offset, if these operations are applied to the data prior to return. ; 21 March 2010. DWF. Problem with these changes, fixed 23 March 2010. DWF. ; Fixed a problem with memory leakage when the input file cannot be read. 1 May 2010. DWF. ; Fixed a problem with memory leakage from created structures. 1 May 2010. DWF. ; Have done some work on parsing HDF-EOS swath files, but currently unused in code. 15 May 2010. DWF. ; Modified the ReadVariable method to check for 0 length dimensions when reading variables ; from HDF files. 21 July 2010. DWF. ; Modified the global attribute structure so that the "filename" field, which holds the ; name of the netCDF of HDF file is now named "ncdf_filename" or "hdf_filename". This ; will avoid conflicts with global attributes with "filename". 20 January 2011. DWF. ; Typo in the section reading calibration data fixed. 12 March 2013. DWF. ; Problem reading a variable containing no attributes fixed. 20 June 2014. DWF. ;- ;******************************************************************************************; ; Copyright (c) 2008-2010, by Fanning Software Consulting, Inc. ; ; All rights reserved. ; ; ; ; Redistribution and use in source and binary forms, with or without ; ; modification, are permitted provided that the following conditions are met: ; ; ; ; * Redistributions of source code must retain the above copyright ; ; notice, this list of conditions and the following disclaimer. ; ; * Redistributions in binary form must reproduce the above copyright ; ; notice, this list of conditions and the following disclaimer in the ; ; documentation and/or other materials provided with the distribution. ; ; * Neither the name of Fanning Software Consulting, Inc. nor the names of its ; ; contributors may be used to endorse or promote products derived from this ; ; software without specific prior written permission. ; ; ; ; THIS SOFTWARE IS PROVIDED BY FANNING SOFTWARE CONSULTING, INC. ''AS IS'' AND ANY ; ; EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ; ; OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT ; ; SHALL FANNING SOFTWARE CONSULTING, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, ; ; INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED ; ; TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; ; ; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ; ; ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ; ; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ; ; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ; ;******************************************************************************************; PRO NCDF_DATA::Browse, $ NO_NEW_FILE=no_new_file, $ SUCCESS=success, $ TITLE=title, $ XOFFSET=xoffset, $ YOFFSET=yoffset ; ; NAME: ; NCDF_DATA::Browse ; ; PURPOSE: ; ; This method is invoked to create a Browser Window the user can ; interact with to explored the data and metadata in a netCDF or HDF file. ; ; CALLING SEQUENCE: ; ; IDL> nCDFObject -> Browse ; ; ARGUMENTS: ; ; None. ; ; KEYWORD PARAMETERS: ; ; NONEWFILE: If this keyword is set, the browser does not allow selecting ; a new netCDF file from the interface. ; ; TITLE: The text on the title bar. By default, 'File Browser'. ; ; SUCCESS: An output keyword set to 1 if this method exits successfully. ; ; XOFFSET: Normally, the Browser Window is centered, however is this ; keyword and the YOFFSET keywords are used, the Browser Window ; can be located with the upper-left corner at these locations in ; device coordinates. The X offset of the Browser Window. ; ; YOFFSET: The Y offset of the Browser Window. ; ; Error handling CATCH, theError IF theError NE 0 THEN BEGIN CATCH, /CANCEL void = cgErrorMsg() Widget_Control, self.tlb, /DESTROY success = 0 RETURN ENDIF ; Assume the best. success = 1 ; Check input parameters. IF N_Elements(xoffset) EQ 0 THEN xoffset = -1 IF N_Elements(yoffset) EQ 0 THEN yoffset = -1 newFileOK = ~Keyword_Set(no_new_file) IF N_Elements(title) EQ 0 THEN title = 'File Browser' ; Only one browser with this TLB on the display at a time. IF Widget_Info(self.tlb, /VALID_ID) THEN BEGIN Widget_Control, self.tlb, /SHOW success = 0 RETURN ENDIF ; Make sure the file has been parsed. IF self.hasBeenParsed EQ 0 THEN self -> ParseFile IF self.hasBeenParsed EQ 0 THEN BEGIN success = 0 RETURN ENDIF ; Get some bitmaps for the widget_tree. bmfile = Filepath(SubDir=['resource','bitmaps'], 'volume.bmp') IF File_Test(bmfile) THEN BEGIN bm = Read_BMP(bmfile, r, g, b) i = Where(bm EQ 8, count) IF count GT 0 THEN bm[i] = 16B variableBM = [ [[r[bm]]], [[g[bm]]], [[b[bm]]] ] ENDIF bmfile = Filepath(SubDir=['resource','bitmaps'], 'axis.bmp') IF File_Test(bmfile) THEN BEGIN bm = Read_BMP(bmfile, r, g, b) i = Where(bm EQ 8, count) IF count GT 0 THEN bm[i] = 16B dimensionBM = [ [[r[bm]]], [[g[bm]]], [[b[bm]]] ] ENDIF bmfile = Filepath(SubDir=['resource','bitmaps'], 'ascii.bmp') IF File_Test(bmfile) THEN BEGIN bm = Read_BMP(bmfile, r, g, b) i = Where(bm EQ 80, count) IF count GT 0 THEN bm[i] = 95B attributeBM = [ [[r[bm]]], [[g[bm]]], [[b[bm]]] ] ENDIF bmfile = Filepath(SubDir=['resource','bitmaps'], 'sum.bmp') IF File_Test(bmfile) THEN BEGIN bm = Read_BMP(bmfile, r, g, b) i = Where(bm EQ 8, count) IF count GT 0 THEN bm[i] = 16B summaryBM = [ [[r[bm]]], [[g[bm]]], [[b[bm]]] ] ENDIF ; Set up the initial tree widget. self.tlb = Widget_Base(TITLE=title, COLUMN=1, UVALUE=self, /BASE_ALIGN_CENTER, $ TLB_SIZE_EVENT=1) rowbase = Widget_Base(self.tlb, ROW=1, XPAD=0, YPAD=0) theTree = Widget_Tree(rowbase, SCR_XSIZE=350, SCR_YSIZE=400, UNAME='theTree') self.textDisplay = Widget_Text(rowbase, SCR_XSIZE=450, SCR_YSIZE=400, /SCROLL) ; Set up fundamental branch. aBranch = Widget_Tree(theTree, Value='File Overview', /FOLDER, /EXPANDED, UNAME='FOLDER') aNode = Widget_Tree(aBranch, Value='Directory', UValue=self.directory, UNAME='DIRECTORY', BITMAP=summaryBM) aNode = Widget_Tree(aBranch, Value='File Name', UValue=self.filename, UNAME='FILENAME', BITMAP=summaryBM) summaryNode = Widget_Tree(aBranch, Value='Summary', UNAME='SUMMARY', BITMAP=summaryBM) Widget_Control, summaryNode, Set_Tree_Select=1 ; Set up global attribute branch. IF Ptr_Valid(self.theAttributes) THEN BEGIN aBranch = Widget_Tree(theTree, Value='Global Attributes', /FOLDER, UNAME='FOLDER') theAttributes = *self.theAttributes FOR j=0,N_Elements(theAttributes)-1 DO BEGIN aNode = Widget_Tree(aBranch, Value=theAttributes[j].name, UValue=theAttributes[j].name, $ UNAME='GLOBAL ATTRIBUTE', BITMAP=attributeBM) ENDFOR ENDIF ; Set up dimension branch. IF Ptr_Valid(self.theDimensions) THEN BEGIN aBranch = Widget_Tree(theTree, Value='Dimensions', /FOLDER, UNAME='FOLDER') theDimensions = *self.theDimensions FOR j=0,N_Elements(theDimensions)-1 DO BEGIN aNode = Widget_Tree(aBranch, Value=theDimensions[j].name, UValue=theDimensions[j].name, $ UNAME='DIMENSION', BITMAP=dimensionBM) ENDFOR ENDIF ; Set up variable branch. IF Ptr_Valid(self.theVariables) THEN BEGIN aBranch = Widget_Tree(theTree, Value='Variables', /FOLDER, UNAME='FOLDER') theVariables = *self.theVariables FOR j=0,N_Elements(theVariables)-1 DO BEGIN IF Ptr_Valid(theVariables[j].var_attributes) THEN BEGIN varBranch = Widget_Tree(aBranch, Value=theVariables[j].name, UValue=theVariables[j].name, $ UNAME='VARIABLE', /FOLDER, BITMAP=variableBM) theAttributes = *theVariables[j].var_attributes FOR k=0,N_Elements(theAttributes)-1 DO BEGIN IF theAttributes[k].name NE "" THEN BEGIN aNode = Widget_Tree(varBranch, Value=theAttributes[k].name, $ UValue=[theVariables[j].name,theAttributes[k].name], $ BITMAP=attributeBM, UNAME='VARIABLE ATTRIBUTE') ENDIF ENDFOR ENDIF ELSE BEGIN aNode = Widget_Tree(aBranch, Value=theVariables[j].name, UValue=theVariables[j].name, $ UNAME='VARIABLE', BITMAP=variableBM) ENDELSE ENDFOR ENDIF ; Application Buttons buttonBase = Widget_Base(self.tlb, /ROW, BASE_ALIGN_CENTER=1) button = Widget_Button(buttonBase, Value='Read Variable', UVALUE='READ_VAR_FROM_GUI') button = Widget_Button(buttonBase, Value='Read Variable with Attributes', UVALUE='READ_VARPLUS_FROM_GUI') button = Widget_Button(buttonBase, Value='Read Global Attribute', UVALUE='READ_ATTRIBUTE_FROM_GUI') button = Widget_Button(buttonBase, Value='Read Entire File', UVALUE='READ_FILE_FROM_GUI') IF newFileOK THEN button = Widget_Button(buttonBase, Value='Open New File', UVALUE='OPEN_NEW_FILE') button = Widget_Button(buttonBase, Value='Exit', UVALUE='QUIT_BROWSER') ; Get the geometries of the tree widget and the button base. These ; will set the minimun and maximum values for resizing. self.geoWindow = Widget_Info(self.tlb, /GEOMETRY) self.geoTree = Widget_Info(theTree, /GEOMETRY) self.geoButton = Widget_Info(buttonBase, /GEOMETRY) self.geoDisplay = Widget_Info(self.textDisplay, /GEOMETRY) self.minxsize = self.geoDisplay.scr_xsize self.minysize = self.geoDisplay.scr_ysize ; Position the application and realize it. IF (xoffset LT 0 AND yoffset LT 0) THEN cgCenterTLB, self.tlb ELSE cgCenterTLB, self.tlb, xoffset, yoffset, /DEVICE, /NOCENTER Widget_Control, self.tlb, /REALIZE self.theTree = theTree XManager, 'ncdf_data', self.tlb, /NO_BLOCK, EVENT_HANDLER='NCDF_DATA_WIDGET_EVENTS', $ CLEANUP='NCDF_DATA_WIDGET_CLEANUP' ; Send an event to start up in the summary browse look. event ={WIDGET_TREE_SEL} event.top = self.tlb event.id = summaryNode event.handler = self.tlb event.clicks = 1 event.type = 0 Widget_Control, summaryNode, Send_Event=event END ;--------------------------------------------------------------------------------------------- PRO NCDF_DATA::CleanParsedStructures ; An internal method used to clean up pointers in names structures ; to prevent memory leakage from the object. ; Clean up all pointers in the global attribute structures. IF Ptr_Valid(self.theAttributes) THEN BEGIN num = N_Elements(*self.theAttributes) FOR j=0,num-1 DO Ptr_Free, (*self.theAttributes)[j].value ENDIF ; Clean up all pointers in the variable structures. IF Ptr_Valid(self.theVariables) THEN BEGIN num = N_Elements(*self.theVariables) FOR j=0,num-1 DO BEGIN Ptr_Free, (*self.theVariables)[j].value Ptr_Free, (*self.theVariables)[j].datasize Ptr_Free, (*self.theVariables)[j].calibration IF Ptr_Valid((*self.theVariables)[j].var_attributes) THEN BEGIN attrs = *(*self.theVariables)[j].var_attributes attnum = N_Elements( attrs ) FOR k=0,attnum-1 DO Ptr_Free, attrs[k].value Ptr_Free, (*self.theVariables)[j].var_attributes ENDIF ENDFOR ENDIF ; Clean up all pointers in the swath structures. IF Ptr_Valid(self.theSwaths) THEN BEGIN num = N_Elements(*self.theSwaths) FOR j=0,num-1 DO BEGIN Ptr_Free, (*self.theSwaths)[j].maps Ptr_Free, (*self.theSwaths)[j].idxmaps Ptr_Free, (*self.theSwaths)[j].dimensions IF Ptr_Valid((*self.theSwaths)[j].attributes) THEN BEGIN attrs = *(*self.theSwaths)[j].attributes attnum = N_Elements( attrs ) FOR k=0,attnum-1 DO Ptr_Free, attrs[k].value Ptr_Free, (*self.theSwaths)[j].attributes ENDIF IF Ptr_Valid((*self.theSwaths)[j].geofields) THEN BEGIN num = N_Elements(*(*self.theSwaths)[j].geofields) FOR j=0,num-1 DO BEGIN thisGeoField = (*(*(*self.theSwaths)[j].geofields))[j] Ptr_Free, thisGeoField.value Ptr_Free, thisGeoField.datasize Ptr_Free, thisGeoField.calibration IF Ptr_Valid(*thisGeoField.var_attributes) THEN BEGIN attrs = *(*thisGeoField.var_attributes) attnum = N_Elements( attrs ) FOR k=0,attnum-1 DO Ptr_Free, attrs[k].value Ptr_Free, (*thisGeoField)[j].var_attributes ENDIF Ptr_Free,(*self.theSwaths)[j].geofields ENDFOR ENDIF IF Ptr_Valid((*self.theSwaths)[j].datafields) THEN BEGIN num = N_Elements(*(*self.theSwaths)[j].datafields) FOR j=0,num-1 DO BEGIN thisGeoField = (*(*(*self.theSwaths)[j].datafields))[j] Ptr_Free, thisGeoField.value Ptr_Free, thisGeoField.datasize Ptr_Free, thisGeoField.calibration IF Ptr_Valid(*thisGeoField.var_attributes) THEN BEGIN attrs = *(*thisGeoField.var_attributes) attnum = N_Elements( attrs ) FOR k=0,attnum-1 DO Ptr_Free, attrs[k].value Ptr_Free, (*thisGeoField)[j].var_attributes ENDIF Ptr_Free,(*self.theSwaths)[j].datafields ENDFOR ENDIF ENDFOR ENDIF END ;--------------------------------------------------------------------------------------------- PRO NCDF_DATA::EventHandler, event ; An internal method used to process events and sent them to appropriate event handler methods. ; Error handling CATCH, theError IF theError NE 0 THEN BEGIN CATCH, /CANCEL void = cgErrorMsg() RETURN ENDIF ; Event branching is based initially on event structure names. thisEvent = Tag_Names(event, /STRUCTURE_NAME) CASE thisEvent OF 'WIDGET_BASE': BEGIN Widget_Control, self.textDisplay, $ SCR_XSIZE = (event.x -(self.geoTree.scr_xsize + 8)) > self.minXSize , $ SCR_YSIZE = (event.y -(self.geoButton.scr_ysize + 8)) > self.minYSize END 'WIDGET_TREE_SEL': self -> SelectionInTree, event 'WIDGET_TREE_EXPAND': 'WIDGET_BUTTON': BEGIN Widget_Control, event.id, GET_UVALUE=buttonUValue CASE buttonUValue OF 'READ_ATTRIBUTE_FROM_GUI': self -> ReadAttributeFromGUI, event 'READ_ATTRIBUTE_AND_LEAVE': self -> ReadAttributeFromGUI_Events, event 'READ_ATTRIBUTE_AND_STAY': self -> ReadAttributeFromGUI_Events, event 'QUIT_READ_ATTRIBUTE_GUI': self -> ReadAttributeFromGUI_Events, event 'READ_VAR_FROM_GUI': self -> ReadVariableFromGUI, event 'READ_AND_LEAVE': self -> ReadVariableFromGUI_Events, event 'READ_AND_STAY': self -> ReadVariableFromGUI_Events, event 'QUIT_READ_VARIABLE_GUI': self -> ReadVariableFromGUI_Events, event 'READ_VARPLUS_FROM_GUI': self -> ReadVarPlusFromGUI, event 'READ_VARPLUS_AND_STAY': self -> ReadVarPlusFromGUI_Events, event 'READ_VARPLUS_AND_LEAVE': self -> ReadVarPlusFromGUI_Events, event 'QUIT_READ_VARPLUS_GUI': self -> ReadVarPlusFromGUI_Events, event 'READ_FILE_FROM_GUI': self -> ReadFileFromGUI, event 'OPEN_NEW_FILE': self -> OpenNewFile, event 'QUIT_BROWSER': Widget_Control, event.top, /DESTROY 'APPEND_FILENAME': ELSE: Print, 'No case for ' + buttonUValue ENDCASE END 'WIDGET_DROPLIST': BEGIN theName = Widget_Info(event.id, /UNAME) CASE theName OF 'VARIABLES': self -> ReadVariableFromGUI_Events, event 'VARIABLESPLUS': self -> ReadVarPlusFromGUI_Events, event 'ATTRIBUTES': self -> ReadAttributeFromGUI_Events, event ENDCASE END 'WIDGET_TEXT_CH': ELSE: BEGIN ok = Dialog_Message('Unrecognized event in EventHandler method. See Console for details.') HELP, event, /Structure END ENDCASE END ;--------------------------------------------------------------------------------------------- FUNCTION NCDF_DATA::FindDimensions, fileID, varID ; Get information about the variable. r = NCDF_VarInq(fileID, varID) dims = LonArr(N_Elements(r.dim)) FOR j=0,N_Elements(dims)-1 DO BEGIN dimID = r.dim[j] NCDF_DIMINQ, fileID, dimID, dimension_name, dimension_size dims[j] = dimension_size ENDFOR ; If this is a character variable, then the first dimension is irrelevant ; if there is more than one variable. IF r.datatype EQ 'CHAR' THEN IF N_Elements(dims) GT 1 THEN dims = dims[1:*] RETURN, dims END ;--------------------------------------------------------------------------------------------- FUNCTION NCDF_DATA::Destroy_From_Browser RETURN, self.destroy_from_browser END ;--------------------------------------------------------------------------------------------- FUNCTION NCDF_DATA::GetAttrNames ; Returns a list of the global attribute names. ; Error handling CATCH, theError IF theError NE 0 THEN BEGIN CATCH, /CANCEL void = cgErrorMsg() RETURN, "" ENDIF ; The file has to be parsed to carry this out. IF self.hasBeenParsed EQ 0 THEN self -> ParseFile numAttrs = N_Elements(*self.theAttributes) attributeList = StrArr(numAttrs) FOR j=0,numAttrs-1 DO BEGIN thisAttribute = (*self.theAttributes)[j] attributeList[j] = thisAttribute.name ENDFOR RETURN, attributeList END ;--------------------------------------------------------------------------------------------- PRO NCDF_DATA::ListAttrNames ; Prints out the global attribute names found in the file. ; Error handling CATCH, theError IF theError NE 0 THEN BEGIN CATCH, /CANCEL void = cgErrorMsg() RETURN ENDIF ; The file has to be parsed to carry this out. IF self.hasBeenParsed EQ 0 THEN self -> ParseFile numAttrs = N_Elements(*self.theAttributes) Print, 'There are ', StrTrim(numAttrs,2), ' attributes in ' + self.filename Print, "" FOR j=0,numAttrs-1 DO BEGIN thisAttribute = (*self.theAttributes)[j] Print, ' ', StrTrim(j+1,2), ') ', thisAttribute.name ENDFOR Print, "" END ;--------------------------------------------------------------------------------------------- FUNCTION NCDF_DATA::GetVarNames ; This function returns a list of variable names found in the file. ; Error handling CATCH, theError IF theError NE 0 THEN BEGIN CATCH, /CANCEL void = cgErrorMsg() RETURN, "" ENDIF ; The file has to be parsed to carry this out. IF self.hasBeenParsed EQ 0 THEN self -> ParseFile numVars = N_Elements(*self.theVariables) varList = StrArr(numVars) FOR j=0,numVars-1 DO BEGIN thisVariable = (*self.theVariables)[j] varList[j] = thisVariable.name ENDFOR RETURN, varList END ;--------------------------------------------------------------------------------------------- PRO NCDF_DATA::ListVarNames ; Prints out the variable names found in the file. ; Error handling CATCH, theError IF theError NE 0 THEN BEGIN CATCH, /CANCEL void = cgErrorMsg() RETURN ENDIF ; The file has to be parsed to carry this out. IF self.hasBeenParsed EQ 0 THEN self -> ParseFile numVars = N_Elements(*self.theVariables) Print, 'There are ', StrTrim(numVars,2), ' variables in ' + self.filename Print, "" FOR j=0,numVars-1 DO BEGIN thisVariable = (*self.theVariables)[j] Print, ' ', StrTrim(j+1,2), ') ', thisVariable.name ENDFOR Print, "" END ;--------------------------------------------------------------------------------------------- FUNCTION NCDF_DATA::GetVarAttrNames, theVariable ; This function returns a list of attribute names which it finds for a particular variable. ; Error handling CATCH, theError IF theError NE 0 THEN BEGIN CATCH, /CANCEL void = cgErrorMsg() RETURN, "" ENDIF ; The file has to be parsed to carry this out. IF self.hasBeenParsed EQ 0 THEN self -> ParseFile ; Get the variable list. theVarStructures = *self.theVariables ; Find this variable in the variable structures. index = Where(theVarStructures.name EQ theVariable, count) IF count EQ 0 THEN Message, 'Cannot find the variable ' + theVariable + ' in the file.' thisVariableStruct = theVarStructures[index] ; Get the pointer to the variable attribute structures. varAttrStructures = *thisVariableStruct.var_attributes ; Extract the attribute names. numAttrs = N_Elements(varAttrStructures) varAttrList = StrArr(numAttrs) FOR j=0,numAttrs-1 DO BEGIN varAttrList[j] = varAttrStructures[j].name ENDFOR RETURN, varAttrList END ;--------------------------------------------------------------------------------------------- PRO NCDF_DATA::OpenNewFile, event ; Creates a dialog for the user to specify the name of a new netCDF or HDF file to open. ; Loads the file into the object. ; Error handling CATCH, theError IF theError NE 0 THEN BEGIN CATCH, /CANCEL void = cgErrorMsg() RETURN ENDIF ; Get the name of a new file. Look in the directory of the last file. filename = Dialog_Pickfile(FILTER=self.extension , PATH=self.directory, $ /READ, TITLE='Select a File to Open') IF filename EQ "" THEN RETURN ; Open the new file. self -> OpenFile, filename END ;--------------------------------------------------------------------------------------------- PRO NCDF_DATA::OpenFile, filename ; ; NAME: ; NCDF_DATA::OpenFile ; ; PURPOSE: ; ; This method is used to open a new netCDF or HDF file and add it to the object. ; ; CALLING SEQUENCE: ; ; IDL> nCDFObject -> OpenFile, filename ; ; ARGUMENTS: ; ; filename: The name of a netCDF or HDF file to open. ; ; KEYWORD PARAMETERS: ; ; None. ; ; Error handling CATCH, theError IF theError NE 0 THEN BEGIN CATCH, /CANCEL void = cgErrorMsg() RETURN ENDIF IF N_Elements(filename) NE 0 THEN BEGIN ; Clean up from the old file that will here before. self -> CleanParsedStructures ; Set the filename and directory name. directory = File_Dirname(filename) IF directory EQ "" THEN CD, CURRENT=directory self.directory = directory self.filename = File_Basename(filename) ; Parse the new file. self.hasbeenParsed = 0 self -> ParseFile ; If a browser is currently displayed, kill it and display ; a browser in the same location with new file information. IF Widget_Info(self.tlb, /Valid_ID) THEN BEGIN thisState = self.destroy_from_browser IF thisState THEN self.destroy_from_browser = 0 Widget_Control, self.tlb, TLB_GET_OFFSET=offsets Widget_Control, self.tlb, /DESTROY self -> Browse, XOFFSET=offsets[0], YOFFSET=offsets[1] self.destroy_from_browser = thisState ENDIF ENDIF ELSE Message, 'Must pass name of file to open.' END ;--------------------------------------------------------------------------------------------- PRO NCDF_DATA::ParseFile ; This internal method parses the new netCDF or HDF file initially, and creates ; the IDL structures necessary for browsing the object. ; Error handling CATCH, theError IF theError NE 0 THEN BEGIN CATCH, /CANCEL void = cgErrorMsg() self.hasBeenParsed = 0 IF N_Elements(fileID) NE 0 THEN NCDF_Close, fileID RETURN ENDIF ; Check to see if file is available. IF self.filename EQ "" THEN BEGIN ok = Dialog_Message('Add a file to the NCDF_DATA object before proceeding.') RETURN ENDIF ; Be sure everything is cleaned up and ready to go. self -> CleanParsedStructures Ptr_Free, self.theAttributes Ptr_Free, self.theDimensions Ptr_Free, self.theCalibration Ptr_Free, self.theSwaths Ptr_Free, self.theVariables Ptr_Free, self.zeroDimensionID ; Special processing if this is an HDF file. Otherwise, we believe it is a netCDF file. self.isHDF = HDF_ISHDF(Filepath(ROOT_DIR=self.directory, self.filename)) IF self.isHDF THEN BEGIN ; Is this an HDF-EOS type file? If so, we want to parse it differently isEOS_File = EOS_Query(Filepath(ROOT_DIR=self.directory, self.filename), info) IF isEOS_File THEN BEGIN IF info.num_grids NE 0 OR info.num_points NE 0 THEN BEGIN Message, 'This program does not current parse HDF-EOS grids or point data.', /INFORMATIONAL ENDIF ; This code is not quite ready. Bypassing this for the moment. self -> Parse_HDF_File ENDIF ELSE self -> Parse_HDF_File RETURN ENDIF ; Open the file and find out how many dimensions, global attributes, and variables are there. fileID = NCDF_Open(Filepath(ROOT_DIR=self.directory, self.filename)) info = NCDF_Inquire(fileID) ; Is there an unlimited dimension in this file? IF info.recdim NE -1 THEN BEGIN NCDF_DIMINQ, fileID, info.recdim, unlimitedName, unlimitedSize unlimitedID = info.recdim ENDIF ELSE unlimitedName = "" ; First, get the global attributes. num_attr = info.ngatts IF num_attr GT 0 THEN BEGIN theAttributes = Replicate({NCDF_DATA_ATTRIBUTE}, num_attr) FOR j=0,num_attr-1 DO BEGIN attribute_name = NCDF_AttName(fileID, j, /GLOBAL) NCDF_AttGet, fileID, attribute_name, theAttribute, /GLOBAL attinfo = NCDF_ATTINQ(fileID, attribute_name, /GLOBAL) att_type = StrUpCase(attinfo.dataType) ; Strings are stored as byte values in attributes, so convert them back. IF (Size(theAttribute, /TNAME) EQ 'BYTE') AND (att_type EQ 'CHAR') $ THEN theAttribute = String(theAttribute) theAttributes[j].attrType = 'GLOBAL' theAttributes[j].dataType = att_type theAttributes[j].length = N_Elements(theAttribute) theAttributes[j].name = attribute_name theAttributes[j].value = Ptr_New(theAttribute) ENDFOR self.theAttributes = Ptr_New(theAttributes, /No_Copy) ENDIF ; Next, get the dimensions. num_dims = info.ndims IF num_dims GT 0 THEN BEGIN theDimensions = REPLICATE({NCDF_DATA_DIMENSION}, num_dims) FOR j=0,num_dims-1 DO BEGIN NCDF_DIMINQ, fileID, j, dimension_name, dimension_size IF dimension_size EQ 0 THEN BEGIN IF Ptr_Valid(self.zeroDimensionID) $ THEN *self.zeroDimensionID = [*self.zeroDimensionID, j] $ ELSE self.zeroDimensionID = Ptr_New(j) ENDIF theDimensions[j].name = dimension_name theDimensions[j].value = String(dimension_size) ENDFOR self.theDimensions = Ptr_New(theDimensions, /No_Copy) ENDIF ; Next, get the variables. num_vars = info.nvars IF num_vars GT 0 THEN BEGIN theVariables = REPLICATE({NCDF_DATA_VARIABLE}, num_vars) FOR j=0,num_vars-1 DO BEGIN ; Get information about the variable. varinfo = NCDF_VarInq(fileID, j) theVariables[j].datatype = varinfo.datatype theVariables[j].name = varinfo.name ; If this variable has attributes, get those, too. IF varinfo.natts GT 0 THEN BEGIN varAttributes = Replicate({NCDF_DATA_ATTRIBUTE}, varinfo.natts) FOR k=0,varinfo.natts-1 DO BEGIN attribute_name = NCDF_AttName(fileID, j, k) NCDF_AttGet, fileID, j, attribute_name, theAttribute attinfo = NCDF_ATTINQ(fileID, j, attribute_name) att_type = StrUpCase(attinfo.dataType) ; Strings are stored as byte values in attributes, so convert them back. IF (Size(theAttribute, /TNAME) EQ 'BYTE') AND (att_type EQ 'CHAR') $ THEN theAttribute = String(theAttribute) varAttributes[k].attrType = StrUpCase(varinfo.name) varAttributes[k].dataType = att_type varAttributes[k].length = N_Elements(theAttribute) varAttributes[k].name = attribute_name varAttributes[k].value = Ptr_New(theAttribute) ENDFOR theVariables[j].var_attributes = Ptr_New(varAttributes) ENDIF ; Get information about the variable, including the dimension IDs. r = NCDF_VarInq(fileID, j) ; Now, read the data so you can collect information about it. It is possible ; that one of the dimensions has a zero length. If true, reading the data will ; cause an error. We want to prevent that, so if we have a zero dimension we ; are going to make sure we don't read that variable. ; IF Ptr_Valid(self.zeroDimensionID) EQ 0 THEN BEGIN IF self.no_read_on_parse THEN BEGIN theVariables[j].dataSize = Ptr_New(self -> FindDimensions(fileID, j)) theVariables[j].minValue = 0 theVariables[j].maxValue = 0 ENDIF ELSE BEGIN NCDF_VarGet, fileID, j, data theVariables[j].dataSize = Ptr_New(self -> FindDimensions(fileID, j)) IF r.datatype NE 'CHAR' THEN BEGIN minData = Min(data, MAX=maxData) theVariables[j].minValue = minData theVariables[j].maxValue = maxData ENDIF Undefine, data ENDELSE ENDIF ELSE BEGIN ; Is there a match between the dimension IDs and any zero dimension ID we ; have stored? match = 0 FOR m = 0, N_Elements(*self.zeroDimensionID)-1 DO BEGIN i = Where(r.dim EQ (*self.zeroDimensionID)[m], count) IF count GT 0 THEN match = 1 ENDFOR IF match GT 0 THEN BEGIN theVariables[j].dataSize = Ptr_New(self -> FindDimensions(fileID, j)) theVariables[j].minValue = 0 theVariables[j].maxValue = 0 ENDIF ELSE BEGIN IF self.no_read_on_parse THEN BEGIN theVariables[j].dataSize = Ptr_New(self -> FindDimensions(fileID, j)) theVariables[j].minValue = 0 theVariables[j].maxValue = 0 ENDIF ELSE BEGIN NCDF_VarGet, fileID, j, data theVariables[j].dataSize = Ptr_New(self -> FindDimensions(fileID, j)) IF r.datatype NE 'CHAR' THEN BEGIN minData = Min(data, MAX=maxData) theVariables[j].minValue = minData theVariables[j].maxValue = maxData ENDIF Undefine, data ENDELSE ENDELSE ENDELSE ENDFOR self.theVariables = Ptr_New(theVariables, /No_Copy) ENDIF ; Successfully parsed file. self.hasBeenParsed = 1 ; Close the file NCDF_Close, fileID END ;--------------------------------------------------------------------------------------------- PRO NCDF_DATA::Parse_HDF_File ; This internal method parses the new HDF file initially, and creates ; the IDL structures necessary for browsing the object. ; Error handling CATCH, theError IF theError NE 0 THEN BEGIN CATCH, /CANCEL void = cgErrorMsg() self.hasBeenParsed = 0 IF N_Elements(fileID) NE 0 THEN HDF_SD_End, fileID RETURN ENDIF ; Check to see if file is available. IF self.filename EQ "" THEN BEGIN ok = Dialog_Message('Add a file to the NCDF_DATA object before proceeding.') RETURN ENDIF ; Be sure everything is cleaned up and ready to go. self -> CleanParsedStructures Ptr_Free, self.theAttributes Ptr_Free, self.theDimensions Ptr_Free, self.theCalibration Ptr_Free, self.theSwaths Ptr_Free, self.theVariables Ptr_Free, self.zeroDimensionID ; Open the file and find out how many dimensions, global attributes, and variables are there. fileID = HDF_SD_START(Filepath(ROOT_DIR=self.directory, self.filename), /READ) HDF_SD_Fileinfo, fileID, num_vars, num_attr ; First, get the global attributes. IF num_attr GT 0 THEN BEGIN theAttributes = Replicate({NCDF_DATA_ATTRIBUTE}, num_attr) FOR j=0,num_attr-1 DO BEGIN HDF_SD_ATTRINFO, fileID, j, DATA=theAttribute, HDF_TYPE=hdf_type, NAME=attribute_name, TYPE=att_type theAttributes[j].attrType = 'GLOBAL' theAttributes[j].dataType = att_type theAttributes[j].length = N_Elements(theAttribute) theAttributes[j].name = attribute_name IF N_Elements(theAttribute) EQ 1 THEN theAttribute = theAttribute[0] theAttributes[j].value = Ptr_New(theAttribute) ENDFOR self.theAttributes = Ptr_New(theAttributes, /No_Copy) ENDIF ; Next, get the variables. IF num_vars GT 0 THEN BEGIN theVariables = REPLICATE({NCDF_DATA_VARIABLE}, num_vars) FOR j=0,num_vars-1 DO BEGIN ; Get information about the variable. sdID = HDF_SD_Select(fileID, j) ; This routine throws all kinds of scary messages if CALDATA, for example, is ; not in the file. Turn this off for this call. !QUIET = 1 HDF_SD_GetInfo, sdID, DIMS=dims, NAME=name, NATTS=natts, NDIMS=ndims, $ RANGE=range, TYPE=datatype, CALDATA=calData !QUIET = 0 theVariables[j].datatype = datatype theVariables[j].name = name theVariables[j].calibration = Ptr_New(calData) ; If this variable has attributes, get those, too. IF natts GT 0 THEN BEGIN varAttributes = Replicate({NCDF_DATA_ATTRIBUTE}, natts+1) FOR k=0,natts-1 DO BEGIN HDF_SD_ATTRINFO, sdID, k, DATA=theAttribute, NAME=attribute_name, TYPE=attribute_datatype varAttributes[k].attrType = StrUpCase(name) varAttributes[k].dataType = attribute_datatype varAttributes[k].length = N_Elements(theAttribute) varAttributes[k].name = attribute_name IF N_Elements(theAttribute) EQ 1 THEN theAttribute = theAttribute[0] varAttributes[k].value = Ptr_New(theAttribute) ENDFOR ; Add the calibration data as an attibute. IF calData.cal EQ 0 THEN BEGIN varAttributes[natts].attrType = StrUpCase(name) varAttributes[natts].dataType = 'STRING' varAttributes[natts].length = 0 varAttributes[natts].name = '_calibration_data' varAttributes[natts].value = Ptr_New('Not Present in File') ENDIF ELSE BEGIN varAttributes[natts].attrType = StrUpCase(name) varAttributes[natts].dataType = 'STRUCT' varAttributes[natts].length = N_Tags(calData, /Length) varAttributes[natts].name = '_calibration_data' varAttributes[natts].value = Ptr_New(calData) ENDELSE theVariables[j].var_attributes = Ptr_New(varAttributes) ENDIF ; Now, read the data so you can collect information about it. theVariables[j].dataSize = Ptr_New(dims) IF N_Elements(range) NE 0 THEN BEGIN theVariables[j].minValue = range[0] theVariables[j].maxValue = range[1] ENDIF ELSE BEGIN IF self.no_read_on_parse THEN BEGIN theVariables[j].minValue = !VALUES.F_NAN theVariables[j].maxValue = !VALUES.F_NAN ENDIF ELSE BEGIN HDF_SD_GetData, sdID, data IF calData.cal NE 0 THEN BEGIN data = calData.cal * (Temporary(data) - calData.offset) ENDIF minData = Min(data, MAX=maxData) theVariables[j].minValue = minData theVariables[j].maxValue = maxData Undefine, data ENDELSE ENDELSE HDF_SD_EndAccess, sdID ENDFOR self.theVariables = Ptr_New(theVariables, /No_Copy) ENDIF ; Successfully parsed file. self.hasBeenParsed = 1 ; Close the file HDF_SD_End, fileID END ;--------------------------------------------------------------------------------------------- PRO NCDF_DATA::Parse_HDF_EOS_File ; This internal method parses the new HDF-EOS file initially, and creates ; the IDL structures necessary for browsing the object. ; Error handling CATCH, theError IF theError NE 0 THEN BEGIN CATCH, /CANCEL void = cgErrorMsg() self.hasBeenParsed = 0 IF N_Elements(fileID) NE 0 THEN BEGIN IF N_Elements(swathID) NE 0 THEN ok = EOS_SW_DETACH(swathID) ok = EOS_SW_CLOSE(fileID) ENDIF RETURN ENDIF ; Check to see if file is available. IF self.filename EQ "" THEN BEGIN ok = Dialog_Message('Add a file to the NCDF_DATA object before proceeding.') RETURN ENDIF ; Be sure everything is cleaned up and ready to go. self -> CleanParsedStructures Ptr_Free, self.theAttributes Ptr_Free, self.theDimensions Ptr_Free, self.theSwaths Ptr_Free, self.theCalibration Ptr_Free, self.theVariables Ptr_Free, self.zeroDimensionID ; Open the file and find out how many swaths, grids, and points there are. filename = Filepath(ROOT_DIR=self.directory, self.filename) ok = EOS_Query(filename, info) ; Process the swaths first. IF info.num_swaths GT 0 THEN BEGIN theSwaths = Replicate({NCDF_DATA_SWATH}, info.num_swaths) FOR j=0, info.num_swaths-1 DO BEGIN swathNames = StrSplit(info.swath_names, ',', /EXTRACT) fileID = EOS_SW_OPEN(filename, /READ) swathID = EOS_SW_Attach(fileID, swathNames[j]) Print, 'Swath Name: ', swathNames[j] theSwaths[j].name = swathNames[j] nattr = EOS_SW_INQATTRS(swathID, attrlist) IF nattr GT 0 THEN BEGIN theAttributes = Replicate({NCDF_DATA_ATTRIBUTE}, nattr) attrNames = StrSplit(attrlist, ',', /EXTRACT) FOR k=0,nattr-1 DO BEGIN ok = EOS_SW_READATTR(swathID, attrNames[k], attrValue) theAttributes[k].name = attrNames[k] theAttributes[k].attrType = 'SWATH ATTRIBUTE' theAttributes[k].datatype = Size(attrValue, /TNAME) theAttributes[k].length = N_Elements(attrValue) IF N_Elements(attrValue) EQ 1 THEN attrValue = attrValue[0] theAttributes[k].value = Ptr_New(attrValue) Help, attrValue ENDFOR theSwaths[j].attributes = Ptr_New(theAttributes) theSwaths[j].nattrs = nattr ENDIF ndims = EOS_SW_INQDIMS(swathID, dimslist, dimSize) IF ndims GT 0 THEN BEGIN dimNames = StrSplit(dimslist, ',', /EXTRACT) theDimensions = Replicate({NCDF_DATA_DIMENSION}, ndims) FOR k=0,ndims-1 DO BEGIN theDimensions[k].name = dimNames[k] theDimensions[k].value = dimSize[k] ENDFOR theSwaths[j].dimensions = Ptr_New(theDimensions) theSwaths[j].ndims = ndims ENDIF ngeofields = EOS_SW_INQGEOFIELDS(swathID, geofieldslist, rank, numbertype) IF ngeofields GT 0 THEN BEGIN geoFieldNames = StrSplit(geofieldslist, ',', /EXTRACT) theGeoFields = Replicate({NCDF_DATA_VARIABLE}, ngeofields) FOR k=0,ngeofields-1 DO BEGIN ; Get information about the variable. sdFileID = HDF_SD_START(filename) sdIndex = HDF_SD_NameToIndex(sdFileID, geoFieldNames[k]) sdID = HDF_SD_Select(sdFileID, sdIndex) ; This routine throws all kinds of scary messages if CALDATA, for example, is ; not in the file. Turn this off for this call. !QUIET = 1 HDF_SD_GetInfo, sdID, DIMS=dims, NAME=name, NATTS=natts, NDIMS=ndims, $ RANGE=range, TYPE=datatype, CALDATA=calData !QUIET = 0 theGeoFields[k].name = geoFieldNames[k] theGeoFields[k].datatype = datatype theGeoFields[k].calibration = Ptr_New(calData) theGeoFields[k].datasize = Ptr_New(dims) IF N_Elements(range) NE 0 THEN BEGIN theGeoFields[k].minValue = range[0] theGeoFields[k].maxValue = range[1] Undefine, range ; Do this so it is not hanging around for the next variable. ENDIF ELSE BEGIN IF self.no_read_on_parse THEN BEGIN theGeoFields[k].minValue = !VALUES.F_NAN theGeoFields[k].maxValue = !VALUES.F_NAN ENDIF ELSE BEGIN HDF_SD_GetData, sdID, data IF calData.cal NE 0 THEN BEGIN data = calData.cal * (Temporary(data) - calData.offset) ENDIF minData = Min(data, MAX=maxData) theGeoFields[k].minValue = minData theGeoFields[k].maxValue = maxData Undefine, data ENDELSE ENDELSE ; If this variable has attributes, get those, too. ; If this variable has attributes, get those, too. IF natts GT 0 THEN BEGIN varAttributes = Replicate({NCDF_DATA_ATTRIBUTE}, natts+1) FOR m=0,natts-1 DO BEGIN HDF_SD_ATTRINFO, sdID, m, DATA=theAttribute, NAME=attribute_name, TYPE=attribute_datatype varAttributes[m].attrType = StrUpCase(name) varAttributes[m].dataType = attribute_datatype varAttributes[m].length = N_Elements(theAttribute) varAttributes[m].name = attribute_name IF N_Elements(theAttribute) EQ 1 THEN theAttribute = theAttribute[0] varAttributes[m].value = Ptr_New(theAttribute) ENDFOR ; Add the calibration data as an attibute. IF calData.cal EQ 0 THEN BEGIN varAttributes[natts].attrType = StrUpCase(name) varAttributes[natts].dataType = 'STRING' varAttributes[natts].length = 0 varAttributes[natts].name = '_calibration_data' varAttributes[natts].value = Ptr_New('Not Present in File') ENDIF ELSE BEGIN varAttributes[natts].attrType = StrUpCase(name) varAttributes[natts].dataType = 'STRUCT' varAttributes[natts].length = N_Tags(calData, /Length) varAttributes[natts].name = '_calibration_data' varAttributes[natts].value = Ptr_New(calData) ENDELSE theGeoFields[k].var_attributes = Ptr_New(varAttributes) ENDIF theSwaths[j].ngeoFields = ngeoFields HDF_SD_EndAccess, sdID ENDFOR ENDIF ndatafields = EOS_SW_INQDATAFIELDS(swathID, datafieldslist, rank, numbertype) IF ndatafields GT 0 THEN BEGIN dataFieldNames = StrSplit(datafieldslist, ',', /EXTRACT) theDataFields = Replicate({NCDF_DATA_VARIABLE}, ndatafields) FOR k=0,ndatafields-1 DO BEGIN ; Get information about the variable. sdFileID = HDF_SD_START(filename) sdIndex = HDF_SD_NameToIndex(sdFileID, dataFieldNames[k]) sdID = HDF_SD_Select(sdFileID, sdIndex) ; This routine throws all kinds of scary messages if CALDATA, for example, is ; not in the file. Turn this off for this call. !QUIET = 1 HDF_SD_GetInfo, sdID, DIMS=dims, NAME=name, NATTS=natts, NDIMS=ndims, $ RANGE=range, TYPE=datatype, CALDATA=calData !QUIET = 0 Print, 'number of swath dataset attributes for variable ' + name + ': ', natts theDataFields[k].name = dataFieldNames[k] theDataFields[k].datatype = datatype theDataFields[k].calibration = Ptr_New(calData) theDataFields[k].datasize = Ptr_New(dims) IF N_Elements(range) NE 0 THEN BEGIN theDataFields[k].minValue = range[0] theDataFields[k].maxValue = range[1] Undefine, range ; Do this so it is not hanging around for the next variable. ENDIF ELSE BEGIN IF self.no_read_on_parse THEN BEGIN theDataFields[k].minValue = !VALUES.F_NAN theDataFields[k].maxValue = !VALUES.F_NAN ENDIF ELSE BEGIN HDF_SD_GetData, sdID, data IF calData.cal NE 0 THEN BEGIN data = calData.cal * (Temporary(data) - calData.offset) ENDIF minData = Min(data, MAX=maxData) theGeoFields[k].minValue = minData theGeoFields[k].maxValue = maxData Undefine, data ENDELSE ENDELSE ; If this variable has attributes, get those, too. ; If this variable has attributes, get those, too. IF natts GT 0 THEN BEGIN varAttributes = Replicate({NCDF_DATA_ATTRIBUTE}, natts+1) FOR m=0,natts-1 DO BEGIN HDF_SD_ATTRINFO, sdID, m, DATA=theAttribute, NAME=attribute_name, TYPE=attribute_datatype varAttributes[m].attrType = StrUpCase(name) varAttributes[m].dataType = attribute_datatype varAttributes[m].length = N_Elements(theAttribute) varAttributes[m].name = attribute_name IF N_Elements(theAttribute) EQ 1 THEN theAttribute = theAttribute[0] varAttributes[m].value = Ptr_New(theAttribute) ENDFOR ; Add the calibration data as an attibute. IF calData.cal EQ 0 THEN BEGIN varAttributes[natts].attrType = StrUpCase(name) varAttributes[natts].dataType = 'STRING' varAttributes[natts].length = 0 varAttributes[natts].name = '_calibration_data' varAttributes[natts].value = Ptr_New('Not Present in File') ENDIF ELSE BEGIN varAttributes[natts].attrType = StrUpCase(name) varAttributes[natts].dataType = 'STRUCT' varAttributes[natts].length = N_Tags(calData, /Length) varAttributes[natts].name = '_calibration_data' varAttributes[natts].value = Ptr_New(calData) ENDELSE theDataFields[k].var_attributes = Ptr_New(varAttributes) ENDIF theSwaths[j].ndataFields = ndataFields HDF_SD_EndAccess, sdID ENDFOR ENDIF nmaps = EOS_SW_INQMAPS(swathID, mapslist, offset, increment) theSwaths[j].nmaps = nmaps IF nmaps GT 0 THEN BEGIN mapNames = StrSplit(mapslist, ',', /EXTRACT) FOR k=0,nmaps-1 DO BEGIN Print, 'Map Name: ', mapNames[k], $ ' Offset: ', offset[k], ' Increment: ', increment[k] ENDFOR Print, '' ENDIF nidxmaps = EOS_SW_INQIDXMAPS(swathID, mapslist, sizes) theSwaths[j].nidxmaps = nidxmaps IF nidxmaps GT 0 THEN BEGIN mapNames = StrSplit(mapslist, ',', /EXTRACT) FOR k=0,nidxmaps-1 DO BEGIN Print, 'Map Name: ', mapNames[k], ' Size: ', sizes[k] ENDFOR Print, '' ENDIF ok = EOS_SW_DETACH(swathID) ok = EOS_SW_CLOSE(fileID) ENDFOR self.theSwaths = Ptr_New(theSwaths) ENDIF fileID = HDF_SD_START(Filepath(ROOT_DIR=self.directory, self.filename), /READ) HDF_SD_Fileinfo, fileID, num_vars, num_attr ; First, get the global attributes. ; IF num_attr GT 0 THEN BEGIN ; theAttributes = Replicate({NCDF_DATA_ATTRIBUTE}, num_attr) ; FOR j=0,num_attr-1 DO BEGIN ; HDF_SD_ATTRINFO, fileID, j, DATA=theAttribute, HDF_TYPE=hdf_type, NAME=attribute_name, TYPE=att_type ; ; theAttributes[j].attrType = 'GLOBAL' ; theAttributes[j].dataType = att_type ; theAttributes[j].length = N_Elements(theAttribute) ; theAttributes[j].name = attribute_name ; IF N_Elements(theAttribute) EQ 1 THEN theAttribute = theAttribute[0] ; theAttributes[j].value = Ptr_New(theAttribute) ; ; ENDFOR ; self.theAttributes = Ptr_New(theAttributes, /No_Copy) ; ENDIF ; Successfully parsed file. self.hasBeenParsed = 1 ; Close the file HDF_SD_End, fileID END ;--------------------------------------------------------------------------------------------- FUNCTION NCDF_DATA::ReadAttribute, theAttribute, SUCCESS=success ; ; NAME: ; NCDF_DATA::ReadAttribute ; ; PURPOSE: ; ; This method is used to read and return a global attribute from a netCDF or HDF file. ; ; CALLING SEQUENCE: ; ; IDL> value = nCDFObject -> ReadAttribute(theAttribute) ; ; RETURN VALUE: ; ; value: A variable containing the attribute. ; ; ARGUMENTS: ; ; theAttribute: The name of the attribute you wish to read from the file. ; ; KEYWORD PARAMETERS: ; ; SUCCESS: An output parameter, set to 1 if the file was read successfully, ; and to 0 otherwise. ; ; Error handling CATCH, theError IF theError NE 0 THEN BEGIN CATCH, /CANCEL void = cgErrorMsg() success = 0 RETURN, -1 ENDIF ; Make sure the file has been parsed. IF self.hasBeenParsed EQ 0 THEN self -> ParseFile ; Check again. success = 0 IF self.hasBeenParsed EQ 0 THEN RETURN, -1 ; Check for the name of the attribute. IF N_Elements(theAttribute) EQ 0 THEN Message, 'Must pass name of the attribute to read.' IF Ptr_Valid(self.theAttributes) EQ 0 THEN Message, 'No global attributes currently available for file.' index = Where(StrUpCase((*self.theAttributes).name) EQ StrUpCase(theAttribute), count) IF count GT 0 THEN BEGIN value = *(*self.theAttributes)[index].value success = 1 ENDIF ELSE Message, 'Cannot locate global attribute ' + theAttribute + ' in file.' RETURN, value END ;--------------------------------------------------------------------------------------------- PRO NCDF_DATA::ReadAttributeFromGUI, event ; This internal method sets up a dialog for obtaining information from the user ; about which variables to read, etc. ; Error handling CATCH, theError IF theError NE 0 THEN BEGIN CATCH, /CANCEL void = cgErrorMsg() RETURN ENDIF ; Get some position information. Widget_Control, event.top, TLB_GET_OFFSET=offsets ; We want a modal pop-up dialog widget. tlb = Widget_Base(GROUP_LEADER=event.top, XOFFSET=offsets[0]+50, YOFFSET=offsets[1]+50, $ COLUMN=1, BASE_ALIGN_CENTER=1, /FLOATING, UVALUE=self, /MODAL) row = Widget_Base(tlb, ROW=2, /GRID_LAYOUT, FRAME=1) label = Widget_Label(row, Value='Attribute to Read: ') theList = ['All', (*self.theAttributes).name] self.attributeID = Widget_Droplist(row, Value=theList, UNAME='ATTRIBUTES', $ UVALUE=['all_attributes', (*self.theAttributes).name], SCR_XSIZE=250) label = Widget_Label(row, Value='Attribute Name: ') self.attrnameID = Widget_Text(row, Value='all_attributes', /Editable, SCR_XSIZE=250) b = Widget_Base(tlb, ROW=1, XPAD=0, YPAD=0, /NONEXCLUSIVE) okToAppend = 1 IF StrPos(cgRootName(self.filename), '.') NE -1 THEN okToAppend = 0 IF StrPos(self.filename, '-') NE -1 THEN okToAppend = 0 IF StrPos(self.filename, ' ') NE -1 THEN okToAppend = 0 IF okToAppend THEN self.appendNameID = Widget_Button(b, Value='Append Filename to Attribute Name', UVALUE='APPEND_FILENAME') buttonrow = Widget_Base(tlb, ROW=1) button = Widget_Button(buttonrow, Value='Read Attribute and Leave', UVALUE='READ_ATTRIBUTE_AND_LEAVE') button = Widget_Button(buttonrow, Value='Read Attribute and Stay', UVALUE='READ_ATTRIBUTE_AND_STAY') button = Widget_Button(buttonrow, Value='Quit', UVALUE='QUIT_READ_ATTRIBUTE_GUI') ; If there is a tree selection, see if this corresponds to a variable in the list. ; If so, set this variable in the droplist widget. theSelection = Widget_Info(self.theTree, /TREE_SELECT) Widget_Control, theSelection, Get_Value=attrName index = Where(theList EQ attrName, count) IF count GT 0 THEN BEGIN Widget_Control, self.attributeID, SET_DROPLIST_SELECT=index Widget_Control, self.attrnameID, Set_Value=IDL_ValidName(theList[index], /CONVERT_ALL) ENDIF ; Get it going... Widget_Control, tlb, /REALIZE XMANAGER, 'read_attribute_and_leave', tlb, EVENT_HANDLER='NCDF_DATA_WIDGET_EVENTS', /NO_BLOCK END ;--------------------------------------------------------------------------------------------- PRO NCDF_DATA::ReadAttributeFromGUI_Events, event ; This internal method processes events from the user dialogs. ; Error handling CATCH, theError IF theError NE 0 THEN BEGIN CATCH, /CANCEL void = cgErrorMsg() RETURN ENDIF thisEvent = Tag_Names(event, /STRUCTURE_NAME) CASE thisEvent OF 'WIDGET_BUTTON': BEGIN Widget_Control, event.id, Get_UValue=buttonValue CASE buttonValue OF 'READ_ATTRIBUTE_AND_STAY': BEGIN ; Get the variable name. Do we need to append the filename to it? IF Widget_Info(self.appendNameID, /Valid_ID) THEN $ addName = Widget_Info(self.appendNameID, /BUTTON_SET) ELSE addName = 0 Widget_Control, self.attrNameID, Get_Value=attrName thisAttrName = IDL_ValidName(attrName, /CONVERT_ALL) IF thisAttrName NE attrName THEN BEGIN Widget_Control, self.attrNameID, Set_Value=thisAttrName attrName = thisAttrName ENDIF attrName = (addName) ? cgRootName(self.filename) + '_' + attrName[0] : attrName[0] IF attrName EQ "" THEN Message, 'Must have a non-null attribute name to create an attribute.' ; Which attribute do you want to read? Widget_Control, self.attributeID, Get_Value=theList index = Widget_Info(self.attributeID, /DROPLIST_SELECT) theAttribute = theList[index] IF StrUpCase(theAttribute) EQ 'ALL' THEN BEGIN theData = self -> ReadGlobalAttr(Success=success) IF success EQ 0 THEN RETURN ENDIF ELSE BEGIN theData = self -> ReadAttribute(theAttribute, Success=success) IF success EQ 0 THEN RETURN ENDELSE ; Create the variable at the main IDL level. (Scope_VarFetch(attrName, LEVEL=1, /ENTER)) = theData Print, 'An attribute named "' + attrName + '" has been created at the main IDL level.' ; Go to the next attribute on the list IF index EQ (N_Elements(theList)-1) THEN index = 0 ELSE index = index + 1 Widget_Control, self.attributeID, SET_DROPLIST_SELECT=index Widget_Control, self.attrNameID, Set_Value=theList[index] END 'READ_ATTRIBUTE_AND_LEAVE': BEGIN ; Get the attribute name. Do we need to append the filename to it? IF Widget_Info(self.appendNameID, /Valid_ID) THEN $ addName = Widget_Info(self.appendNameID, /BUTTON_SET) ELSE addName = 0 Widget_Control, self.attrNameID, Get_Value=attrName attrName = (addName) ? cgRootName(self.filename) + '_' + attrName[0] : attrName[0] thisAttrName = IDL_ValidName(attrName, /CONVERT_ALL) IF thisAttrName NE attrName THEN BEGIN Widget_Control, self.attrNameID, Set_Value=thisAttrName attrName = thisAttrName ENDIF IF attrName EQ "" THEN Message, 'Must have a non-null attribute name to create an attribute.' ; Which attribute do you want to read? Widget_Control, self.attributeID, Get_Value=theList index = Widget_Info(self.attributeID, /DROPLIST_SELECT) theAttribute = theList[index] IF StrUpCase(theAttribute) EQ 'ALL' THEN BEGIN theData = self -> ReadGlobalAttr(Success=success) IF success EQ 0 THEN RETURN ENDIF ELSE BEGIN theData = self -> ReadAttribute(theAttribute, Success=success) IF success EQ 0 THEN RETURN ENDELSE ; Create the attribute at the main IDL level. (Scope_VarFetch(attrName, LEVEL=1, /ENTER)) = theData Print, 'An attribute named "' + attrName + '" has been created at the main IDL level.' Widget_Control, event.top, /DESTROY END 'QUIT_READ_ATTRIBUTE_GUI': Widget_Control, event.top, /DESTROY ENDCASE END 'WIDGET_DROPLIST': BEGIN ; The name of the variable to write has to be changed when the droplist value changes. Widget_Control, event.id, Get_UVALUE=list Widget_Control, self.attrNameID, Set_Value=IDL_ValidName(list[event.index], /CONVERT_ALL) END 'WIDGET_TEXT': ; Nothing to do here. We just want to read the value. Don't what it is. ENDCASE END ;--------------------------------------------------------------------------------------------- FUNCTION NCDF_DATA::ReadDimension, dimensionName, SUCCESS=success ; ; NAME: ; NCDF_DATA::ReadDimension ; ; PURPOSE: ; ; This method is used to read and return a dimension of a netCDF file. ; ; CALLING SEQUENCE: ; ; IDL> dimension = nCDFObject -> ReadDimension(dimensionName) ; ; RETURN VALUE: ; ; dimension: The value of the dimension. ; ; ARGUMENTS: ; ; dimensionName: The name of the dimension to read. ; ; KEYWORD PARAMETERS: ; ; SUCCESS: An output parameter, set to 1 if the file was read successfully, ; and to 0 otherwise. ; ; Error handling CATCH, theError IF theError NE 0 THEN BEGIN CATCH, /CANCEL void = cgErrorMsg() success = 0 RETURN, -1 ENDIF ; Check for the name of the file. theFile = Filepath(ROOT_DIR=self.directory, self.filename) ; Open the file. fileID = NCDF_Open(theFile) info = NCDF_Inquire(fileID) ; Add the dimensions. IF info.ndims GT 0 THEN BEGIN dimsStruct = Create_Struct('ndims', info.ndims) FOR j=0,info.ndims-1 DO BEGIN NCDF_DIMINQ, fileID, j, name, value name = IDL_ValidName(name, /CONVERT_ALL) dimsStruct = Create_Struct(dimsStruct, name, value) ENDFOR ENDIF ; Can you find a field in the structure with the dimension name? fields = Tag_Names(dimsStruct) i = Where(fields EQ StrUpCase(dimensionName), count) IF count EQ 0 THEN Message, 'Cannot find a dimension with name: ' + dimensionName + ' in file.' value = dimsStruct.(i) ; Close the file, set status flag, return the data. NCDF_CLOSE, fileID success = 1 RETURN, value END ;--------------------------------------------------------------------------------------------- FUNCTION NCDF_DATA::ReadFile, theFile, SUCCESS=success ; ; NAME: ; NCDF_DATA::ReadFile ; ; PURPOSE: ; ; This method is used to read and return the contents of a netCDF or HDF file. ; ; CALLING SEQUENCE: ; ; IDL> data = nCDFObject -> ReadFile(theFile) ; ; RETURN VALUE: ; ; data: A structure variable containing the filename, a structure of global attributes, ; a structure of dimensions, and one struture for each variable in the file. ; ; ARGUMENTS: ; ; theFile: The optional name of a netCDF or HDF file to read. If not supplied, the ; name of the file currently stored in the object will be read. ; ; KEYWORD PARAMETERS: ; ; SUCCESS: An output parameter, set to 1 if the file was read successfully, ; and to 0 otherwise. ; ; Error handling CATCH, theError IF theError NE 0 THEN BEGIN CATCH, /CANCEL void = cgErrorMsg() success = 0 RETURN, -1 ENDIF ; Check for the name of the file. IF N_Elements(theFile) EQ 0 THEN theFile = Filepath(ROOT_DIR=self.directory, self.filename) IF File_Test(theFile, /READ) EQ 0 THEN Message, 'Specified file does not exist or is not readable.' ; Branch appropriately. IF self.isHDF THEN BEGIN ; Open the file. fileID = HDF_SD_Start(theFile) ; Create the initial structure. struct = Create_Struct('_filename', self.filename) ; Add the global attributes. g_attributes = self -> ReadGlobalAttr(Success=success) IF success THEN struct = Create_Struct(struct, '_global_attr', Temporary(g_attributes)) HDF_SD_Fileinfo, fileID, nvars, nattrs FOR j=0,nvars-1 DO BEGIN ; Select the variable and read it. varID = HDF_SD_Select(fileID, j) HDF_SD_GetData, varID, data HDF_SD_GetInfo, varID, NAME=varName, NATTS=natts data = Reform(Temporary(data)) varStruct = Create_Struct('data', Temporary(data)) ; If this variable has attributes, get those, too. IF natts GT 0 THEN BEGIN varAttributes = Replicate({NCDF_DATA_ATTRIBUTE}, natts) FOR k=0,natts-1 DO BEGIN HDF_SD_ATTRINFO, varID, k, DATA=value, NAME=attrName attrName = IDL_ValidName(attrName, /CONVERT_ALL) IF Where(Tag_Names(varStruct) EQ StrUpCase(attrName)) NE -1 THEN CONTINUE varStruct = Create_Struct(varStruct, attrName, value) ENDFOR struct = Create_Struct(struct, varName, Temporary(varStruct)) ENDIF ELSE BEGIN struct = Create_Struct(struct, varName, Temporary(varStruct)) ENDELSE ENDFOR HDF_SD_EndAccess, varID HDF_SD_END, fileID success = 1 ENDIF ELSE BEGIN ; Open the file. fileID = NCDF_Open(theFile) info = NCDF_Inquire(fileID) ; Create the initial structure. struct = Create_Struct('_filename', self.filename) ; Add the global attributes. g_attributes = self -> ReadGlobalAttr(Success=success) IF success THEN struct = Create_Struct(struct, '_global_attr', Temporary(g_attributes)) ; Add the dimensions. IF info.ndims GT 0 THEN BEGIN dimsStruct = Create_Struct('_ndims', info.ndims) FOR j=0,info.ndims-1 DO BEGIN NCDF_DIMINQ, fileID, j, name, value name = IDL_ValidName(name, /CONVERT_ALL) dimsStruct = Create_Struct(dimsStruct, name, value) ENDFOR struct = Create_Struct(struct, '_dimensions', dimsStruct) ENDIF ; Add the variables. IF info.nvars GT 0 THEN BEGIN FOR j=0,info.nvars-1 DO BEGIN varInfo = NCDF_VarInq(fileID, j) NCDF_VarGet, fileID, j, data IF Size(data, /N_DIMENSIONS) GT 0 THEN data = REFORM(Temporary(data)) varStruct = Create_Struct('data', Temporary(data)) ; Add the variable attributes to the structure. FOR k=0,varInfo.natts-1 DO BEGIN attrName = NCDF_AttName(fileID, j, k) NCDF_AttGet, fileID, j, attrName, theAttribute attinfo = NCDF_ATTINQ(fileID, j, attrName) att_type = StrUpCase(attinfo.dataType) ; Strings are stored as byte values in attributes, so convert them back. IF (Size(theAttribute, /TNAME) EQ 'BYTE') AND (att_type EQ 'CHAR') $ THEN theAttribute = String(theAttribute) attrName = IDL_ValidName(attrName, /CONVERT_ALL) varStruct = Create_Struct(varStruct, attrName, theAttribute) ENDFOR struct = Create_Struct(struct, IDL_ValidName(varInfo.name, /CONVERT_ALL), $ Temporary(varStruct)) ENDFOR ENDIF ; Close the file, set status flag, return the data. NCDF_CLOSE, fileID success = 1 ENDELSE RETURN, struct END ;--------------------------------------------------------------------------------------------- PRO NCDF_DATA::ReadFileFromGUI, event ; This internal method obtains the name of the variable from the user ; and creates a variable of that name at the main IDL level. ; Error handling CATCH, theError IF theError NE 0 THEN BEGIN CATCH, /CANCEL void = cgErrorMsg() RETURN ENDIF ; What would you like to name the variable. varName = TextBox(Title='Name of IDL Variable...', Label='Name of IDL Variable:', $ Value='data', Cancel=cancelled) IF cancelled THEN RETURN varName = IDL_ValidName(varName, /CONVERT_ALL) IF varName EQ "" THEN Message, 'Variable names cannot be NULL.' ; Read the NCDF data file. Widget_Control, /HOURGLASS data = self -> ReadFile(Success=success) IF success EQ 0 THEN RETURN ; Create a main-level variable. (Scope_VarFetch(varName, LEVEL=1, /ENTER)) = data Print, 'A variable named "' + varName + '" has been created at the main IDL level.' END ;--------------------------------------------------------------------------------------------- FUNCTION NCDF_DATA::ReadGlobalAttr, SUCCESS=success ; ; NAME: ; NCDF_DATA::ReadGlobalAttr ; ; PURPOSE: ; ; This method is used to read and return the global attributes of a netCDF or HDF file. ; ; CALLING SEQUENCE: ; ; IDL> struct = nCDFObject -> ReadGlobalAttr() ; ; RETURN VALUE: ; ; struct: A structure variable containing global attributes of the file. ; The attribute names are the fields of the structure. ; ; ARGUMENTS: ; ; None. The global attributes of the file loaded into the object will be read and returned. ; ; KEYWORD PARAMETERS: ; ; SUCCESS: An output parameter, set to 1 if the file was read successfully, ; and to 0 otherwise. ; ; Error handling CATCH, theError IF theError NE 0 THEN BEGIN CATCH, /CANCEL void = cgErrorMsg() success = 0 RETURN, -1 ENDIF ; Make sure the file has been parsed. IF self.hasBeenParsed EQ 0 THEN self -> ParseFile ; Check again. success = 0 IF self.hasBeenParsed EQ 0 THEN RETURN, -1 IF self.isHDF THEN BEGIN ; Open the file and find out how many dimensions, global attributes, and variables are there. fileID = HDF_SD_START(Filepath(ROOT_DIR=self.directory, self.filename), /READ) HDF_SD_Fileinfo, fileID, num_vars, num_attr ; Create a structure to hold the global attribute values. attrStruct = Create_Struct('hdf_filename', self.filename) FOR j=0,num_attr-1 DO BEGIN HDF_SD_ATTRINFO, fileID, j, DATA=value, NAME=name ; Names cannot have oddball characters in them. They have to ; conform to IDL's rules for creating variable names. name = IDL_ValidName(name, /CONVERT_ALL) IF Where(Tag_Names(attrStruct) EQ StrUpCase(name)) NE -1 THEN CONTINUE attrStruct = Create_Struct(attrStruct, name, value) ENDFOR ; Close the file, set status flag, return the data. HDF_SD_End, fileID success = 1 RETURN, attrStruct ENDIF ELSE BEGIN ; Open the file. fileID = NCDF_Open(Filepath(ROOT_DIR=self.directory, self.filename)) info = NCDF_Inquire(fileID) ; Create a structure to hold the global attribute values. attrStruct = Create_Struct('ncdf_filename', self.filename) FOR j=0,info.ngatts-1 DO BEGIN name = NCDF_AttName(fileID, j, /GLOBAL) NCDF_AttGet, fileID, name, value, /GLOBAL attinfo = NCDF_ATTINQ(fileID, name, /GLOBAL) att_type = StrUpCase(attinfo.dataType) IF Size(value, /TNAME) EQ 'BYTE' AND att_type EQ 'CHAR' THEN value = String(value) ; Names cannot have oddball characters in them. They have to ; conform to IDL's rules for creating variable names. name = IDL_ValidName(name, /CONVERT_ALL) attrStruct = Create_Struct(attrStruct, name, value) ENDFOR ; Close the file, set status flag, return the data. NCDF_CLOSE, fileID success = 1 RETURN, attrStruct ENDELSE END ;--------------------------------------------------------------------------------------------- FUNCTION NCDF_DATA::ReadVariable, theVariable, $ SUCCESS=success, $ COUNT=count, $ FILLVALUE=fillvalue, $ MISSINGINDICES=missingIndices, $ OFFSET=offset, $ START=start, $ STRIDE=stride ; ; NAME: ; NCDF_DATA::ReadVariable ; ; PURPOSE: ; ; This method is used to read and return a variable from a netCDF or HDF file. ; ; CALLING SEQUENCE: ; ; IDL> data = nCDFObject -> ReadVariable(theVariable) ; ; RETURN VALUE: ; ; data: The nCDF variable. ; ; ARGUMENTS: ; ; theVariable: The name of the variable you wish to read from the file. ; ; INPUT KEYWORD PARAMETERS: ; ; COUNT: An optional vector containing the counts to be used in reading theVariable. ; Count is a 1-based vector with an element for each dimension. The default ; matches the size of the variable so that all data is written out. ; ; OFFSET: An optional vector containing the starting position for the read. The default ; start position is [0, 0, ...]. ; ; START: Equivalent to the OFFSET vector, except for HDF files. ; ; STRIDE: An optional vector containing the strides, or sampling intervals, between ; accessed values of the netCDF variable. The default stride vector is that ; for a contiguous read, [1, 1, ...]. Note that for HDF files, the default ; STRIDE vector is [0, 0, ...]. ; ; OUTPUT KEYWORD PARAMETERS: ; ; FILLVALUE: The value that is being used for the "missing" value in this variable. ; ; MISSINGINDICES: A vector containing the missing indices in the returned data. Missing ; data is identified by either the depreciated "missing_value" attribute ; or the approved "_FillValue" attribute. ; ; SUCCESS: An output parameter, set to 1 if the file was read successfully, ; and to 0 otherwise. ; ; ; Error handling CATCH, theError IF theError NE 0 THEN BEGIN CATCH, /CANCEL void = cgErrorMsg() IF self.isHDF THEN HDF_SD_End, fileID ELSE NCDF_CLOSE, fileID success = 0 RETURN, -1 ENDIF ; Check for the name of the variable. IF N_Elements(theVariable) EQ 0 THEN Message, 'Must pass name of variable to read.' ; Assume no success. success = 0 ; Read the variable, based on what kind of file this is. IF self.isHDF THEN BEGIN ; Open the file. fileID = HDF_SD_Start(Filepath(ROOT_DIR=self.directory, self.filename)) ; Get the index of the variable. index = HDF_SD_NameToIndex(fileID, theVariable) IF index EQ -1 THEN Message, 'Variable (' + theVariable + ') not found.' ; Select the variable and read it. varID = HDF_SD_Select(fileID, index) ; Make sure this variable has a valid dimension. HDF_SD_GetInfo, varID, DIMS=dims IF dims[0] EQ 0 THEN BEGIN void = Dialog_Message('Requested data variable has a dimension of 0 and cannot be read.') RETURN, -1 ENDIF ; Read the data. HDF_SD_GetData, varID, data, COUNT=count, START=start, STRIDE=stride ; This routine throws all kinds of scary messages if CALDATA is ; not in the file. Turn this off for this call. !QUIET = 1 HDF_SD_GetInfo, varID, CALDATA=calData !QUIET = 0 HDF_SD_EndAccess, varID ; Reverse the indices in HDF files. data = Reform(Temporary(data)) IF calData.cal NE 0 THEN data = calData.cal * (Temporary(data) - calData.offset) ; Close the file HDF_SD_End, fileID success = 1 RETURN, data ENDIF ELSE BEGIN ; Open the file. fileID = NCDF_Open(Filepath(ROOT_DIR=self.directory, self.filename)) ; Get the variable ID. varID = NCDF_VarID(fileID, theVariable) ; Get information about the variable. r = NCDF_VarInq(fileID, varID) ; Do we have to worry about zero dimensions? IF Ptr_Valid(self.zeroDimensionID) THEN BEGIN ; Is there a match between the dimension IDs and any zero dimension ID we ; have stored? match = 0 FOR m = 0, N_Elements(*self.zeroDimensionID)-1 DO BEGIN i = Where(r.dim EQ (*self.zeroDimensionID)[m], count) IF count GT 0 THEN match = 1 ENDFOR IF match GT 0 THEN BEGIN ok = Dialog_Message('This variable has a dimension of length zero and cannot be read.') NCDF_CLOSE, fileID success = 0 RETURN, -1 ENDIF ENDIF ; Read the data. NCDF_VarGet, fileID, varID, data, COUNT=count, OFFSET=offset, STRIDE=stride ; Get the variable attribute names IF r.natts GT 0 THEN varAttNames = StrArr(r.natts) FOR k=0,r.natts-1 DO BEGIN varAttNames[k] = NCDF_AttName(fileID, varID, k) ENDFOR ; Does this variable contain "missing" values. If so, identify and return ; the missing data indices so they can be identified after scaling. IF N_Elements(varAttNames) NE 0 THEN BEGIN index = Where(StrUpCase(varAttNames) EQ 'MISSING_VALUE', count) IF count GT 0 THEN BEGIN varAttName = (varAttNames[index])[0] NCDF_AttGet, fileID, varID, varAttName, missingValue missingIndices = Where(data EQ missingValue, missingCount) ENDIF index = Where(StrUpCase(varAttNames) EQ '_FILLVALUE', count) IF count GT 0 THEN BEGIN varAttName = (varAttNames[index])[0] NCDF_AttGet, fileID, varID, varAttName, missingValue missingIndices = Where(data EQ missingValue, missingCount) ENDIF ; Is there a scale_factor attribute? If so, get and scale the data. IF N_Elements(varAttNames) NE 0 THEN BEGIN index = Where(StrUpCase(varAttNames) EQ 'SCALE_FACTOR', count) IF count GT 0 THEN BEGIN varAttName = (varAttNames[index])[0] NCDF_AttGet, fileID, varID, varAttName, scale_factor IF scale_factor NE 1.0 THEN data = Temporary(data) * scale_factor ENDIF ; Is there an add_offset attribute? If so, get and add to the data. index = Where(StrUpCase(varAttNames) EQ 'ADD_OFFSET', count) IF count GT 0 THEN BEGIN varAttName = (varAttNames[index])[0] NCDF_AttGet, fileID, varID, varAttName, add_offset data = Temporary(data) + add_offset ENDIF ENDIF ; If there was missing data, restore it. IF (N_Elements(missingIndices) NE 0) THEN BEGIN IF missingCount GT 0 THEN data[missingIndices] = missingValue ENDIF ENDIF ; Is this a CHAR data type? If so, convert it to a string. IF StrUpCase(r.datatype) EQ 'CHAR' THEN data = String(data) ; Close the file, set status flag, return the data. NCDF_CLOSE, fileID success = 1 RETURN, data ENDELSE END ;--------------------------------------------------------------------------------------------- PRO NCDF_DATA::ReadVariableFromGUI, event ; This internal method sets up a dialog for obtaining information from the user ; about which variables to read, etc. ; Error handling CATCH, theError IF theError NE 0 THEN BEGIN CATCH, /CANCEL void = cgErrorMsg() RETURN ENDIF ; Get some position information. Widget_Control, event.top, TLB_GET_OFFSET=offsets ; We want a modal pop-up dialog widget. tlb = Widget_Base(GROUP_LEADER=event.top, XOFFSET=offsets[0]+50, YOFFSET=offsets[1]+50, $ COLUMN=1, BASE_ALIGN_CENTER=1, /FLOATING, UVALUE=self, /MODAL) row = Widget_Base(tlb, ROW=2, /GRID_LAYOUT, FRAME=1) label = Widget_Label(row, Value='Variable to Read: ') theList = [(*self.theVariables).name] self.variablelistID = Widget_Droplist(row, Value=[(*self.theVariables).name], $ UVALUE=[(*self.theVariables).name], SCR_XSIZE=250, UNAME='VARIABLES') label = Widget_Label(row, Value='Variable Name: ') self.varnameID = Widget_Text(row, Value=IDL_ValidName((*self.theVariables)[0].name, /CONVERT_ALL), $ /Editable, SCR_XSIZE=250) b = Widget_Base(tlb, ROW=1, XPAD=0, YPAD=0, /NONEXCLUSIVE) okToAppend = 1 IF StrPos(cgRootName(self.filename), '.') NE -1 THEN okToAppend = 0 IF StrPos(self.filename, '-') NE -1 THEN okToAppend = 0 IF StrPos(self.filename, ' ') NE -1 THEN okToAppend = 0 IF okToAppend THEN self.appendNameID = Widget_Button(b, Value='Append Filename to Variable Name', UVALUE='APPEND_FILENAME') buttonrow = Widget_Base(tlb, ROW=1) button = Widget_Button(buttonrow, Value='Read Variable and Leave', UVALUE='READ_AND_LEAVE') button = Widget_Button(buttonrow, Value='Read Variable and Stay', UVALUE='READ_AND_STAY') button = Widget_Button(buttonrow, Value='Quit', UVALUE='QUIT_READ_VARIABLE_GUI') ; If there is a tree selection, see if this corresponds to a variable in the list. ; If so, set this variable in the droplist widget. theSelection = Widget_Info(self.theTree, /TREE_SELECT) Widget_Control, theSelection, Get_Value=varName index = Where(theList EQ varName, count) IF count GT 0 THEN BEGIN Widget_Control, self.variablelistID, SET_DROPLIST_SELECT=index Widget_Control, self.varnameID, Set_Value=IDL_ValidName(theList[index], /CONVERT_ALL) ENDIF ; Get it going... Widget_Control, tlb, /REALIZE XMANAGER, 'read_and_leave', tlb, EVENT_HANDLER='NCDF_DATA_WIDGET_EVENTS', /NO_BLOCK END ;--------------------------------------------------------------------------------------------- FUNCTION NCDF_DATA::ReadVarAttr, theVariableName, theAttributeName ; This method reads and returns a particular variable attribute. ; Both the name of the variable and the name of the attribute are ; required parameters. ; Error handling CATCH, theError IF theError NE 0 THEN BEGIN CATCH, /CANCEL void = cgErrorMsg() RETURN, "" ENDIF IF N_Params() NE 2 THEN Message, 'Both the variable name and the attribute name must be present.' ; The file has to be parsed to carry this out. IF self.hasBeenParsed EQ 0 THEN self -> ParseFile ; Get the variable list. theVarStructures = *self.theVariables ; Find this variable in the variable structures. index = Where(StrUpCase(theVarStructures.name) EQ StrUpCase(theVariableName), count) IF count EQ 0 THEN Message, 'Cannot find the variable ' + theVariableName + ' in the file.' thisVariableStruct = theVarStructures[index] ; Get the pointer to the variable attribute structures. varAttrStructures = *thisVariableStruct.var_attributes ; Find the name of the attribute in the varAttrStructures list index = Where(StrUpCase(varAttrStructures.name) EQ StrUpCase(theAttributeName), count) IF count EQ 0 THEN Message, 'Cannot find the attribute ' + theAttributeName + ' in the file.' ; Extract the attribute names. theAttributeStruct = varAttrStructures[index] theAttributeValue = *theAttributeStruct.value RETURN, theAttributeValue END ;--------------------------------------------------------------------------------------------- PRO NCDF_DATA::ReadVariableFromGUI_Events, event ; This internal method processes events from the user dialogs. ; Error handling CATCH, theError IF theError NE 0 THEN BEGIN CATCH, /CANCEL void = cgErrorMsg() RETURN ENDIF thisEvent = Tag_Names(event, /STRUCTURE_NAME) CASE thisEvent OF 'WIDGET_BUTTON': BEGIN Widget_Control, event.id, Get_UValue=buttonValue CASE buttonValue OF 'READ_AND_STAY': BEGIN ; Get the variable name. Do we need to append the filename to it? IF Widget_Info(self.appendNameID, /Valid_ID) THEN $ addName = Widget_Info(self.appendNameID, /BUTTON_SET) ELSE addName = 0 Widget_Control, self.varNameID, Get_Value=varName varName = (addName) ? cgRootName(self.filename) + '_' + varName[0] : varName[0] thisVarName = IDL_ValidName(varName, /CONVERT_ALL) IF thisVarName NE varName THEN BEGIN Widget_Control, self.varNameID, Set_Value=IDL_ValidName(thisVarName, /CONVERT_ALL) varName = thisVarName ENDIF IF varName EQ "" THEN Message, 'Must have a non-null variable name to create a variable.' ; Which variable do you want to read? Widget_Control, self.variableListID, Get_UValue=theList index = Widget_Info(self.variableListID, /DROPLIST_SELECT) theVariable = theList[index] Widget_Control, /HOURGLASS theData = self -> ReadVariable(theVariable, Success=success) IF success EQ 0 THEN RETURN ; Create the variable at the main IDL level. (Scope_VarFetch(varName, LEVEL=1, /ENTER)) = theData Print, 'A variable named "' + varName + '" has been created at the main IDL level.' ; Go to the next variable on the list IF index EQ (N_Elements(theList)-1) THEN index = 0 ELSE index = index + 1 Widget_Control, self.variableListID, SET_DROPLIST_SELECT=index Widget_Control, self.varnameID, Set_Value=IDL_ValidName(theList[index], /CONVERT_ALL) END 'READ_AND_LEAVE': BEGIN ; Get the variable name. Do we need to append the filename to it? IF Widget_Info(self.appendNameID, /Valid_ID) THEN $ addName = Widget_Info(self.appendNameID, /BUTTON_SET) ELSE addName = 0 Widget_Control, self.varNameID, Get_Value=varName varName = (addName) ? cgRootName(self.filename) + '_' + varName[0] : varName[0] IF varName EQ "" THEN Message, 'Must have a non-null variable name to create a variable.' ; Which variable do you want to read? Widget_Control, self.variableListID, Get_UValue=theList index = Widget_Info(self.variableListID, /DROPLIST_SELECT) theVariable = theList[index] Widget_Control, /HOURGLASS theData = self -> ReadVariable(theVariable, Success=success) IF success EQ 0 THEN RETURN ; Create the variable at the main IDL level. (Scope_VarFetch(varName, LEVEL=1, /ENTER)) = theData Print, 'A variable named "' + varName + '" has been created at the main IDL level.' Widget_Control, event.top, /DESTROY END 'QUIT_READ_VARIABLE_GUI': Widget_Control, event.top, /DESTROY ENDCASE END 'WIDGET_DROPLIST': BEGIN ; The name of the variable to write has to be changed when the droplist value changes. Widget_Control, event.id, Get_UVALUE=list Widget_Control, self.varNameID, Set_Value=IDL_ValidName(list[event.index], /CONVERT_ALL) END 'WIDGET_TEXT': ; Nothing to do here. We just want to read the value. Don't care what it is. ENDCASE END ;--------------------------------------------------------------------------------------------- FUNCTION NCDF_DATA::ReadVariableWithAttr, theVariable, SUCCESS=success ; ; NAME: ; NCDF_DATA::ReadVariableWithAttr ; ; PURPOSE: ; ; This method is used to read and return a variable and its attributes from a netCDF or HDF file. ; ; CALLING SEQUENCE: ; ; IDL> struct = nCDFObject -> ReadVariable(theVariable) ; ; RETURN VALUE: ; ; struct: A structure containing the variable (in the field "data") and its ; attributes in other fields. Plus, the field NDIMS holds the number ; of dimensions of the variable, and the field DIMS is a vector of ; the dimensions of the variable (for HDF files) or the dimension ; IDs (for netCDF file). ; ; ARGUMENTS: ; ; theVariable: The name of the variable you wish to read from the file. ; ; KEYWORD PARAMETERS: ; ; SUCCESS: An output parameter, set to 1 if the file was read successfully, ; and to 0 otherwise. ; ; Error handling CATCH, theError IF theError NE 0 THEN BEGIN CATCH, /CANCEL void = cgErrorMsg() success = 0 RETURN, -1 ENDIF ; Make sure the file has been parsed. IF self.hasBeenParsed EQ 0 THEN self -> ParseFile ; Check again. success = 0 IF self.hasBeenParsed EQ 0 THEN RETURN, -1 ; Check for the name of the variable. IF N_Elements(theVariable) EQ 0 THEN Message, 'Must pass name of variable to read.' ; Branch on type of file to read. IF self.isHDF THEN BEGIN ; Open the file. fileID = HDF_SD_Start(Filepath(ROOT_DIR=self.directory, self.filename)) ; Get the variable ID. index = HDF_SD_NameToIndex(fileID, theVariable) IF index EQ -1 THEN Message, 'Variable (' + theVariable + ') not found.' ; Select the variable and read it. varID = HDF_SD_Select(fileID, index) HDF_SD_GetData, varID, data ; This routine throws all kinds of scary messages if CALDATA is ; not in the file. Turn this off for this call. !QUIET = 1 HDF_SD_GetInfo, varID, CALDATA=calData, DIMS=dims, NDIMS=ndims !QUIET = 0 ; Reverse the indices in HDF files and calibrate, if neccesary. data = Reform(Temporary(data)) IF calData.cal NE 0 THEN data = calData.cal * (Temporary(data) - calData.offset) varStruct = Create_Struct('data', Temporary(data)) ; If this variable has attributes, get those, too. HDF_SD_GetInfo, varID, NATTS=natts IF natts GT 0 THEN BEGIN varAttributes = Replicate({NCDF_DATA_ATTRIBUTE}, natts+1) FOR k=0,natts-1 DO BEGIN HDF_SD_ATTRINFO, varID, k, DATA=value, NAME=attrName attrName = IDL_ValidName(attrName, /CONVERT_ALL) IF Where(Tag_Names(varStruct) EQ StrUpCase(attrName)) NE -1 THEN CONTINUE varStruct = Create_Struct(varStruct, attrName, value) ENDFOR IF calData.cal EQ 0 $ THEN varStruct = Create_Struct(varStruct, '_calibration_data', 'Not Present in File') $ ELSE varStruct = Create_Struct(varStruct, '_calibration_data', calData) ENDIF ; Add dimension informatio to the structure. varStruct = Create_Struct(varStruct, 'ndims', ndims, 'dims', dims) HDF_SD_EndAccess, varID HDF_SD_END, fileID success = 1 ENDIF ELSE BEGIN ; Open the file. fileID = NCDF_Open(Filepath(ROOT_DIR=self.directory, self.filename)) ; Get the variable ID. varID = NCDF_VarID(fileID, theVariable) ; Get information about the variable. varInfo = NCDF_VarInq(fileID, varID) ; Do we have to worry about zero dimensions? IF Ptr_Valid(self.zeroDimensionID) THEN BEGIN ; Is there a match between the dimension IDs and any zero dimension ID we ; have stored? match = 0 FOR m = 0, N_Elements(*self.zeroDimensionID)-1 DO BEGIN i = Where(varInfo.dim EQ (*self.zeroDimensionID)[m], count) IF count GT 0 THEN match = 1 ENDFOR IF match GT 0 THEN BEGIN ok = Dialog_Message('This variable has a dimension of length zero and cannot be read.') NCDF_CLOSE, fileID success = 0 RETURN, -1 ENDIF ENDIF ; Read the variable. NCDF_VarGet, fileID, varID, data IF Size(data, /N_DIMENSIONS) GT 0 THEN data = REFORM(Temporary(data)) IF StrUpCase(varInfo.datatype) EQ 'CHAR' THEN data = String(Temporary(data)) varStruct = Create_Struct('data', Temporary(data)) ; Add the variable attributes to the structure. FOR k=0,varInfo.natts-1 DO BEGIN attrName = NCDF_AttName(fileID, varID, k) NCDF_AttGet, fileID, varID, attrName, value IF Size(value, /TNAME) EQ 'BYTE' THEN value = String(value) attrName = IDL_ValidName(attrName, /CONVERT_ALL) IF Where(Tag_Names(varStruct) EQ StrUpCase(attrName)) NE -1 THEN CONTINUE varStruct = Create_Struct(varStruct, attrName, value) ENDFOR ; Add a dimensions field to the structure. varStruct = Create_Struct(varStruct, 'ndims', varInfo.Ndims, 'dims', varInfo.dim) ; Close the file, set status flag, return the data. NCDF_CLOSE, fileID success = 1 ENDELSE RETURN, varstruct END ;--------------------------------------------------------------------------------------------- PRO NCDF_DATA::ReadVarPlusFromGUI, event ; This internal method sets up a dialog for obtaining information from the user ; about which variables to read, etc. ; Error handling CATCH, theError IF theError NE 0 THEN BEGIN CATCH, /CANCEL void = cgErrorMsg() RETURN ENDIF ; Get some position information. Widget_Control, event.top, TLB_GET_OFFSET=offsets ; We want a modal pop-up dialog widget. tlb = Widget_Base(GROUP_LEADER=event.top, XOFFSET=offsets[0]+50, YOFFSET=offsets[1]+50, $ COLUMN=1, BASE_ALIGN_CENTER=1, /FLOATING, UVALUE=self, /MODAL) row = Widget_Base(tlb, ROW=2, /GRID_LAYOUT, FRAME=1) label = Widget_Label(row, Value='Variable to Read: ') theList = [(*self.theVariables).name] self.varpluslistID = Widget_Droplist(row, Value=[(*self.theVariables).name], $ UVALUE=[(*self.theVariables).name], SCR_XSIZE=250, UNAME='VARIABLESPLUS') label = Widget_Label(row, Value='Variable Name: ') thisVarname = IDL_ValidName((*self.theVariables)[0].name + '_struct', /CONVERT_ALL) self.varplusnameID = Widget_Text(row, Value=thisVarname, /Editable, SCR_XSIZE=250) b = Widget_Base(tlb, ROW=1, XPAD=0, YPAD=0, /NONEXCLUSIVE) okToAppend = 1 IF StrPos(cgRootName(self.filename), '.') NE -1 THEN okToAppend = 0 IF StrPos(self.filename, '-') NE -1 THEN okToAppend = 0 IF StrPos(self.filename, ' ') NE -1 THEN okToAppend = 0 IF okToAppend THEN self.appendNameID = Widget_Button(b, Value='Append Filename to Variable Name', UVALUE='APPEND_FILENAME') buttonrow = Widget_Base(tlb, ROW=1) button = Widget_Button(buttonrow, Value='Read Variable and Leave', UVALUE='READ_VARPLUS_AND_LEAVE') button = Widget_Button(buttonrow, Value='Read Variable and Stay', UVALUE='READ_VARPLUS_AND_STAY') button = Widget_Button(buttonrow, Value='Quit', UVALUE='QUIT_READ_VARPLUS_GUI') ; If there is a tree selection, see if this corresponds to a variable in the list. ; If so, set this variable in the droplist widget. theSelection = Widget_Info(self.theTree, /TREE_SELECT) Widget_Control, theSelection, Get_Value=varName index = Where(theList EQ varName, count) IF count GT 0 THEN BEGIN Widget_Control, self.varpluslistID, SET_DROPLIST_SELECT=index Widget_Control, self.varplusnameID, Set_Value=IDL_ValidName(theList[index], /CONVERT_ALL) ENDIF ; Get it going... Widget_Control, tlb, /REALIZE XMANAGER, 'read_and_leave', tlb, EVENT_HANDLER='NCDF_DATA_WIDGET_EVENTS', /NO_BLOCK END ;--------------------------------------------------------------------------------------------- PRO NCDF_DATA::ReadVarPlusFromGUI_Events, event ; This internal method processes events from the user dialogs. ; Error handling CATCH, theError IF theError NE 0 THEN BEGIN CATCH, /CANCEL void = cgErrorMsg() RETURN ENDIF thisEvent = Tag_Names(event, /STRUCTURE_NAME) CASE thisEvent OF 'WIDGET_BUTTON': BEGIN Widget_Control, event.id, Get_UValue=buttonValue CASE buttonValue OF 'READ_VARPLUS_AND_STAY': BEGIN ; Get the variable name. Do we need to append the filename to it? IF Widget_Info(self.appendNameID, /Valid_ID) THEN $ addName = Widget_Info(self.appendNameID, /BUTTON_SET) ELSE addName = 0 Widget_Control, self.varPlusNameID, Get_Value=varName varName = (addName) ? cgRootName(self.filename) + '_' + varName[0] : varName[0] thisVarName = IDL_ValidName(varName, /CONVERT_ALL) IF thisVarName NE varName THEN BEGIN Widget_Control, self.varPlusNameID, Set_Value=IDL_ValidName(thisVarName, /CONVERT_ALL) varName = thisVarName ENDIF IF varName EQ "" THEN Message, 'Must have a non-null variable name to create a variable.' ; Which variable do you want to read? Widget_Control, self.varpluslistID, Get_UValue=theList index = Widget_Info(self.varpluslistID, /DROPLIST_SELECT) theVariable = theList[index] theData = self -> ReadVariableWithAttr(theVariable, Success=success) IF success EQ 0 THEN RETURN ; Create the variable at the main IDL level. (Scope_VarFetch(varName, LEVEL=1, /ENTER)) = theData Print, 'A structure variable named "' + varName + '" has been created at the main IDL level.' ; Go to the next variable on the list IF index EQ (N_Elements(theList)-1) THEN index = 0 ELSE index = index + 1 Widget_Control, self.varpluslistID, SET_DROPLIST_SELECT=index Widget_Control, self.varplusnameID, $ Set_Value=IDL_ValidName(theList[index] + '_struct', /CONVERT_ALL) END 'READ_VARPLUS_AND_LEAVE': BEGIN ; Get the variable name. Do we need to append the filename to it? IF Widget_Info(self.appendNameID, /Valid_ID) THEN $ addName = Widget_Info(self.appendNameID, /BUTTON_SET) ELSE addName = 0 Widget_Control, self.varplusNameID, Get_Value=varName varName = (addName) ? cgRootName(self.filename) + '_' + varName[0] : varName[0] IF varName EQ "" THEN Message, 'Must have a non-null variable name to create a variable.' ; Which variable do you want to read? Widget_Control, self.varpluslistID, Get_UValue=theList index = Widget_Info(self.varpluslistID, /DROPLIST_SELECT) theVariable = theList[index] theData = self -> ReadVariableWithAttr(theVariable, Success=success) IF success EQ 0 THEN RETURN ; Create the variable at the main IDL level. (Scope_VarFetch(varName, LEVEL=1, /ENTER)) = theData Print, 'A structure variable named "' + varName + '" has been created at the main IDL level.' Widget_Control, event.top, /DESTROY END 'QUIT_READ_VARPLUS_GUI': Widget_Control, event.top, /DESTROY ENDCASE END 'WIDGET_DROPLIST': BEGIN ; The name of the variable to write has to be changed when the droplist value changes. Widget_Control, event.id, Get_UVALUE=list Widget_Control, self.varPlusNameID, Set_Value=IDL_ValidName(list[event.index], /CONVERT_ALL) END 'WIDGET_TEXT': ; Nothing to do here. We just want to read the value. Don't what it is. ENDCASE END ;--------------------------------------------------------------------------------------------- PRO NCDF_DATA::SelectionInTree, event ; This internal method processes events from the tree widget. ; Error handling CATCH, theError IF theError NE 0 THEN BEGIN CATCH, /CANCEL void = cgErrorMsg() RETURN ENDIF ; Create variable for better formatting. tab = ' ' ; What to do depends on the node name of the tree selection. nodeName = Widget_Info(event.id, /UNAME) CASE nodeName OF 'FOLDER': Widget_Control, self.textDisplay, Set_Value="" 'GLOBAL ATTRIBUTE': BEGIN si = 0 Widget_Control, event.id, Get_Value=name gattr = *self.theAttributes i = Where(gattr.name EQ name, count) IF count GT 0 THEN BEGIN text = StrArr(3) thisAttr = (*self.theAttributes)[i] IF thisAttr.dataType EQ 'STRING' OR thisAttr.dataType EQ 'CHAR' THEN BEGIN str = TextLineFormat(*thisAttr.value, LENGTH=80) lines = N_Elements(str) text = [text, StrArr(2 + lines)] text[si] = StrUpCase(thisAttr.name) + ':' text[si+1:si+1+(lines-1)] = tab + str si = si + 2 + lines ENDIF ELSE BEGIN var = *thisAttr.value IF N_Elements(var) EQ 0 THEN var = var[0] Help, var, OUTPUT=helptext, /STRUCTURE text = [text, StrArr(N_Elements(helptext) + 2)] text[si] = StrUpCase(thisAttr.name) + ':' aString = StrMid(helptext, 3) parts = StrSplit(aString, '=', /Extract) lastPart = StrCompress(parts[1], /REMOVE_ALL) IF StrUpCase(lastPart) EQ 'ARRAY[2]' THEN BEGIN IF Size(var, /TNAME) EQ 'BYTE' THEN BEGIN lastpart = '[' + StrTrim(Fix(var[0]),2) + ', ' + StrTrim(Fix(var[1]),2) + ']' ENDIF ELSE BEGIN lastpart = '[' + StrTrim(var[0],2) + ', ' + StrTrim(var[1],2) + ']' ENDELSE ENDIF aString = ' ' + StrCompress(parts[0],/REMOVE_ALL) + ' = ' + lastPart text[si+1] = aString si = si + N_Elements(helptext) + 2 ENDELSE Widget_Control, self.textDisplay, Set_Value=text ENDIF ELSE Message, 'Cannot find global attribute ' + name END 'DIMENSION': BEGIN si = 0 Widget_Control, event.id, Get_Value=name dims = *self.theDimensions i = Where(dims.name EQ name, count) IF count GT 0 THEN BEGIN text = StrArr(3) thisDim = (*self.theDimensions)[i] text[si] = StrUpCase(thisDim.name) + ':' text[si+1] = tab + StrTrim(thisDim.value,2) ENDIF Widget_Control, self.textDisplay, Set_Value=text END 'VARIABLE': BEGIN Widget_Control, event.id, Get_Value=name vars = *self.theVariables IF N_Elements(vars) EQ 0 THEN vars = vars[0] i = Where(vars.name EQ name, count) FOR k=0,count-1 DO BEGIN thisVar = vars[i[k]] text = StrArr(6) text[0] = tab + 'NAME: ' + name text[1] = tab + 'DATATYPE: ' + thisVar.datatype n = StrTrim(N_Elements(*thisVar.datasize),2) f = ' (' + n + '(I0, :, ", "))' d = String(*thisVar.datasize, FORMAT=f) text[2] = tab + 'N_DIMENSIONS: ' + StrTrim(N_Elements(*thisVar.datasize),2) text[3] = tab + 'DIMENSIONS: [' + d + ']' IF (self.no_read_on_parse EQ 0) THEN BEGIN IF thisVar.datatype NE 'CHAR' THEN BEGIN text[4] = tab + 'MIN VALUE: ' + StrTrim(thisVar.minValue,2) text[5] = tab + 'MAX VALUE: ' + StrTrim(thisVar.maxValue,2) ENDIF ENDIF Widget_Control, self.textDisplay, Set_Value=text ENDFOR END 'FILENAME': BEGIN text = StrArr(3) text[0] = 'FILENAME:' text[1] = tab + self.filename Widget_Control, self.textDisplay, Set_Value=text END 'DIRECTORY': BEGIN text = StrArr(3) text[0] = 'DIRECTORY:' text[1] = tab + self.directory Widget_Control, self.textDisplay, Set_Value=text END 'SUMMARY': BEGIN text = StrArr(3) text[0] = 'DIRECTORY:' text[1] = tab + self.directory si = 3 text = [text, strArr(3)] text[3] = 'FILENAME:' text[4] = tab + self.filename si = si + 3 IF Ptr_Valid(self.theAttributes) THEN BEGIN attr = *self.theAttributes FOR j=0,N_Elements(attr)-1 DO BEGIN thisAttr = attr[j] IF thisAttr.dataType EQ 'STRING' OR thisAttr.dataType EQ 'CHAR' THEN BEGIN str = TextLineFormat(*thisAttr.value, LENGTH=80) lines = N_Elements(str) text = [text, StrArr(2 + lines)] text[si] = StrUpCase(thisAttr.name) + ':' text[si+1:si+1+(lines-1)] = tab + str si = si + 2 + lines ENDIF ELSE BEGIN var = *thisAttr.value Help, var, OUTPUT=helptext, /STRUCTURE text = [text, StrArr(N_Elements(helptext) + 2)] text[si] = StrUpCase(thisAttr.name) + ':' aString = StrMid(helptext, 3) parts = StrSplit(aString, '=', /Extract) lastPart = StrCompress(parts[1], /REMOVE_ALL) IF StrUpCase(lastPart) EQ 'ARRAY[2]' THEN BEGIN IF Size(var, /TNAME) EQ 'BYTE' THEN BEGIN lastpart = '[' + StrTrim(Fix(var[0]),2) + ', ' + StrTrim(Fix(var[1]),2) + ']' ENDIF ELSE BEGIN lastpart = '[' + StrTrim(var[0],2) + ', ' + StrTrim(var[1],2) + ']' ENDELSE ENDIF aString = ' ' + StrCompress(parts[0],/REMOVE_ALL) + ' = ' + lastPart text[si+1] = aString si = si + N_Elements(helptext) + 2 ENDELSE ENDFOR ENDIF Widget_Control, self.textDisplay, Set_Value=text END 'VARIABLE ATTRIBUTE': BEGIN si = 0 Widget_Control, event.id, Get_UValue=value varname = value[0] name = value[1] vars = *self.theVariables i = Where(vars.name EQ varname, count) thisVar = vars[i] var_attributes = *thisVar.var_attributes i = Where(var_attributes.name EQ name, count) IF count GT 0 THEN BEGIN text = StrArr(3) thisAttr = var_attributes[i] IF thisAttr.dataType EQ 'STRING' OR thisAttr.dataType EQ 'CHAR' THEN BEGIN str = TextLineFormat(*thisAttr.value, LENGTH=80) lines = N_Elements(str) text = [text, StrArr(2 + lines)] text[si] = StrUpCase(thisAttr.name) + ':' text[si+1:si+1+(lines-1)] = tab + str si = si + 2 + lines ENDIF ELSE BEGIN var = *thisAttr.value Help, var, OUTPUT=helptext, /STRUCTURE text = [text, StrArr(N_Elements(helptext) + 2)] text[si] = StrUpCase(thisAttr.name) + ':' aString = StrMid(helptext, 3) IF Size(var, /TNAME) NE 'STRUCT' THEN BEGIN parts = StrSplit(aString, '=', /Extract) lastPart = StrCompress(parts[1], /REMOVE_ALL) IF StrUpCase(lastPart) EQ 'ARRAY[2]' THEN BEGIN IF Size(var, /TNAME) EQ 'BYTE' THEN BEGIN lastpart = '[' + StrTrim(Fix(var[0]),2) + ', ' + StrTrim(Fix(var[1]),2) + ']' ENDIF ELSE BEGIN lastpart = '[' + StrTrim(var[0],2) + ', ' + StrTrim(var[1],2) + ']' ENDELSE ENDIF aString = ' ' + StrCompress(parts[0],/REMOVE_ALL) + ' = ' + lastPart text[si+1] = aString si = si + N_Elements(helptext) + 2 ENDIF ELSE BEGIN text[si + 1] = ' ' + 'Structure Variable:' FOR kk=2,N_Elements(aString) DO BEGIN text[si + kk] = ' ' + aString[kk-1] ENDFOR ENDELSE ENDELSE Widget_Control, self.textDisplay, Set_Value=text ENDIF ELSE Message, 'Cannot find global attribute ' + name END ELSE: Message, 'Unexpected event from ' + nodename + '. Please investigate.' ENDCASE END ;--------------------------------------------------------------------------------------------- PRO NCDF_DATA::CLEANUP ; This is the main cleanup routine for the object. Delete all created pointers. self -> CleanParsedStructures Ptr_Free, self.theAttributes Ptr_Free, self.theDimensions Ptr_Free, self.theCalibration Ptr_Free, self.theSwaths Ptr_Free, self.theVariables Ptr_Free, self.zeroDimensionID END ;--------------------------------------------------------------------------------------------- FUNCTION NCDF_DATA::INIT, filename, $ BROWSE=browse, $ DESTROY_FROM_BROWSER=destroy_from_browser, $ EXTENSION=extension, $ NO_READ_ON_PARSE=no_read_on_parse, $ NO_NEW_FILE=no_new_file ; Error handling. Return 0 if can't finish. CATCH, theError IF theError NE 0 THEN BEGIN CATCH, /CANCEL void = cgErrorMsg() RETURN, 0 ENDIF ; Check parameters. IF N_Elements(filename) NE 0 THEN BEGIN IF File_Test(filename, /READ) EQ 0 THEN Message, 'Specified file does not exist or is not readable.' basename = File_BaseName(filename) directory = File_DirName(filename, /MARK_DIRECTORY) IF directory EQ '.\' OR directory EQ './' THEN CD, Current=directory self.filename = basename self.directory = directory ENDIF IF N_Elements(extension) EQ 0 THEN extension = '*.nc;*.ncd;*.ncdf;*.hdf' ; Set other object properties self.destroy_from_browser = Keyword_Set(destroy_from_browser) self.extension = extension self.no_read_on_parse = Keyword_Set(no_read_on_parse) ; Browse now? success = 1 IF Keyword_Set(browse) THEN self -> Browse, SUCCESS=success IF success EQ 0 THEN BEGIN Obj_Destroy, self RETURN, 0 ENDIF ; Determine if this is a netCDF or HDF file. self.isHDF = HDF_ISHDF(Filepath(ROOT_DIR=self.directory, self.filename)) RETURN, 1 END ;--------------------------------------------------------------------------------------------- PRO NCDF_DATA_ATTRIBUTE__DEFINE struct = { NCDF_DATA_ATTRIBUTE, $ attrtype: "", $ datatype: "", $ length: 0L, $ name: "", $ value: Ptr_New() } END ;--------------------------------------------------------------------------------------------- PRO NCDF_DATA_DIMENSION__DEFINE struct = { NCDF_DATA_DIMENSION, $ name: "", $ value: "" } ; Length or UNLIMITED. END ;--------------------------------------------------------------------------------------------- PRO NCDF_DATA_VARIABLE__DEFINE struct = { NCDF_DATA_VARIABLE, $ datasize: Ptr_New(), $ datatype: "", $ minValue: 0.0D, $ maxValue: 0.0D, $ name: "", $ var_attributes: Ptr_New(), $ calibration: Ptr_New(), $ value: Ptr_New() } END;--------------------------------------------------------------------------------------------- PRO NCDF_DATA_SWATH__DEFINE struct = { NCDF_DATA_SWATH, $ name: "", $ nattrs: 0L, $ ndims: 0L, $ ngeofields: 0L, $ ndatafields: 0L, $ nmaps: 0L, $ nidxmaps: 0L, $ attributes: Ptr_New(), $ dimensions: Ptr_New(), $ geofields: Ptr_New(), $ datafields: Ptr_New(), $ maps: Ptr_New(), $ idxmaps: Ptr_New() } END;--------------------------------------------------------------------------------------------- PRO NCDF_DATA_WIDGET_CLEANUP, tlb Widget_Control, tlb, GET_UVALUE=self IF self -> Destroy_From_Browser() THEN Obj_Destroy, self END ;--------------------------------------------------------------------------------------------- PRO NCDF_DATA_WIDGET_EVENTS, event Widget_Control, event.TOP, GET_UVALUE=self self -> EventHandler, event END ;--------------------------------------------------------------------------------------------- PRO NCDF_DATA__DEFINE, class class = { NCDF_DATA, $ ; The object class NCDF_DATA. appendNameID: 0L, $ ; The button for appending filenames to variables. attributeID: 0L, $ ; The widget containing the attribute list. attrNameID: 0L, $ ; The widget containing the main-level attribute name. filename: "", $ ; The filename of the netCDF or HDF file. destroy_from_browser: 0B, $ ; A flag to indicate the object is destroyed if brower is destroyed. directory: "", $ ; The directory the file is located in. extension: '', $ ; The file extension for FILTER keyword in DIALOG_PICKFILE. geoDisplay: {WIDGET_GEOMETRY}, $ ; Widget geometries for calculating resizeable windows. geoWindow: {WIDGET_GEOMETRY}, $ geoButton: {WIDGET_GEOMETRY}, $ geoTree: {WIDGET_GEOMETRY}, $ hasBeenParsed: 0B, $ ; A flag to indicate if the file has been parsed. no_read_on_parse: 0B, $ ; A flag to indicate that the variables should not be read while parsing. isHDF: 0B, $ ; A flag to indicate this is an HDF file instead of a netCDF file. minXSize: 0L, $ ; Minimum X size of the Browser window. minYSize: 0L, $ ; Minimum Y size of the Browser window. textDisplay: 0L, $ ; The widget where text information is displayed theAttributes: Ptr_New(), $ ; An array of global attribute structures. theDimensions: Ptr_New(), $ ; An array of dimension structures. theCalibration: Ptr_New(),$ ; An array of calibration structures for the HDF SD variable. zeroDimensionID: Ptr_New(), $ ; A pointer to the dimension IDs whose current size is 0. theSwaths: Ptr_New(), $ ; A pointer to an array of swath structures. theTree: 0L, $ ; The tree widget ID. theVariables: Ptr_New(), $ ; An array of variable structures. tlb: 0L, $ ; The TLB of the browser window. varpluslistID: 0L, $ ; The list of variable plus attributes to be read. varplusnameID: 0L, $ ; The widget containing the main-level variable name. variablelistID: 0L, $ ; The list of variables available to be read. varnameID: 0L $ ; The widget containing the main-level variable name. } END ;---------------------------------------------------------------------------------------------