;+
; NAME:
;       NCDF_FILE_EXAMPLES
;
; PURPOSE:
;
;       This is a utility routine that demonstrates how to use the NCDF_FILE object 
;       to create a new netCDF file, copy information from one netCDF file to another, 
;       read information out of a netCDF file, and add a new variable to an already 
;       existing netCDF file.
;
; 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:
;
;       Utilities
;
; CALLING SEQUENCE:
;
;       NCDF_File_Examples
;
; ARGUMENTS:
;
;       None.
;
; KEYWORDS:
;
;       None.
;
; MODIFICATION HISTORY:
;
;       Written by:  David W. Fanning, 3 February 2010.
;       Updated to use a time variable for the frame number. 29 Oct 2011.
;       Added example of adding a new variable to an already exisiting netCDF file. 9 Jan 2015. 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.                            ;
;******************************************************************************************;
PRO NCDF_File_Examples

; This is an example program to demonstrate some of the capabilities
; of the NCDF_FILE objects and its related objects.

    ; Error handling.
    Catch, theError
    IF theError NE 0 THEN BEGIN
        Catch, /CANCEL
        void = cgErrorMsg()
        Obj_Destroy, fileObj
        Obj_Destroy, sObj
        Obj_Destroy, dObj
        RETURN
    ENDIF
    
    ; Initial definition of objects, in case there is a problem.
    ; This will allow proper cleanup of open netCDF files.
    fileObj = Obj_New()
    sObj = Obj_New()
    dObj = Obj_New()

    ; Open a new file for writing.
    fileObj = Obj_New("NCDF_File", /CREATE, /CLOBBER, 'ncdf_example.nc')
    IF Obj_Valid(fileObj) EQ 0 THEN $
        Message, 'Invalid file object returned from NCDF_FILE INIT.'
    
    ; Create some data to put into the file.
    data = cgDemoData(18)
    dims = Size(data, /DIMENSIONS)
    colortable = 34
    
    ; Add global attributes to the file.
    fileObj -> WriteGlobalAttr, DATATYPE='CHAR', 'title', $
        'NCDF_FILE Example File.'
    fileObj -> WriteGlobalAttr, DATATYPE='CHAR', 'description', $
        'An example of how to create a netCDF file with the NCDF_FILE object.'
    fileObj -> WriteGlobalAttr, 'created', Systime(), DATATYPE='CHAR'
    
    ; Add dimensions to the file.
    fileObj -> WriteDim, 'xsize', dims[0], OBJECT=xdimObj
    fileObj -> WriteDim, 'ysize', dims[1], OBJECT=ydimObj
    fileObj -> WriteDim, 'time', 24, OBJECT=timeDimObj
    
    ; Get the dimension names.
    dimNames = [xdimObj->GetName(), ydimObj->GetName(), timeDimObj->GetName()]

    ; Define a variable for the file.
    fileObj -> WriteVarDef, 'data', dimNames, DATATYPE='BYTE', OBJECT=dataObj
    IF Obj_Valid(dataObj) EQ 0 THEN Message, 'Invalid data object returned.'
    
    ; Define variable attributes.
    fileObj -> WriteVarAttr, dataObj, 'comment', 'Frames of a data animation.'
    fileObj -> WriteVarAttr, 'data', 'colortable', colortable, DATATYPE='SHORT'
    
    ; Define a time variable for the file. Months, starting 15 Jan 2008 and going
    ; for 24 months. Offset to 1 Jan 1600 00:00:00.
    offset = Julday(1,1,1600,0,0,0)
    time = TimeGen(24, START=Julday(01,15,2008, 0, 0, 0), UNITS='Months', STEP_SIZE=1) - offset
    fileObj -> WriteVarDef, 'time', [timeDimObj->GetName()], DATATYPE='DOUBLE', OBJECT=timeObj
    IF Obj_Valid(timeObj) EQ 0 THEN Message, 'Invalid time object returned.'
    
    ; Define variable attributes.
    fileObj -> WriteVarAttr, timeObj, 'Units', 'Julian days since 1 January 1600 00:00:00.'

    ; Write the data to the file. Here we are going to write 24 frames of data.
    FOR j=0,23 DO BEGIN
        fileObj -> WriteVarData, 'data', cgDemoData(18), OFFSET=[0,0,j]
    ENDFOR
    
    ; Write the time into the file.
    fileObj -> WriteVarData, 'time', time
    
    ; Sync the file by writing memory to disk.
    fileObj -> Sync
    
    ; Browse the file.
    fileObj -> Browse, XOFFSET=250, YOFFSET=250, TITLE='Example File'
    
    ; Destroy this file object.
    Obj_Destroy, fileObj

