;+ ; NAME: ; NCDF_FILE ; ; PURPOSE: ; ; The pupose of this NCDF_File object is three-fold. (1) Allow the user to easily ; determine what information is inside a netCDF file and allow easy access ; to such information. (2) Allow the user to easily create a netCDF file from ; scratch. (3) Allow the user to easily copy information from one netCDF ; file to another. ; ; 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_FILE', filename) ; ; ARGUMENTS: ; ; filename: The name of a netCDF file to read, write to, or browse. ; ; KEYWORD PARAMETERS: ; ; ALERT: Set this keyword if you wish to have alert from the object's error logger. ; Input. Default is 1. ; ; BROWSE: If this keyword is set, the Browse Window is invoked as soon ; as the object is initiated. Input. Default is 0. ; ; CLOBBER: Set this keyword if you are opening a netCDF file that already exists and ; you want to overwrite the existing file. Input. Default is 0. ; ; CREATE: Set this keyword if you wish to create a new netCDF file to write ; into. Input. Default is 0, which means the file will be opened as ; "read-only". ; ; DELETE_ON_DESTROY: Set this keyword if you wish to delete the error log file when ; the ErrorLogger object is destroyed. This will only happen if the ErrorLogger ; object is not in an error state. Input. Default is 1. ; ; MODIFY: Set this keyword if you wish to modify (write to) a file you are opening. ; If not set, the file will be opened as "read-only". ; ; ; METHODS: ; ; The following methods are available. Each is documented in front of the method. ; ; ncdfObject -> Browse ; ncdfObject -> CopyVarAttrTo, varName, attrName, destObj ; ncdfObject -> CopyVarDataTo, varName, destObj, COUNT=count, OFFSET=offset, STRIDE=stride ; ncdfObject -> CopyVarDefTo, varName, destObj ; ncdfObject -> CopyGlobalAttrTo, attrName, destObj ; ncdfObject -> CopyDimTo, dimName, destObj ; dimNames = ncdfObject -> GetDimNames(COUNT=dimCount) ; dimValue = ncdfObject -> GetDimValue(dimName) ; fileID = ncdfObject -> GetFileID() ; globalAttrNames = ncdfObject -> GetGlobalAttrNames(COUNT=attrCount) ; attrValue = ncdfObject -> GetGlobalAttrValue(attrName, DATATYPE=datatype) ; ncdfObject -> GetProperty, .... ; property = ncdfObject -> GetProperty(thisProperty) ; varAttrNames = ncdfObject -> GetVarAttrNames(varName, COUNT=attrCount) ; varAttrValue = ncdfObject -> GetVarAttrValue(varName, varAttrName, COUNT=attrCount) ; varNames = ncdfObject -> GetVarNames(COUNT=varCount) ; varData = ncdfObject -> GetVarData(varName, COUNT=count, OFFSET=offset, STRIDE=stride) ; answer = ncdfObject -> HasGlobalAttr(attrName, OBJECT=object) ; answer = ncdfObject -> HasDim(dimName, OBJECT=object) ; answer = ncdfObject -> HasVar(varName, OBJECT=object) ; answer = ncdfObject -> HasVarAttr(varName, attrName, OBJECT=object) ; ncdfObject -> PrintFileInfo ; ncdfObject -> ParseFile ; ncdfObject -> SetMode, DEFINE=define, DATA=data ; ncdfObject -> WriteVarData, varName, data, COUNT=count, OFFSET=offset, STRIDE=stride ; ncdfObject -> WriteVarDef, varName, dimNames, DATATYPE=datatype, VAROBJ=varObj ; ncdfObject -> WriteDim, dimName, dimSize, UNLIMITED=unlimited ; ncdfObject -> WriteGlobalAttr, attrName, attrValue, DATATYPE=datatype ; ncdfObject -> WriteVarAttr, attrName, attrValue, varObj, DATATYPE=datatype ; ; NOTES: ; ; Note that all variable, attribute, and dimension names in a netCDF file are CASE SENSITIIVE!! ; Thus, it is a good idea to use the methods provided in this object to obtain and examine ; information in the file, as these names are handled in a case sensitive manner. ; ; Whenever you are creating a new netCDF file, you should try to create the file in ; the following way. ; 1. Create your global attributes. ; 2. Create the dimensions you will be using to describe the variables. ; 3. Define the variables. To do this correctly, dimensions MUST be defined. ; 4. Define variable attributes. ; 5. Load your variables with data. ; ; Note that the data type of the _FillValue variable attribute MUST match the ; data type of the variable data. Otherwise, you will have MANY problems! This ; is a common source of error. ; ; Note that in almost all cases where you see the names "varName", "dimName", or ; "attrName" used as input variables, you can substitute the proper object ; reference in place of the actual name. In other words, you could get the value ; of a variable attribute by doing something like this: ; ; check = ncdfObject -> HasAttr('history', OBJECT=attrObj) ; IF check THEN attrValue = ncdfObject -> GetGlobalAttrValue(attrObj) ; ; as opposed to this: ; ; IF check THEN attrValue = ncdfObject -> GetGlobalAttrValue('history') ; EXAMPLE: ; ; IDL> filename = 'example.nc' ; IDL> ncdfObj = Obj_New('NCDF_FILE', filename) ; IDL> ncdfObj -> Browse ; IDL> Obj_Destroy, ncdfObj ; ; MODIFICATION HISTORY: ; Written by: David W. Fanning, 3 Feb 2010, using (stealing, really) plenty of ideas ; from Mark Hadfield's Motley Library. Mark's mghncfile object is terrific, but it ; had a number of limitations for my particular application, which I have attemped ; to correct in my version of the software. But I wouldn't have even attempted this ; had Mark not blazed the trail and Matt Savoie not insisted that I look at Mark's ; wonderful library. ; Changes in the way dimensions with a zero length are handled. 11 Feb 2010, DWF. ; Added GetVarInfo method. 20 March 2010. DWF. ; Added MISSINGINIDCES and FILLVALUE output keywords to GetVarData method. 20 March 2010. DWF. ; Added output keywords SCALE_FACTOR, ADD_OFFSET, and DATATYPE to GetVarData method ; so that these values can be obtained with the data. 29 Apr 2010. DWF. ; I changed "missingValue" to "fillValue" some time ago, but I missed one in ; the GetVarData method. Fixed. 7 June 2010. DWF. ; Used the undefine procedure OBJ_DELETE, rather than OBJ_DESTROY. Sheesh! 18 June 2010. DWF. ; Added NETCDF4_FORMAT keyword. 13 Feb 2012. DWF. ; Added a bunch of new IDL 8.0 and 8.1 keyword to the WriteVarDef method to allow ; access to these keywords in NCDF_VarDef. Also modified the NETCDF4_FORMAT keyword ; to apply only in IDL versions 8.0 and higher. 21 Feb 2012. DWF. ; Small typo fixed in setting CHAR datatype for IDL 8.1 and higher. 22 May 2013. DWF. ; Typo (CONTINUOUS->CONTIGUOUS) fixed in WriteDefVar method. 30 July 2013. DWF. ; Modified CopyVarDefTo method to allow new NCDF4 keywords. 30 July 2013. DWF. ; Added DelGlobalAttr method to allow deletion of global attributes. 11 Jan 2014. DWF. ; Found typo in UBYTE variable code, which I fixed. 12 Fef 2016. DWF. ; ;- ;******************************************************************************************; ; Copyright (c)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. ; ;******************************************************************************************; ;------------------------------------------------------------------------------------------; ; ; NAME: ; NCDF_File::Browse ; ; Purpose: ; ; Allows the user to browse the netCDF file interactively. Variables, attributes, ; and dimensions can be saved to the main IDL level or command line where they can ; be manipulated further. ; ; Method Syntax: ; ; obj -> Browse ; ; Auguments: ; ; None. ; ; Keywords: ; ; TITLE: A text string that will be the title of the browser window. (Optional) ; XOFFSET: The X offset of the top-left corner of the browser. (Optional) ; YOFFSET: The Y offset of the top-left corner of the browser. (Optional) ; ;------------------------------------------------------------------------------------------; PRO NCDF_File::Browse, TITLE=title, XOFFSET=xoffset, YOFFSET=yoffset NCDF_Browser, self.filename, $ /NO_NEW_FILE, $ TITLE=title, $ XOFFSET=xoffset, $ YOFFSET=yoffset END ;------------------------------------------------------------------------------------------; ; ; NAME: ; NCDF_File::Close_File ; ; Purpose: ; ; Closes the netCDF file, if open. ; ; Method Syntax: ; ; obj -> Browse ; ; Auguments: ; ; None. ; ; Keywords: ; ; None. ; ;------------------------------------------------------------------------------------------; PRO NCDF_File::Close_File IF self.fileID GT 0 THEN NCDF_Close, self.fileID self.fileID = -1 END ;------------------------------------------------------------------------------------------; ; ; NAME: ; NCDF_File::CreateVarObj ; ; Purpose: ; ; Creates a NCDF_Variable object and adds it the the variable container. ; ; Method Syntax: ; ; obj -> CreateVarObj, varName ; ; Auguments: ; ; varName: The case sensitive name of the variable. ; ; Keywords: ; ; None. ; ; Notes: An internal method. ; ;------------------------------------------------------------------------------------------; PRO NCDF_File::CreateVarObj, varName ; Compiler options. Compile_Opt DEFINT32 Compile_Opt STRICTARR Compile_Opt STRICTARRSUBS Compile_Opt LOGICAL_PREDICATE ; Error handling. Catch, theError IF theError NE 0 THEN BEGIN Catch, /CANCEL self.errorLogger -> AddError RETURN ENDIF varObj = Obj_New('NCDF_Variable', varName, self) self.vars -> Add, varObj END ;------------------------------------------------------------------------------------------; ; ; NAME: ; NCDF_File::CreateAttrObj ; ; Purpose: ; ; Creates a NCDF_Attribute object and adds it the the attribute container. ; ; Method Syntax: ; ; obj -> CreateAttrObj, attrName ; ; Auguments: ; ; attrName: The case sensitive name of the attribute. ; ; Keywords: ; ; None. ; ; Notes: An internal method. ; ;------------------------------------------------------------------------------------------; PRO NCDF_File::CreateAttrObj, attrName ; Compiler options. Compile_Opt DEFINT32 Compile_Opt STRICTARR Compile_Opt STRICTARRSUBS Compile_Opt LOGICAL_PREDICATE ; Error handling. Catch, theError IF theError NE 0 THEN BEGIN Catch, /CANCEL self.errorLogger -> AddError RETURN ENDIF attrObj = Obj_New('NCDF_Attribute', attrName, self) self.attrs -> Add, attrObj END ;------------------------------------------------------------------------------------------; ; ; NAME: ; NCDF_File::CreateDimObj ; ; Purpose: ; ; Creates a NCDF_Dimension object and adds it the the dimension container. ; ; Method Syntax: ; ; obj -> CreateDimObj, dimensionName ; ; Auguments: ; ; dimensionName: The case sensitive name of the dimension. ; ; Keywords: ; ; None. ; ; Notes: An internal method. ; ;------------------------------------------------------------------------------------------; PRO NCDF_File::CreateDimObj, dimensionName ; Compiler options. Compile_Opt DEFINT32 Compile_Opt STRICTARR Compile_Opt STRICTARRSUBS Compile_Opt LOGICAL_PREDICATE ; Error handling. Catch, theError IF theError NE 0 THEN BEGIN Catch, /CANCEL self.errorLogger -> AddError RETURN ENDIF dimObj = Obj_New('NCDF_Dimension', dimensionName, self) self.dims -> Add, dimObj END ;------------------------------------------------------------------------------------------; ; ; NAME: ; NCDF_File::CopyVarAttrTo ; ; Purpose: ; ; Copies a variable attribute from this object to another NCDF_FILE object. ; ; Method Syntax: ; ; obj -> CopyVarAttrTo, varName, attrName, destObj ; ; Auguments: ; ; varName: The case sensitive name of the variable you wish to copy. ; attrName: The case sensitive name of the variable attribute you wish to copy. ; destObj: The object reference of a NCDF_FILE object you wish to copy ; the variable attribute to. ; ; Keywords: ; ; None. ; ; Notes: The variable will have had to have been previously defined for the file. ; ;------------------------------------------------------------------------------------------; PRO NCDF_File::CopyVarAttrTo, varName, attrName, destObj ; Compiler options. Compile_Opt DEFINT32 Compile_Opt STRICTARR Compile_Opt STRICTARRSUBS Compile_Opt LOGICAL_PREDICATE ; Error handling. Catch, theError IF theError NE 0 THEN BEGIN Catch, /CANCEL self.errorLogger -> AddError RETURN ENDIF IF N_Elements(varName) EQ 0 THEN $ Message, 'A variable name or object reference is required.' IF N_Elements(attrName) EQ 0 THEN $ Message, 'An attribute name or object reference is required.' ; Make sure the file has been parsed. IF ~self.fileHasBeenParsed THEN self -> ParseFile ; Were you passed the name of a variable or a variable object? CASE Size(varName, /TNAME) OF 'STRING': BEGIN varObj = self.vars -> FindByName(varName, COUNT=varCount, /CASE_SENSITIVE) IF varCount EQ 0 THEN $ Message, 'Cannot find a variable object with name ' + varName + '.' IF ~Obj_Valid(varObj) THEN $ Message, 'Invalid object with name "' + varName + '" has been found.' END 'OBJREF': BEGIN varObj = varName END ELSE: Message, 'Input variable name or object is the wrong data type.' ENDCASE ; Get and return the variable's attribute value. attrValue = varObj -> GetAttrValue(attrName, DATATYPE=attrDataType) ; Write the variable attribute in the destination object. destObj -> WriteVarAttr, varName, attrName, attrValue, DATATYPE=attrDataType END ;------------------------------------------------------------------------------------------; ; ; NAME: ; NCDF_File::CopyVariableTo ; ; Purpose: ; ; The NCDF_File object has methods to do low-level manipulation of netCDF files, but ; this method is a high-level method to copy a variable from one file to another. ; This method will find all of the variable parts it needs in the file (dimensions, ; variable definition, variable attributes, and even variable data) and will copy ; everything it finds (if needed!) into the destination file. This eliminates a lot ; of the druge work that goes into understanding exactly how everything works. ; ; Method Syntax: ; ; obj -> CopyVariableTo, varName, destObj ; ; Auguments: ; ; varName: The case sensitive name of the variable you wish to copy ; the variable definition from. ; destObj: The object reference of a NCDF_FILE object you wish to copy the ; variable definition to. ; ; Keywords: ; ; NODATA: If this keyword is set, the variable's data is not copied to the file. ; ;------------------------------------------------------------------------------------------; PRO NCDF_File::CopyVariableTo, varName, destObj ; Compiler options. Compile_Opt DEFINT32 Compile_Opt STRICTARR Compile_Opt STRICTARRSUBS Compile_Opt LOGICAL_PREDICATE ; Error handling. Catch, theError IF theError NE 0 THEN BEGIN Catch, /CANCEL self.errorLogger -> AddError RETURN ENDIF IF N_Elements(varName) EQ 0 THEN $ Message, 'A variable name or object reference is required.' IF N_Elements(destObj) EQ 0 THEN $ Message, 'A destination NCDF_FILE object is required.' IF ~Obj_Valid(destObj) THEN $ Message, 'The destination NCDF_FILE object is not valid.' IF ~destObj -> GetProperty('Writable') THEN $ Message, 'The destination NCDF_FILE object is not writeable.' ; Make sure the file has been parsed. IF ~self.fileHasBeenParsed THEN self -> ParseFile ; Were you passed the name of a variable or a variable object? CASE Size(varName, /TNAME) OF 'STRING': BEGIN varObj = self.vars -> FindByName(varName, COUNT=strCount, /CASE_SENSITIVE) IF strCount EQ 0 THEN Message, 'Cannot find a variable object with name ' + varName + '.' IF ~Obj_Valid(varObj) THEN Message, 'Invalid object with name "' + varName + '" has been found.' END 'OBJREF': BEGIN varObj = varName END ELSE: Message, 'Input variable name or object is the wrong data type.' ENDCASE ; Start with this variable's dimensions. Have these been defined ; for the variable in the destination object? If not, define them. dimNames = VarObj -> GetDimNames(COUNT=dimCount) FOR j=0,dimCount-1 DO BEGIN thisName = dimNames[j] IF ~destObj->HasDim(thisName) THEN self -> CopyDimTo, thisName, destObj ENDFOR ; Has this variable been defined in the destination object. ? If not, do it. IF ~destObj->HasVar(varName) THEN self -> CopyVarDefTo, varName, destObj ; Does the variable have attributes? Copy these, too. varAttrNames = varObj -> GetAttrNames(COUNT=attrCount) FOR j=0,attrCount-1 DO BEGIN thisAttrName = varAttrNames[j] IF ~destObj->HasVarAttr(varName, thisAttrName) THEN $ self -> CopyVarAttrTo, varName, thisAttrName, destObj ENDFOR ; Finally, copy the variable's data to the destination object. IF ~Keyword_Set(nodata) THEN self -> CopyVarDataTo, varName, destObj END ;------------------------------------------------------------------------------------------; ; ; NAME: ; NCDF_File::CopyVarDataTo ; ; Purpose: ; ; Copies variable data from this object to another NCDF_FILE object. ; ; Method Syntax: ; ; obj -> CopyVarDataTo, varName, destObj, COUNT=count, OFFSET=offset, STRIDE=stride ; ; Auguments: ; ; varName: The case sensitive name of the variable you wish to copy the data from. ; destObj: The object reference of a NCDF_FILE object you wish to copy the data to. ; ; Keywords: ; ; COUNT: An optional vector containing the counts to be used in reading the ; variable. 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, ...]. ; 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, ...]. ; ; Notes: The variable will have had to have been previously defined for the file. ; ;------------------------------------------------------------------------------------------; PRO NCDF_File::CopyVarDataTo, varName, destObj, COUNT=count, OFFSET=offset, STRIDE=stride ; Compiler options. Compile_Opt DEFINT32 Compile_Opt STRICTARR Compile_Opt STRICTARRSUBS Compile_Opt LOGICAL_PREDICATE ; Error handling. Catch, theError IF theError NE 0 THEN BEGIN Catch, /CANCEL self.errorLogger -> AddError RETURN ENDIF IF N_Elements(varName) EQ 0 THEN $ Message, 'A variable name or object reference is required.' IF N_Elements(destObj) EQ 0 THEN $ Message, 'A destination NCDF_FILE object is required.' IF ~Obj_Valid(destObj) THEN $ Message, 'The destination NCDF_FILE object is not valid.' IF ~destObj -> GetProperty('Writable') THEN $ Message, 'The destination NCDF_FILE object is not writeable.' ; Make sure the file has been parsed. IF ~self.fileHasBeenParsed THEN self -> ParseFile ; Were you passed the name of a variable or a variable object? CASE Size(varName, /TNAME) OF 'STRING': BEGIN varObj = self.vars -> FindByName(varName, COUNT=strCount, /CASE_SENSITIVE) IF strCount EQ 0 THEN Message, 'Cannot find a variable object with name ' + varName + '.' IF ~Obj_Valid(varObj) THEN Message, 'Invalid object with name "' + varName + '" has been found.' END 'OBJREF': BEGIN varObj = varName END ELSE: Message, 'Input variable name or object is the wrong data type.' ENDCASE ; Gather information. data = varObj ->GetValue(COUNT=count, OFFSET=offset, STRIDE=stride) ; Copy the information to the destination object. destObj -> WriteVarData, varName, data, COUNT=count, OFFSET=offset, STRIDE=stride END ;------------------------------------------------------------------------------------------; ; ; NAME: ; NCDF_File::CopyVarDefTo ; ; Purpose: ; ; Copies a variable definition from this object to another NCDF_FILE object. ; Note that dimension IDs are required to define a variable. This method assumes ; that whatever dimensions are defined for the variable you are copying are already ; defined in the file object you are copying this variable to. ; ; Method Syntax: ; ; obj -> CopyVarDefTo, varName, destObj ; ; Auguments: ; ; varName: The case sensitive name of the variable you wish to copy ; the variable definition from. ; destObj: The object reference of a NCDF_FILE object you wish to copy the ; variable definition to. ; ; Keywords: ; ; CHUNK_DIMENSIONS: Set this keyword equal to a vector containing the chunk dimensions for the variable. ; A new NetCDF variable is chunked by default, using a default chunk value that is ; the full dimension size for limited dimensions, and 1 for unlimited dimensions. ; CHUNK_DIMENSIONS must have the same number of elements as the number of dimensions ; specified by Dim. If the CONTIGUOUS keyword is set, the value of the ; CHUNK_DIMENSIONS keyword is ignored. Available only in IDL 8.0 and higher. ; GZIP: Set this keyword to an integer between zero and nine to specify the level ; of GZIP compression applied to the variable. Lower compression values result ; in faster but less efficient compression. This keyword is ignored if the ; CHUNK_DIMENSIONS keyword is not set. This keyword is ignored if the CONTIGUOUS ; keyword is set. If the GZIP keyword is set, the CONTIGUOUS keyword is ignored. ; You can only use GZIP compression with NCDF 4 files. Available only in ; IDL 8.0 and higher. ; SHUFFLE: Set this keyword to apply the shuffle filter to the variable. If the GZIP ; keyword is not set, this keyword is ignored. The shuffle filter de-interlaces blocks ; of data by reordering individual bytes. Byte shuffling can sometimes ; increase compression density because bytes in the same block positions ; often have similar values, and grouping similar values together often ; leads to more efficient compression. Available only in IDL 8.0 and higher. ; ;------------------------------------------------------------------------------------------; PRO NCDF_File::CopyVarDefTo, varName, destObj, $ CHUNK_DIMENSIONS=chunk_dimensions, $ GZIP=gzip, $ SHUFFLE=shuffle ; Compiler options. Compile_Opt DEFINT32 Compile_Opt STRICTARR Compile_Opt STRICTARRSUBS Compile_Opt LOGICAL_PREDICATE ; Error handling. Catch, theError IF theError NE 0 THEN BEGIN Catch, /CANCEL self.errorLogger -> AddError RETURN ENDIF IF N_Elements(varName) EQ 0 THEN $ Message, 'A variable name or object reference is required.' IF N_Elements(destObj) EQ 0 THEN $ Message, 'A destination NCDF_FILE object is required.' IF ~Obj_Valid(destObj) THEN $ Message, 'The destination NCDF_FILE object is not valid.' IF ~destObj -> GetProperty('Writable') THEN $ Message, 'The destination NCDF_FILE object is not writeable.' ; Make sure the file has been parsed. IF ~self.fileHasBeenParsed THEN self -> ParseFile ; Were you passed the name of a variable or a variable object? CASE Size(varName, /TNAME) OF 'STRING': BEGIN varObj = self.vars -> FindByName(varName, COUNT=varCount, /CASE_SENSITIVE) IF varCount EQ 0 THEN $ Message, 'Cannot find a variable object with name ' + varName + '.' IF ~Obj_Valid(varObj) THEN $ Message, 'Invalid object with name "' + varName + '" has been found.' END 'OBJREF': BEGIN varObj = varName END ELSE: Message, 'Input variable name or object is the wrong data type.' ENDCASE ; Gather information. dimNames = varObj -> GetDimNames(COUNT=dimCount) datatype = varObj -> GetProperty('DATATYPE') ; See if you can match the names of the dimensions with the dimensions ; that are current defined for the destination object. IF dimCount GT 0 THEN BEGIN destDimContainer = destObj -> GetProperty('DIMS') dimIDs = LonArr(dimCount) FOR j=0,dimCount-1 DO BEGIN thisObj = destDimContainer -> FindByName(dimNames[j], COUNT=found) IF found EQ 0 THEN Message, 'Cannot find a dimension named "' + $ dimNames[j] + '" in the destination object.' dimIDs[j] = thisObj -> GetID() ENDFOR ENDIF ; Copy the information to the destination object. IF dimCount EQ 0 THEN BEGIN destObj -> WriteVarDef, varName, DATATYPE=datatype, $ CHUNK_DIMENSIONS=chunk_dimensions, $ GZIP=gzip, $ SHUFFLE=shuffle ENDIF ELSE BEGIN destObj -> WriteVarDef, varName, dimIDs, DATATYPE=datatype, $ CHUNK_DIMENSIONS=chunk_dimensions, $ GZIP=gzip, $ SHUFFLE=shuffle ENDELSE END ;------------------------------------------------------------------------------------------; ; ; NAME: ; NCDF_File::CopyGlobalAttrTo ; ; Purpose: ; ; Copies a global attribute from this object to another NCDF_FILE object. ; ; Method Syntax: ; ; obj -> CopyGlobalAttrTo, attrName, destObj ; ; Auguments: ; ; attrName: The case sensitive name of the global attribute you wish to copy ; to the destination object. ; destObj: The object reference of a NCDF_FILE object you wish to copy the ; variable definition to. ; ; Keywords: ; ; None. ; ;------------------------------------------------------------------------------------------; PRO NCDF_File::CopyGlobalAttrTo, attrName, destObj ; Compiler options. Compile_Opt DEFINT32 Compile_Opt STRICTARR Compile_Opt STRICTARRSUBS Compile_Opt LOGICAL_PREDICATE ; Error handling. Catch, theError IF theError NE 0 THEN BEGIN Catch, /CANCEL self.errorLogger -> AddError RETURN ENDIF IF N_Elements(attrName) EQ 0 THEN $ Message, 'A global attrubute name or object reference is required.' IF N_Elements(destObj) EQ 0 THEN $ Message, 'A destination NCDF_FILE object is required.' IF ~Obj_Valid(destObj) THEN $ Message, 'The destination NCDF_FILE object is not valid.' IF ~destObj -> GetProperty('Writable') THEN $ Message, 'The destination NCDF_FILE object is not writeable.' ; Make sure the file has been parsed. IF ~self.fileHasBeenParsed THEN self -> ParseFile ; Were you passed the name of an attribute or an attribute object? CASE Size(attrName, /TNAME) OF 'STRING': BEGIN attrObj = self.attrs -> FindByName(attrName, COUNT=attrCount, /CASE_SENSITIVE) IF attrCount EQ 0 THEN Message, 'Cannot find a dimension object with name ' + attrName + '.' IF ~Obj_Valid(attrObj) THEN Message, 'Invalid object with name "' + attrName + '" has been found.' END 'OBJREF': BEGIN attrObj = attrName END ELSE: Message, 'Input global attribute name or object is the wrong data type.' ENDCASE ; Gather information. attrValue = attrObj -> GetValue() dataType = attrObj -> GetProperty('DATATYPE') ; Copy the information to the destination object. destObj -> WriteGlobalAttr, attrName, attrValue, DATATYPE=datatype END ;------------------------------------------------------------------------------------------; ; ; NAME: ; NCDF_File::CopyDimTo ; ; Purpose: ; ; Copies a dimension from this object to another NCDF_FILE object. ; ; Method Syntax: ; ; obj -> CopyDimTo, dimName, destObj ; ; Auguments: ; ; dimName: The case sensitive name of the dimension you wish to copy to the ; destination object. ; destObj: The object reference of a NCDF_FILE object you wish to copy the ; dimension to. ; ; Keywords: ; ; None. ; ;------------------------------------------------------------------------------------------; PRO NCDF_File::CopyDimTo, dimName, destObj ; Compiler options. Compile_Opt DEFINT32 Compile_Opt STRICTARR Compile_Opt STRICTARRSUBS Compile_Opt LOGICAL_PREDICATE ; Error handling. Catch, theError IF theError NE 0 THEN BEGIN Catch, /CANCEL self.errorLogger -> AddError RETURN ENDIF IF N_Elements(dimName) EQ 0 THEN $ Message, 'A dimension name or object reference is required.' IF N_Elements(destObj) EQ 0 THEN $ Message, 'A destination NCDF_FILE object is required.' IF ~Obj_Valid(destObj) THEN $ Message, 'The destination NCDF_FILE object is not valid.' IF ~destObj -> GetProperty('Writable') THEN $ Message, 'The destination NCDF_FILE object is not writeable.' ; Make sure the file has been parsed. IF ~self.fileHasBeenParsed THEN self -> ParseFile ; Were you passed the name of a dimension or an dimenison object? CASE Size(dimName, /TNAME) OF 'STRING': BEGIN dimObj = self.dims -> FindByName(dimName, COUNT=dimCount, /CASE_SENSITIVE) IF dimCount EQ 0 THEN $ Message, 'Cannot find a dimension object with name ' + dimName + '.' IF ~Obj_Valid(dimObj) THEN $ Message, 'Invalid object with name "' + dimName + '" has been found.' END 'OBJREF': BEGIN dimObj = dimName END ELSE: Message, 'Input dimension name or object is the wrong data type.' ENDCASE ; Gather information. dimSize = dimObj -> GetValue() unlimited = dimObj -> GetProperty('UNLIMITED') ; Copy the information to the destination object. destObj -> WriteDim, dimName, dimSize, UNLIMITED=unlimited END ;------------------------------------------------------------------------------------------; ; ; NAME: ; NCDF_File::DelGlobalAttr ; ; Purpose: ; ; Deletes a global attribute from this netCDF file. ; ; Method Syntax: ; ; obj -> DelGlobalAttr, attrName ; ; Auguments: ; ; attrName: The case sensitive name of the global attribute you wish to delete. ; ; ;------------------------------------------------------------------------------------------; PRO NCDF_File::DelGlobalAttr, attrName ; Compiler options. Compile_Opt DEFINT32 Compile_Opt STRICTARR Compile_Opt STRICTARRSUBS Compile_Opt LOGICAL_PREDICATE ; Error handling. Catch, theError IF theError NE 0 THEN BEGIN Catch, /CANCEL self.errorLogger -> AddError RETURN ENDIF ; The file has to be writable to delete a global attribute. IF ~self.writable THEN $ Message, 'Cannot delete a global attribute from a READ-ONLY file.' ; Check parameters. IF N_Elements(attrName) EQ 0 THEN Message, 'The attribute name is required.' ; Make sure the attribute is available. Another possible behavior ; would be to do nothing and return instead of throwing an error check = self -> HasGlobalAttr(attrName, OBJECT=attrObj) IF ~check THEN Message, 'The attribute name is not present in the file or is spelled incorrectly: ' + attrName ; Put the file into define mode self -> SetMode, /DEFINE ; Delete the attribute from the file. NCDF_ATTDEL, self.fileID, attrName, /GLOBAL ; We should also remove the attribute object from the container self.attrs -> Remove, attrObj Undefine, attrObj END ;------------------------------------------------------------------------------------------; ; ; NAME: ; NCDF_File::GetDimNames ; ; Purpose: ; ; Returns the names of all the dimensions in the file. ; ; Method Syntax: ; ; dimNames = obj -> GetDimNames(COUNT=dimCount) ; ; Auguments: ; ; None. ; ; Keywords: ; ; COUNT: An output keyword that reports the number of dimension names found. ; ; Return Value: ; ; dimNames: A string array containing the names of the dimensions in the file. ; ;------------------------------------------------------------------------------------------; FUNCTION NCDF_File::GetDimNames, COUNT=dimCount ; Compiler options. Compile_Opt DEFINT32 Compile_Opt STRICTARR Compile_Opt STRICTARRSUBS Compile_Opt LOGICAL_PREDICATE ; Error handling. Catch, theError IF theError NE 0 THEN BEGIN Catch, /CANCEL self.errorLogger -> AddError RETURN, "" ENDIF ; Make sure the file has been parsed. IF ~self.fileHasBeenParsed THEN self -> ParseFile ; Count the number of dimension objects. dimCount = self.dims -> Count() ; If there are no dimensions, return null string. IF dimCount EQ 0 THEN RETURN, "" dimNames = StrArr(dimCount) FOR j=0,dimCount-1 DO BEGIN thisObj = self.dims -> Get(POSITION=j) dimNames[j] = thisObj -> GetName() ENDFOR ; Return a scalar if necessary. IF N_Elements(dimNames) EQ 1 THEN dimNames = dimNames[0] RETURN, dimNames END ;------------------------------------------------------------------------------------------; ; ; NAME: ; NCDF_File::GetDimValue ; ; Purpose: ; ; Returns the value (the size) of a dimension. ; ; Method Syntax: ; ; dimValue = obj -> GetDimValue(dimName) ; ; Auguments: ; ; dimName: The case sensitive name of the dimension you want the value (size) of. ; ; Keywords: ; ; None. ; ; Return Value: ; ; dimValue: An integer that gives the size of the dimension. ; ;------------------------------------------------------------------------------------------; FUNCTION NCDF_File::GetDimValue, dimName ; Compiler options. Compile_Opt DEFINT32 Compile_Opt STRICTARR Compile_Opt STRICTARRSUBS Compile_Opt LOGICAL_PREDICATE ; Error handling. Catch, theError IF theError NE 0 THEN BEGIN Catch, /CANCEL self.errorLogger -> AddError RETURN, "" ENDIF IF N_Elements(dimName) EQ 0 THEN $ Message, 'A dimension name or object reference is required.' ; Make sure the file has been parsed. IF ~self.fileHasBeenParsed THEN self -> ParseFile ; Were you passed the name of a dimension or dimension object? CASE Size(dimName, /TNAME) OF 'STRING': BEGIN dimObj = self.dims -> FindByName(dimName, COUNT=dimCount, /CASE_SENSITIVE) IF dimCount EQ 0 THEN $ Message, 'Cannot find a dimension object with name ' + dimName + '.' IF ~Obj_Valid(dimObj) THEN $ Message, 'Invalid object with name "' + dimName + '" has been found.' END 'OBJREF': BEGIN dimObj = dimName END ELSE: Message, 'Input dimension name or object is the wrong data type.' ENDCASE ; Get the data. dimData = dimObj -> GetValue() RETURN, dimData END ;------------------------------------------------------------------------------------------; ; ; NAME: ; NCDF_File::GetFileID ; ; Purpose: ; ; Returns the netCDF file identifier. ; ; Method Syntax: ; ; fileID = obj -> GetFileID() ; ; Auguments: ; ; None. ; ; Keywords: ; ; None. ; ; Return Value: ; ; fileID: The netCDF file identifier that is required to interact with the file. ; ; ;------------------------------------------------------------------------------------------; FUNCTION NCDF_File::GetFileID RETURN, self.fileID END ;------------------------------------------------------------------------------------------; ; ; NAME: ; NCDF_File::GetGlobalAttrNames ; ; Purpose: ; ; Returns the names of all the global attributes in the file. ; ; Method Syntax: ; ; attrNames = obj -> GetGlobalAttrNames(COUNT=attrCount) ; ; Auguments: ; ; None. ; ; Keywords: ; ; COUNT: An output keyword that reports the number of dimension names found. ; ; Return Value: ; ; attrNames: A string array containing the names of the global attributes in the file. ; ;------------------------------------------------------------------------------------------; FUNCTION NCDF_File::GetGlobalAttrNames, COUNT=attrCount ; Compiler options. Compile_Opt DEFINT32 Compile_Opt STRICTARR Compile_Opt STRICTARRSUBS Compile_Opt LOGICAL_PREDICATE ; Error handling. Catch, theError IF theError NE 0 THEN BEGIN Catch, /CANCEL self.errorLogger -> AddError RETURN, "" ENDIF ; Make sure the file has been parsed. IF ~self.fileHasBeenParsed THEN self -> ParseFile ; Count the number of global attribute objects. attrCount = self.attrs -> Count() ; If there are no global attributes, return null string. IF attrCount EQ 0 THEN RETURN, "" attrNames = StrArr(attrCount) FOR j=0,attrCount-1 DO BEGIN thisObj = self.attrs -> Get(POSITION=j) attrNames[j] = thisObj -> GetName() ENDFOR ; Return a scalar if necessary. IF N_Elements(attrNames) EQ 1 THEN attrNames = attrNames[0] RETURN, attrNames END ;------------------------------------------------------------------------------------------; ; ; NAME: ; NCDF_File::GetGlobalAttrValue ; ; Purpose: ; ; Returns the value of a global attributes in the file. ; ; Method Syntax: ; ; attrValue = obj -> GetGlobalAttrValue(attrName, DATATYPE=datatype) ; ; Auguments: ; ; attrName: The case sensitive name of a global attribute. ; ; Keywords: ; ; DATATYPE: An output keyword that contains the data type of the global attribute. ; ; Return Value: ; ; attrValue: The value of the global attribute. ; ;------------------------------------------------------------------------------------------; FUNCTION NCDF_File::GetGlobalAttrValue, attrName, DATATYPE=datatype ; Compiler options. Compile_Opt DEFINT32 Compile_Opt STRICTARR Compile_Opt STRICTARRSUBS Compile_Opt LOGICAL_PREDICATE ; Error handling. Catch, theError IF theError NE 0 THEN BEGIN Catch, /CANCEL self.errorLogger -> AddError RETURN, "" ENDIF IF N_Elements(attrName) EQ 0 THEN $ Message, 'A global attribute name or object reference is required.' ; Make sure the file has been parsed. IF ~self.fileHasBeenParsed THEN self -> ParseFile ; Were you passed the name of an attribute or an attribute object? CASE Size(attrName, /TNAME) OF 'STRING': BEGIN attrObj = self.attrs -> FindByName(attrName, COUNT=attrCount, /CASE_SENSITIVE) IF attrCount EQ 0 THEN $ Message, 'Cannot find an attribute object with name ' + attrName + '.' IF ~Obj_Valid(attrObj) THEN $ Message, 'Invalid object with name "' + attrName + '" has been found.' END 'OBJREF': BEGIN attrObj = attrName END ELSE: Message, 'Input attribute name or object is the wrong data type.' ENDCASE ; Get the data. attrData = attrObj -> GetValue(DATATYPE=datatype) RETURN, attrData END ;------------------------------------------------------------------------------------------; ; ; NAME: ; NCDF_File::GetProperty ; ; Purpose: ; ; Returns various properties of the object via output keyword parameters. ; ; Method Syntax: ; ; obj -> GetProperty, .... ; ; Auguments: ; ; None. ; ; Keywords: ; ; ALL: If set, return all properties of the object in a structure variable. ; ATTRNAMES: This output variable returns all the global attribute names. ; DEFINE: This output variable returns a 1 if the file is in DEFINE mode. ; DIMNAMES: This output variable returns all the dimension names. ; ERRORLOGGER: This output variable returns the errorlogger object. ; FILEID: This output variable returns the netCDF file identifier. ; FILENAME: This output variable returns the name of the netCDF file. ; FILEHASBEENPARSED: This output variable returns a 1 if the file has been parsed. ; N_ATTRS: This output variable returns the number of global attributes in ; the file. ; N_DIMS: This output variable returns the number of dimensions in the file. ; N_VARS: This output variable returns the number of variables in the file. ; UNLIMITED: This output variable returns a vector of 0s and 1s, one element ; for each dimension, indicating if the dimension is unlimited or not. ; VARNAMES: This output variable returns the names of variables in the file. ; WRITEABLE: This output variable returns a 1 if the file is writable. ; ;------------------------------------------------------------------------------------------; PRO NCDF_File::GetProperty, $ ALL=all, $ ATTRNAMES=attrnames, $ DEFINE=define, $ DIMNAMES=dim_names, $ DIMENSIONS=dimensions, $ ERRORLOGGER=errorLogger, $ FILEID=fileID, $ FILENAME=filename, $ FILEHASBEENPARSED=fileHasBeenParsed, $ N_DIMS=n_dims, $ N_VARS=n_vars, $ N_ATTRS=n_attrs, $ UNLIMITED=unlimited, $ VARNAMES=varnames, $ WRITABLE=writable ; Compiler options. Compile_Opt DEFINT32 Compile_Opt STRICTARR Compile_Opt STRICTARRSUBS Compile_Opt LOGICAL_PREDICATE ; Error handling. Catch, theError IF theError NE 0 THEN BEGIN Catch, /CANCEL self.errorLogger -> AddError RETURN ENDIF ; Make sure the file has been parsed. IF ~self.fileHasBeenParsed THEN self -> ParseFile ; Easily returned information. errorLogger = self.errorLogger filename = self.filename fileID = self.fileID fileHasBeenParsed = self.fileHasBeenParsed define = self.define writable = self.writable ; What's in the file? info = NCDF_Inquire(self.fileID) n_dims = info.ndims n_vars = info.nvars n_attrs = info.ngatts ; Get the attribute names, if needed. IF Arg_Present(attrnames) || Arg_Present(all) THEN BEGIN IF n_attrs EQ 0 THEN BEGIN attrnames = "" ENDIF ELSE BEGIN attrnames = StrArr(n_attrs) FOR j=0,n_attrs-1 DO BEGIN thisAttr = self.attrs -> Get(POSITION=j) attrnames[j] = thisAttr -> GetProperty('NAME') ENDFOR ENDELSE ENDIF ; Get the dimension names, and dimension IDs, if needed. IF Arg_Present(dimNames) || Arg_Present(dimensions) || Arg_Present(all) THEN BEGIN IF n_dims EQ 0 THEN BEGIN dimNames = "" dimensions = 0 unlimited = 0 ENDIF ELSE BEGIN dimNames = StrArr(n_dims) dimensions = LonArr(n_dims) unlimited = BytArr(n_dims) FOR j=0, n_dims-1 DO BEGIN dimsObj = self.dims -> Get(POSITION=j) dimNames[j] = dimsObj -> GetProperty('NAME') dimensions[j] = dimsObj -> GetProperty('SIZE') unlimited[j] = dimsObj -> GetProperty('UNLIMITED') ENDFOR ENDELSE ENDIF ; Get the variable names, if needed. IF Arg_Present(varNames) || Arg_Present(all) THEN BEGIN IF n_vars EQ 0 THEN BEGIN varNames = "" ENDIF ELSE BEGIN varNames = StrArr(n_vars) FOR j=0,n_vars-1 DO BEGIN thisVar = self.vars -> Get(POSITION=j) varNames[j] = thisVar -> GetProperty('NAME') ENDFOR ENDELSE ENDIF IF Arg_Present(all) THEN BEGIN all = { ATTRNAMES:attrnames, $ DEFINE:define, $ DIMNAMES:dim_names, $ DIMENSIONS:dimensions, $ ERRORLOGGER:errorLogger, $ FILEID:fileID, $ FILENAME:filename, $ FILEHASBEENPARSED:fileHasBeenParsed, $ N_DIMS:n_dims, $ N_VARS:n_vars, $ N_ATTRS:n_attrs, $ UNLIMITED:unlimited, $ VARNAMES:varnames, $ WRITABLE:writable } ENDIF END ;------------------------------------------------------------------------------------------; ; ; NAME: ; NCDF_File::GetProperty ; ; Purpose: ; ; Returns various properties of the object one at a time. This is a shorthand and ; generic way to get the value of an object's "properties", which are defined as ; the IDL variables in the object's class structure. ; ; Method Syntax: ; ; propertyValue = obj -> GetProperty(thisProperty) ; ; Auguments: ; ; thisProperty: A string variable that is equivalent to a field in the object's ; class structure. See the *__DEFINE procedure for which properties ; can be returned. The property is case insensitive. ; ; Keywords: ; ; None. ; ; Return Value: ; ; propertyValue: The value of a particular object property. Note that pointer ; properties will return the variable the pointer points to. ; ;------------------------------------------------------------------------------------------; FUNCTION NCDF_File::GetProperty, thisProperty ; Compiler options. Compile_Opt DEFINT32 Compile_Opt STRICTARR Compile_Opt STRICTARRSUBS Compile_Opt LOGICAL_PREDICATE ; Error handling. Catch, theError IF theError NE 0 THEN BEGIN Catch, /CANCEL self.errorLogger -> AddError RETURN, 0 ENDIF ; Get the self structure as a structure, rather than as an object. Call_Procedure, StrLowCase(Obj_Class(self)) + '__define', classStruct ; Find the property in this class structure. index = Where(StrPos(Tag_Names(classStruct), StrUpCase(thisProperty)) EQ 0, count) index = index[0] ; What happened? CASE count OF 0: Message, 'Property ' + StrUpCase(thisProperty) + ' could not be found.' 1: propertyValue = self.(index) ELSE: Message, 'Ambiguous property. Use more characters to specify it.' ENDCASE ; If this is a pointer, you want the thing pointed to. IF Size(propertyValue, /TNAME) EQ 'POINTER' THEN propertyValue = *propertyValue RETURN, propertyValue END ;------------------------------------------------------------------------------------------; ; ; NAME: ; NCDF_File::GetVarAttrNames ; ; Purpose: ; ; Returns the names of variable attributes in the file. ; ; Method Syntax: ; ; attrNames = obj -> GetVarAttrNames(varName, COUNT=varAttrCount) ; ; Auguments: ; ; varName: The case sensitive name of the variable you want the attributes of. ; ; Keywords: ; ; COUNT: The number of variable attributes found. ; ; Return Value: ; ; attrNames: A string array containing the names of the variable attributes. ; ;------------------------------------------------------------------------------------------; FUNCTION NCDF_File::GetVarAttrNames, varName, COUNT=varAttrCount ; Compiler options. Compile_Opt DEFINT32 Compile_Opt STRICTARR Compile_Opt STRICTARRSUBS Compile_Opt LOGICAL_PREDICATE ; Error handling. Catch, theError IF theError NE 0 THEN BEGIN Catch, /CANCEL self.errorLogger -> AddError RETURN, "" ENDIF IF N_Elements(varName) EQ 0 THEN $ Message, 'A variable name or object reference is required.' ; Make sure the file has been parsed. IF ~self.fileHasBeenParsed THEN self -> ParseFile ; Were you passed the name of a variable or a variable object? CASE Size(varName, /TNAME) OF 'STRING': BEGIN varObj = self.vars -> FindByName(varName, COUNT=varCount, /CASE_SENSITIVE) IF varCount EQ 0 THEN $ Message, 'Cannot find a variable object with name ' + varName + '.' IF ~Obj_Valid(varObj) THEN $ Message, 'Invalid object with name "' + varName + '" has been found.' END 'OBJREF': BEGIN varObj = varName END ELSE: Message, 'Input variable name or object is the wrong data type.' ENDCASE ; Get the variable attribute names. attrNames = varObj -> GetAttrNames(COUNT=varAttrCount) RETURN, attrNames END ;------------------------------------------------------------------------------------------; ; ; NAME: ; NCDF_File::GetVarAttrValue ; ; Purpose: ; ; Returns the value of a variable attribute in the file. ; ; Method Syntax: ; ; attrValue = obj -> GetVarAttrValue(varName, attrName, DATATYPE=datatype) ; ; Auguments: ; ; varName: The case sensitive name of a variable whose attribute you want ; to obtain. ; attrName: The case sensitive name of a global attribute. ; ; Keywords: ; ; DATATYPE: An output keyword that contains the data type of the attribute. ; ; Return Value: ; ; attrValue: The value of the variable attribute. ; ;------------------------------------------------------------------------------------------; FUNCTION NCDF_File::GetVarAttrValue, varName, attrName, DATATYPE=datatype ; Compiler options. Compile_Opt DEFINT32 Compile_Opt STRICTARR Compile_Opt STRICTARRSUBS Compile_Opt LOGICAL_PREDICATE ; Error handling. Catch, theError IF theError NE 0 THEN BEGIN Catch, /CANCEL self.errorLogger -> AddError RETURN, "" ENDIF IF N_Elements(varName) EQ 0 THEN $ Message, 'A variable name or object reference is required.' IF N_Elements(attrName) EQ 0 THEN $ Message, 'An attribute name or object reference is required.' ; Make sure the file has been parsed. IF ~self.fileHasBeenParsed THEN self -> ParseFile ; Were you passed the name of a variable or a variable object? CASE Size(varName, /TNAME) OF 'STRING': BEGIN varObj = self.vars -> FindByName(varName, COUNT=varCount, /CASE_SENSITIVE) IF varCount EQ 0 THEN $ Message, 'Cannot find a variable object with name ' + varName + '.' IF ~Obj_Valid(varObj) THEN $ Message, 'Invalid object with name "' + varName + '" has been found.' END 'OBJREF': BEGIN varObj = varName END ELSE: Message, 'Input variable name or object is the wrong data type.' ENDCASE ; Get and return the variable's attribute value. attrValue = varObj -> GetAttrValue(attrName, DATATYPE=datatype) RETURN, attrValue END ;------------------------------------------------------------------------------------------; ; ; NAME: ; NCDF_File::GetVarNames ; ; Purpose: ; ; Returns the names of the variables in the file. ; ; Method Syntax: ; ; varNames = obj -> GetVarNames(COUNT=varCount) ; ; Auguments: ; ; None. ; ; Keywords: ; ; COUNT: The number of variables found. ; ; Return Value: ; ; varNames: A string array containing the names of the variables in the file. ; ;------------------------------------------------------------------------------------------; FUNCTION NCDF_File::GetVarNames, COUNT=varCount ; Compiler options. Compile_Opt DEFINT32 Compile_Opt STRICTARR Compile_Opt STRICTARRSUBS Compile_Opt LOGICAL_PREDICATE ; Error handling. Catch, theError IF theError NE 0 THEN BEGIN Catch, /CANCEL self.errorLogger -> AddError RETURN, "" ENDIF ; Make sure the file has been parsed. IF ~self.fileHasBeenParsed THEN self -> ParseFile ; Count the number of global attribute objects. varCount = self.vars -> Count() ; If there are no global attributes, return null string. IF varCount EQ 0 THEN RETURN, "" varNames = StrArr(varCount) FOR j=0,varCount-1 DO BEGIN thisObj = self.vars -> Get(POSITION=j) varNames[j] = thisObj -> GetName() ENDFOR ; Return a scalar if necessary. IF varCount EQ 1 THEN varNames = varNames[0] RETURN, varNames END ;------------------------------------------------------------------------------------------; ; ; NAME: ; NCDF_File::GetVarData ; ; Purpose: ; ; Returns the variable data from the file. ; ; Method Syntax: ; ; data = obj -> GetVarData(varName, COUNT=count, OFFSET=offset, STRIDE=stride) ; ; Auguments: ; ; varName: The case sensitive name of a variable whose data you want to obtain. ; ; Input Keywords: ; ; COUNT: An optional vector containing the counts to be used in reading the ; variable. 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, ...]. ; 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, ...]. ; ; Output Keywords: ; ; ADD_OFFSET: The add_offset value for the variable, if there is one. ; ; DATATYPE: The data type of the variable, before the scale and offset are applied. ; The same as what comes back from datatype = Size(rawVariable, /TNAME). ; ; 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. ; ; SCALE_FACTOR: The scale factor for the variable, if there is one. ; ; Return Value: ; ; data: The data obtained from the variable. If there is an ADD_OFFSET and ; SCALE_FACTOR attribute for this variable, the returned data is scaled ; and offset before returning. The "missing" or "fill value" is not ; changed by scaling and offsetting. ; ;------------------------------------------------------------------------------------------; FUNCTION NCDF_File::GetVarData, varName, $ ADD_OFFSET=add_offset, $ COUNT=count, $ DATATYPE=datatype, $ FILLVALUE=fillValue, $ OFFSET=offset, $ SCALE_FACTOR=scale_factor, $ STRIDE=stride, $ MISSINGINDICES=missingIndices ; Compiler options. Compile_Opt DEFINT32 Compile_Opt STRICTARR Compile_Opt STRICTARRSUBS Compile_Opt LOGICAL_PREDICATE ; Error handling. Catch, theError IF theError NE 0 THEN BEGIN Catch, /CANCEL self.errorLogger -> AddError RETURN, "" ENDIF IF N_Elements(varName) EQ 0 THEN $ Message, 'A variable name or variable object reference is required.' ; Make sure the file has been parsed. IF ~self.fileHasBeenParsed THEN self -> ParseFile ; Were you passed the name of a variable or a variable object? CASE Size(varName, /TNAME) OF 'STRING': BEGIN varObj = self.vars -> FindByName(varName, COUNT=varCount, /CASE_SENSITIVE) IF varCount EQ 0 THEN $ Message, 'Cannot find a variable object with name ' + varName + '.' IF ~Obj_Valid(varObj) THEN $ Message, 'Invalid object with name "' + varName + '" has been found.' END 'OBJREF': BEGIN varObj = varName END ELSE: Message, 'Input variable name or object is the wrong data type.' ENDCASE ; Get the data. varData = varObj -> GetValue( $ ADD_OFFSET=add_offset, $ COUNT=count, $ DATATYPE=datatype, $ FILLVALUE=fillvalue, $ OFFSET=offset, $ MISSINGINDICES=missingindices, $ SCALE_FACTOR=scale_factor, $ STRIDE=stride) RETURN, varData END ;------------------------------------------------------------------------------------------; ; ; NAME: ; NCDF_File::GetVarInfo ; ; Purpose: ; ; Returns information about a specified variable from the file. ; ; Method Syntax: ; ; info = obj -> GetVarInfo(varName) ; ; Auguments: ; ; varName: The case sensitive name of a variable whose information you want to obtain. ; ; Keywords: ; ; None. ; ; Return Value: ; ; info: A structure contains the following fields. ; ; info = { dims: varObj -> GetDimSizes(), $ ; dimNames: varObj -> GetDimNames(), $ ; attrNames: varObj -> GetAttrNames(), $ ; dataType: varObj -> GetProperty('datatype'), $ ; nattrs: varObj -> GetProperty('nattrs'), $ ; ndims: varObj -> GetProperty('ndims') } ; ; In addition, the structure will contain the fields "scale_factor," "add_offset," ; and "_FillValue" if these attributes are available for the variable. ; ;------------------------------------------------------------------------------------------; FUNCTION NCDF_File::GetVarInfo, varName ; Compiler options. Compile_Opt DEFINT32 Compile_Opt STRICTARR Compile_Opt STRICTARRSUBS Compile_Opt LOGICAL_PREDICATE ; Error handling. Catch, theError IF theError NE 0 THEN BEGIN Catch, /CANCEL self.errorLogger -> AddError RETURN, "" ENDIF IF N_Elements(varName) EQ 0 THEN $ Message, 'A variable name or variable object reference is required.' ; Make sure the file has been parsed. IF ~self.fileHasBeenParsed THEN self -> ParseFile ; Were you passed the name of a variable or a variable object? CASE Size(varName, /TNAME) OF 'STRING': BEGIN varObj = self.vars -> FindByName(varName, COUNT=varCount, /CASE_SENSITIVE) IF varCount EQ 0 THEN $ Message, 'Cannot find a variable object with name ' + varName + '.' IF ~Obj_Valid(varObj) THEN $ Message, 'Invalid object with name "' + varName + '" has been found.' END 'OBJREF': BEGIN varObj = varName END ELSE: Message, 'Input variable name or object is the wrong data type.' ENDCASE ; Get the information you need. hasVar = self -> HasVar(varName, OBJECT=varObj) IF ~hasVar THEN Message, 'Cannot find a variable with the name ' + varName + '.' RETURN, varObj -> GetInfo() END ;------------------------------------------------------------------------------------------; ; ; NAME: ; NCDF_File::HasGlobalAttr ; ; Purpose: ; ; Indicates, by returning a 1, that this particular global attribute is found in the ; file. If not found, this function returns a 0. ; ; Method Syntax: ; ; isFound = obj -> HasGlobalAttr(attrName, OBJECT=object) ; ; Auguments: ; ; attrName: The case sensitive name of a global attribute. ; ; Keywords: ; ; OBJECT: If the attribute exists, this keyword returns the global attribute's ; object reference. Such a reference can be used in place of the ; global attribute's name in most methods. ; ; Return Value: ; ; isFound: If an attribute with this name is found, this variable is set to ; 1. It is set to 0 otherwise. ; ;------------------------------------------------------------------------------------------; FUNCTION NCDF_File::HasGlobalAttr, attrName, OBJECT=object ; Compiler options. Compile_Opt DEFINT32 Compile_Opt STRICTARR Compile_Opt STRICTARRSUBS Compile_Opt LOGICAL_PREDICATE ; Error handling. Catch, theError IF theError NE 0 THEN BEGIN Catch, /CANCEL self.errorLogger -> AddError RETURN, "" ENDIF ; Can you find a global attribute object with this name? object = self.attrs -> FindByName(attrName, COUNT=count) IF count GT 0 THEN RETURN, 1 ELSE RETURN, 0 END ;------------------------------------------------------------------------------------------; ; ; NAME: ; NCDF_File::HasDim ; ; Purpose: ; ; Indicates, by returning a 1, that this particular dimension is found in the ; file. If not found, this function returns a 0. ; ; Method Syntax: ; ; isFound = obj -> HasDim(dimName, OBJECT=object) ; ; Auguments: ; ; dimName: The case sensitive name of a dimension. ; ; Keywords: ; ; OBJECT: If the dimension exists, this keyword returns the dimension's ; object reference. Such a reference can be used in place of the ; dimension's name in most methods. ; ; Return Value: ; ; isFound: If a dimension with this name is found, this variable is set to ; 1. It is set to 0 otherwise. ; ;------------------------------------------------------------------------------------------; FUNCTION NCDF_File::HasDim, dimName, OBJECT=object ; Compiler options. Compile_Opt DEFINT32 Compile_Opt STRICTARR Compile_Opt STRICTARRSUBS Compile_Opt LOGICAL_PREDICATE ; Error handling. Catch, theError IF theError NE 0 THEN BEGIN Catch, /CANCEL self.errorLogger -> AddError RETURN, "" ENDIF ; Can you find a dimension object with this name? object = self.dims -> FindByName(dimName, COUNT=count) IF count GT 0 THEN RETURN, 1 ELSE RETURN, 0 END ;------------------------------------------------------------------------------------------; ; ; NAME: ; NCDF_File::HasVar ; ; Purpose: ; ; Indicates, by returning a 1, that this particular variable is found in the ; file. If not found, this function returns a 0. ; ; Method Syntax: ; ; isFound = obj -> HasVar(varName, OBJECT=object) ; ; Auguments: ; ; varName: The case sensitive name of a variable. ; ; Keywords: ; ; OBJECT: If the variable exists, this keyword returns the variable's ; object reference. Such a reference can be used in place of the ; variable's name in most methods. ; ; Return Value: ; ; isFound: If a variable with this name is found, this variable is set to ; 1. It is set to 0 otherwise. ; ;------------------------------------------------------------------------------------------; FUNCTION NCDF_File::HasVar, varName, OBJECT=object ; Compiler options. Compile_Opt DEFINT32 Compile_Opt STRICTARR Compile_Opt STRICTARRSUBS Compile_Opt LOGICAL_PREDICATE ; Error handling. Catch, theError IF theError NE 0 THEN BEGIN Catch, /CANCEL self.errorLogger -> AddError RETURN, "" ENDIF ; Can you find a variable object with this name? object = self.vars -> FindByName(varName, COUNT=count) IF count GT 0 THEN RETURN, 1 ELSE RETURN, 0 END ;------------------------------------------------------------------------------------------; ; ; NAME: ; NCDF_File::HasVarAttr ; ; Purpose: ; ; Indicates, by returning a 1, that this particular variable attribute is found in the ; ; file. If not found, this function returns a 0. ; ; Method Syntax: ; ; isFound = obj -> HasVarAttr(varName, attrName, OBJECT=object) ; ; Auguments: ; ; varName: The case sensitive name of a variable whose attribute we want to find. ; attrName: The case sensitive name of a variable attribute. ; ; Keywords: ; ; OBJECT: If the variable exists, this keyword returns the variable attribute's ; object reference. Such a reference can be used in place of the ; variable attribute's name in most methods. ; ; Return Value: ; ; isFound: If a variable attribute with this name is found, this variable ; is set to 1. It is set to 0 otherwise. ; ;------------------------------------------------------------------------------------------; FUNCTION NCDF_File::HasVarAttr, varName, varAttrName, OBJECT=object ; Compiler options. Compile_Opt DEFINT32 Compile_Opt STRICTARR Compile_Opt STRICTARRSUBS Compile_Opt LOGICAL_PREDICATE ; Error handling. Catch, theError IF theError NE 0 THEN BEGIN Catch, /CANCEL self.errorLogger -> AddError RETURN, "" ENDIF ; Can you find a variable object with this name? varObj = self.vars -> FindByName(varName, COUNT=count) IF count EQ 0 THEN Message, 'Cannot find a variable with name "' + varName + '".' ; Can you find a variable attribute with this name. hasAttr = varObj[0] -> HasAttr(varAttrName, OBJECT=object) RETURN, hasAttr END ;------------------------------------------------------------------------------------------; ; ; NAME: ; NCDF_File::PrintFileInfo ; ; Purpose: ; ; Prints file information out to the IDL console window or, optionally, to a file. ; ; Method Syntax: ; ; obj -> PrintFileInfo, outputFile ; ; Auguments: ; ; outputFile: An optional filename. If present, the output is written to this ; file instead of to the console. ; ; Keywords: ; ; None. ; ;------------------------------------------------------------------------------------------; PRO NCDF_File::PrintFileInfo, outputFile ; Compiler options. Compile_Opt DEFINT32 Compile_Opt STRICTARR Compile_Opt STRICTARRSUBS Compile_Opt LOGICAL_PREDICATE ; Error handling. Catch, theError IF theError NE 0 THEN BEGIN Catch, /CANCEL self.errorLogger -> AddError IF lun NE -1 THEN Free_Lun, lun RETURN ENDIF ; Make sure the file has been parsed. IF ~self.fileHasBeenParsed THEN self -> ParseFile ; Are we writing to a file or to standard output? IF N_Elements(outputFile) NE 0 THEN BEGIN OpenW, lun, outputFile, /GET_LUN ENDIF ELSE lun = -1 ; Gather information. numGAttrs = self.attrs -> Count() numDims = self.dims -> Count() numVars = self.vars -> Count() PrintF, lun, 'File Information: ', self.filename PrintF, lun, 'Number of Global Attributes: ', StrTrim(numGAttrs,2) PrintF, lun, 'Number of Dimensions: ', StrTrim(numDims,2) PrintF, lun, 'Number of Variables: ', StrTrim(numVars,2) PrintF, lun, '' PrintF, lun, 'Global Attributes: ' FOR j=0,numGAttrs-1 DO BEGIN attrObj = self.attrs -> Get(POSITION=j) name = attrObj -> GetProperty('NAME') PrintF, lun, ' ', name ENDFOR PrintF, lun, '' PrintF, lun, 'Dimensions: ' FOR j=0,numDims-1 DO BEGIN dimsObj = self.dims -> Get(POSITION=j) name = dimsObj -> GetProperty('NAME') size = dimsObj -> GetProperty('SIZE') unlimited = dimsObj -> GetProperty('UNLIMITED') IF unlimited EQ 1 THEN unlimited = ' (unlimited)' ELSE unlimited = "" PrintF, lun, ' ', name, ': ', StrTrim(size,2), unlimited ENDFOR PrintF, lun, '' PrintF, lun, 'Variables: ' FOR j=0,numVars-1 DO BEGIN varObj = self.vars -> Get(POSITION=j) name = varObj -> GetProperty('NAME') attrs = varObj -> GetProperty('ATTRS') PrintF, lun, ' ', name nattrs = attrs -> Count() FOR k=0,nattrs-1 DO BEGIN attrObj = attrs -> Get(POSITION=k) attrname = attrObj -> GetProperty('NAME') PrintF, lun, ' ', attrname ENDFOR ENDFOR PrintF, lun, '' ; Clean up. IF lun NE -1 THEN Free_Lun, lun END ;------------------------------------------------------------------------------------------; ; ; NAME: ; NCDF_File::ParseFile ; ; Purpose: ; ; Parses the file and creates the appropriate file objects for all subsequent ; object operations. ; ; Method Syntax: ; ; obj -> ParseFile ; ; Auguments: ; ; None. ; ; Keywords: ; ; None. ; ;------------------------------------------------------------------------------------------; PRO NCDF_File::ParseFile ; Compiler options. Compile_Opt DEFINT32 Compile_Opt STRICTARR Compile_Opt STRICTARRSUBS Compile_Opt LOGICAL_PREDICATE ; Error handling. Catch, theError IF theError NE 0 THEN BEGIN Catch, /CANCEL self.errorLogger -> AddError RETURN ENDIF ; Only need to parse this file once. IF self.fileHasBeenParsed THEN RETURN ; Purge all containers and destroy all objects in the container. theseObjects = self.attrs -> Get(/ALL, COUNT=objCount) self.attrs -> Remove, /ALL FOR j=0,objCount-1 DO Obj_Destroy, theseObjects[j] theseObjects = self.dims -> Get(/ALL, COUNT=objCount) self.dims -> Remove, /ALL FOR j=0,objCount-1 DO Obj_Destroy, theseObjects[j] theseObjects = self.vars -> Get(/ALL, COUNT=objCount) self.vars -> Remove, /ALL FOR j=0,objCount-1 DO Obj_Destroy, theseObjects[j] ; Gather information about the file. info = NCDF_Inquire(self.fileID) ; Parse the global variables. num_attr = info.ngatts IF num_attr GT 0 THEN BEGIN FOR j=0,num_attr-1 DO BEGIN attribute_name = NCDF_AttName(self.fileID, j, /GLOBAL) NCDF_AttGet, self.fileID, attribute_name, theAttribute, /GLOBAL self -> CreateAttrObj, attribute_name ENDFOR ENDIF ; Parse the dimensions. num_dims = info.ndims IF num_dims GT 0 THEN BEGIN FOR j=0,num_dims-1 DO BEGIN NCDF_DIMINQ, self.fileID, j, dimension_name, dimension_size self -> CreateDimObj, dimension_name ENDFOR ENDIF ; Parse the variables. num_vars = info.nvars IF num_vars GT 0 THEN BEGIN FOR j=0,num_vars-1 DO BEGIN varinfo = NCDF_VarInq(self.fileID, j) self -> CreateVarObj, varinfo.name ENDFOR ENDIF self.fileHasBeenParsed = 1 END ;------------------------------------------------------------------------------------------; ; ; NAME: ; NCDF_File::SetMode ; ; Purpose: ; ; Sets the file mode to DEFINE or DATA, as needed. Only one of the two keywords ; should be used. ; ; Method Syntax: ; ; obj -> SetMode, DATA=data , DEFINE=define ; ; Auguments: ; ; None. ; ; Keywords: ; ; DATA: If this keyword is set, the file is put into DATA mode. ; DEFINE: If this keyword is set, the file is put into DEFINE mode. ; ;------------------------------------------------------------------------------------------; PRO NCDF_File::SetMode, DATA=data, DEFINE=define ; Compiler options. Compile_Opt DEFINT32 Compile_Opt STRICTARR Compile_Opt STRICTARRSUBS Compile_Opt LOGICAL_PREDICATE ; Error handling. CATCH, theError IF theError NE 0 THEN BEGIN CATCH, /CANCEL self.errorLogger -> AddError RETURN ENDIF ; Can't have both keywords set. IF Keyword_Set(define) && Keyword_Set(data) THEN BEGIN Message, 'You cannot set both DEFINE and DATA keywords.' ENDIF ; If nothing is set, put us into DEFINE mode. IF ~Keyword_Set(define) && ~Keyword_Set(data) THEN define = 1 ; Set define mode. IF Keyword_Set(define) THEN BEGIN data = 0 IF self.define NE 1 THEN BEGIN NCDF_Control, self.fileID, /REDEF self.define = 1 ENDIF ENDIF ; Set data mode. IF Keyword_Set(data) THEN BEGIN IF self.define NE 0 THEN BEGIN NCDF_Control, self.fileID, /ENDEF self.define = 0 ENDIF ENDIF END ;------------------------------------------------------------------------------------------; ; ; NAME: ; NCDF_File::Sync ; ; Purpose: ; ; Writes data in memory to the disk. ; ; Method Syntax: ; ; obj -> Sync ; ; Auguments: ; ; None. ; ; Keywords: ; ; None. ; ;------------------------------------------------------------------------------------------; PRO NCDF_File::Sync ; Compiler options. Compile_Opt DEFINT32 Compile_Opt STRICTARR Compile_Opt STRICTARRSUBS Compile_Opt LOGICAL_PREDICATE ; Error handling. CATCH, theError IF theError NE 0 THEN BEGIN CATCH, /CANCEL self.errorLogger -> AddError RETURN ENDIF ; The file has to be writable to sync the file. IF ~self.writable THEN Message, 'Cannot sync a READ-ONLY file.' ; The file has to be in DATA mode. self -> SetMode, /DATA ; Sync the file. NCDF_Control, self.fileID, /SYNC END ;------------------------------------------------------------------------------------------; ; ; NAME: ; NCDF_File::WriteVarData ; ; Purpose: ; ; Writes variable data into this netCDF file. It assumes the variable has previously ; been defined for this file. ; ; Method Syntax: ; ; obj -> WriteVarData, varName, data, COUNT=count, OFFSET=offset, STRIDE=stride ; ; Auguments: ; ; varName: The case sensitive name of the variable you wish to write data to. ; data: The data to be written into this variable. ; ; Keywords: ; ; COUNT: An optional vector containing the counts to be used in reading the ; variable. 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, ...]. ; 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, ...]. ; ; Notes: The variable will have had to have been previously defined for the file. ; ;------------------------------------------------------------------------------------------; PRO NCDF_File::WriteVarData, varName, data, COUNT=count, OFFSET=offset, STRIDE=stride ; Compiler options. Compile_Opt DEFINT32 Compile_Opt STRICTARR Compile_Opt STRICTARRSUBS Compile_Opt LOGICAL_PREDICATE ; Error handling. Catch, theError IF theError NE 0 THEN BEGIN Catch, /CANCEL self.errorLogger -> AddError RETURN ENDIF ; The file has to be writable to add a variable definition. IF ~self.writable THEN Message, 'Cannot add a variable definition to a READ-ONLY file.' ; Check parameters. IF N_Elements(varName) EQ 0 THEN $ Message, 'A variable name or variable object reference is required.' IF N_Elements(data) EQ 0 THEN $ Message, 'Data is required to write the variable into the file.' ; Were you passed the name of a variable or a variable object? CASE Size(varName, /TNAME) OF 'STRING': BEGIN varObj = self.vars -> FindByName(varName, COUNT=varCount, /CASE_SENSITIVE) IF varCount EQ 0 THEN $ Message, 'Cannot find a variable object with name ' + varName + '.' IF ~Obj_Valid(varObj) THEN $ Message, 'Invalid object with name ' + varName + ' has been found.' END 'OBJREF': BEGIN varObj = varName END ELSE: Message, 'Input variable name or object is the wrong data type.' ENDCASE ; Get the variable ID. varName = varObj -> GetName() ; Put the file into data mode. self -> SetMode, /DATA ; Write the data to the file. NCDF_VarPut, self.fileID, varName, data, COUNT=count, OFFSET=offset, STRIDE=stride END ;------------------------------------------------------------------------------------------; ; ; NAME: ; NCDF_File::WriteVarDef ; ; Purpose: ; ; Writes the variable definition into this netCDF file. ; ; Method Syntax: ; ; obj -> WriteVarDef, varName, dimNames, DATATYPE=datatype, OBJECT=object ; ; Auguments: ; ; varName: The case sensitive name of the variable you wish to define. ; dimNames: The names of dimensions that have been previously defined in the ; file and that are associated with this variable. A string array. ; If dimNames is missing, then the variable is assumed to be a scalar. ; ; Keywords: ; ; CHUNK_DIMENSIONS: Set this keyword equal to a vector containing the chunk dimensions for the variable. ; A new NetCDF variable is chunked by default, using a default chunk value that is ; the full dimension size for limited dimensions, and 1 for unlimited dimensions. ; CHUNK_DIMENSIONS must have the same number of elements as the number of dimensions ; specified by Dim. If the CONTIGUOUS keyword is set, the value of the ; CHUNK_DIMENSIONS keyword is ignored. Available only in IDL 8.0 and higher. ; CONTIGUOUS: Set this keyword to store a NetCDF variable as a single array in a file. ; Contiguous storage works well for smaller variables such as coordinate variables. ; Contiguous storage works only for fixed-sized datasets (those without any unlimited ; dimensions). You can’t use compression or other filters with contiguous data. ; If the CONTIGUOUS keyword is set, the value of the CHUNK_DIMENSIONS keyword is ignored. ; The CONTIGUOUS keyword is ignored if the GZIP keyword is set. Available only in ; IDL 8.0 and higher. ; DATATYPE: The netCDF data type of the variable. This is REQUIRED. The appropriate ; netCDF data types are: "BYTE", "CHAR", "SHORT", "LONG" "FLOAT", or ; "DOUBLE". In IDL 8.1, the data types "STRING", "UBYTE", UINT64", ; "ULONG" and "USHORT" were added. ; GZIP: Set this keyword to an integer between zero and nine to specify the level ; of GZIP compression applied to the variable. Lower compression values result ; in faster but less efficient compression. This keyword is ignored if the ; CHUNK_DIMENSIONS keyword is not set. This keyword is ignored if the CONTIGUOUS ; keyword is set. If the GZIP keyword is set, the CONTIGUOUS keyword is ignored. ; You can only use GZIP compression with NCDF 4 files. Available only in ; IDL 8.0 and higher. ; ; OBJECT: If a variable is successfully defined, this keyword will return the ; object reference to that variable. ; SHUFFLE: Set this keyword to apply the shuffle filter to the variable. If the GZIP ; keyword is not set, this keyword is ignored. The shuffle filter de-interlaces blocks ; of data by reordering individual bytes. Byte shuffling can sometimes ; increase compression density because bytes in the same block positions ; often have similar values, and grouping similar values together often ; leads to more efficient compression. Available only in IDL 8.0 and higher. ; ;------------------------------------------------------------------------------------------; PRO NCDF_File::WriteVarDef, varName, dimNames, $ CHUNK_DIMENSIONS=chunk_dimensions, $ CONTIGUOUS=contiguous, $ DATATYPE=datatype, $ GZIP=gzip, $ OBJECT=object, $ SHUFFLE=shuffle ; Compiler options. Compile_Opt DEFINT32 Compile_Opt STRICTARR Compile_Opt STRICTARRSUBS Compile_Opt LOGICAL_PREDICATE ; Error handling. Catch, theError IF theError NE 0 THEN BEGIN Catch, /CANCEL self.errorLogger -> AddError RETURN ENDIF ; The file has to be writable to add a variable definition. IF ~self.writable THEN $ Message, 'Cannot add a variable definition to a READ-ONLY file.' ; Check parameters. IF N_Elements(varName) EQ 0 THEN $ Message, 'Variable name is required for variable definition.' IF N_Elements(datatype) EQ 0 THEN Message, 'Variable data type is required.' ; Check the data type to see that it conforms to netCDF protocol. CASE StrUpCase(datatype) OF 'BYTE': tbyte = 1 'CHAR': tchar = 1 'DOUBLE': tdouble = 1 'FLOAT': tfloat = 1 'LONG': tlong = 1 'SHORT': tshort = 1 'INT': tshort = 1 'STRING': tchar = 1 'UBYTE': tubyte = 1 'ULONG': tulong = 1 'UINT64': tuint64 = 1 'UINT': tuint = 1 'USHORT': tushort = 1 ELSE: Message, 'Unknown DATATYPE for netCDF files: ' + datatype ENDCASE ; If the dimension names are present, use them to get the dimension IDs, which are ; needed to define the variable. ndims = N_Elements(dimNames) IF ndims NE 0 THEN BEGIN dimIDs = LonArr(ndims) IF Size(dimNames, /TNAME) EQ 'STRING' THEN BEGIN FOR j=0,ndims-1 DO BEGIN dimObj = self.dims -> FindByName(dimNames[j], COUNT=dimCount, /CASE_SENSITIVE) CASE dimCount OF 0: Message, 'Cannot find dimension object with the name: ' + dimNames[j] 1: BEGIN dimIDs[j] = dimObj -> GetID() END 2: Message, 'Found more than one dimension with the name: ' + dimNames[j] ENDCASE ENDFOR ENDIF ELSE BEGIN dimIDs = dimNames ENDELSE ENDIF ; Put the file into define mode. self -> SetMode, /DEFINE ; Define the variable. IF N_Elements(dimIDs) EQ 0 THEN BEGIN release = Float(!Version.Release) CASE 1 OF (release LT 8.0) && (!Version.Release NE '7.1.1'): BEGIN varID = NCDF_VarDef(self.fileID, varName, $ BYTE=tbyte, $ CHAR=tchar, $ DOUBLE=tdouble, $ FLOAT=tfloat, $ LONG=tlong, $ SHORT=tshort) END (!Version.Release EQ '7.1.1') || ((release GE 7.2) && (release LT 8.1)): BEGIN varID = NCDF_VarDef(self.fileID, varName, $ BYTE=tbyte, $ CHAR=tchar, $ CHUNK_DIMENSIONS=chunk_dimensions, $ CONTIGUOUS=contiguous, $ DOUBLE=tdouble, $ FLOAT=tfloat, $ GZIP=gzip, $ LONG=tlong, $ SHORT=tshort, $ SHUFFLE=shuffle) END release GE 8.1: BEGIN varID = NCDF_VarDef(self.fileID, varName, $ BYTE=tbyte, $ CHAR=tchar, $ CHUNK_DIMENSIONS=chunk_dimensions, $ CONTIGUOUS=contiguous, $ DOUBLE=tdouble, $ FLOAT=tfloat, $ GZIP=gzip, $ LONG=tlong, $ SHORT=tshort, $ SHUFFLE=shuffle, $ STRING=tstring, $ UBYTE=tubyte, $ UINT64=tuint64, $ ULONG=tulong, $ USHORT=tushort) END ENDCASE ENDIF ELSE BEGIN release = Float(!Version.Release) CASE 1 OF (release LT 8.0) && (!Version.Release NE '7.1.1'): BEGIN varID = NCDF_VarDef(self.fileID, varName, dimIDs, $ BYTE=tbyte, $ CHAR=tchar, $ DOUBLE=tdouble, $ FLOAT=tfloat, $ LONG=tlong, $ SHORT=tshort) END (!Version.Release EQ '7.1.1') || ((release GE 7.2) && (release LT 8.1)): BEGIN varID = NCDF_VarDef(self.fileID, varName, dimIDs, $ BYTE=tbyte, $ CHAR=tchar, $ CHUNK_DIMENSIONS=chunk_dimensions, $ CONTIGUOUS=contiguous, $ DOUBLE=tdouble, $ FLOAT=tfloat, $ GZIP=gzip, $ LONG=tlong, $ SHORT=tshort, $ SHUFFLE=shuffle) END release GE 8.1: BEGIN varID = NCDF_VarDef(self.fileID, varName, dimIDs, $ BYTE=tbyte, $ CHAR=tchar, $ CHUNK_DIMENSIONS=chunk_dimensions, $ CONTIGUOUS=contiguous, $ DOUBLE=tdouble, $ FLOAT=tfloat, $ GZIP=gzip, $ LONG=tlong, $ SHORT=tshort, $ SHUFFLE=shuffle, $ STRING=tstring, $ UBYTE=tubyte, $ UINT64=tuint64, $ ULONG=tulong, $ USHORT=tushort) END ENDCASE ENDELSE ; Create a variable object and add it to the variable list. self -> CreateVarObj, varName ; Need to return the variable object? IF Arg_Present(object) THEN BEGIN object = self.vars -> FindByName(varName, COUNT=varCount, /CASE_SENSITIVE) IF varCount EQ 0 THEN $ Message, 'Cannot find an object with name ' + varName + $ ' in the object container.' ENDIF END ;------------------------------------------------------------------------------------------; ; ; NAME: ; NCDF_File::WriteDim ; ; Purpose: ; ; Writes the variable definition into this netCDF file. ; ; Method Syntax: ; ; obj -> WriteDim, dimName, dimSize, UNLIMITED=unlimited, OBJECT=object ; ; Auguments: ; ; dimName: The case sensitive name of the dimension you wish to define. ; dimSize: The size of the dimension. Required, unless UNLIMITED is set. ; ; Keywords: ; ; UNLIMITED: Set this keyword if you wish this to be an unlimited dimension. ; In general, only one unlimited dimension is allowed per netCDF file. ; OBJECT: If a dimension is successfully defined, this keyword will return the ; dimension object reference to that dimension. ; ;------------------------------------------------------------------------------------------; PRO NCDF_File::WriteDim, dimName, dimSize, UNLIMITED=unlimited, OBJECT=object ; Compiler options. Compile_Opt DEFINT32 Compile_Opt STRICTARR Compile_Opt STRICTARRSUBS Compile_Opt LOGICAL_PREDICATE ; Error handling. Catch, theError IF theError NE 0 THEN BEGIN Catch, /CANCEL self.errorLogger -> AddError RETURN ENDIF ; The file has to be writable to add a dimension. IF ~self.writable THEN Message, 'Cannot add a dimension to a READ-ONLY file.' ; Check parameters. IF N_Elements(dimName) EQ 0 THEN $ Message, 'The name of the dimension is required.' IF ~Keyword_Set(unlimited) THEN BEGIN IF N_Elements(dimSize) EQ 0 THEN $ Message, 'The size of the dimension is required.' ENDIF ; Put the file into define mode self -> SetMode, /DEFINE ; Add the dimension to the file. IF Keyword_Set(unlimited) THEN BEGIN void = NCDF_DimDef(self.fileID, dimName, /UNLIMITED) ENDIF ELSE BEGIN void = NCDF_DimDef(self.fileID, dimName, dimSize) ENDELSE ; Create a dimension object and add it to the dimension container. self -> CreateDimObj, dimName ; Need to return the dimension object? IF Arg_Present(object) THEN BEGIN object = self.dims -> FindByName(dimName, COUNT=dimCount, /CASE_SENSITIVE) IF dimCount EQ 0 THEN $ Message, 'Cannot find an object with name ' + dimName + $ ' in the object container.' ENDIF END ;------------------------------------------------------------------------------------------; ; ; NAME: ; NCDF_File::WriteGlobalAttr ; ; Purpose: ; ; Writes a global attribute into this netCDF file. ; ; Method Syntax: ; ; obj -> WriteGlobalAttr, attrName, attrValue, DATATYPE=datatype, OBJECT=object ; ; Auguments: ; ; attrName: The case sensitive name of the global attribute you wish to write. ; attrValue: The value of the attribute. Required. ; ; Keywords: ; ; DATATYPE: The netCDF data type of the variable. This is REQUIRED. The appropriate ; netCDF data types are: "BYTE", "CHAR", "SHORT", "LONG" "FLOAT", or ; "DOUBLE". ; OBJECT: If a dimension is successfully defined, this keyword will return the ; dimension object reference to that dimension. ; ;------------------------------------------------------------------------------------------; PRO NCDF_File::WriteGlobalAttr, attrName, attrValue, DATATYPE=datatype, OBJECT=object ; Compiler options. Compile_Opt DEFINT32 Compile_Opt STRICTARR Compile_Opt STRICTARRSUBS Compile_Opt LOGICAL_PREDICATE ; Error handling. Catch, theError IF theError NE 0 THEN BEGIN Catch, /CANCEL self.errorLogger -> AddError RETURN ENDIF ; The file has to be writable to add a global attribute. IF ~self.writable THEN $ Message, 'Cannot add a global attribute to a READ-ONLY file.' ; Check parameters. IF N_Elements(attrName) EQ 0 THEN Message, 'The attribute name is required.' IF N_Elements(attrValue) EQ 0 THEN Message, 'The attribute value is required.' IF N_Elements(datatype) EQ 0 THEN datatype = NCDF_CastDataType(attrValue) ; Make sure the attribute name doesn't have spaces. parts = StrSplit(attrName, /EXTRACT) IF N_Elements(parts) GT 1 THEN $ Message, 'Global attribute names cannot have spaces in them.' ; Set the appropriate netCDF data type keyword. CASE StrUpCase(datatype) OF 'BYTE': tbyte = 1 'CHAR': tchar = 1 'DOUBLE': tdouble = 1 'FLOAT': tfloat = 1 'LONG': tlong = 1 'SHORT': tshort = 1 'INT': tshort = 1 'STRING': tchar = 1 'ULONG': tlong = 1 'UINT': tlong = 1 ELSE: Message, 'Unknown DATATYPE for netCDF files: ' + datatype ENDCASE ; Put the file into define mode self -> SetMode, /DEFINE ; Add the attribute to the file. NCDF_AttPut, self.fileID, attrName, attrValue, $ /GLOBAL, $ BYTE=tbyte, $ CHAR=tchar, $ DOUBLE=tdouble, $ FLOAT=tfloat, $ LENGTH=length, $ LONG=tlong, $ SHORT=tshort ; Create an attribute object and add it to the attribute container. self -> CreateAttrObj, attrName ; Need to return the dimension object? IF Arg_Present(object) THEN BEGIN object = self.attrs -> FindByName(attrName, COUNT=attrCount, /CASE_SENSITIVE) IF attrCount EQ 0 THEN $ Message, 'Cannot find an object with name ' + attrName + $ ' in the object container.' ENDIF END ;------------------------------------------------------------------------------------------; ; ; NAME: ; NCDF_File::WriteVarAttr ; ; Purpose: ; ; Writes a variable attribute into this netCDF file. ; ; Method Syntax: ; ; obj -> WriteVarAttr, varName, attrName, attrValue, DATATYPE=datatype ; ; Auguments: ; ; varName: The case sensitive variable name for which the attribute is being ; defined. ; attrName: The case sensitive name of the global attribute you wish to write. ; attrValue: The value of the attribute. Required. ; ; Keywords: ; ; DATATYPE: The netCDF data type of the variable. The appropriate netCDF data types ; are: "BYTE", "CHAR", "SHORT", "LONG" "FLOAT", or "DOUBLE". ; OBJECT: If a dimension is successfully defined, this keyword will return the ; dimension object reference to that dimension. ; ;------------------------------------------------------------------------------------------; PRO NCDF_File::WriteVarAttr, varName, attrName, attrValue, DATATYPE=datatype ; Compiler options. Compile_Opt DEFINT32 Compile_Opt STRICTARR Compile_Opt STRICTARRSUBS Compile_Opt LOGICAL_PREDICATE ; Error handling. Catch, theError IF theError NE 0 THEN BEGIN Catch, /CANCEL self.errorLogger -> AddError RETURN ENDIF ; The file has to be writable to add a variable attribute. IF ~self.writable THEN Message, 'Cannot add a variable attribute to a READ-ONLY file.' ; Check parameters. IF N_Elements(attrName) EQ 0 THEN Message, 'The attribute name is required.' IF N_Elements(attrValue) EQ 0 THEN Message, 'The attribute value is required.' IF N_Elements(varName) EQ 0 THEN Message, 'A variable name or object is required.' IF N_Elements(datatype) EQ 0 THEN datatype = NCDF_CastDataType(attrValue) ; Make sure the attribute name doesn't have spaces. parts = StrSplit(attrName, /EXTRACT) IF N_Elements(parts) GT 1 THEN $ Message, 'Variable attribute names cannot have spaces in them.' ; Were you passed the name of a variable or a variable object? CASE Size(varName, /TNAME) OF 'STRING': BEGIN varObj = self.vars -> FindByName(varName, COUNT=varCount, /CASE_SENSITIVE) IF varCount EQ 0 THEN $ Message, 'Cannot find a variable object with name ' + varName + '.' IF ~Obj_Valid(varObj) THEN $ Message, 'Invalid object with name "' + varName + '" has been found.' END 'OBJREF': BEGIN varObj = varName END ELSE: Message, 'Input variable name or object is the wrong data type.' ENDCASE ; Is this a valid variable object? IF ~Obj_Valid(varObj) THEN Message, 'Invalid variable object. Cannot add attribute.' ; Put the file into define mode self -> SetMode, /DEFINE ; Add the attribute to the variable. varObj -> AddAttr, attrName, attrValue, DATATYPE=datatype END ; ;-------------------------------------------------------------------------------------------------- ; NAME: ; NCDF_FILE::CLEANUP ; ; PURPOSE: ; ; The cleanup method for the NCDF_FILE object. ; ; ARGUMENTS: ; ; None. ; ; KEYWORD PARAMETERS: ; ; None. ; ;---------------------------------------------------------------------------------------------- PRO NCDF_File::CLEANUP IF self.fileID GT 0 THEN NCDF_Close, self.fileID self.errorLogger -> CloseFile Obj_Destroy, self.errorLogger Obj_Destroy, self.vars Obj_Destroy, self.attrs Obj_Destroy, self.dims END ;-------------------------------------------------------------------------------------------------- ; NAME: ; NCDF_FILE::INIT ; ; PURPOSE: ; ; The initialization method for the NCDF_FILE object. ; ; ARGUMENTS: ; ; filename: The name of a netCDF file to open. ; ; KEYWORD PARAMETERS: ; ; ALERT: Set this keyword if you wish to have alert from the object's error logger. ; Input. Default is 1. ; ; BROWSE: If this keyword is set, the Browse Window is invoked as soon ; as the object is initiated. Input. Default is 0. ; ; CLOBBER: Set this keyword if you are opening a netCDF file that already exists and ; you want to overwrite the existing file. Input. Default is 0. ; ; CREATE: Set this keyword if you wish to create a new netCDF file to write ; into. Input. Default is 0, which means the file will be opened as ; "read-only". ; ; DELETE_ON_DESTROY: Set this keyword if you wish to delete the error log file when ; the ErrorLogger object is destroyed. This will only happen if the ErrorLogger ; object is not in an error state. Input. Default is 1. ; ; ERRORLOGGERNAME: The name of the ErrorLogger filename that captures errors from this ; program. Optional. If not provided a default name will be provided, based ; on the current local system time. ; ; MODIFY: Set this keyword if you wish to modify (write to) a file you are opening. ; If not set, the file will be opened as "read-only". ; ; NETCDF4_FORMAT: Set this keyowrd to create a new NetCDF 4 file. In NetCDF 4 files, data ; is created and accessed with the HDF5 library. NetCDF 4 files are valid HDF5 files, ; and may be read with HDF5 routines. Note that if a NetCDF 4 file is modified using ; the HDF5 routines, rather than with the NetCDF 4 routines, the file is no longer a ; valid NetCDF 4 file, and may no longer be readable with the NetCDF routines. ; You need IDL 8.0 to use this keyword. ; ; NOCLUTTER: Set the keyword to set the ErrorLogger NOCLUTTER keyword. ; ; TIMESTAMP: Set this keyword is you want the ErrorLogger filename to have a time stamp ; appended to it. ; ;---------------------------------------------------------------------------------------------- FUNCTION NCDF_FILE::INIT, filename, $ ALERT=alert, $ BROWSE=browse, $ CLOBBER=clobber, $ CREATE=create, $ DELETE_ON_DESTROY=delete_on_destroy, $ ERRORLOGGERNAME=errorLoggerName, $ MODIFY=modify, $ NETCDF4_FORMAT=netcdf4_format, $ NOCLUTTER=noclutter, $ TIMESTAMP=timestamp ; Compiler options. Compile_Opt DEFINT32 Compile_Opt STRICTARR Compile_Opt STRICTARRSUBS Compile_Opt LOGICAL_PREDICATE ; Error handling. Return 0 if can't finish. CATCH, theError IF theError NE 0 THEN BEGIN CATCH, /CANCEL self.errorLogger -> AddError Obj_Destroy, self.errorLogger IF self.fileID GT 0 THEN NCDF_Close, self.fileID RETURN, 0 ENDIF ; Create the error logger. self.errorLogger = Obj_New('ErrorLogger', errorLoggerName, ALERT=1, $ DELETE_ON_DESTROY=1, TIMESTAMP=Keyword_Set(timestamp), NOCLUTTER=noclutter) ; Check keywords. IF N_Elements(filename) EQ 0 THEN BEGIN filters = ['*.nc', '*.ncdf*'] filename = Dialog_Pickfile(FILTER=filters, TITLE='Select netCDF File...') IF filename EQ "" THEN RETURN, 0 ENDIF ; Make sure you have an absolute filename. root_name = File_Basename(filename) IF root_name EQ filename THEN BEGIN CD, CURRENT=thisDir absFilename = FilePath(ROOT_DIR=thisDir, filename) filename = absFilename ENDIF ; Set default values, if keywords are not already set. SetDefaultValue, alert, 1, /Boolean SetDefaultValue, create, 0, /Boolean SetDefaultValue, clobber, 0, /Boolean SetDefaultValue, modify, 0, /Boolean SetDefaultValue, delete_on_destroy, 1, /Boolean SetDefaultValue, noclutter, 0, /Boolean SetDefaultValue, netcdf4_format, 0, /Boolean self.errorLogger -> SetProperty, ALERT=alert, DELETE_ON_DESTROY=delete_on_destroy, NOCLUTTER=noclutter ; If you are not going to create the file, you are going to open it. ; If you want to modify the file, it is going to be writable. mode = create ? 'CREATE' : 'OPEN' self.writable = (mode EQ 'OPEN' && modify) || mode EQ 'CREATE' ; Are you creating the file? Make sure it doesn't already exist. If it does, ; you will have to clobber it. IF mode EQ 'CREATE' THEN BEGIN IF File_Test(filename) THEN BEGIN CASE clobber OF 1: File_Delete, filename, /ALLOW_NONEXISTENT 0: Message, "The specified netCDF file already exists and cannot " + $ "be overwritten unless CLOBBER is set." ENDCASE ENDIF ENDIF ; If the user wants to open the file, make sure it is possible. IF mode EQ 'OPEN' THEN BEGIN IF ~File_Test(filename, /READ) THEN $ Message, "The specified netCDF file cannot be opened for reading: " + filename IF modify THEN BEGIN IF ~File_Test(filename, /WRITE) THEN $ Message, "The specified netCDF file cannot be opened for writing: " + filename ENDIF ENDIF ; Store the filename. self.filename = filename ; Open or create the file. CASE mode OF 'OPEN': BEGIN self.fileID = NCDF_Open(self.filename, WRITE=self.writable) self.define = 0 END 'CREATE': BEGIN IF (!Version.Release EQ '7.1.1') || (Float(!Version.Release) GE 7.2) THEN BEGIN self.fileID = NCDF_Create(self.filename, CLOBBER=clobber, NETCDF4_FORMAT=netcdf4_format) ENDIF ELSE BEGIN self.fileID = NCDF_Create(self.filename, CLOBBER=clobber) ENDELSE self.define = 1 END ENDCASE ; Initialize object containers for contents. self.vars = Obj_New('NCDF_Container') self.attrs = Obj_New('NCDF_Container') self.dims = Obj_New('NCDF_Container') ; Did the user want to browse this file? IF Keyword_Set(browse) THEN self -> Browse ; If this file has been opened for reading or modifying, then parse it. IF ~self.writable THEN self -> Parsefile IF Keyword_Set(modify) THEN self -> Parsefile RETURN, 1 END ; -------------------------------------------------------------------------------------------- PRO NCDF_File__DEFINE, class class = { NCDF_FILE, $ filename: "", $ ; The name of the netCDF file. fileID: 0L, $ ; The netCDF file ID. errorlogger: Obj_New(), $ ; The error logger object. vars: Obj_New(), $ ; An object container containing variable objects. attrs: Obj_New(), $ ; An object container containing global attribute objects. dims: Obj_New(), $ ; An object container containing dimension objects. writable: 0B, $ ; A flag that indicates the file is writable. fileHasBeenParsed: 0B, $ ; A flag that is 1 when the file has been parsed. file_extension: "", $ ; A file extension that is used with Dialog_Pickfile() define: 0B $ ; A flag that indicates the file is in define mode. } END ; --------------------------------------------------------------------------------------------