;**************************************************************************************
;**************************************************************************************

    ; Open the file you just created and copy the information in it to another file.
    sourceFile = 'ncdf_example.nc'
    destFile = 'ncdf_example_copy.nc'
    
    ; Open the source file in read-only mode.
    sObj = Obj_New('NCDF_FILE', sourceFile, $
        ErrorLoggerName='sourcefilelogger', /TIMESTAMP)
    IF Obj_Valid(sObj) EQ 0 THEN Message, 'Source object cannot be created.'
    
    ; Open the destination file for writing.
    dObj = Obj_New('NCDF_FILE', destFile, /CREATE, /CLOBBER, $
        ErrorLoggerName='destinationfilelogger', /TIMESTAMP)
    IF Obj_Valid(dObj) EQ 0 THEN Message, 'Destination object cannot be created.'
    
    ; Find all the global attributes in the source file and copy them.
    attrNames = sObj -> GetGlobalAttrNames(COUNT=attrCount)
    FOR j=0,attrCount-1 DO BEGIN
        sObj -> CopyGlobalAttrTo, attrNames[j], dObj
    ENDFOR
    
    ; Find all the dimensions in the source file and copy them.
    dimNames = sObj -> GetDimNames(COUNT=dimCount)
    FOR j=0,dimCount-1 DO BEGIN
        sObj -> CopyDimTo, dimNames[j], dObj
    ENDFOR
  
    ; Find all the variable definitions, attributes and data in the 
    ; source file and copy them.
    varNames = sObj -> GetVarNames(COUNT=varCount)
    FOR j=0,varCount-1 DO BEGIN
        sObj -> CopyVarDefTo, varNames[j], dObj
        varAttrNames = sObj -> GetVarAttrNames(varNames[j], COUNT=varAttrCount)
        FOR k=0,varAttrCount-1 DO BEGIN
            sObj -> CopyVarAttrTo, varNames[j], varAttrNames[k], dObj
        ENDFOR
        sObj -> CopyVarDataTo, varNames[j], dObj
    ENDFOR

    ; Sync the destination file.
    dObj -> Sync
    
    ; Browse the destination file.
    dObj -> Browse, XOFFSET=350, YOFFSET=375, TITLE='Copy of Example File'
    
    ; Destroy both the source and destination objects.
    Obj_Destroy, dObj
    Obj_Destroy, sObj
    
;**************************************************************************************
;**************************************************************************************

    ; Open the original file and make an annimation out of the data in the file.
    ; Open the source file in read-only mode.
    sObj = Obj_New('NCDF_FILE', sourceFile)
    IF Obj_Valid(sObj) EQ 0 THEN Message, 'Source object cannot be created.'
    
    ct = sObj -> GetVarAttrValue('data', 'colortable')
    LoadCT, ct, /SILENT
    
    check = sObj -> HasDim('xsize', OBJECT=xsizeObj)
    IF check THEN xsize = xsizeObj -> GetSize() ELSE Message, 'Cannot find dimension XSIZE.'

    check = sObj -> HasDim('ysize', OBJECT=ysizeObj)
    IF check THEN ysize = ysizeObj -> GetSize() ELSE Message, 'Cannot find dimension YSIZE.'
    
    check = sObj -> HasDim('time', OBJECT=timeDimObj)
    IF check THEN numMonths = timeDimObj -> GetSize() ELSE Message, 'Cannot find dimension TIME.'
    
    ; Set up the animation.
    XInterAnimate, SET=[xsize, ysize, numMonths], /SHOWLOAD
    time = sObj -> GetVarData('time')
    time = time + Julday(1,1,1600,0,0,0)
    FOR j=0,numMonths-1 DO BEGIN
        data = sObj -> GetVarData('data', COUNT=[xsize, ysize, 1], OFFSET=[0,0,j])
        cgImage, data, /KEEP, /NOINTERP
        CalDat, time[j], month, day, year
        theDate = StrTrim(day,2) + ' ' + cgMonths(month) + ' ' + StrTrim(year,2)
        cgText, 0.1, 0.05, /NORMAL, 'Date: ' + theDate, COLOR='black', FONT=0
        XInterAnimate, FRAME=j, WINDOW=!D.Window
    ENDFOR
    XInterAnimate, 25
    
    ; Destroy the file object.
    Obj_Destroy, sObj


;**************************************************************************************
;**************************************************************************************
    
    ; Open the original file in MODIFY mode so you can write the new variable into it.
    sObj = Obj_New('NCDF_FILE', sourceFile, /MODIFY)
    IF Obj_Valid(sObj) EQ 0 THEN Message, 'Source object cannot be created.'
    
    ; Create an image variable to add to the file.
    image = Congrid(cgDemoData(18), 500, 250)
    s = Size(image, /Dimensions)
    img_xsize = s[0]
    img_ysize = s[1]
    
    ; Add dimensions to the file.
    sObj -> WriteDim, 'img_xsize', img_xsize, OBJECT=ximgdimObj
    sObj -> WriteDim, 'img_ysize', img_ysize, OBJECT=yimgdimObj
    
    ; Get the dimension names.
    dimNames = [ximgdimObj->GetName(), yimgdimObj->GetName()]
    
    ; Define a variable for the file.
    sObj -> WriteVarDef, 'image', dimNames, DATATYPE='BYTE', OBJECT=imageObj
    IF Obj_Valid(imageObj) EQ 0 THEN Message, 'Invalid data object returned.'
    
    ; Define variable attributes.
    sObj -> WriteVarAttr, imageObj, 'comment', 'Image variable added later.'
    
    ; Write the data to the file.
    sObj -> WriteVarData, 'image', image
    
    ; Sync the file by writing memory to disk.
    sObj -> Sync
    
    ; Browse the file.
    sObj -> Browse, XOFFSET=350, YOFFSET=350, TITLE='File with Variable Added'
    
    ; Destroy this file object.
    Obj_Destroy, sObj
    
    END
