Skip to content
Snippets Groups Projects
idlcr8ascii.pro 148.08 KiB
;Main Program Version: idlcr8ascii.pro v4.0b27, 20240927
;  Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org
;
;Sub-versions:
;  v3.01, 20081020 - If the HDF4 file is created using the HDF4.2r3 library, then extra information
;                    regarding the dimensions of the VAR_DEPEND values may be included in the file
;                    (which show up as extra datasets in the HDF_SD_FILEINFO call). A check for
;                    this is carried out and, if found, the information is excluded from the output.
;  v3.02, 20090311 - Improve HDF4.2r3 library checks by comparing the number of datasets recorded
;                    under DATA_VARIABLES, with the number returned by the HDF_SD_FILEINFO call;
;                    Change 'WARNING' to 'INFORMATION; Add procedure to handle 'INFORMATION' text
;                    output; Add 'INFORMATION' text if anything irregular found with the datasets;
;                    Change 'Data dimensions exceed 8' and 'Data type not allowable' ERROR messages
;                    to 'INFORMATION' messages. Make log file output to the same directory as the
;                    first HDF file to be read.
;  v3.03, 20091208 - Incorporate HDF_SD_ISCOORDVAR to differentiate between datasets and dimension
;                    variable names. Add check for allowable VAR_DATA_TYPE=STRING. Add all
;                    INFORMATION text messages to INFOTXT_OUTPUT_A procedure to avoid duplication
;                    of code that creates the messages; Add INFORMATION messages when reading HDF5
;                    files; Rename AVDC Button to FORMAT and replace keyword option /AVDC with
;                    /FORMAT. Improve checks on parameter and keyword inputs; Improve checks for
;                    non-standard (i.e. non-groundbased) HDF files.
;  v4.0b1, 20101122  - Adopt GEOMS metadata standard; Because FORMAT attribute(s) have been dropped
;                    can no longer use defined formatting values when outputing data to files;
;                    Default ASCII formatting adopted - scientific notation for REAL and DOUBLE,
;                    except for DATETIME and related values, which have formatting dependent on
;                    the data type; Numeric metadata values remain in their original data type
;                    when writing to session memory, meaning: variable attribute labels and values
;                    now written to separate structure variables sds.va_l and sds.va_v (previously
;                    sds.va), and structure has 2 dimensions (n_sds, n_atts) instead of 1
;                    (previously n_sds); The dataset is written to the initial n_atts index (i.e.
;                    sds[n_sds,0].data); Rename saved SDS datatype according to GEOMS rules
;                    (affects INT, LONG, and FLOAT); Add JDF_2_DATETIME and JULIAN_DATE routines
;                    to the program; Include coordinate variables if they and the non-coordinate
;                    variables add up to the number of saved DATA_VARIABLES. Add return error code
;                    option so that program returns to the calling program if an error is
;                    generated.
;  v4.0b2, 20110323  - If required reverse the order of VAR_SIZE and VAR_DEPEND values so that they
;                    match the order of the multi-dimensional dataset. Note that the HDF read programs
;                    automatically transpose the multi-dimensional datasets to conform to the IDL
;                    convention of fastest changing index being listed first (opposite for HDF).
;  v4.0b3, 20111014  - Add netCDF support - writes variable attributes to GEOMS standard attributes
;                    where possible, otherwise writes to VAR_NOTES
;  v4.0b4, 20111208  - Test for dimension ordering of the netCDF files, and reverse if required (same
;                    rules as for HDF). Create separate Dimension Ordering Routine
;  v4.0b5, 20120328  - Add options to convert HDF/netCDF files to other HDF/netCDF formats e.g. H4 to
;                    H5 etc. Requires IDLcr8HDF in the search path to do this.
;  v4.0b6, 20120426  - Bug fix when a netCDF file only contains datasets with single dimensions -
;                    VAR_SIZE information was not being extracted during the read process.
;  v4.0b7  20130114  - Add check for IDL being run in DEMO mode by using LMGR command. Replace PRINTF,-1
;                    statements (i.e. when dux[0] eq -1) with PRINT (o/w causes DEMO mode to stop). Also
;                    disable /LOG, /FORMAT and /DUMP keywords in DEMO mode.
;  v4.0b8  20140325  - Fix bug that meant VAR_SIZE and VAR_DEPEND values were not being automatically
;                    reversed if the array sizes were the same and VAR_DEPEND did not include a DATETIME
;                    value. Now defaults to the 'reverse' option in the TEST_DIM_ORDER routine if no
;                    other conditions are satisfied; Fix bug that caused crash when VAR_SIZE values were
;                    not of type string.
;  v4.0b9  20141110  - Fix bug that caused multi-dimensional array ordering to not be correctly identified
;                    if the first dataset to be checked in the file had the same number of elements in
;                    the array (e.g. was a set of Averaging Kernels).
;  v4.0b10 20150127  - Allow for VAR_FILL_VALUE values for string datasets to be written and saved as an
;                    (empty) string of the correct length. Ensure string datasets are all written and
;                    saved to the correct length (that of the longest string in the dataset).
;  v4.0b12 20151109  - Convert DATETIME (and related) max, min and fill values to LONG64 data type before
;                    determining the format for ASCII output to account for very large values.
;  v4.0b15 20160725  - Fix bug causing program to crash when the number of DATA_VARIABLES values is greater
;                    than the number of datasets in the file.
;  v4.0b19 20180218  - Fix bug that caused multi-dimensional array ordering to not be correctly identified
;                    if the datasets have the same VAR_SIZE i.e. rev_vd_vs stays as 2 through all the
;                    checks. Now rev_vd_vs changes to 1 (VAR_SIZE, VAR_DEPEND and dataset ordering is 
;                    reversed) if rev_vd_vs=2 after all the checks.
;  v4.0b20 20181116  - Fix bug that caused multi-dimensional array ordering to not be correctly written if
;                    not consistent through the file i.e. rev_vd_vs eq 3. Now rev_vd_vs changes to 1 
;                    (VAR_SIZE, VAR_DEPEND and dataset ordering is reversed), but information message 
;                    advising of the issue is still reported.
;  v4.0b21 20190514  - Fix bug that occurred when GEOMS variable attribute information is missing from an 
;                    HDF4 file but written to the heap structure based on the contents of the file. Previously
;                    the variable attribute label was not written correctly.
;  v4.0b22 20190806  - Provide support for reading netCDF4 files. Currently uses the HDF5 routines to do this
;                    so need to account for features unique to netCDF4 such as; the inclusion of dimension
;                    names; standard netCDF attribute names; empty attribute values that do not include a 
;                    null termination character (which causes an error in the H5A_Read routine). New 
;                    sub-routines include is_a_number_ascii, alpha_numeric_underscore, nc_dimension_chk,
;                    file_format_a.
;  v4.0b23 20190821  - Fixed bugs associated with H5 TAG_NAMES checks introduced in v4.0b22.                    
;  v4.0b24 20200830  - Add INFORMATION messages (20-22) that identify issues with the VAR_DEPEND and VAR_SIZE
;                    attributes, that were previously fixed 'quietly' by the program; Check input GEOMS file
;                    for zero filesize before doing checks.
;  v4.0b25 20220805  - For NetCDF3 or 4, if SDS_NAME NE VAR_NAME then converts non-alphanumeric VAR_NAME 
;                    characters to '_' and compares with SDS_NAME. If there is a match then program quietly 
;                    converts SDS_NAME to VAR_NAME for testing, otherwise generates an error; If input file has
;                    an HDF5 file signature then checks for .nc .nc4 .netcdf and .netcdf4 extension and also
;                    calls NCDF_PARSE (if ge 8.5.2) to check for NC4 compatibility; Create DATA_TYPE_CHECKS
;                    procedure to do all the data type checks. Unable to check for unsigned 8-bit in netCDF3 
;                    files at this time; Generate an INFORMATION/ERROR message if VAR_UNITS is numeric
;                    (same as VAR_SIZE); INFORMATION/ERROR message added if a mis-match is found between
;                    the input file name and the FILE_NAME attribute value.
;  v4.0b26 20240912  - For NetCDF3 files only output WARNING to check for signed BYTE entries when the dataset
;                    is of type BYTE (infotxt_output_a message 23).
;  v4.0b27 20240927  - For netCDF4 files allow for additional standard netCDF attribute names.

PRO idlcr8ascii_common
;Procedure to define the data COMMON block WIDGET_WIN_A, containing common variables
;associated with the Graphical User Interface
; ----------
;  Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org
;
;  History:
;    20061004: Introduced to IDLCR8ASCII - Version 2.0
;    20080302: 'cwidg' variable removed, and 'dux' array added to identify requested
;              output options - Version 3.0
;    20101122: Add rerr - Version 4.0b1
;
;
;  Input: Nil
;
;  Output: Nil
;
;  Called by: N/A
;
;  Subroutines Called: None

COMMON WIDGET_WIN_A,wtxt,lineno,base,o3,b3,dux,rerr

END ; Procedure idlcr8ascii_common



PRO intro_a_event, ev
;Procedure to define how Events from the Start-up Introduction Window are handled
; ----------
;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org
;
;  History:
;    20061004: Introduced to IDLCR8ASCII - Version 2.0
;    20080302: Code added to define the Pop-up Window and Log Output events added to
;              the Introduction Window - Version 3.0
;
;  Input: ev - Selected widget event structure
;
;  Output: o3 - Common string array defining the various options for program output
;
;  Called by: XMANAGER in INTRO_A
;
;  Subroutines Called: None

COMMON WIDGET_WIN_A

;The uservalue is retrieved from a widget when an event occurs
WIDGET_CONTROL,ev.id,GET_UVALUE=uv
IF (uv EQ 'C') OR (uv EQ 'P') OR (uv EQ 'idlcr8ascii.log') OR $
   (uv EQ 'H4') OR (uv EQ 'H5') OR (uv EQ 'N3') THEN BEGIN
  CASE 1 OF
    uv EQ 'C': BEGIN
                 uvi=1 & eadd=12L
               END
    uv EQ 'P': uvi=3
    uv EQ 'H4': BEGIN
                  uvi=5 & eadd=6L
                END
    uv EQ 'H5': BEGIN
                  uvi=6 & eadd=5L
                END
    uv EQ 'N3': BEGIN
                  uvi=7 & eadd=4L
                END
    ELSE: uvi=4
  ENDCASE
  IF o3[uvi] EQ uv THEN o3[uvi]='0' ELSE o3[uvi]=uv
  IF (uv EQ 'C') OR (uv EQ 'H4') OR (uv EQ 'H5') OR (uv EQ 'N3') THEN BEGIN
    IF (o3[1] EQ '0') AND (o3[5] EQ '0') AND (o3[6] EQ '0') AND (o3[7] EQ '0') THEN $
      WIDGET_CONTROL,ev.id+eadd,Sensitive=0 $
    ELSE BEGIN
      WIDGET_CONTROL,ev.id+eadd,Sensitive=1
      IF uv EQ 'C' THEN BEGIN
        WIDGET_CONTROL,ev.id+1,/Set_Button
        o3[3]='P'
      ENDIF
    ENDELSE
  ENDIF
ENDIF ELSE IF (uv EQ 'F') OR (uv EQ 'D') THEN o3[0]=uv $ ;Assign button event to a variable name
ELSE IF uv EQ 'Cont' THEN o3[0]='0' ;changes o3[0] from -1 to 0
IF (uv NE 'C') AND (uv NE 'P') AND (uv NE 'idlcr8ascii.log') AND (uv NE 'H4') AND $
   (uv NE 'H5') AND (uv NE 'N3') THEN WIDGET_CONTROL,ev.top,/DESTROY

END ;Intro_Event handler



PRO intro_a, intype
;Procedure which creates an Introduction Window at start-up when IDLCR8ASCII is called without parameters,
;or invalid parameters, or is called by IDL Virtual Machine. The user has a choice of continuing with
;the program after selecting input options, or stopping the program.
; ----------
;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org
;
;  History:
;    20061004: Introduced to IDLCR8ASCII - Version 2.0
;    20080302: Swapped the order of the option windows, so that the options indicated by the check
;              boxes appear above the option boxes that close the Introduction Window and continue
;              with the program; Added options to allow the user to send input/output log information
;              to a Pop-Up Window and/or a Log File - Version 3.0
;    20091208: Change AVDC button to FORMAT and keyword option from /AVDC to /FORMAT - Version 3.03
;    20101122: Add vertxt array. Modify Introduction text to indicate that program accepts 'GEOMS
;              Compliant' files, and also to show the new structure format - Version 4.0b1
;
;  Input: intype - Integer set to -2 or -3: -2 indicates normal state; -3 indicates that input
;                  parameters or keywords at the IDLCR8ASCII call are invalid.
;
;  Output: Nil
;
;  Called by: IDLCR8ASCII
;
;  Subroutines Called: INTRO_A_EVENT (via XMANAGER)

COMMON WIDGET_WIN_A

;procedure which provides an introduction message before starting the program.
nhdr=46 & errtxt=STRARR(nhdr)
vertxt=['idlcr8ascii-v4.0_Readme.pdf','v4.0b27 September 2024']
errtxt[1]='Welcome to IDLcr8ASCII.  This program reads GEOMS compliant HDF4, HDF5 and netCDF files and'
errtxt[2]='saves contents to either session memory, an output window (summary only) or to ASCII or formatted'
errtxt[3]='files (also refer to '+vertxt[0]+').'
errtxt[5]='Inputs to the program:'
errtxt[6]='  HDF/netCDF FILE(s) - GEOMS compliant HDF4, HDF5 or netCDF files.  Input can be by DIALOG_BOX'
errtxt[7]='    (IDL VM or full version of IDL), or by command line (full version of IDL only).'
errtxt[9]='Choice of output is made by selecting options in this DIALOG_BOX (IDL VM or full version'
errtxt[10]='  of IDL), or by ''Keywords'' at the command line input (full version only), as follows:'
errtxt[11]='  /F or /FORMAT - generates two output files (with .META and .DATA extensions).  The resulting'
errtxt[12]='    files can be used as inputs to GEOMS compliant write programs (IDLcr8HDF etc), OR'
errtxt[13]='  /D or /DUMP - generates two output files (with .META and .DATA extensions).  Data values'
errtxt[14]='    will be shown as indicated in the array format defined by VAR_SIZE, AND/OR'
errtxt[15]='  /C or /CATALOG - sends variable index, variable name, data format, and dimension size'
errtxt[16]='    information to an output window and/or log file, AND/OR'
errtxt[17]='  /P or /POPUP - sends log input/output information to a Pop-up Dialog Box, AND/OR'
errtxt[18]='  /L or /LOG - sends log input/output information to a file named idlcr8ascii.log'
errtxt[19]='  /H4 - outputs contents of the input file as an HDF4 file'
errtxt[20]='  /H5 - outputs contents of the input file as an HDF5 file'
errtxt[21]='  /NC3 - outputs contents of the input file as a netCDF3 file'
errtxt[23]='Example of command line input:  idlcr8ascii,DFSpec[,/F][,/D][,/C][,/P][,/L][,/H4][,/H5][,/NC3]'
errtxt[24]='  where ''DFSpec'' can either be a string array containing filenames or a scalar string containing a'
errtxt[25]='  filespec or a single file name.'
errtxt[26]='If running the full version of IDL, and input is a single HDF file, output can also be saved'
errtxt[27]='  to session memory.  This can be done by calling idlcr8ascii with the following command line:'
errtxt[28]='  idlcr8ascii,DFFile,GA,SDS[,/F][,/D][,/C][,/P][,/L][,/H4][,/H5][,/NC3].'
errtxt[29]='DFFile can either be the name of an input file or '''' (in which case a DIALOG_BOX will open'
errtxt[30]='  and prompt for the input filename).  ''GA'' is a returned string array containing the Global'
errtxt[31]='  Attributes, and ''SDS'' is a returned structure using pointers containing the Variable'
errtxt[32]='  Attribute Labels (sds.va_l), Variable Attribute Values (sds.va_v), and the Data (sds.data).
errtxt[34]='Contacts -'
errtxt[35]='  Ian Boyd, Bryan Scientific Consulting LLC (iboyd@bryanscientific.org)'
errtxt[36]='  9 Cambridge Terrace'
errtxt[37]='  Masterton 5810, New Zealand'
errtxt[39]='  Ann Mari Fjaeraa, EVDC Project Manager (amf@nilu.no)'
errtxt[40]='  Norwegian Institute for Air Research, Instituttveien 18'
errtxt[41]='  Postbox 100, N-2027 KJELLER, NORWAY'
errtxt[43]='EVDC Website: Tools and documentation available from http://evdc.esa.int/'
errtxt[45]='To continue, please choose from the options below.'
errtxt='        '+errtxt

;Set-up text display widget
demomode=LMGR(/DEMO) & optsens=1
IF intype EQ -3 THEN xtxt=' - Command Line Input Error' ELSE xtxt=''
base=WIDGET_BASE(Title='idlcr8ascii '+vertxt[1]+xtxt,Tlb_Frame_Attr=1,/Column) ;,Tab_Mode=1)
wtxt=WIDGET_TEXT(base,xsize=90,ysize=25,/Scroll)

tip='Left Mouse Click or Tab to entry and hit <Spacebar>'
base2=WIDGET_BASE(base,/Nonexclusive)
cattxt='Catalog - Generate a summary of the contents of the input file(s) and send to an output window or file'
poptext='Open a Pop-Up Window to display log input/output (default if ''Catalog'' option is chosen)'
logtext='Append log input/output to the file ''idlcr8ascii.log'' '
IF demomode THEN BEGIN
  logtext=logtext+'(No log or netCDF file output permitted in IDL DEMO Mode)' & optsens=0
ENDIF ELSE logtext=logtext+'(will create the file IF it doesn''t exist)'
h4text='Convert input file(s) to HDF4'
h5text='Convert input file(s) to HDF5'
nctext='Convert input file(s) to netCDF3'
fctext1='For the following 3 options, any existing files will be overwritten, IDLcr8HDF must be in the IDL Search Path,'
fctext2='and a valid Table Attribute Values (TAV) file must be in the same directory as the Input file(s).'
b1=WIDGET_BUTTON(base2,value=cattxt,uvalue='C',frame=3)
b2=WIDGET_BUTTON(base2,value=poptext,uvalue='P',frame=3)
b3=WIDGET_BUTTON(base2,value=logtext,uvalue='idlcr8ascii.log',sensitive=optsens,frame=3)

base3=WIDGET_LABEL(base)
WIDGET_CONTROL,base3,set_value=fctext1
base4=WIDGET_LABEL(base)
WIDGET_CONTROL,base4,set_value=fctext2

base5=WIDGET_BASE(base,/Nonexclusive,ROW=1)
b4=WIDGET_BUTTON(base5,value=h4text,uvalue='H4',frame=3)
b5=WIDGET_BUTTON(base5,value=h5text,uvalue='H5',frame=3)
b6=WIDGET_BUTTON(base5,value=nctext,uvalue='N3',sensitive=optsens,frame=3)

base6=WIDGET_BASE(base,/Row)
b7=WIDGET_BUTTON(base6,value='ASCII Formatted',uvalue='F',sensitive=optsens,frame=3) ;,Tooltip=Tip)
b8=WIDGET_BUTTON(base6,value='ASCII Dump',uvalue='D',sensitive=optsens,frame=3) ;,Tooltip=Tip)
b9=WIDGET_BUTTON(base6,value='Continue',uvalue='Cont',frame=3,Sensitive=0) ;,Tooltip=Tip)
b10=WIDGET_BUTTON(base6,value='Stop',uvalue='S',frame=3) ;,ToolTip=Tip)
WIDGET_CONTROL,base,/Realize
WIDGET_CONTROL,b1,/Input_Focus
FOR i=0,N_ELEMENTS(errtxt)-1 do $
  WIDGET_CONTROL,wtxt,set_value=errtxt[i],/Append
XMANAGER,'intro_a',base

END ;Intro_A



PRO idlcr8ascii_event, ev
;Procedure to close the pop-up logging window after user selects 'Finish'.
; ----------
;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org
;
;  History:
;    20061004: Introduced to IDLCR8ASCII - Version 2.0
;
;  Input: Selected widget event structure
;
;  Output: Nil
;
;  Called by: XMANAGER in IDLCR8ASCII and STOP_WITH_ERROR_A
;
;  Subroutines Called: None

WIDGET_CONTROL,ev.top,/DESTROY
RETALL & HEAP_GC

END ;Proc IDLcr8ASCII_Event



PRO stop_with_error_a, txt1, txt2, lu
;Procedure called when an error in the program is detected. An error message is displayed
;and the program stopped and reset. If necessary, open files are closed. The error message
;is displayed in one or more of the following: a Pop-up dialog window; the Pop-up logging
;window; the Output Logging window in the IDLDE.
; ----------
;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org
;
;  History:
;    20050729: Original IDLCR8ASCII Routine - Version 1.0
;    20061004: Set-up so that the error output is displayed in the output window dependent on the
;              method that IDLCR8ASCII is called. If txt1 is preceeded by 'D_' or is null, the
;              error output is to a Pop-up Dialog window. Otherwise output will be to a logging
;              window. Renamed from STOP_WITH_ERROR to STOP_WITH_ERROR_A to avoid a name conflict
;              with the equivalent procedure in IDLCR8HDF. Common variable definition WIDGET_WIN_A
;              added - Version 2.0
;    20080302: Added code which sends the error message to the IDLDE output window and/or an external
;              file (as determined by the dux array values) - Version 3.0
;    20101122: Allow routine to return to the calling program, rather than stop the application, if
;              a 'reterr' argument is included in the call to idlcr8ascii - Version 4.0b1

;
;  Inputs: txt1 - the initial text line of the error message. Generally contains the name of the routine
;                 where the error was detected.
;          txt2 - the second text line which generally describes the error state.
;          lu - Where applicable, the file unit that needs to be closed at the termination of the program,
;               otherwise set to -1.
;
;  Output: Nil
;
;  Called by: The routine in which the error was detected. The following routines call STOP_WITH_ERROR:
;             READ_HDF_SDS; IDLCR8ASCII
;
;  Subroutines Called: IDLCR8ASCII_EVENT (via XMANAGER)

COMMON WIDGET_WIN_A

IF lu NE -1L THEN FREE_LUN,lu

IF txt1 EQ '' THEN BEGIN ;<cancel> chosen on Intro box
  res=DIALOG_MESSAGE('IDLcr8ASCII Stopped!',/Information,Title='EVDC/AVDC IDLcr8ASCII')
ENDIF ELSE BEGIN
  IF STRMID(txt1,0,2) EQ 'D_' THEN txtx=STRMID(txt1,2) ELSE txtx=txt1
  FOR i=dux[0],dux[1],dux[2] DO BEGIN
    IF i EQ -1 THEN BEGIN
      PRINT,'  ERROR in '+txtx & PRINT,'  '+txt2
      PRINT,'' & PRINT,'IDLcr8ASCII stopped - Program Ended on '+SYSTIME(0)
    ENDIF ELSE BEGIN
      PRINTF,i,'  ERROR in '+txtx & PRINTF,i,'  '+txt2
      PRINTF,i,'' & PRINTF,i,'IDLcr8ASCII stopped - Program Ended on '+SYSTIME(0)
    ENDELSE
  ENDFOR
  IF dux[1] GT -1 THEN FREE_LUN,dux[1]
  IF (STRMID(txt1,0,2) EQ 'D_') AND (rerr EQ 'NA') THEN BEGIN
    ;write error to DIALOG Box instead of Popup window
    errtxt2=STRARR(4)
    errtxt2[0]=STRMID(txt1,2) & errtxt2[1]=txt2 & errtxt2[3]='IDLcr8ASCII Stopped!'
    res=DIALOG_MESSAGE(errtxt2,/Error,Title='EVDC/AVDC IDLcr8ASCII Error')
  ENDIF ELSE IF rerr EQ 'NA' THEN BEGIN ;write error to Popup window
    lineno=lineno+4L
    WIDGET_CONTROL,wtxt,set_value='    ERROR in '+txt1,/Append
    WIDGET_CONTROL,wtxt,set_value='    '+txt2,/Append
    WIDGET_CONTROL,wtxt,set_value='',/Append,Set_text_top_line=lineno
    WIDGET_CONTROL,wtxt,set_value='HDF file read stopped - hit <Finish> to close program',/Append
    WIDGET_CONTROL,b3,Sensitive=1,/Input_Focus
    XMANAGER,'stop_with_error_a',base,Event_Handler='idlcr8ascii_event'
  ENDIF
ENDELSE
HEAP_GC
IF rerr EQ 'NA' THEN RETALL ELSE rerr='Unable to read HDF or NC file - '+txt2

END ;Procedure Stop_With_Error_A



FUNCTION is_a_number_ascii, value
  ON_IOERROR, ConversionError
  IF STRTRIM(value,2) EQ '' THEN RETURN, 0B
  n=DOUBLE(value)
  RETURN, 1B
  ConversionError:
  RETURN, 0B
END



FUNCTION alpha_numeric_underscore, str_value
;Function that returns the input string with non-alphanumeric characters replaced with '_'
; ----------
;Written by Ian Boyd for the EVDC - iboyd@bryanscientific.org
;
;  History:
;    20190806: Introduced - Version 4.0b22
;
;  Input: str_value: string
;
;  Returns: string with non-alphanumeric values replaced with '_'
;
;  Called by: Read_HDF_SDS
;
ON_IOERROR, ConversionError
numb=BINDGEN(10)+48B ;0-9
ucb=BINDGEN(26)+65B  ;A-Z
lcb=BINDGEN(26)+97B  ;a-z
str_value=STRING(str_value)
new_str_value=str_value
n_char=STRLEN(str_value)
FOR i=0L,n_char-1L DO BEGIN
  byt_value=BYTE(STRMID(new_str_value,i,1))
  testn=(byt_value GE numb[0]) AND (byt_value LE numb[9])
  testu=(byt_value GE ucb[0]) AND (byt_value LE ucb[25])
  testl=(byt_value GE lcb[0]) AND (byt_value LE lcb[25])
  IF (~testn) AND (~testu) AND (~testl) THEN $
    STRPUT,new_str_value,'_',i
ENDFOR
RETURN, new_str_value
ConversionError:
RETURN, 'io_error'

END ;Function alpha_numeric_underscore



PRO infotxt_output_a, in0, in1
;Procedure called to report information relevant to the reading of the HDF file.
;This information can be reported to a Pop-up logging window, IDLDE output log window, and/or
;the log file. Code for this output was originally written directly in the affected procedures
; ----------
;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org
;
;  History:
;    20090311: Introduced to IDLCR8ASCII - Version 3.02
;    20091208: Added all the INFORMATION text messages to the routine, to avoid duplication
;              in the HDF4 and HDF5 read sections of READ_HDF_SDS - Version 3.03
;    20160213: Update information message when in0[0] EQ 13 - Version 4.0b13
;    20161130: Added in0[0] messages 18 and 19 to cover non-GEOMS metadata attributes issues
;              - Version 4.0b16
;    20220805: Modified message 2 (full message created in DATA_TYPE_CHECKS) and added messages
;              23 and 24 - Version 4.0b25
;    20240912: Changed WARNING message in 23 so that it is only generated when the NC3 dataset
;              is of type BYTE - Version 4.0b26
;
;  Inputs: in0 - Integer array containing input used to make correct text output
;          in1 - String array containing input used to make correct text output
;
;  Output: Nil
;
;  Called by: The routine in which the request for INFORMATION text was made.
;             The following routines call INFOTXT_OUTPUT_A: READ_HDF_SDS; OUTPUT_HDF_DATA;
;             IDLCR8ASCII
;
;  Subroutines Called: None

COMMON WIDGET_WIN_A

CASE 1 OF
  in0[0] EQ 0: BEGIN
      infotxt=STRARR(3)
      IF LONG(in1[0]) EQ 0L THEN BEGIN
        infotxt[0]='  INFORMATION: Global Attribute DATA_VARIABLES not found or has no values.'
        infotxt[1]='    Dataset listing order matches the order that the dataset is read in the GEOMS file.'
      ENDIF ELSE BEGIN
        infotxt[0]='  INFORMATION: Number of Global Attribute DATA_VARIABLES values ('+in1[0]+')'
        infotxt[1]='    does not match the number of datasets saved to the GEOMS file ('+in1[1]+')'
      ENDELSE
      infotxt[2]='    Number of datasets determined from '+in1[2]+' call'
    END
  in0[0] EQ 1: infotxt='  INFORMATION: '+in1[0]+' has '+in1[1]+' data dimensions'
  in0[0] EQ 2: infotxt='  INFORMATION: '+in1[0]
  in0[0] EQ 3: BEGIN
      infotxt=STRARR(4)
      IF (STRPOS(STRUPCASE(in1[1]),STRUPCASE(in1[0])) NE -1) AND (in0[1] EQ 4) AND (in0[2] EQ 0) THEN $
        infotxt[0]='  INFORMATION: Dataset Name is truncated (File created with HDF4.2r1 library or earlier):' $
      ELSE infotxt[0]='  INFORMATION: Dataset Name does not match VAR_NAME value:'
      infotxt[1]='    SDS_NAME: '+in1[0]
      infotxt[2]='    VAR_NAME: '+in1[1]
      IF in0[2] EQ 0 THEN infotxt[3]='    Output uses VAR_NAME from Metadata' $
      ELSE infotxt[3]='    Output uses Dataset Name from the HDF file'
    END
  in0[0] EQ 4: BEGIN
      infotxt=STRARR(2)
      IF in0[1] EQ 0 THEN BEGIN
        infotxt[0]='  INFORMATION: Metadata label VAR_NAME not found for dataset:'
        infotxt[1]='    SDS_NAME: '+in1[0]
      ENDIF ELSE IF (in0[2] EQ 0) AND (in0[3] NE 0L) THEN BEGIN
        infotxt[0]='  INFORMATION: VAR_NAME value does not match any DATA_VARIABLES values:'
        infotxt[1]='    VAR_NAME: '+in1[1]
      ENDIF
    END
  in0[0] EQ 5: BEGIN
      infotxt=STRARR(2)
      infotxt[0]='  INFORMATION: No Variable Attributes returned after call to '+in1[1]
      infotxt[1]='    SDS_NAME: '+in1[0]
    END
  in0[0] EQ 6: BEGIN
      infotxt='  INFORMATION: Metadata Dataset listing order may not match the order of'
      infotxt=infotxt+' the Global Attribute DATA_VARIABLES Values'
    END
  in0[0] EQ 7: BEGIN
      infotxt='  INFORMATION: Can read one HDF or netCDF file into session memory.'
      infotxt=infotxt+' Only first file in the array will be read'
    END
  in0[0] EQ 8: BEGIN
      infotxt='  INFORMATION: '+in1[0]+' is a coordinate variable'
    END
  in0[0] EQ 9: BEGIN
      infotxt=STRARR(3)
      infotxt[0]='  INFORMATION: Calibrated data in dataset '+in1[0]
      infotxt[1]='    has been converted back to its original form using the formula:'
      infotxt[2]='    Orig_Data = Scale_Factor * (Cal_Data - Offset)'
    END
  in0[0] EQ 10: BEGIN
      infotxt=STRARR(2)
      infotxt[0]='  INFORMATION: VAR_DATA_TYPE='+in1[1]+' for Calibrated data in dataset '+in1[0]
      infotxt[1]='    has been changed to the original datatype of '+in1[2]
    END
  in0[0] EQ 11: BEGIN
      infotxt=STRARR(2)
      CASE 1 OF
        in0[1] EQ 20: BEGIN
            infotxt[0]='  INFORMATION: Order of multi-dimensional datasets changed to match the programming'
            infotxt[0]=infotxt[0]+' language convention:
            infotxt[1]='    IDL/Fortran - fastest changing dimension first (datasets transposed when'
            infotxt[1]=infotxt[1]+' reading/writing to the HDF file)'
          END
        in0[1] MOD 10 EQ 0: BEGIN
            infotxt[0]='  INFORMATION: Listing order of multi-dimensional VAR_DEPEND and VAR_SIZE values'
            infotxt[1]='    in the HDF file changed to match corresponding dataset dimension ordering'
          END
        ELSE: BEGIN
            infotxt[0]='  INFORMATION: Unable to determine the correct listing order of multi-dimensional'
            infotxt[1]='    VAR_SIZE and VAR_DEPEND values in the HDF file'
          END
      ENDCASE
    END
  in0[0] EQ 12: BEGIN
      infotxt=STRARR(2)
      infotxt[0]='  INFORMATION: Table Attribute Values file must be in the same directory as the input file'
      infotxt[1]='    if wanting to convert from one Data Format to another'
    END
  in0[0] EQ 13: BEGIN
      infotxt=STRARR(2)
      infotxt[0]='  INFORMATION: IDLcr8HDF called but it is not in the IDL Search Path or it encountered an'
      infotxt[1]='    unexpected error. If the latter please contact Ian Boyd at iboyd@bryanscientific.org'
    END
  in0[0] EQ 14: BEGIN
      infotxt=STRARR(2)
      infotxt[0]='  INFORMATION: /POPUP keyword cannot be used together with the ''reterr'' argument.'
      infotxt[1]='    The request for a POPUP window has been ignored'
    END
  in0[0] EQ 15: BEGIN
      infotxt=STRARR(2)
      infotxt[0]='  INFORMATION: Argument ''reterr'' must be of type string.' ;a returnable variable of type string.'
      infotxt[1]='    idlcr8ascii will stop normally if an error is encountered'
    END
  in0[0] EQ 16: BEGIN
      infotxt=STRARR(2)
      infotxt[0]='  INFORMATION: /LOG, /FORMAT, and /DUMP keywords cannot be used in IDL DEMO mode and will be ignored.'
      infotxt[1]='    To generate ASCII files please use the free IDL Virtual Machine or a licenced version of IDL'
    END
  in0[0] EQ 17: BEGIN
      infotxt=STRARR(2)
      infotxt[0]='  INFORMATION: NetCDF file create feature is disabled in IDL DEMO mode.'
      infotxt[1]='    To generate files please use the free IDL Virtual Machine or a licenced version of IDL'
    END
  in0[0] EQ 18: BEGIN
      infotxt='  INFORMATION: Value for Attribute Label '+in1[0]+' is an invalid Data Type'
    END
  in0[0] EQ 19: BEGIN
      infotxt='  INFORMATION: '+in1[0]+' is not a valid GEOMS Metadata Attribute'
    END
  in0[0] EQ 20: BEGIN
      infotxt='  INFORMATION: '+in1[0]+' entry must be written to the file as a STRING for '+in1[1]
    END
  in0[0] EQ 21: BEGIN
      infotxt='  INFORMATION: '+in1[0]+' sub-values must be separated by '';'' for '+in1[1]
    END
  in0[0] EQ 22: BEGIN
      infotxt='  INFORMATION: Spaces not permitted in the '+in1[0]+' entry for '+in1[1]
    END    
  in0[0] EQ 23: BEGIN
      infotxt=STRARR(2)
      itxt1='  WARNING: This program can''t check that BYTE datasets and attributes contain only'
      itxt2=' 8-bit unsigned values (0-255) in netCDF3 files.'
      infotxt[0]=itxt1+itxt2
      itxt1='    Please check that the '+in1[0]+' dataset and attributes'
      itxt2=' with BYTE data type do not contain negative 8-bit signed values'
      infotxt[1]=itxt1+itxt2
    END
  in0[0] EQ 24: BEGIN
      infotxt=STRARR(2)
      infotxt[0]='  INFORMATION: Input file name does not match the Global Attribute entry'
      infotxt[1]='    FILE_NAME='+in1[0]
    END
ENDCASE

dm=SIZE(infotxt,/N_Elements)
IF o3[3] EQ '' THEN lineno=lineno+dm
FOR n=0,dm-1 DO BEGIN
  IF o3[3] EQ '' THEN BEGIN
    IF n EQ dm THEN WIDGET_CONTROL,wtxt,set_value='',/Append $
    ELSE WIDGET_CONTROL,wtxt,set_value=infotxt[n],/Append,Set_text_top_line=lineno
  ENDIF
  FOR m=dux[0],dux[1],dux[2] DO $
    IF m EQ -1 THEN PRINT,infotxt[n] ELSE PRINTF,m,infotxt[n] ;write out to log(s)
ENDFOR

END ;Procedure InfoTxt_Output_A



FUNCTION jdf_2_datetime, jdf, MJD2000=mjd2000, SHORTISO8601=iso, LONGISO8601=isoms
;Computes the UT date/time from JDF/MJD2000.
; ----------
;   Bojan R. Bojkov
;   bojan.r.bojkov@nasa.gov
;   03/04/2004
;   03/11/2004 bug fix
;   05/27/2005 add ShortISO8601 and LongISO8601 switches,
;              and change seconds value to decimal seconds (Ian Boyd)
;
; References ----------
;   Explan. Supp. Astron. Almanac, p.604 (1992); through E. Celarier
;
; Caveats ----------
;   Valid for all YYYY >= -4712 (i.e. for all JD .ge. 0)
;   The true Gregorian calendar was only adopted on 15 October 1582,
;   so any dates before this are "virtual" Gregorian dates.
;
; Input ----------
;   jdf     : double precision value
;   mjd2000 : flag if input is MJD2000
;   iso     : flag if output is in ISO8601 (YYYYMMDDThhmmssZ)
;   isoms   : flag if output is in ISO8601ms (YYYYMMDDThhmmss.sssZ)
;
; Output ---------
;   floating point array [YYYY, MM, DD, hh, mn, ss.sss],
;   or ISO8601 string format
; External subroutines ---------
;   NONE

jdf=DOUBLE(jdf)

j0=2451544.5D
IF KEYWORD_SET(mjd2000) THEN jdhold=jdf+j0 ELSE jdhold=jdf
jdi=LONG(jdhold)
df=jdhold-jdi

;Determine hh, mm, ss, ms
hh=(df+0.5)*24.D
q=WHERE(hh GE 24.D)
IF q[0] NE -1 THEN BEGIN
  hh[q]=hh[q]-24.D
  jdi[q]=jdi[q]+1
ENDIF
t1=hh
hh=LONG(t1)
t2=(t1-hh)*60.D
mn=LONG(t2)
t3=(t2-mn)*60.D
ss=t3
;ss=LONG(t3)
;ms=LONG((t3-ss)*1.d3)

;Determine YYYY, MM, DD
t1=jdi+68569L
t2=(4*t1)/146097L
t1=t1-(146097L*t2+3L)/4L
t3=(4000L*(t1+1L))/1461001L
t1=t1-(1461L*t3)/4L + 31L
t4=(80L*t1)/2447L

dd=t1-(2447L*t4)/80L
t1=t4/11L
mm=t4+2L-12L*t1
yyyy=100L*(t2-49L)+t3+t1

dt=TRANSPOSE([[yyyy],[mm],[dd],[hh],[mn],[ss]])
IF (KEYWORD_SET(iso)) OR (KEYWORD_SET(isoms)) THEN BEGIN
  dts=STRARR(6)
  IF (KEYWORD_SET(iso)) AND (ss-FIX(ss) GE 0.5) THEN BEGIN
    ;recalculate to get correct seconds value
    jdhold=jdf+0.000008D ;add ~0.7 secs
    IF KEYWORD_SET(mjd2000) THEN jdhold=jdhold+j0
    CALDAT,jdhold,mm,dd,yyyy,hh,mn,ss
    dt=TRANSPOSE([[yyyy],[mm],[dd],[hh],[mn],[ss]])
  ENDIF
  dt=LONG(dt)
  FOR i=1,5 DO $
    IF dt[i] LT 10L THEN dts[i]='0'+STRTRIM(dt[i],2) ELSE dts[i]=STRTRIM(dt[i],2)
  IF KEYWORD_SET(isoms) THEN BEGIN
    ssd=STRING(format='(f6.3)',ss)
    IF FLOAT(ssd) LT 10.0 THEN dts[5]='0'+STRTRIM(ssd,2) ELSE dts[5]=STRTRIM(ssd,2)
  ENDIF
  RETURN,STRTRIM(dt[0],2)+dts[1]+dts[2]+'T'+dts[3]+dts[4]+dts[5]+'Z'
ENDIF ELSE RETURN, dt

END ;Function jdf_2_datetime



FUNCTION julian_date, date_ut, ISO8601=iso, MJD2000=mjd2000
;Computes the Julian date (jd) or date in MJD2000 format in double precision.
; ----------
;   Bojan R. Bojkov
;   bojan.r.bojkov@nasa.gov
;   06/03/2001
;   09/24/2001: Code cleanup                        - Function Version 1.0
;   10/16/2001: Mode corrections                    - Function Version 1.01
;   03/07/2002: Comment cleanup and variable change - Function Version 2.0
;   10/08/2002: Added jdf option                    - Function Version 2.1
;   07/21/2003: Converted to a function             - Function Version 3.0
;   05/25/2005: Added ISO8601 and MJD2000 keywords.
;               Input can either be a string or a numeric array
;               (see below for input details). Returns -99999.d
;               if the Input is invalid - Ian Boyd
;
; References ----------
;   Hughes, D.W., Yallop, B.D. and Hohenkerk, C.Y., "The Equation of Time",
;     Mon. Not. R. astr. Soc., 238, pp 1529-1535. 1989.
;
;   Results verified using:
;     Meeus, J, "Astronomical Algorithms", William-Bell, Inc., Richmond VA,
;     p62, 1991.
;
; Caveats ----------
;   NONE
;
; Input ----------
;   date_ut: Numeric array (UT: YYYY, MM, DD, [hh, mm, ss]), or if ISO8601
;            keyword set, a string containing datetime as YYYYMMDDThhmmssZ
;
; Output ---------
;   jd:      julian day, or if MJD2000 keyword set,
;            a double precision day in MJD2000 format
;
; External subroutines ---------
;   NONE

ON_IOERROR,TypeConversionError
valid=0 ;Type conversion check
inputerror=0 ;Check that input is as expected
;Check input format
IF KEYWORD_SET(iso) THEN BEGIN ;input is YYYYMMDDThhmmssZ
  IF STRLEN(date_ut) EQ 16 THEN BEGIN
    ;test for numeric values (will return type conversion error value if not a number)
    FOR i=0,7 DO check=FIX(STRMID(date_ut,i,1))
    FOR i=9,14 DO check=FIX(STRMID(date_ut,i,1))
    dt=INTARR(6)
    dt[0]=FIX(STRMID(date_ut,0,4)) & dt[1]=FIX(STRMID(date_ut,4,2))
    dt[2]=FIX(STRMID(date_ut,6,2)) & dt[3]=FIX(STRMID(date_ut,9,2))
    dt[4]=FIX(STRMID(date_ut,11,2)) & dt[5]=FIX(STRMID(date_ut,13,2))
    date_ut=dt & achk=[0,6] ;to add hhmmss component
  ENDIF ELSE inputerror=1
ENDIF ELSE BEGIN
  ;check that input contains at least YMD (and at most YMDhms) information and is either
  ;an integer, long, float or double array
  achk=SIZE(date_ut)
  IF (achk[1] LT 3) OR (achk[1] GT 6) OR (achk[2] LT 2) OR (achk[2] GT 5) THEN inputerror=1
ENDELSE

IF inputerror EQ 0 THEN BEGIN ;can perform JD calculation
  IF date_ut[1] GT 2 THEN BEGIN
    y=DOUBLE(date_ut[0])
    m=DOUBLE(date_ut[1]-3)
    d=DOUBLE(date_ut[2])
  ENDIF ELSE BEGIN
    y=DOUBLE(date_ut[0]-1)
    m=DOUBLE(date_ut[1]+9)
    d=DOUBLE(date_ut[2])
  ENDELSE

  ;Compute Julian date:
  j=LONG(365.25D0*(y+4712.D0))+LONG(30.6D0*m+0.5D0)+59.D0+d-0.5D0

  ;Check for Julian or Gregorian calendar:
  IF j LT 2299159.5D0 THEN jd=j $ ;If Julian calendar.
  ELSE BEGIN ;If Gregorian calendar.
    gn=38.D0-LONG(3.D0*LONG(49.D0+y/100.D0)/4.D0)
    jd=j+gn
  ENDELSE

  ;add hhmmss values if present
  CASE 1 OF
    achk[1] EQ 4: jd=jd+DOUBLE(date_ut[3])/24.D ;hh only
    achk[1] EQ 5: jd=jd+(DOUBLE(date_ut[3])*3600.D + DOUBLE(date_ut[4])*60.D)/86400.D ;hhmm only
    achk[1] EQ 6: jd=jd+(DOUBLE(date_ut[3])*3600.D + DOUBLE(date_ut[4])*60.D + $
                         DOUBLE(date_ut[5]))/86400.D ;hhmmss
    ELSE:
  ENDCASE

  valid=1 ;Type conversion OK

  ;Set to MJD2000 if required
  IF KEYWORD_SET(mjd2000) THEN jd=jd-2451544.5D
ENDIF

TypeConversionError:
IF valid EQ 0 THEN RETURN,-99999.D ELSE RETURN, jd

END ;Function Julian_Date



PRO nc_dimension_chk, ftype, sd_id, di, n_sds, dgi, n_sdsg
;Procedure to check for netCDF dimension datasets. These will be quietly ignored during the
;read process 
; ----------
;Written by Ian Boyd for the EVDC - iboyd@bryanscientific.org
;
;  History:
;    20190806: Introduced - Version 4.0b22
;
;  Inputs: ftype - format type, HDF5 or netCDF
;          sd_id - GEOMS file identifier number
;          di    - index values for datasets
;          n_sds - number of datasets determined by H5G_GET_NMEMBERS call
;
;  Outputs: dgi    - index values for GEOMS datasets (not generated by netCDF libraries)
;           n_sdsg - number of GEOMS datasets
;
;  Called by: Read_HDF_SDS
;
dimen_names=['constant','independent_','_strlen']
n_dn=N_ELEMENTS(dimen_names)
ds_names=STRARR(n_sds) & gdi=INTARR(n_sds)-1
FOR i=0,n_sds-1 DO BEGIN
  IF ftype EQ 'H5' THEN sds_name=H5G_GET_MEMBER_NAME(sd_id,'/',di[i]) $
  ELSE BEGIN ;netCDF file  
    varstruct=NCDF_VARINQ(sd_id,i)
    sds_name=varstruct.name
  ENDELSE  
  ds_names[i]=STRTRIM(STRLOWCASE(sds_name),2)
ENDFOR

FOR i=0,n_sds-1 DO BEGIN
  dnicnt=0 & j=0
  WHILE (dnicnt EQ 0) AND (j LE n_dn-1) DO $
    IF STRPOS(ds_names[i],dimen_names[j]) NE -1 THEN dnicnt++ ELSE j++
  IF dnicnt NE 0 THEN BEGIN
    ;Check that names are actually netCDF dimension names o/w will treat as a dataset name
    CASE j OF
      0: IF ds_names[i] NE dimen_names[j] THEN dnicnt=0 ;i.e. not a netCDF dimension name
      1: BEGIN
           ;check that first part of the name is INDEPENDENT_ and the second part is a number
           IF STRMID(ds_names[i],0,STRLEN(dimen_names[j])) NE dimen_names[j] $
             THEN dnicnt=0 $
           ELSE BEGIN
             res=STRSPLIT(ds_names[i],'_',/EXTRACT,COUNT=cres)
             res=[res,''] ;to ensure at least 2 extracted values in ds_names[i] 
             if ~IS_A_NUMBER_ASCII(res[1]) THEN dnicnt=0 ; i.e. not a netCDF dimension name
           ENDELSE        
         END
      2: BEGIN
          ;check that first part of the name is a named dataset
          res=STRSPLIT(ds_names[i],'_',/EXTRACT)
          ni=WHERE(res[0] EQ ds_names,ncnt)
          IF ncnt EQ 0 THEN dnicnt=0 ;i.e. not a netCDF dimension name
         END
    ENDCASE
  ENDIF
  IF dnicnt EQ 0 THEN gdi[i]=i ;i.e. this is a valid dataset name and not a netCDF dimension name
ENDFOR

dgi=WHERE(gdi NE -1,n_sdsg) ;dgi and n_sdsg are returned by the procedure

END ;Procedure nc_dimension_chk



FUNCTION file_format_a, infile
;Function to identify the format of the input file - tests for HDF4, HDF5/netCDF4 and netCDF3.
;netCDF4 files will be identified as HDF5 and read in using the HDF5 library
; ----------
;Written by Ian Boyd for the EVDC - iboyd@bryanscientific.org
;
;  History:
;    20190806: Introduced - Version 4.0b22
;    20220805: Additional checks for netCDF4 files if the file signature ia HDF5 - Version 4.0b25
;
;  Inputs: infile - Input GEOMS compliant file
;
;  Outputs: string identifying the type of file (H4, H5, NC) or XX for unidentified file
;
;  Called by: IDLCR8ASCII
;
;Markers
;HDF4: 14   3  19   (1   0  16   0   1  95  98   0  30)
;HDF5: 137  72  68  70 (13  10  26  10   0   0   0   0)
;nc3:  67  68  70   (1   0   0   0   0   0   0   0  10)
;nc4:  137  72  68  70 (13  10  26  10  (2   8   8   0))

h4id=[14B, 3B, 19B]
h5id=[137B, 72B, 68B, 70B] ;HDF
nc3id=[67B, 68B, 70B] ;CDF

;First test using start of file byte markers
fid=BYTARR(4)
openr,fu,infile,/GET_LUN
readu,fu,fid
FREE_LUN,fu

idfound='XX'
CASE 1 OF
  ARRAY_EQUAL(fid[0:2],h4id): idfound='H4'
  ARRAY_EQUAL(fid,h5id): idfound='H5'
  ARRAY_EQUAL(fid[0:2],nc3id): idfound='N3'
  ELSE:
ENDCASE

;Second test, if required, using IDL commands
IF idfound EQ 'XX' THEN BEGIN
  IF HDF_ISHDF(infile) EQ 1 THEN idfound='H4' $
  ELSE IF FLOAT(!Version.Release) GE 5.6 THEN BEGIN
    IF H5F_IS_HDF5(infile) EQ 1 THEN idfound='H5' ;netCDF4 or HDF5
  ENDIF
  IF idfound EQ 'XX' THEN BEGIN
    ;final test for netCDF3
    validname=1
    CATCH, ncdferror ;To catch the error when attempting to open the file as a netCDF
    ;Error Handler
    IF ncdferror NE 0 THEN BEGIN ;not a netCDF file
      validname=0
      CATCH, /CANCEL
    ENDIF
    IF validname EQ 1 THEN BEGIN
      fileid=NCDF_OPEN(infile)
      ;If get to here then file is netCDF
      CATCH, /CANCEL
      NCDF_CLOSE,fileid
      idfound='N3'
    ENDIF
  ENDIF
ENDIF

IF idfound EQ 'H5' THEN BEGIN
  ;For H5, try to identify whether it is H5 or N4
  nc4ext=['nc','nc4','netcdf','netcdf4']  
  fileext=STRMID(infile,STRPOS(infile,'.',/REVERSE_SEARCH)+1)
  nci=WHERE(STRLOWCASE(fileext) EQ nc4ext,nccnt)
  
  vr=!Version.Release ;IDL version check (needs to be 8.5.2 or greater for NCDF_PARSE check
  vrlast=STRMID(vr,STRPOS(vr,'.',/REVERSE_SEARCH)+1)
  IF (FLOAT(vr) GT 8.5) OR ((STRMID(vr,0,3) EQ '8.5') AND (FIX(vrlast GE 2))) THEN parsechk=1B ELSE parsechk=0B
  IF (nccnt NE 0) AND (parsechk) THEN BEGIN
    ;Catch error from attempting to parse non-NetCDF-4 compliant HDF5 file
    validname=1
    CATCH, ncdferror ;To catch the error when attempting to open the file as a netCDF
    ;Error Handler
    IF ncdferror NE 0 THEN BEGIN ;not a netCDF file
      validname=0
      CATCH, /CANCEL
    ENDIF
    IF validname EQ 1 THEN BEGIN
      fileid=NCDF_PARSE(infile)
      ;If get to here then file can be opened using N4 libraries, so most likely a netcdf4 file
      CATCH, /CANCEL
      idfound='N4'
    ENDIF
  ENDIF ELSE IF nccnt NE 0 THEN idfound='N4' ;Unable to do NCDF_PARSE check but still likely nc4
ENDIF

RETURN, idfound

END ;File_Format_A



PRO test_dim_order, va_name, va_value, va_type, sds_dim,  sds_name, rev_vd_vs
;Procedure to test the dimension ordering of any multi-dimensional datasets in the input DF file, and
;return correct ordering code (dimension ordering for idlcr8ascii uses IDL/Fortran convention)
; ----------
;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org
;
;  History:
;    20111208: Introduced - Version 4.0b4
;    20140325: Fix bug that meant VAR_SIZE and VAR_DEPEND values were not being automatically
;              reversed if the array sizes were the same and VAR_DEPEND did not include a DATETIME
;              value. Now defaults to the 'reverse' option if no other conditions are satisfied;
;              Fix bug that caused crash when VAR_SIZE values were not of type string - Version 4.0b8
;    20141110: Fix bug that caused multi-dimensional array ordering to not be correctly identified
;              if the first dataset to be checked in the file had the same number of elements in
;              the array (e.g. was a set of Averaging Kernels) - Version 4.0b9
;    20200930: Add INFORMATION messages (20-22) that identify issues with the VAR_DEPEND and VAR_SIZE
;              attributes that were previously fixed 'quietly' by the program; Add sds_name to the 
;              variables required by the procedure - Version 4.0b24          
;
;  Inputs: va_name - An abbreviated version of the Variable Name; either 'VD' (VAR_DEPEND) or 'VS'
;                    (VAR_SIZE)
;          va_value - The corresponding Variable Value
;          sds_dim - Dimension information for the DF Dataset being tested
;          sds_name - Dataset name, required if an INFORMATION message is generated 
;
;  Outputs: rev_vd_vs - Scalar to indicate whether VAR_DEPEND and VAR_SIZE values (and data, in the case
;                       of netCDF measurements) need to be reversed
;
;  Called by: Read_HDF_SDS

IF va_type NE 'STRING' THEN BEGIN ;VAR_SIZE contains numeric values instead of in the form of a string
  vavhold='' & n_vav=N_ELEMENTS(va_value)
  FOR k=0,n_vav-1 DO BEGIN
    IF k EQ n_vav-1 THEN vtxt='' ELSE vtxt=';'
    vavhold=vavhold+STRTRIM(va_value[k],2)+vtxt
  ENDFOR
  IF va_name EQ 'VD' THEN vtxt='VAR_DEPEND' ELSE vtxt='VAR_SIZE' 
  INFOTXT_OUTPUT_A,[20],[vtxt,STRTRIM(sds_name,2)]
ENDIF ELSE vavhold=va_value
vs_v=STRCOMPRESS(STRSPLIT(vavhold,';, ',/EXTRACT,COUNT=rcnt),/REMOVE_ALL)

IF (rcnt GT 1) AND ((STRPOS(vavhold,';') EQ -1) OR (STRPOS(vavhold,',') NE -1)) THEN BEGIN
  IF va_name EQ 'VD' THEN itxt='VAR_DEPEND' ELSE itxt='VAR_SIZE'
  INFOTXT_OUTPUT_A,[21],[itxt,STRTRIM(sds_name,2)] ;sub-values should be separated by ';'
ENDIF

IF (rcnt EQ 1) OR ((rcnt GT 1) AND (STRPOS(vavhold,';') NE -1)) THEN BEGIN
  IF STRCOMPRESS(vavhold,/REMOVE_ALL) NE vavhold THEN BEGIN
    IF va_name EQ 'VD' THEN itxt='VAR_DEPEND' ELSE itxt='VAR_SIZE'
    INFOTXT_OUTPUT_A,[22],[itxt,STRTRIM(sds_name,2)] ;no spaces permitted
  ENDIF
ENDIF

IF va_name EQ 'VD' THEN BEGIN ;VAR_DEPEND tests
  dti=WHERE(STRUPCASE(vs_v) EQ 'DATETIME',dticnt)
  ;Note: if DATETIME has VAR_SIZE=1 then the ordering is dependent on the order of the VAR_SIZE values only
  IF (rcnt GT 1) AND (dticnt NE 0) THEN BEGIN ;multi-dimensions including DATETIME
    test1=(dti[0] EQ rcnt-1) AND (sds_dim[0] GT 1)
    ;DATETIME is the last value and present only once and has VAR_SIZE GT 1
    test2=(dti[0] EQ 0) AND (dticnt EQ 1) AND (sds_dim[0] GT 1)
    ;DATETIME is the first value and present only once and has VAR_SIZE GT 1
    IF (test1) AND (rev_vd_vs EQ 2) THEN rev_vd_vs=10 $
    ELSE IF (test2) AND (rev_vd_vs EQ 2) THEN rev_vd_vs=11 $
    ELSE IF ((test1) AND (rev_vd_vs MOD 10 EQ 1)) OR ((test2) AND (rev_vd_vs EQ 10)) THEN $
      rev_vd_vs=3 $ ;inconsistent rules regarding dimension ordering in file
    ELSE IF (test2) AND (rev_vd_vs EQ 0) THEN rev_vd_vs=20
    ;Dimension ordering is slowest changing first so change dataset ordering as well as
    ;VAR_DEPEND and VAR_SIZE ordering
  ENDIF
ENDIF ELSE BEGIN ;VAR_SIZE tests
  IF rcnt GT 1 THEN BEGIN ;multi-dimensions
    ;check all values are numeric
    numchk=STRJOIN(vs_v,/SINGLE) & valok=1
    FOR k=0,STRLEN(numchk)-1 DO BEGIN
      bchar=BYTE(STRMID(numchk,k,1))
      IF (bchar LT 48) OR (bchar GT 57) THEN valok=0 ;i.e. non-numeric character
    ENDFOR
    IF valok EQ 1 THEN BEGIN ;all values are numeric so do checks
      vs_vl=LONG(vs_v)
      test1=ARRAY_EQUAL(vs_vl,sds_dim)
      test2=ARRAY_EQUAL(REVERSE(vs_vl),sds_dim) ;test in case values are the same e.g. 40;40
      ;If arrays are the same size then the default will be to reverse the VS and VD values
      ;unless a different dimension ordering has already been identified
      IF (test1 EQ 1) AND (test2 EQ 0) AND (rev_vd_vs EQ 2) THEN rev_vd_vs=0 $
      ELSE IF (test1 EQ 0) AND (test2 EQ 1) AND (rev_vd_vs EQ 2) THEN rev_vd_vs=1 $
      ELSE IF ((test1 EQ 1) AND (test2 EQ 0) AND (rev_vd_vs MOD 10 EQ 1)) OR $
              ((test1 EQ 0) AND (test2 EQ 1) AND (rev_vd_vs MOD 10 EQ 0)) THEN $
        rev_vd_vs=3 $ ;inconsistent VAR_SIZE and sds_dim agreement from dataset to dataset
      ELSE IF (test1 EQ 1) AND (test2 EQ 1) AND (rev_vd_vs EQ 2) THEN $
        rev_vd_vs=2 $ ;first set of measurements tested have same dimensions so do not do anything
      ELSE IF rev_vd_vs EQ 2 THEN rev_vd_vs=1 ;default if no other criteria are found as this
      ;will then automatically reverse the VD and VS values
    ENDIF
  ENDIF
ENDELSE

END ;Procedure Test_Dim_Order



PRO data_type_checks, ftype, dt_chk_labels, hdftype, idltype, sds_name, var_name
;VAR_DATA_TYPE corresponds to the dataset data type and the VAR_VALID_MIN, VAR_VALID_MAX 
;and VAR_FILL_VALUES also match the dataset data type. Note limitation that 8-bit
;unsigned integer (BYTE) values cannot be identified in netCDF3 files
; ----------
;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org
;
;  History:
;    20220805: Introduced to IDLCR8ASCII - Version 4.0b25
;
;  Inputs: ftype - a string identifying the file type (H4, H5, N3 or N4)
;          dt_chk_labels - string array containing labels of attributes (plus 'Dataset')
;                          upon which the data type checks are done
;          hdftype - string array containing the data type returned by the respective
;                    ftype specific calls
;          idltype - string array containing the data types as IDL codes or names
;          sds_name - string holding dataset name returned by the respective ftype specific
;                     cals
;          var_name - string holding the VAR_NAME value (should be the same as sds_name)                                    
;
;  Outputs: N/A
;  Called by: READ_HDF_SDS
;
;  Subroutines Called: INFOTXT_OUTPUT_A (if program discovers an issue with the data type)
;    Information Conditions:
;      1. VAR_DATA_TYPE is not an allowable data type
;      2. Dataset, Valid Min, Valid Max and/or Fill Value data types do not match the VAR_DATA_TYPE (if applicable)
;      3. Valid Min, Valid Max and/or Fill Value data types do not match the Dataset data type (if applicable) 
;      3. Dataset, Valid Min, Valid Max and/or Fill value data types are not allowable

;Allowable VAR_DATA_TYPEs
avdt=['','BYTE','SHORT','INTEGER','REAL','DOUBLE','','STRING','','','','','','',''] ;'LONG'] ;corresponding to IDL codes 1,2,3,4,5,7,14 (LONG/14 not used)
idl_name=['UNDEFINED','BYTE','SHORT','INTEGER','REAL','DOUBLE','COMPLEX','STRING','STRUCT','DCOMPLEX', $
          'POINTER','OBJREF','UINT','ULONG','LONG64','ULONG64'] ;Note: Integer, Long and Float renamed Short, Integer and Real for sds_type
hdf_dt=['','DFNT_UINT8','DFNT_INT16','DFNT_INT32','DFNT_FLOAT32','DFNT_FLOAT64','','DFNT_CHAR8'] ;allowable HDF4 datatypes (note BYTE=8-bit unsigned)
;hdf_all=['DFNT_NONE','DFNT_UINT8','DFNT_INT16','DFNT_INT32','DFNT_FLOAT32','DFNT_FLOAT64','','DFNT_CHAR8', $
;         '','','','','DFNT_UINT16','DFNT_UINT32','DFNT_INT8']   
h5_dt=['H5T_INTEGER_0','H5T_INTEGER_1','H5T_FLOAT','H5T_STRING'] ;allowable HDF5/NC4 datatypes (0=unsigned, 1=signed)

;dt_chk_labels=['Dataset','VAR_VALID_MIN','VAR_VALID_MAX','VAR_FILL_VALUE','VAR_DATA_TYPE'] ;dataset and attribute labels for data type checks
n_lab=N_ELEMENTS(dt_chk_labels)
dterr=STRARR(n_lab) ;non-zero indicates error

IF var_name NE '' THEN usevn=var_name ELSE usevn=sds_name ;determine VAR_NAME value to use for error messages

IF (ftype EQ 'H5') OR (ftype EQ 'N4') THEN BEGIN
  ;need to convert hdftype and idltype values to equivalent idl_name and hdf_dt values
  FOR i=0,n_lab-2 DO BEGIN
    idltype[i]=idl_name[idltype[i]] ;convert IDL type code to type name
    CASE 1 OF
      hdftype[i] EQ 'H5T_FLOAT': BEGIN
          IF idltype[i] EQ 'DOUBLE' THEN hdftype[i]='DFNT_FLOAT64' $
          ELSE hdftype[i]='DFNT_FLOAT32'
        END
      hdftype[i] EQ 'H5T_STRING': hdftype[i]='DFNT_CHAR8'
      hdftype[i] EQ 'H5T_INTEGER_0': BEGIN
          CASE idltype[i] OF
            'BYTE': hdftype[i]='DFNT_UINT8'
            ('SHORT') OR ('UINT'): hdftype[i]='DFNT_UINT16'
            ('INTEGER') OR ('ULONG'): hdftype[i]='DFNT_UINT32'
            'ULONG64': hdftype[i]='DFNT_UINT64'
            ELSE: hdftype[i]='DFNT_UNDF'
          ENDCASE
        END
      hdftype[i] EQ 'H5T_INTEGER_1': BEGIN
          CASE idltype[i] OF
            'BYTE': hdftype[i]='DFNT_INT8'
            'SHORT': hdftype[i]='DFNT_INT16'
            'INTEGER': hdftype[i]='DFNT_INT32'
            'LONG64': hdftype[i]='DFNT_INT64'
            ELSE: hdftype[i]='DFNT_UNDF'
          ENDCASE
        END
      ELSE:
    ENDCASE
  ENDFOR
ENDIF ELSE IF ftype EQ 'N3' THEN BEGIN
  ;need to convert hdftype and idltype values to equivalent idl_name and hdf_dt values
  writeonce=0B
  FOR i=0,n_lab-2 DO BEGIN
    idltype[i]=idl_name[idltype[i]] ;convert IDL type code to type name
    CASE 1 OF
      hdftype[i] EQ 'BYTE_0': hdftype[i]='DFNT_UINT8'
      hdftype[i] EQ 'BYTE_1': hdftype[i]='DFNT_INT8'
      hdftype[i] EQ 'INT': hdftype[i]='DFNT_INT16'
      hdftype[i] EQ 'LONG': hdftype[i]='DFNT_INT32'
      hdftype[i] EQ 'FLOAT': hdftype[i]='DFNT_FLOAT32'
      hdftype[i] EQ 'DOUBLE': hdftype[i]='DFNT_FLOAT64'
      hdftype[i] EQ 'CHAR': hdftype[i]='DFNT_CHAR8'
      ELSE: hdftype[i]='DFNT_UNDF'
    ENDCASE
    IF (idltype[i] EQ 'BYTE') AND (idltype[0] EQ 'BYTE') AND (~writeonce) THEN BEGIN
      ;For VAR_VALID_MIN/MAX and VAR_FILL_VALUE only output if the dataset is of type BYTE as well
      ;o/w probably means that the value was written as a character string instead of as numbers 
      INFOTXT_OUTPUT_A,[23],[usevn]
      writeonce=1B
    ENDIF  
  ENDFOR

ENDIF

;Check VAR_DATA_TYPE value
hdti=WHERE((idltype[n_lab-1] EQ avdt) AND (idltype[n_lab-1] NE ''),hdtcnt)
IF hdtcnt EQ 0 THEN BEGIN
  IF idltype[n_lab-1] NE '' THEN $
    dterr[n_lab-1]=usevn+' '+dt_chk_labels[n_lab-1]+'='+idltype[n_lab-1]+' is not a valid GEOMS data type'
ENDIF

;Check dataset, min, max and fill data types are valid
FOR i=0,n_lab-2 DO BEGIN
  IF i EQ 0 THEN itxt=' value(s) ' ELSE itxt=' value '
  hdti=WHERE((hdftype[i] EQ hdf_dt) AND (hdftype[i] NE ''),hdtcnt)
  IF hdtcnt EQ 0 THEN BEGIN ;identify the actual data type of the dataset
    hdti=WHERE(idltype[i] EQ idl_name,hdtcnt)
    IF hdtcnt EQ 0 THEN IF idltype[i] NE '' THEN hdti[0]=0
    CASE hdti[0] OF
     -1: IF i EQ 0 THEN dterr[i]=usevn+' '+dt_chk_labels[i]+' data type is not identifiable'
      1: dterr[i]=usevn+' '+dt_chk_labels[i]+itxt+'must be 8-bit unsigned INTEGER (BYTE)'
      2: dterr[i]=usevn+' '+dt_chk_labels[i]+itxt+'must be 16-bit signed INTEGER'
      3: dterr[i]=usevn+' '+dt_chk_labels[i]+itxt+'must be 32-bit signed INTEGER'
      ELSE: dterr[i]=usevn+' '+dt_chk_labels[i]+itxt+'not a valid GEOMS data type: '+idl_name[hdti[0]]
    ENDCASE
  ENDIF
ENDFOR

;If valid VAR_DATA_TYPE is present check that the dataset and min, max and fill data types match
IF dterr[n_lab-1] EQ '' THEN BEGIN
  ;Use error generated in SET_UP_STRUCTURE in idlcr8hdf instead
  ;FOR i=0,n_lab-2 DO BEGIN
  ;  IF (idltype[n_lab-1] NE idltype[i]) AND (idltype[i] NE '') THEN BEGIN
  ;    dterr[i]=usevn+' '+dt_chk_labels[i]+' data type ('+hdftype[i]+') does not match VAR_DATA_TYPE='+idltype[n_lab-1]
  ;    ;note: overwrites any existing reported error
  ;  ENDIF
  ;ENDFOR
ENDIF ELSE IF dterr[0] EQ '' THEN BEGIN
  ;VAR_DATA_TYPE not valid so check min, max and fill data types against the dataset data type
  FOR i=1,n_lab-2 DO BEGIN
    IF (idltype[0] NE idltype[i]) AND (idltype[i] NE '') THEN BEGIN
      dterr[i]=usevn+' '+dt_chk_labels[i]+' data type ('+idl_name[i]+') does not match Dataset data type ('+idl_name[0]+')'
      ;note: overwrites any existing reported error
    ENDIF              
  ENDFOR
ENDIF

;Output information messages
FOR i=0,n_lab-1 DO $
  IF dterr[i] NE '' THEN INFOTXT_OUTPUT_A,[2],[dterr[i]]

END ;Data_Type_Checks



PRO read_hdf_sds, ifile, ga, sds, catinfo
;Procedure to read the contents of a GEOMS standard HDF or netCDF compatible file
;into session memory
; ----------
;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org
;
;  History:
;    20050729: Original IDLCR8ASCII Routine - Version 1.0
;    20050912: Removed Common variable definition CATALOGINFO and made the variable a parameter passed
;              to the procedure; Checks that the attribute variable names read by HDF_SD_GETINFO, match
;              those recorded in DATA_VARIABLES in the Global Attributes. If not use the variable name
;              listed in DATA_VARIABLES - Version 1.1
;    20061004: Common variable definition WIDGET_WIN_A added for Error calls; Add option to read HDF5
;              files (needs IDL5.6 or newer); Add check that the HDF4 file has valid information on the
;              number of global and variable attributes; Checks added in the event that the HDF4 or
;              HDF5 file has been created by the NCSA utility programs H5toH4 or H4toH5 from an original
;              AVDC/Envisat/NDACC HDF4 or HDF5 file; Ensures variable attributes and data are listed in
;              the same order as that given under DATA_VARIABLES - Version 2.0
;    20081020 - If the HDF4 file is created using the HDF4.2r3 library, then extra information
;               regarding the dimensions of the VAR_DEPEND values may be included in the file (which
;               show up as extra datasets in the HDF_SD_FILEINFO call). A check for this is carried out
;               and, if found, the information is excluded from the output - Version 3.01
;    20090311: Improve HDF4.2r3 library checks by comparing the number of datasets recorded under
;              DATA_VARIABLES, with the number returned by the HDF_SD_FILEINFO call; Change 'WARNING' to
;              'INFORMATION and make calls to procedure INFOTXT_OUTPUT_A to avoid duplication. Add
;              'INFORMATION' text if anything irregular found with the datasets; Change 'Data dimensions
;               exceed 8' and 'Data type not allowable' ERROR messages to 'INFORMATION' messages
;               - Version 3.02
;    20091208: Incorporate HDF_SD_ISCOORDVAR to differentiate between datasets and dimension variable
;              names. Add check for allowable VAR_DATA_TYPE=STRING. Add all INFORMATION text messages to
;              INFOTXT_OUTPUT_A procedure to avoid duplication of code that creates the messages;
;              Add INFORMATION messages when reading HDF5 files; Improve checks for non-standard (i.e.
;              non-groundbased) HDF input file - Version 3.03
;    20101122: Numeric metadata values remain in their original data type when transferring to session
;              memory, meaning: variable attribute labels and values now written to separate structure
;              variables sds.va_l and sds.va_v (previously sds.va), and structure has 2 dimensions
;              (n_sds, n_atts) instead of 1 (previously n_sds); The dataset is written to the initial
;              n_atts index (i.e. sds[n_sds,0].data); Rename saved SDS datatype according to GEOMS rules
;              (affects INT, LONG, and FLOAT); Compare HDF dimensions with VAR_SIZE values and transpose
;              VAR_SIZE and VAR_DEPEND values if required - Version 4.0b1
;    20111014: Add netCDF read capability - writes variable attributes to standard GEOMS variable
;              attribute labels where possible, otherwise appends information to VAR_NOTES - Version 4.0b3
;    20120426: Bug fix when a netCDF file only contains datasets with single dimensions, then VAR_SIZE
;              information was not being extracted during the read process - Version 4.0b6
;    20140325: Fix bug that caused crash when VAR_SIZE values were not of type string - Version 4.0b8
;    20150127: Allow for VAR_FILL_VALUE values for string datasets to be written and saved as an
;              (empty) string of the correct length. Ensure string datasets are all written and
;              saved to the correct length (that of the longest string in the dataset) - Version 4.0b10
;    20150217: Do not remove whitespace of any variable attribute values that are written as strings
;              - Version 4.0b11
;    20160614: Some HDF4 string datasets may not have 2 dimensions, so do not need to remove the first
;              dimension (= maximum number of characters) - Version 4.0b14
;    20160725: Fix bug causing program to crash when the number of DATA_VARIABLES values is greater
;              than the number of datasets in the file - Version 4.0b15
;    20161130: Assign SDS_name to catinfo for attributes when the VAR_NAME is not present; rewrite
;              netCDF section to align it with HDF5 section and to account for dataset ordering that
;              does not match that in DATA_VARIABLES - Version 4.0b16
;    20161213: Fix bug when reading in netCDF files to stop all dataset variable attribute values to
;              be converted to string format (now matches HDF4 and HDF5 checks). Fix bug that caused
;              the program to crash when VAR_DEPEND=CONSTANT in netCDF files (sds_ndim=0) - Version 4.0b17
;    20161218: Fix bug when reading netCDF attribute values that are of type byte rather than string -
;              Version 4.0b18
;    20180218  Fix bug that caused multi-dimensional array ordering to not be correctly identified
;              if the datasets have the same VAR_SIZE. Now rev_vd_vs changes to 1 (VAR_SIZE and
;              VAR_DEPEND ordering is reversed) if rev_vd_vs=2 after all the checks - Version 4.0b19
;    20181116  Fix bug that caused multi-dimensional array ordering to not be correctly written if
;              not consistent through the file i.e. rev_vd_vs eq 3. Now rev_vd_vs changes to 1
;              (VAR_SIZE, VAR_DEPEND and dataset ordering is reversed), but information message
;              advising of the issue is still reported - Version 4.0b20
;    20190514  Fix bug that occurred when GEOMS variable attribute information is missing from an
;              HDF4 file but written to the heap structure based on the contents of the file. Previously
;              the variable attribute label was not written correctly - Version 4.0b21
;    20190806  Provide support for reading netCDF4 files. Currently uses the HDF5 routines to do this
;              so need to account for features unique to netCDF4 such as; the inclusion of dimension
;              names; standard netCDF attribute names; empty attribute values that do not include a
;              null termination character (which causes an error in the H5A_Read routine)
;              - Version 4.0b22
;    20190821  Fixed bugs associated with H5 TAG_NAMES checks introduced in v4.0b22 - Version 4.0b23
;    20200930  Add sds_name to TEST_DIM_ORDER procedure call - Version 4.0b24
;    20220805  For NetCDF3 or 4, if SDS_NAME NE VAR_NAME then converts non-alphanumeric VAR_NAME
;              characters to '_' and compares with SDS_NAME. If there is a match then program quietly
;              converts SDS_NAME to VAR_NAME for testing, otherwise generates an error; Add code to
;              collect information for the call to the DATA_TYPE_CHECKS procedure; Generate an 
;              INFORMATION/ERROR message if VAR_UNITS is numeric (same as VAR_SIZE); INFORMATION/ERROR 
;              message added if a mis-match is found between the input file name and the FILE_NAME attribute value.
 
;
;  Inputs: ifile - a string containing the name of the input file to be read in.
;          catinfo - a string array identifying the type of input file ('H4','H5','N4','N3')
;
;  Outputs: ga - a string array containing the global attribute labels and values extracted from the HDF
;                file.
;           sds - a structure using pointers, of size [n_sds,n_atts], containing the variable attribute
;                 labels and values (sds[n,m].va_l and sds[n,m].va_v) and the data (sds[n,0].data)
;                 extracted from the HDF file.
;           catinfo - a string array of size [n_sds,4] (where n_sds is the number of datasets in the HDF
;                     file), containing information on the variable name, data type, data dimension, and
;                     number of attributes.
;
;  Called by: IDLCR8ASCII
;
;  Subroutines Called: STOP_WITH_ERROR_A (if error state detected)
;                      INFOTXT_OUTPUT_A (if program can make a change)
;                      DATA_TYPE_CHECKS (to perform data type checks on the datasets and some attributes)
;    Possible Conditions for STOP_WITH_ERROR call (plus [line number] where called):
;      1. No global attributes or datasets present in the root group of an HDF5 file.
;      2. The number of global attributes or datasets is zero or not able to be determined
;         in an HDF4 file.
;
;    Information Conditions (when the program is able to make changes):
;      1. DATA_VARIABLES global attribute not found or has no values.
;      2. Number of datasets given under DATA_VARIABLES is not equal to the number saved to the
;         file.
;      3. The number of dimensions in a dataset exceeds 8.
;      4. The data type of a dataset is not BYTE, SHORT, INTEGER, LONG, REAL, DOUBLE, or STRING.
;      5. Dataset name is truncated or does not match VAR_NAME value.
;      6. Metadata label VAR_NAME not found for the dataset.
;      7. VAR_NAME does not match any DATA_VARIABLES values.
;      8. No Variable Attributes returned after HDF call.
;      9. Metadata Dataset listing order may not match the order of the Global Attribute
;         DATA_VARIABLES Values.
;     10. Variable is written to the HDF file as a coordinate variable.

COMMON WIDGET_WIN_A

;Note any calibrated (scaled) HDF4 dataset is corrected using the formula specified in
;UG_print42r3.pdf pg. 3-107: orig = cal * (cal_val - offset)
ncsa_cal=['scale_factor','scale_factor_err','add_offset','add_offset_err','calibrated_nt']

;Initialize scalar to indicate whether VAR_DEPEND and VAR_SIZE values need to be reversed
rev_vd_vs=2 ;1 = reverse attribute values; 0 = no reverse (and will generate message);
;2 = no action required; 3 = rev_vd_vs changed between 0 and 1 during checking, therefore error
;Note if rev_vd_vs is 2 or 3 after all the checks then it will be changed to 1 so that dimensions
;are swapped by default

;List of Standard GEOMS Variable Attributes
attr_arr_data=['VAR_NAME','VAR_DESCRIPTION','VAR_NOTES','VAR_SIZE','VAR_DEPEND',$
               'VAR_DATA_TYPE','VAR_UNITS','VAR_SI_CONVERSION','VAR_VALID_MIN',$
               'VAR_VALID_MAX','VAR_FILL_VALUE']
n_aad=N_ELEMENTS(attr_arr_data)

;Possible error messages for this procedure
proname='Read_HDF_SDS procedure: '
errtxt=STRARR(2)
errtxt[0]=' found in the Root Group of the HDF5 File.'
errtxt[1]=' is zero or not able to be determined (check for corrupted file).'
lu=-1
slabel=['VAR_UNITS','VAR_SI_CONVERSION','VAR_VALID_MIN','VAR_VALID_MAX','VAR_FILL_VALUE'] ;save values as is if dataset is of type STRING
dt_chk_labels=['Dataset','VAR_VALID_MIN','VAR_VALID_MAX','VAR_FILL_VALUE','VAR_DATA_TYPE'] ;dataset and attribute labels for data type checks
n_lab=N_ELEMENTS(dt_chk_labels)

;If necessary, free up memory by destroying the heap variables pointed at by its pointer arguments
;from previous calls to this program
IF N_ELEMENTS(sds) NE 0 THEN PTR_FREE,sds.va_l,sds.va_v,sds.data

;Define the HDF SDS storage structure
sds_set={va_l: PTR_NEW(), $ ;Variable Attribute Labels
         va_v: PTR_NEW(), $ ;Variable Attribute Values
         data: PTR_NEW()}   ;SD data array

ftype=catinfo[0,0]
ifilechk=STRLOWCASE(STRTRIM(FILE_BASENAME(ifile),2)) ;for FILE_NAME check
IF ftype EQ 'H4' THEN BEGIN

  ;The HDF_SD_START function opens an HDF file and initializes the SD interface.
  sd_id=HDF_SD_START(ifile,/READ)

  ;Determine the number of SDS (n_sds) and global attributes (n_ga) found
  ;in the current file.
  HDF_SD_FILEINFO,sd_id,n_sds,n_ga
  IF n_sds EQ 0L THEN ntxt='Number of SDS datasets' $
  ELSE IF n_ga EQ 0L THEN ntxt='Number of Global Attributes' $
  ELSE ntxt=''
  IF ntxt NE '' THEN BEGIN
    STOP_WITH_ERROR_A,o3[3]+proname,ntxt+errtxt[1],lu & RETURN
  ENDIF
  ga=STRARR(n_ga) ;set the Global Attribute dimensions
  vn=[''] ;initialize vn array (to hold Dataset names)
  n_sds_hold=n_sds & n_vn=0L & do_coordvar=0

  ;Read the file's Global Attributes and determine number of DATA_VARIABLES
  FOR i=0L,n_ga-1L DO BEGIN
    ;The HDF_SD_ATTRINFO procedure reads or retrieves info about an SD attribute.
    HDF_SD_ATTRINFO,sd_id,i,NAME=ga_name,DATA=ga_data,HDF_TYPE=ga_hdftype
    ga_name=STRTRIM(ga_name,2) & ga_data=STRTRIM(ga_data,2)
    ;Assign global attributes to ga
    ga[i]=ga_name+'='+ga_data
    ;read list of DATA_VARIABLES into array
    IF STRUPCASE(ga_name) EQ 'DATA_VARIABLES' THEN vn=STRSPLIT(ga_data,' ;',/Extract,COUNT=n_vn)
    ;String labels for GA not checked at this time as extra Global Attributes could be numeric
    ;IF (ga_hdftype NE 'DFNT_NONE') AND (ga_hdftype NE 'DFNT_CHAR8') THEN $
    ;  INFOTXT_OUTPUT_A,[20],['Global Attribute',STRTRIM(ga_name,2)]
    IF STRUPCASE(ga_name) EQ 'FILE_NAME' THEN BEGIN
      ;check that the actual file name matches the FILE_NAME entry
      IF STRLOWCASE(STRTRIM(ga_data,2)) NE ifilechk THEN INFOTXT_OUTPUT_A,[24],[ga_data]
    ENDIF
  ENDFOR

  IF n_sds_hold NE n_vn THEN BEGIN
    ;Do check for sds being a Dimension attribute
    do_coordvar=1
    FOR i=0L,n_sds-1L DO BEGIN
      ;The HDF_SD_SELECT function returns an SD dataset ID given the current
      ;SD interface ID, and the zero-based SD dataset index.
      sds_id=HDF_SD_SELECT(sd_id,i)
      IF HDF_SD_ISCOORDVAR(sds_id) THEN n_sds_hold=n_sds_hold-1L
      ;Closes the SDS interface.
      HDF_SD_ENDACCESS,sds_id
    ENDFOR
    IF n_sds_hold EQ 0L THEN BEGIN
      STOP_WITH_ERROR_A,o3[3]+proname,'Number of SDS datasets'+errtxt[1],lu
      RETURN
    ENDIF

    IF n_sds_hold NE n_vn THEN $
      INFOTXT_OUTPUT_A,[0],[STRTRIM(n_vn,2),STRTRIM(n_sds_hold,2),'HDF_SD_FILEINFO']
      ;DATA_VARIABLES values not read successfully, or number of datasets given under DATA_VARIABLES
      ;is not equal to the number saved to the file
  ENDIF

  ;Determine maximum number of attributes, and match the dataset order to DATA_VARIABLES list if possible
  max_atts=0L & oi=LONARR(n_sds_hold)
  dv_order=1 & c_sds=0L
  catinfo=STRARR(n_sds_hold,4) ;output info for catalog output
  FOR i=0L,n_sds-1L DO BEGIN
    vnv='' & sds_name=''
    hdftype=STRARR(n_lab-1) & idltype=STRARR(n_lab) ;to hold data types for dataset, min, max and fill values, and VAR_DATA_TYPE (idltype only)
    ;The HDF_SD_SELECT function returns an SD dataset ID given the current
    ;SD interface ID, and the zero-based SD dataset index.
    sds_id=HDF_SD_SELECT(sd_id,i)

    IF (do_coordvar EQ 0) OR (NOT HDF_SD_ISCOORDVAR(sds_id)) THEN BEGIN
      ;The HDF_SD_GETINFO procedure retrieves information about an SD dataset.
      HDF_SD_GETINFO,sds_id,NATTS=sds_natts, $      ;no. attributes
                            HDF_TYPE=sds_hdftype, $ ;HDF data type
                            TYPE=sds_type, $        ;data type
                            DIMS=sds_dim, $         ;dimension information (automatically reversed by IDL)
                            NAME=sds_name           ;dataset name

      ;Check for string attribute and, if so, remove the first dimension
      IF (sds_type EQ 'STRING') AND (N_ELEMENTS(sds_dim) NE 1) THEN sds_dim=sds_dim[1:N_ELEMENTS(sds_dim)-1]

      ;Check for multi-dimensional dataset
      n_sds_dim=N_ELEMENTS(sds_dim)
      IF n_sds_dim GT 1 THEN multi_dim=1

      ;Check number of dimensions
      IF n_sds_dim gt 8 THEN INFOTXT_OUTPUT_A,[1],[sds_name,STRTRIM(n_sds_dim,2)]

      ;Check for coordinate variable
      IF HDF_SD_ISCOORDVAR(sds_id) THEN INFOTXT_OUTPUT_A,[8],[sds_name]

      ;Check that a dataset name has been successfully read
      sds_name=STRTRIM(sds_name,2)
      IF sds_name EQ '' THEN sds_name='N/A'

      ;Rename data type to be compatible with the Metadata guidelines
      sds_type=STRTRIM(STRUPCASE(sds_type),2)
      IF sds_type EQ 'INT' THEN sds_type='SHORT' $
      ELSE IF sds_type EQ 'LONG' THEN sds_type='INTEGER' $
      ELSE IF sds_type EQ 'FLOAT' THEN sds_type='REAL'

      hdftype[0]=sds_hdftype & idltype[0]=sds_type

      IF sds_natts NE 0L THEN BEGIN
        IF sds_natts GT max_atts THEN max_atts=sds_natts
        vnf=0 & lcnt=0 & vcnt=sds_natts
        ;Extract the variable attributes
        FOR j=0L,sds_natts-1L DO BEGIN
          ;The HDF_SD_ATTRINFO procedure reads or retrieves information about an SD attribute.
          HDF_SD_ATTRINFO,sds_id,j,NAME=va_name,DATA=va_value,HDF_TYPE=va_hdftype,TYPE=va_type
          va_name=STRTRIM(va_name,2)
          schk=WHERE(STRUPCASE(va_name) EQ slabel,schkcnt)

          ;Rename data type to be compatible with the Metadata guidelines
          va_type=STRTRIM(STRUPCASE(va_type),2)
          IF va_type EQ 'INT' THEN va_type='SHORT' $
          ELSE IF va_type EQ 'LONG' THEN va_type='INTEGER' $
          ELSE IF va_type EQ 'FLOAT' THEN va_type='REAL'

          IF ((sds_type EQ 'STRING') AND (schkcnt EQ 0)) OR ((sds_type NE 'STRING') AND (va_type EQ 'STRING')) THEN $
            va_value=STRTRIM(va_value,2)
          ;Check returned dataset attributes (sds_) with saved attributes (va_)
          IF (STRUPCASE(va_name) EQ 'VAR_NAME') AND (vnf EQ 0) THEN BEGIN
            ;check that the dataset name matches the VAR_NAME
            vnf=1
            IF STRUPCASE(va_value) NE STRUPCASE(sds_name) THEN BEGIN
              ;Try and determine which is wrong - Dataset name or VAR_NAME - default is Dataset name
              li=WHERE((STRUPCASE(sds_name) EQ STRUPCASE(vn)) AND (vn[0] NE ''),lcnt)
              ;If lcnt NE 0 then Dataset name matches DATA_VARIABLES value so assume that VAR_NAME is wrong
              IF lcnt NE 0 THEN usesds=1 ELSE usesds=0
              INFOTXT_OUTPUT_A,[3,4,usesds],[sds_name,va_value]
              IF usesds EQ 0 THEN sds_name=va_value ELSE va_value=sds_name
            ENDIF
            vnv=va_value
            ;match the order of the dataset to the DATA_VARIABLES list
            li=WHERE(STRUPCASE(va_value) EQ STRUPCASE(vn),lcnt)
          ENDIF
          IF STRUPCASE(va_name) EQ 'VAR_DATA_TYPE' THEN idltype[n_lab-1]=STRUPCASE(STRTRIM(va_value,2)) $
          ELSE IF STRUPCASE(va_name) EQ 'VAR_UNITS' THEN BEGIN
            ;check that VAR_UNITS is a string value (in event that VAR_UNITS=1)  
            IF va_type NE 'STRING' THEN $
              INFOTXT_OUTPUT_A,[20],['VAR_UNITS',STRTRIM(sds_name,2)]
          ENDIF ELSE IF STRUPCASE(va_name) EQ 'VAR_DEPEND' THEN $ ;do dimension ordering checks
            TEST_DIM_ORDER,'VD',va_value,va_type,sds_dim,sds_name,rev_vd_vs $
          ELSE IF STRUPCASE(va_name) EQ 'VAR_SIZE' THEN $ ;do dimension ordering checks
            TEST_DIM_ORDER,'VS',va_value,va_type,sds_dim,sds_name,rev_vd_vs $
          ELSE IF (STRUPCASE(va_name) EQ 'VAR_VALID_MIN') THEN BEGIN
            hdftype[1]=va_hdftype & idltype[1]=va_type 
          ENDIF ELSE IF (STRUPCASE(va_name) EQ 'VAR_VALID_MAX') THEN BEGIN
            hdftype[2]=va_hdftype & idltype[2]=va_type 
          ENDIF ELSE IF (STRUPCASE(va_name) EQ 'VAR_FILL_VALUE') THEN BEGIN
            hdftype[3]=va_hdftype & idltype[3]=va_type
          ENDIF
        ENDFOR
      ENDIF ELSE BEGIN ;No Variable Attributes found
        vcnt=3
        ;Can the SDS_NAME be matched with a DATA_VARIABLES value?
        li=WHERE(STRUPCASE(sds_name) EQ STRUPCASE(vn),lcnt)
        IF lcnt NE 0 THEN vnf=1 ELSE vnf=0
        INFOTXT_OUTPUT_A,[5],[sds_name,'HDF_SD_GETINFO']
      ENDELSE

      ;Check for possible problems with determining array index
      IF lcnt NE 0 THEN BEGIN
        IF li[0] GE n_sds_hold THEN dv_order=0 $ ;can occur when number of DATA_VARIABLE values is greater than number of datasets
        ELSE IF catinfo[li[0],0] NE '' THEN dv_order=0 ;This array has already been written to
      ENDIF ELSE IF (lcnt EQ 0) OR (vnf EQ 0) THEN dv_order=0
      ;Write out information text if sds_name cannot be matched and determine array index
      IF dv_order EQ 0 THEN BEGIN
        li=WHERE(catinfo[*,0] EQ '') ;determine lowest available array index to write info to
        in0=[4,vnf,lcnt,n_vn]
        IF (vnf EQ 0) OR ((lcnt EQ 0) AND (n_vn NE 0L)) THEN INFOTXT_OUTPUT_A,in0,[sds_name,vnv]
      ENDIF

      oi[c_sds]=li[0] ;Attribute order index
      c_sds=c_sds+1L

      catinfo[li[0],0]=sds_name & catinfo[li[0],1]=sds_type
      catinfo[li[0],2]=STRTRIM(sds_dim[0],2) & catinfo[li[0],3]=vcnt
      IF N_ELEMENTS(sds_dim) GT 1 THEN $
        FOR j=1,N_ELEMENTS(sds_dim)-1 DO catinfo[li[0],2]=catinfo[li[0],2]+';'+STRTRIM(sds_dim[j],2)
    ENDIF

    ;Do data type checks on the Dataset, VAR_DATA_TYPE value and data types of VAR_VALID_MIN, VAR_VALID_MAX and VAR_FILL_VALUES
    DATA_TYPE_CHECKS,ftype,dt_chk_labels,hdftype,idltype,sds_name,vnv
    ;Closes the SDS interface.
    HDF_SD_ENDACCESS,sds_id
  ENDFOR

  IF max_atts EQ 0L THEN max_atts=3 ;Use information from HDF_SD_GETINFO call only
  
  rev_vd_vsh=rev_vd_vs ;keep original value in hold variable (required if rev_vd_vs eq 3)
  IF (rev_vd_vs EQ 2) OR (rev_vd_vs EQ 3) THEN rev_vd_vs=1 ;change default so that VAR_DEPEND and VAR_SIZE 
  ;are reversed if not o/w changed in TEST_DIM_ORDER
  
  ;Dimension the structure to the number of datasets x number of attributes
  sds=REPLICATE(sds_set, n_sds_hold, max_atts)

  ;Write attributes to structure
  c_sds=0L
  !QUIET=1 ;suppress system error and information messages (used for CALDATA call in HDF_SD_GETINFO)
  FOR i=0L,n_sds-1L DO BEGIN
    notranspose=1 ;0/1 will change to 0 if the dataset needs to be transposed
    ;The HDF_SD_SELECT function returns an SD dataset ID given the current
    ;SD interface ID, and the zero-based SD dataset index.
    sds_id=HDF_SD_SELECT(sd_id,i)
    nocal=1 ;Boolean to identify dataset which has been calibrated
    IF (do_coordvar EQ 0) OR (NOT HDF_SD_ISCOORDVAR(sds_id)) THEN BEGIN
      ;The HDF_SD_GETINFO procedure retrieves information about an SD dataset.
      ;Note - any saved pre-defined attributes will be called with HDF_SD_ATTRINFO call
      HDF_SD_GETINFO,sds_id,NATTS=sds_natts, $      ;no. attributes
                            HDF_TYPE=sds_hdftype, $ ;HDF data type
                            TYPE=sds_type, $        ;data type
                            DIMS=sds_dim, $         ;dimension information
                            NAME=sds_name, $        ;dataset name
                            CALDATA=sds_cal         ;pre-defined calibration info

      ;Check to see whether data has had scale factor and offset applied
      IF (sds_cal.Cal NE 0.D) OR (sds_cal.Offset NE 0.D) THEN BEGIN
        nocal=0 ;Dataset has been calibrated
        ;identify datatype of the original dataset
        CASE 1 OF
          sds_cal.Num_Type EQ 21L: vdt_val='BYTE'
          sds_cal.Num_Type EQ 22L: vdt_val='SHORT'
          sds_cal.Num_Type EQ 24L: vdt_val='INTEGER'
          sds_cal.Num_Type EQ 5L: vdt_val='REAL'
          sds_cal.Num_Type EQ 6L: vdt_val='DOUBLE'
          ELSE: vdt_val='' ;invalid or cannot determine original datatype
        ENDCASE
      ENDIF

      IF sds_natts NE 0L THEN BEGIN
        ;Extract the variable attributes
        jh=0 ;will only loop if attribute is not a pre-defined calibration attribute
        FOR j=0L,sds_natts-1L DO BEGIN
          ;The HDF_SD_ATTRINFO procedure reads or retrieves information about an SD attribute.
          HDF_SD_ATTRINFO,sds_id,j,NAME=va_name,DATA=va_value,TYPE=va_type
          va_name=STRTRIM(va_name,2)

          schk=WHERE(STRUPCASE(va_name) EQ slabel,schkcnt)
          IF ((sds_type EQ 'STRING') AND (schkcnt EQ 0)) OR ((sds_type NE 'STRING') AND (va_type EQ 'STRING')) THEN $
            va_value=STRTRIM(va_value,2)
          IF (nocal EQ 0) AND (STRUPCASE(va_name) EQ 'VAR_DATA_TYPE') THEN BEGIN
            ;If required prepare INFORMATION text (written after DATA has been corrected)
            IF (vdt_val NE STRUPCASE(va_value)) AND (vdt_val NE '') THEN BEGIN
              io_arr=[STRTRIM(sds_name,2),va_value,vdt_val]
              va_value=vdt_val
            ENDIF ELSE BEGIN
              io_arr=[''] & vdt_val=va_value
            ENDELSE
          ENDIF

          IF ((STRUPCASE(va_name) EQ 'VAR_DEPEND') OR (STRUPCASE(va_name) EQ 'VAR_SIZE')) AND $
            ((rev_vd_vs MOD 10 EQ 1) OR (rev_vd_vs EQ 20)) THEN BEGIN ;need to reverse the va_values

            IF va_type NE 'STRING' THEN BEGIN ;VAR_SIZE contains numeric values instead of in the form of a string
              vavhold='' & n_vav=N_ELEMENTS(va_value)
              FOR k=0,n_vav-1 DO BEGIN
                IF k EQ n_vav-1 THEN vtxt='' ELSE vtxt=';'
                vavhold=vavhold+STRTRIM(va_value[k],2)+vtxt
              ENDFOR
            ENDIF ELSE vavhold=va_value

            vs_v=STRCOMPRESS(STRSPLIT(vavhold,';, ',/EXTRACT,COUNT=rcnt),/REMOVE_ALL)
            IF rcnt GT 1 THEN BEGIN ;multi-dimensions
              IF (rev_vd_vs EQ 20) THEN notranspose=0 ;need to transpose dataset as well
              vs_v=REVERSE(vs_v)
              FOR k=0,rcnt-1 DO $
                IF k EQ 0 THEN va_value=vs_v[k] ELSE va_value=va_value+';'+vs_v[k]
            ENDIF
          ENDIF

          ;Test for pre-defined calibration attribute and do not save as the data have
          ;been coverted back to original values
          pi=WHERE(STRLOWCASE(va_name) EQ ncsa_cal,pcnt)
          IF pcnt EQ 0 THEN BEGIN
            sds[oi[c_sds],jh].va_l=PTR_NEW(va_name)
            sds[oi[c_sds],jh].va_v=PTR_NEW(va_value)
            jh=jh+1
          ENDIF
        ENDFOR
      ENDIF ELSE BEGIN ;No Variable Attributes found
        va_lab=['VAR_NAME','VAR_SIZE','VAR_DATA_TYPE']
        FOR j=0,2 DO sds[oi[c_sds],j].va_l=PTR_NEW(va_lab[j])
        sds[oi[c_sds],0].va_v=PTR_NEW(sds_name)
        sds[oi[c_sds],1].va_v=PTR_NEW(sds_dim)
        ;Rename format to be compatible with the Metadata guidelines
        IF sds_type EQ 'INT' THEN sds_type='SHORT' $
        ELSE IF sds_type EQ 'LONG' THEN sds_type='INTEGER' $
        ELSE IF sds_type EQ 'FLOAT' THEN sds_type='REAL'
        sds[oi[c_sds],2].va_v=PTR_NEW(sds_type)
      ENDELSE
      ;Extract the data
      HDF_SD_GETDATA,sds_id,datasize
      ;Test to see if dataset dimension ordering needs to be changed
      IF notranspose EQ 0 THEN datasize=TRANSPOSE(datasize)

      IF nocal EQ 0 THEN BEGIN
        ;apply scale factor and offset corrections to data and write text to log file
        CASE 1 OF
          vdt_val EQ 'BYTE': datasize=BYTE(sds_cal.Cal*(datasize-sds_cal.Offset))
          vdt_val EQ 'SHORT': datasize=FIX(sds_cal.Cal*(datasize-sds_cal.Offset))
          vdt_val EQ 'INTEGER': datasize=LONG(sds_cal.Cal*(datasize-sds_cal.Offset))
          vdt_val EQ 'REAL': datasize=FLOAT(sds_cal.Cal*(datasize-sds_cal.Offset))
          ELSE: datasize=DOUBLE(sds_cal.Cal*(datasize-sds_cal.Offset))
        ENDCASE
        INFOTXT_OUTPUT_A,[9],[STRTRIM(sds_name,2)]
        IF io_arr[0] NE '' THEN INFOTXT_OUTPUT_A,[10],io_arr
      ENDIF
      sds[oi[c_sds],0].data=PTR_NEW(datasize)
      c_sds=c_sds+1L
    ENDIF

    ;Closes the SDS interface.
    HDF_SD_ENDACCESS,sds_id
  ENDFOR
  !QUIET=0 ;Allow system error and information messages
  ;The HDF_SD_END function closes the SD interface to an HDF file.
  HDF_SD_END,sd_id
ENDIF ELSE IF (ftype EQ 'H5') OR (ftype EQ 'N4') THEN BEGIN ;HDF5 or netCDF4 format file
  stdfm=1 ;Boolean indicating standard AVDC/EVDC/NDACC H5 format or not
  dv_order=1 ;Boolean indicating whether Metadata Variable listing order matches DATA_VARIABLES values
  n_vn=0L ;Initialize the number of Variable Names in the standard format h5 file
  
  ;labels to quietly ignore if the file is netCDF4
  nc4_ignore=['reference_list','dimension_list','class','name','_netcdf4dimid','_netcdf4coordinates','_ncproperties']
  
  ;Do bulk read so errors generated by netCDF4 inconsistencies can be identified without
  ;crashing the program e.g. null string attribute values 
  h5p=H5_PARSE(ifile) 
  
  hdf_file_id=H5F_OPEN(ifile)
  sd_id=H5G_OPEN(hdf_file_id,'/')

  ;Get number of attributes within the root group (these should be the Global Attributes)
  n_ga=H5A_GET_NUM_ATTRS(sd_id)
  ;Get the number of objects within the root group (including datasets, groups etc)
  n_obj=H5G_GET_NMEMBERS(sd_id,'/')

  IF n_obj NE 0L THEN BEGIN
    d_obj=INTARR(n_obj) & g_obj=INTARR(n_obj)
    ;Determine number of datasets.  Note: if H5 file created using H4toH5 then an extra
    ;group will have been created which will not be used.  Also datasets may be listed
    ;alphabetically. This section also determines whether the H5 file is a standard
    ;AVDC/EVDC/NDACC groundbased file or otherwise.
    FOR i=0L,n_obj-1L DO BEGIN
      sds_name=H5G_GET_MEMBER_NAME(sd_id,'/',i)
      h5fstat=H5G_GET_OBJINFO(sd_id,sds_name)
      IF h5fstat.type EQ 'DATASET' THEN d_obj[i]=1 $
      ELSE IF h5fstat.type EQ 'GROUP' THEN g_obj[i]=1
    ENDFOR
    di=WHERE(d_obj EQ 1,n_sds)
    gi=WHERE(g_obj EQ 1,n_grp)
    IF (n_ga EQ 0) AND (n_sds EQ 0) AND (n_grp NE 0) THEN stdfm=0
  ENDIF ELSE n_sds=0L

  IF stdfm EQ 0 THEN catinfo[0,0]='HE5' ;non-standard dataset so need to call READ_NONSTD_H5

  IF n_ga NE 0 THEN BEGIN
    ga=STRARR(n_ga) ;set the Global Attribute dimensions
    vn=[''] ;initialize vn array (to hold Dataset names)
    ;Read in the Global Attribute labels and values
    FOR i=0L,n_ga-1L DO BEGIN
      ga_id=H5A_OPEN_IDX(sd_id,i)
      ga_name=H5A_GET_NAME(ga_id) & ga_name=STRTRIM(ga_name,2)
      ;Check for netCDF4 specific attributes and ignore
      nc4i=WHERE(STRLOWCASE(ga_name) EQ nc4_ignore,nc4icnt)
      IF nc4icnt EQ 0 THEN BEGIN            
        ;test for valid attribute value - can be invalid if it is netCDF4 and equal to ''
        gni=WHERE(TAG_NAMES(h5p) EQ STRUPCASE(ga_name))
        attest=h5p.(gni[0])
        IF STRTRIM(attest._data[0],2) EQ '<read error>' THEN ga_data='' $
        ELSE BEGIN
          dt_id=H5A_GET_TYPE(ga_id)
          ga_data=H5A_READ(ga_id) & ga_data=STRTRIM(ga_data,2)
          ga_datatype=H5T_GET_CLASS(dt_id)
          H5T_CLOSE,dt_id
          ;String labels for GA not checked at this time as extra Global Attributes could be numeric
          ;IF ga_datatype NE 'H5T_STRING' THEN $ 
          ;  INFOTXT_OUTPUT_A,[20],['Global Attribute',STRTRIM(ga_name,2)]
        ENDELSE
        ;Check for _GLOSDS suffix and strip (added for compatibility with H4toH5 utility)
        IF STRMID(STRUPCASE(ga_name),STRLEN(ga_name)-7) EQ '_GLOSDS' THEN $
          ga_name=STRMID(ga_name,0,STRPOS(ga_name,'_',/Reverse_Search))
        WHILE STRMID(ga_name,STRLEN(ga_name)-1) EQ '_' DO ga_name=STRMID(ga_name,0,STRLEN(ga_name)-1)
        ga[i]=ga_name+'='+ga_data
      ENDIF
      H5A_CLOSE,ga_id
      ;Extract the list of the DATA_VARIABLES
      IF STRUPCASE(ga_name) EQ 'DATA_VARIABLES' THEN vn=STRSPLIT(ga_data,' ;',/Extract,COUNT=n_vn)
      IF STRUPCASE(ga_name) EQ 'FILE_NAME' THEN BEGIN
        ;check that the actual file name matches the FILE_NAME entry
        IF STRLOWCASE(STRTRIM(ga_data,2)) NE ifilechk THEN INFOTXT_OUTPUT_A,[24],[ga_data]
      ENDIF
    ENDFOR
    ;recalculate n_ga and ga in case of netCDF4 name which is ignored
    gai=WHERE(ga NE '',n_ga) 
    IF n_ga NE 0 THEN ga=ga[gai]
   ENDIF ELSE IF stdfm EQ 1 THEN BEGIN
    STOP_WITH_ERROR_A,o3[3]+proname,'No Global Attributes'+errtxt[0],lu
    RETURN
  ENDIF

  IF n_sds NE 0L THEN BEGIN
    NC_DIMENSION_CHK,'H5',sd_id,di,n_sds,dgi,n_sdsg ;check for netCDF dimension names (will quietly ignore)
    di=dgi & n_sds=n_sdsg
    IF n_vn NE n_sds THEN $
      INFOTXT_OUTPUT_A,[0],[STRTRIM(n_vn,2),STRTRIM(n_sds,2),'H5G_GET_NMEMBERS']
      ;DATA_VARIABLES values not read successfully, or number of datasets given under DATA_VARIABLES
      ;is not equal to the number saved to the file

    ;Determine maximum number of Attributes, correct dataset order, and dimension order
    max_atts=0L & oi=LONARR(n_sds)
    dv_order=1
    catinfo=STRARR(n_sds,4) ;output info for catalog output
    FOR i=0,n_sds-1 DO BEGIN
      vnv='' & sds_name=''
      hdftype=STRARR(n_lab-1) & idltype=STRARR(n_lab) ;to hold data types for dataset, min, max and fill values, and VAR_DATA_TYPE (idltype only)

      sds_name=H5G_GET_MEMBER_NAME(sd_id,'/',di[i])
      ds_id=H5D_OPEN(sd_id,sds_name)
      ;need to swap non-alphanumeric characters to '_' to match names from h5_parse for attribute values test
      h5p_name=ALPHA_NUMERIC_UNDERSCORE(STRUPCASE(sds_name))
 
      IF sds_name EQ '' THEN sds_name='N/A'

      ;determine dataset datatype
      dt_id=H5D_GET_TYPE(ds_id)
      dt_datatype=H5T_GET_CLASS(dt_id)
      IF dt_datatype EQ 'H5T_INTEGER' THEN BEGIN
        dt_sign=H5T_GET_SIGN(dt_id) ;0=unsigned; 1=signed
        dt_datatype=dt_datatype+'_'+STRTRIM(dt_sign,2)
        ;Byte needs to be unsigned, Short and Integer are signed
      ENDIF
      H5T_CLOSE,dt_id

      datasize=H5D_READ(ds_id)
      sds_dim=SIZE(datasize,/DIMENSIONS) & n_sds_dim=N_ELEMENTS(sds_dim)
      FOR sdl=0,n_sds_dim-1 DO IF sds_dim[sdl] EQ 0 THEN sds_dim[sdl]=1 ;constant scalar h5
      IF (ftype EQ 'N4') AND (dt_datatype EQ 'H5T_STRING') THEN BEGIN
        ;need to do char_dim checks for STRING datasets and remove the string length dimension
        char_dim=sds_dim[0]
        IF n_sds_dim EQ 1 THEN sds_dim[0]=1 $
        ELSE BEGIN
          sds_dim=sds_dim[1:n_sds_dim-1] & n_sds_dim=n_sds_dim-1
        ENDELSE
        dschk=STRARR(sds_dim)
        FOR sdl=0L,char_dim-1L DO dschk=dschk+datasize[sdl,*]
        datasize=dschk
      ENDIF
      sds_type=SIZE(datasize,/TYPE)

      hdftype[0]=dt_datatype & idltype[0]=STRTRIM(sds_type,2)

      ;Determine the number of attributes associated with the dataset
      sds_natts=H5A_GET_NUM_ATTRS(ds_id)
      IF sds_natts GT max_atts THEN max_atts=sds_natts

      IF sds_natts NE 0L THEN BEGIN
        vnf=0 & lcnt=0
        ci=STRARR(4) ;array containing catalog attributes
        ;Read in Dataset Attributes
        jj=0 ;use this index in the event of invalid attribute names (e.g. dimension_list from netCDF4 file)
        FOR j=0L,sds_natts-1L DO BEGIN
          da_id=H5A_OPEN_IDX(ds_id,j)
          da_name=H5A_GET_NAME(da_id) & da_name=STRTRIM(da_name,2)

          ;determine attribute datatype
          at_id=H5A_GET_TYPE(da_id)
          at_datatype=H5T_GET_CLASS(at_id)
          IF at_datatype EQ 'H5T_INTEGER' THEN BEGIN
            at_sign=H5T_GET_SIGN(at_id) ;0=unsigned; 1=signed
            at_datatype=at_datatype+'_'+STRTRIM(at_sign,2)
            ;Byte needs to be unsigned, Short and Integer are signed
          ENDIF
          H5T_CLOSE,at_id
          
          nc4i=WHERE(STRLOWCASE(da_name) EQ nc4_ignore,nc4icnt)
          IF nc4icnt EQ 0 THEN BEGIN
            ;test for valid attribute value - can be invalid if it is netCDF4 and equal to ''
            gni=WHERE(TAG_NAMES(h5p) EQ STRUPCASE(h5p_name)) ;Tag index from h5_parse read
            attest=h5p.(gni[0])
            gni=WHERE(TAG_NAMES(attest) EQ STRUPCASE(da_name))
            attestx=attest.(gni[0])
            IF STRTRIM(attestx._data[0],2) EQ '<read error>' THEN va_value='' $
            ELSE va_value=H5A_READ(da_id)
            IF SIZE(va_value,/TYPE) EQ 7 THEN va_type='STRING' ELSE va_type='NON_STRING'
            idltypehold=STRTRIM(SIZE(va_value,/TYPE),2)
            schk=WHERE(STRUPCASE(da_name) EQ slabel,schkcnt)
            IF ((sds_type EQ 7) AND (schkcnt EQ 0)) OR ((sds_type NE 7) AND (va_type EQ 'STRING')) THEN $
              va_value=STRTRIM(va_value,2)
            CASE 1 OF
              (STRUPCASE(da_name) EQ 'VAR_NAME') AND (vnf EQ 0):BEGIN
                  vnf=1
                  ;check that the dataset name matches the VAR_NAME
                  IF STRUPCASE(va_value) NE STRUPCASE(sds_name) THEN BEGIN
                    test1=ftype EQ 'N4'
                    test2=STRUPCASE(ALPHA_NUMERIC_UNDERSCORE(va_value)) EQ STRUPCASE(sds_name)
                    ;For nc4 files check if '.'s have been replaced by '_'s in the SDS_NAME. If so then it is OK
                    ;and quietly change SDS_NAME to the VAR_NAME value
                    IF (test1) AND (test2) THEN sds_name=va_value $
                    ELSE BEGIN
                      ;Try and determine which is wrong - Dataset name or VAR_NAME - default is Dataset name
                      li=WHERE((STRUPCASE(sds_name) EQ STRUPCASE(vn)) AND (vn[0] NE ''),lcnt)
                      ;If lcnt NE 0 then Dataset name matches DATA_VARIABLES value so assume that VAR_NAME is wrong
                      IF lcnt NE 0 THEN usesds=1B ELSE usesds=0B
                      INFOTXT_OUTPUT_A,[3,5,usesds],[sds_name,va_value]
                      IF usesds THEN va_value=sds_name else sds_name=va_value
                    ENDELSE
                  ENDIF
                  ;Determine actual list order from DATA_VARIABLE listing (H5 lists datasets alphabetically)
                  li=WHERE(STRUPCASE(va_value[0]) EQ STRUPCASE(vn),lcnt)
                  ci[0]=va_value & vnv=va_value
                END
              STRUPCASE(da_name) EQ 'VAR_DATA_TYPE': BEGIN
                  ci[1]=va_value & idltype[n_lab-1]=STRUPCASE(STRTRIM(va_value,2))
                END
              STRUPCASE(da_name) EQ 'VAR_UNITS': BEGIN
                  ;check that VAR_UNITS is a string value (in event that VAR_UNITS=1)
                  IF va_type NE 'STRING' THEN $
                    INFOTXT_OUTPUT_A,[20],['VAR_UNITS',STRTRIM(sds_name,2)]
                END
              STRUPCASE(da_name) EQ 'VAR_DEPEND': BEGIN
                  TEST_DIM_ORDER,'VD',va_value,va_type,sds_dim,sds_name,rev_vd_vs
                END
              STRUPCASE(da_name) EQ 'VAR_SIZE': BEGIN
                  TEST_DIM_ORDER,'VS',va_value,va_type,sds_dim,sds_name,rev_vd_vs
                END
              STRUPCASE(da_name) EQ 'VAR_VALID_MIN': BEGIN
                  hdftype[1]=at_datatype & idltype[1]=idltypehold
                END
              STRUPCASE(da_name) EQ 'VAR_VALID_MAX': BEGIN
                  hdftype[2]=at_datatype & idltype[2]=idltypehold
                END
              STRUPCASE(da_name) EQ 'VAR_FILL_VALUE': BEGIN
                  hdftype[3]=at_datatype & idltype[3]=idltypehold
                END
              ELSE:
            ENDCASE
            jj++
          ENDIF ;ELSE IF nc4i[0] EQ 4 THEN catinfo[0,0]='H5N' ;netCDF4 file - not implemented yet
          H5A_CLOSE,da_id
        ENDFOR
        ci[3]=STRTRIM(jj,2)
        IF ci[0] EQ '' THEN ci[0]=sds_name
      ENDIF ELSE BEGIN ;No Attributes, so can only write dataset name to Metadata
        ci=[STRUPCASE(sds_name),'','','1'] & vnv=sds_name
        ;Can the SDS_NAME be matched with a DATA_VARIABLES value?
        li=WHERE(STRUPCASE(sds_name) EQ STRUPCASE(vn),lcnt)
        IF lcnt NE 0 THEN vnf=1 ELSE vnf=0
        INFOTXT_OUTPUT_A,[5],[sds_name,'H5A_GET_NUM_ATTRS']
      ENDELSE
      FOR k=0,N_ELEMENTS(sds_dim)-1 DO BEGIN
        IF k EQ 0 THEN ci[2]=STRTRIM(sds_dim[k],2) ELSE ci[2]=ci[2]+';'+STRTRIM(sds_dim[k],2)
      ENDFOR

      ;Check for possible problems with determining array index
      IF lcnt NE 0 THEN BEGIN
        IF li[0] GE n_sds THEN dv_order=0 $ ;can occur when number of DATA_VARIABLE values is greater than number of datasets
        ELSE IF catinfo[li[0],0] NE '' THEN dv_order=0 ;This array has already been written to
      ENDIF ELSE IF (lcnt EQ 0) OR (vnf EQ 0) THEN dv_order=0
      ;Write out information text if sds_name cannot be matched and determine array index
      IF dv_order EQ 0 THEN BEGIN
        li=WHERE(catinfo[*,0] EQ '') ;determine lowest available array index to write info to
        in0=[4,vnf,lcnt,n_vn]
        IF (vnf EQ 0) OR ((lcnt EQ 0) AND (n_vn NE 0L)) THEN INFOTXT_OUTPUT_A,in0,[sds_name,vnv]
      ENDIF

      H5D_CLOSE,ds_id
      oi[i]=li[0] ;to put datasets in the correct order
      catinfo[li[0],*]=ci[*]
            
      ;Do data type checks on the Dataset, VAR_DATA_TYPE value and data types of VAR_VALID_MIN, VAR_VALID_MAX and VAR_FILL_VALUES
      DATA_TYPE_CHECKS,ftype,dt_chk_labels,hdftype,idltype,sds_name,vnv

    ENDFOR
  ENDIF ELSE IF stdfm EQ 1 THEN BEGIN
    STOP_WITH_ERROR_A,o3[3]+proname,'No Datasets'+errtxt[0],lu & RETURN
  ENDIF

  rev_vd_vsh=rev_vd_vs ;keep original value in hold variable (required if rev_vd_vs eq 3)
  IF (rev_vd_vs EQ 2) OR (rev_vd_vs EQ 3) THEN rev_vd_vs=1 ;change default so that VAR_DEPEND and VAR_SIZE 
  ;are reversed if not o/w changed in TEST_DIM_ORDER

  ;Dimension the structure to the size of the SDS datasets (with dimension n_sds)
  sds=REPLICATE(sds_set, n_sds, max_atts)
  ;Put datasets and attributes into sds structure
  FOR i=0,n_sds-1 DO BEGIN
    notranspose=1 ;0/1 will change to 0 if the dataset needs to be transposed
    sds_name=H5G_GET_MEMBER_NAME(sd_id,'/',di[i])
    ds_id=H5D_OPEN(sd_id,sds_name)
    ;need to swap non-alphanumeric characters to '_' to match names from h5_parse for attribute values test
    h5p_name=ALPHA_NUMERIC_UNDERSCORE(STRUPCASE(sds_name))

    IF sds_name EQ '' THEN sds_name='N/A'
    datasize=H5D_READ(ds_id)
    sds_type=SIZE(datasize,/TYPE)
    sds_dim=SIZE(datasize,/DIMENSIONS) & n_sds_dim=N_ELEMENTS(sds_dim)
    FOR sdl=0,n_sds_dim-1 DO IF sds_dim[sdl] EQ 0 THEN sds_dim[sdl]=1 ;constant scalar h5
    IF (ftype EQ 'N4') AND (sds_type EQ 7) THEN BEGIN
      ;need to do char_dim checks for STRING datasets and remove the string length dimension
      char_dim=sds_dim[0]
      IF n_sds_dim EQ 1 THEN sds_dim[0]=1 $
      ELSE BEGIN
        sds_dim=sds_dim[1:n_sds_dim-1] & n_sds_dim=n_sds_dim-1
      ENDELSE
      dschk=STRARR(sds_dim)
      FOR sdl=0L,char_dim-1L DO dschk=dschk+datasize[sdl,*]
      datasize=dschk
    ENDIF

    ;Determine the number of attributes associated with the dataset
    sds_natts=H5A_GET_NUM_ATTRS(ds_id)

    IF sds_natts NE 0L THEN BEGIN
      ;Read in Dataset Attributes
      jj=0 ;use this index in the event of invalid attribute names (e.g. dimension_list fro netCDF4 file)
      FOR j=0L,sds_natts-1L DO BEGIN
        da_id=H5A_OPEN_IDX(ds_id,j)
        da_name=H5A_GET_NAME(da_id) & da_name=STRTRIM(da_name,2)
        nc4i=WHERE(STRLOWCASE(da_name) EQ nc4_ignore,nc4icnt)
        IF nc4icnt EQ 0 THEN BEGIN
          ;test for valid attribute value - can be invalid if it is netCDF4 and equal to ''
          gni=WHERE(TAG_NAMES(h5p) EQ STRUPCASE(h5p_name)) ;Tag index from h5_parse read
          attest=h5p.(gni[0])
          gni=WHERE(TAG_NAMES(attest) EQ STRUPCASE(da_name))
          attestx=attest.(gni[0])
          IF STRTRIM(attestx._data[0],2) EQ '<read error>' THEN va_value='' $
          ELSE va_value=H5A_READ(da_id)

          IF SIZE(va_value,/TYPE) EQ 7 THEN va_type='STRING' ELSE va_type='NON_STRING'
          schk=WHERE(STRUPCASE(da_name) EQ slabel,schkcnt)
          IF ((sds_type EQ 7) AND (schkcnt EQ 0)) OR ((sds_type NE 7) AND (va_type EQ 'STRING')) THEN $
            va_value=STRTRIM(va_value,2)
          H5A_CLOSE,da_id

          IF ((STRUPCASE(da_name) EQ 'VAR_DEPEND') OR (STRUPCASE(da_name) EQ 'VAR_SIZE')) AND $
             ((rev_vd_vs MOD 10 EQ 1) OR (rev_vd_vs EQ 20)) THEN BEGIN ;need to reverse the va_values

            IF va_type NE 'STRING' THEN BEGIN ;VAR_SIZE contains numeric values instead of in the form of a string
              vavhold='' & n_vav=N_ELEMENTS(va_value)
              FOR k=0,n_vav-1 DO BEGIN
                IF k EQ n_vav-1 THEN vtxt='' ELSE vtxt=';'
                vavhold=vavhold+STRTRIM(va_value[k],2)+vtxt
              ENDFOR
            ENDIF ELSE vavhold=va_value

            vs_v=STRCOMPRESS(STRSPLIT(vavhold,';',/EXTRACT,COUNT=rcnt),/REMOVE_ALL)
            IF rcnt GT 1 THEN BEGIN ;multi-dimensions
              IF (rev_vd_vs EQ 20) THEN $ ;AND (STRPOS(STRUPCASE(va_value),'DATETIME') NE -1) THEN $
                notranspose=0 ;need to transpose dataset as well
              vs_v=REVERSE(vs_v)
              FOR k=0,rcnt-1 DO $
                IF k EQ 0 THEN va_value=vs_v[k] ELSE va_value=va_value+';'+vs_v[k]
            ENDIF
          ENDIF
          sds[oi[i],jj].va_l=PTR_NEW(da_name)
          sds[oi[i],jj].va_v=PTR_NEW(va_value)
          jj++
        ENDIF
      ENDFOR
    ENDIF ELSE BEGIN ;No Attributes, so can only write dataset name to structure
      val='VAR_NAME'
      sds[oi[i],0].va_l=PTR_NEW(val)
      sds[oi[i],0].va_v=PTR_NEW(sds_name)
    ENDELSE

    H5D_CLOSE,ds_id
    ;Test to see if dataset dimension ordering needs to be changed
    IF notranspose EQ 0 THEN datasize=TRANSPOSE(datasize)
    sds[oi[i],0].data=PTR_NEW(datasize)
  ENDFOR

  H5G_CLOSE,sd_id
  H5F_CLOSE,hdf_file_id
ENDIF ELSE BEGIN ;netCDF3 file (N3)
  ncdf_desc=['long_name']
  ncdf_std=['units','valid_range','_fillvalue']
  ncdf_dimen=['constant','independent']

  dv_order=1 ;Boolean indicating whether Metadata Variable listing order matches DATA_VARIABLES values
  n_vn=0L ;Initialize the number of Variable Names in the standard format netCDF file

  fileid=NCDF_OPEN(ifile)
  ;Get number of global atts and variables
  filestruct=NCDF_INQUIRE(fileid)
  n_dim=filestruct.ndims
  n_ga=filestruct.ngatts
  n_sds=filestruct.nvars

  IF n_sds EQ 0L THEN ntxt='Number of SDS datasets' $
  ELSE IF n_ga EQ 0L THEN ntxt='Number of Global Attributes' $
  ELSE ntxt=''
  IF ntxt NE '' THEN BEGIN
    STOP_WITH_ERROR_A,o3[3]+proname,ntxt+errtxt[1],lu & RETURN
  ENDIF

  ;read global attributes
  ga=STRARR(n_ga) & vn=['']
  FOR j=0,n_ga-1 DO BEGIN
    ga_name=NCDF_ATTNAME(fileid,j,/GLOBAL)
    NCDF_ATTGET,fileid,ga_name,ga_data,/GLOBAL
    ga_data=STRTRIM(ga_data,2)
    ga[j]=STRTRIM(STRUPCASE(ga_name),2)+'='+ga_data
    ;read list of DATA_VARIABLES into array
    IF STRUPCASE(ga_name) EQ 'DATA_VARIABLES' THEN vn=STRSPLIT(ga_data,' ;',/Extract,COUNT=n_vn)
    IF STRUPCASE(ga_name) EQ 'FILE_NAME' THEN BEGIN
      ;check that the actual file name matches the FILE_NAME entry
      IF STRLOWCASE(STRTRIM(ga_data,2)) NE ifilechk THEN INFOTXT_OUTPUT_A,[24],[ga_data]
    ENDIF
  ENDFOR

  ;read dimension information
  IF n_dim NE 0 THEN BEGIN
    mv_dim=STRARR(n_dim,2)
    FOR j=0,n_dim-1 DO BEGIN
      NCDF_DIMINQ,fileid,j,dimname,dimsize
      mv_dim[j,0]=STRTRIM(dimname,2)
      mv_dim[j,1]=STRTRIM(dimsize,2)
    ENDFOR
  ENDIF ELSE mv_dim=['']

  di=INDGEN(n_sds)
  NC_DIMENSION_CHK,'N3',fileid,di,n_sds,dgi,n_sdsg
  di=dgi & n_sds=n_sdsg ;di contains index values of valid sds datasets

  IF n_vn NE n_sds THEN $
    INFOTXT_OUTPUT_A,[0],[STRTRIM(n_vn,2),STRTRIM(n_sds,2),'NCDF_INQUIRE']
    ;DATA_VARIABLES values not read successfully, or number of datasets given under DATA_VARIABLES
    ;is not equal to the number saved to the file

  ;Determine maximum number of Attributes, correct dataset order, and dimension order
  max_atts=0L & oi=LONARR(n_sds)
  catinfo=STRARR(n_sds,4) ;output info for catalog output

  FOR j=0,n_sds-1 DO BEGIN
    vnv='' & sds_name=''
    hdftype=STRARR(n_lab-1) & idltype=STRARR(n_lab) ;to hold data types for dataset, min, max and fill values, and VAR_DATA_TYPE (idltype only)

    ;Extract data from a netCDF dataset and get size information
    NCDF_VARGET,fileid,di[j],sds_data
    sds_type=SIZE(sds_data,/TYPE)

    varstruct=NCDF_VARINQ(fileid,di[j])
    sds_natts=varstruct.natts
    IF sds_natts GT max_atts THEN max_atts=sds_natts
    sds_name=varstruct.name
    sds_datatype=varstruct.datatype ;'BYTE', 'CHAR', 'INT', 'LONG', 'FLOAT', or 'DOUBLE'
    sds_dimid=varstruct.dim
    sds_ndim=varstruct.ndims

    ;determine the actual dimension values from the dim IDs given in varstruct.dim
    ;IF sds_ndim NE 0 THEN BEGIN
    ;  sds_dim=LONARR(sds_ndim)
    ;  FOR k=0,sds_ndim-1 DO sds_dim[k]=mv_dim[sds_dimid[k],1]
    ;ENDIF ELSE sds_dim=[1L]
    
    IF (sds_datatype EQ 'CHAR') AND (sds_type EQ 1) THEN sds_type=7
    sds_dim=SIZE(sds_data,/DIMENSIONS) & n_sds_dim=N_ELEMENTS(sds_dim)
    FOR sdl=0,n_sds_dim-1 DO IF sds_dim[sdl] EQ 0 THEN sds_dim[sdl]=1 ;constant scalar h5
    IF sds_type EQ 7 THEN BEGIN
      ;need to do char_dim checks for STRING datasets and remove the string length dimension
      char_dim=sds_dim[0]
      IF n_sds_dim EQ 1 THEN sds_dim[0]=1 $
      ELSE BEGIN
        sds_dim=sds_dim[1:n_sds_dim-1] & n_sds_dim=n_sds_dim-1
      ENDELSE
    ENDIF
    
    IF sds_datatype EQ 'BYTE' THEN $
       IF MIN(sds_data) LT 0 THEN sds_datatype='BYTE_1' ELSE sds_datatype='BYTE_0'
       ;This doesn't work as NCDF_VARGET converts BYTE values to 8-bit unsigned if they are negative signed values

    hdftype[0]=sds_datatype & idltype[0]=STRTRIM(sds_type,2)

    ;Read Variable Attributes
    IF sds_natts NE 0L THEN BEGIN
      vnf=0 & lcnt=0 & vnv=''
      ci=STRARR(4) ;array containing catalog attributes
      std_fnd=INTARR(n_aad)
      ;Read in Dataset Attributes
      FOR k=0,sds_natts-1 DO BEGIN
        varattsname=NCDF_ATTNAME(fileid,di[j],k)
        NCDF_ATTGET,fileid,di[j],varattsname,va_value
        attstruct=NCDF_ATTINQ(fileid,di[j],varattsname)
        va_name=STRUPCASE(varattsname)
        vavtype=SIZE(va_value,/TYPE)

        IF (vavtype EQ 7) OR (vavtype EQ 1) THEN va_type='STRING' ELSE va_type='NON-STRING'
        hdftypehold=attstruct.datatype
        IF hdftypehold EQ 'BYTE' THEN $
          IF va_value LT 0 THEN hdftypehold='BYTE_1' ELSE hdftypehold='BYTE_0'
          ;This doesn't work as NCDF_VARGET converts BYTE values to 8-bit unsigned if they are negative signed values
        idltypehold=vavtype
        
        schk=WHERE(va_name EQ slabel,schkcnt)
        test1=(sds_datatype EQ 'CHAR') AND (schkcnt EQ 0) ;string dataset but not one of the slabel values
        test2=(sds_datatype NE 'CHAR') AND (va_type EQ 'STRING') ;not a string dataset but string attribute value
        test3=(sds_datatype EQ 'CHAR') AND (schkcnt NE 0) AND (vavtype EQ 1) ;string dataset, slabel and attribute value is of type byte
        IF (test1) OR (test2) THEN va_value=STRTRIM(va_value,2) $
        ELSE IF test3 THEN BEGIN
          va_value=STRING(va_value) & idltypehold=7
        ENDIF

        CASE 1 OF
          (STRUPCASE(va_name) EQ 'VAR_NAME') AND (vnf EQ 0):BEGIN
              vnf=1
              ;check that the dataset name matches the VAR_NAME
              IF STRUPCASE(va_value) NE STRUPCASE(sds_name) THEN BEGIN
                test1=ftype EQ 'N3'
                test2=STRUPCASE(ALPHA_NUMERIC_UNDERSCORE(va_value)) EQ STRUPCASE(sds_name)
                ;For nc3 files check if '.'s have been replaced by '_'s in the SDS_NAME. If so then it is OK
                ;and quietly change SDS_NAME to the VAR_NAME value
                IF (test1) AND (test2) THEN sds_name=va_value $
                ELSE BEGIN
                  ;Try and determine which is wrong - Dataset name or VAR_NAME - default is Dataset name
                  li=WHERE((STRUPCASE(sds_name) EQ STRUPCASE(vn)) AND (vn[0] NE ''),lcnt)
                  ;If lcnt NE 0 then Dataset name matches DATA_VARIABLES value so assume that VAR_NAME is wrong
                  IF lcnt NE 0 THEN usesds=1 ELSE usesds=0
                  INFOTXT_OUTPUT_A,[3,5,usesds],[sds_name,va_value]
                  IF usesds EQ 0 THEN sds_name=va_value ELSE va_value=sds_name
                ENDELSE
              ENDIF
              ;Determine actual list order from DATA_VARIABLE listing
              li=WHERE(STRUPCASE(va_value[0]) EQ STRUPCASE(vn),lcnt)
              ci[0]=va_value & vnv=va_value
            END
          STRUPCASE(va_name) EQ 'VAR_DATA_TYPE': BEGIN
              ci[1]=va_value & idltype[n_lab-1]=STRUPCASE(STRTRIM(va_value,2))
            END
          STRUPCASE(va_name) EQ 'VAR_UNITS': BEGIN
              ;check that VAR_UNITS is a string value (in event that VAR_UNITS=1)
              IF va_type NE 'STRING' THEN $
                INFOTXT_OUTPUT_A,[20],['VAR_UNITS',STRTRIM(sds_name,2)]
            END
          STRUPCASE(va_name) EQ 'VAR_DEPEND': BEGIN
              TEST_DIM_ORDER,'VD',va_value,va_type,sds_dim,sds_name,rev_vd_vs
            END
          STRUPCASE(va_name) EQ 'VAR_SIZE': BEGIN
              TEST_DIM_ORDER,'VS',va_value,va_type,sds_dim,sds_name,rev_vd_vs
            END
          STRUPCASE(va_name) EQ 'VAR_VALID_MIN': BEGIN
              hdftype[1]=hdftypehold & idltype[1]=idltypehold
            END
          STRUPCASE(va_name) EQ 'VAR_VALID_MAX': BEGIN
              hdftype[2]=hdftypehold & idltype[2]=idltypehold
            END
          STRUPCASE(va_name) EQ 'VAR_FILL_VALUE': BEGIN
              hdftype[3]=hdftypehold & idltype[3]=idltypehold
            END
          ELSE:
        ENDCASE
      ENDFOR
      ci[3]=STRTRIM(sds_natts,2)
      IF ci[0] EQ '' THEN ci[0]=sds_name
    ENDIF ELSE BEGIN ;No Attributes, so can only write dataset name to Metadata
      ci=[STRUPCASE(sds_name),'','','1'] & vnv=sds_name
      ;Can the SDS_NAME be matched with a DATA_VARIABLES value?
      li=WHERE(STRUPCASE(sds_name) EQ STRUPCASE(vn),lcnt)
      IF lcnt NE 0 THEN vnf=1 ELSE vnf=0
      INFOTXT_OUTPUT_A,[5],[sds_name,'NCDF_VARINQ']
    ENDELSE
    FOR k=0,N_ELEMENTS(sds_dim)-1 DO BEGIN
      IF k EQ 0 THEN ci[2]=STRTRIM(sds_dim[k],2) ELSE ci[2]=ci[2]+';'+STRTRIM(sds_dim[k],2)
    ENDFOR

    ;Check for possible problems with determining array index
    IF lcnt NE 0 THEN BEGIN
      IF li[0] GE n_sds THEN dv_order=0 $ ;can occur when number of DATA_VARIABLE values is greater than number of datasets
      ELSE IF catinfo[li[0],0] NE '' THEN dv_order=0 ;This array has already been written to
    ENDIF ELSE IF (lcnt EQ 0) OR (vnf EQ 0) THEN dv_order=0
    ;Write out information text if sds_name cannot be matched and determine array index
    IF dv_order EQ 0 THEN BEGIN
      li=WHERE(catinfo[*,0] EQ '') ;determine lowest available array index to write info to
      in0=[4,vnf,lcnt,n_vn]
      IF (vnf EQ 0) OR ((lcnt EQ 0) AND (n_vn NE 0L)) THEN INFOTXT_OUTPUT_A,in0,[sds_name,vnv]
    ENDIF

    oi[j]=li[0] ;to put datasets in the correct order
    catinfo[li[0],*]=ci[*]

    ;Do data type checks on the Dataset, VAR_DATA_TYPE value and data types of VAR_VALID_MIN, VAR_VALID_MAX and VAR_FILL_VALUES
    DATA_TYPE_CHECKS,ftype,dt_chk_labels,hdftype,idltype,sds_name,vnv

  ENDFOR

  rev_vd_vsh=rev_vd_vs ;keep original value in hold variable (required if rev_vd_vs eq 3)
  IF (rev_vd_vs EQ 2) OR (rev_vd_vs EQ 3) THEN rev_vd_vs=1 ;change default so that VAR_DEPEND and VAR_SIZE
  ;are reversed if not o/w changed in TEST_DIM_ORDER
 
  ;Dimension the structure to the size of the SDS datasets (with dimension n_sds)
  sds=REPLICATE(sds_set, n_sds, max_atts)
  ;Put datasets and attributes into sds structure
  FOR j=0,n_sds-1 DO BEGIN
    notranspose=1 ;0/1 will change to 0 if the dataset needs to be transposed
    varstruct=NCDF_VARINQ(fileid,di[j])
    sds_name=varstruct.name
    IF sds_name EQ '' THEN sds_name='N/A'
    sds_natts=varstruct.natts
    sds_datatype=varstruct.datatype
    sds_ndim=varstruct.ndims

    ;Extract data from a netCDF dataset and get size information
    NCDF_VARGET,fileid,di[j],sds_data

    IF sds_natts NE 0L THEN BEGIN
      ;Read in Dataset Attributes
      FOR k=0L,sds_natts-1L DO BEGIN
        varattsname=NCDF_ATTNAME(fileid,di[j],k)
        NCDF_ATTGET,fileid,di[j],varattsname,va_value
        va_name=STRUPCASE(varattsname)
        vavtype=SIZE(va_value,/TYPE)
        vavhold=va_value
        IF (vavtype EQ 7) OR (vavtype EQ 1) THEN va_type='STRING' ELSE va_type='NON-STRING'

        schk=WHERE(va_name EQ slabel,schkcnt)
        test1=(sds_datatype EQ 'CHAR') AND (schkcnt EQ 0) ;string dataset but not one of the slabel values
        test2=(sds_datatype NE 'CHAR') AND (va_type EQ 'STRING') ;not a string dataset but string attribute value
        test3=(sds_datatype EQ 'CHAR') AND (schkcnt NE 0) AND (vavtype EQ 1) ;string dataset, slabel and attribute value is of type byte
        IF (test1) OR (test2) THEN va_value=STRTRIM(va_value,2) $
        ELSE IF test3 THEN va_value=STRING(va_value)        

        IF ((STRUPCASE(va_name) EQ 'VAR_DEPEND') OR (STRUPCASE(va_name) EQ 'VAR_SIZE')) AND $
          ((rev_vd_vs MOD 10 EQ 1) OR (rev_vd_vs EQ 20)) THEN BEGIN ;need to reverse the va_values

          IF va_type NE 'STRING' THEN BEGIN ;VAR_SIZE contains numeric values instead of in the form of a string
            vavhold='' & n_vav=N_ELEMENTS(va_value)
            FOR i=0,n_vav-1 DO BEGIN
              IF i EQ n_vav-1 THEN vtxt='' ELSE vtxt=';'
              vavhold=vavhold+STRTRIM(va_value[i],2)+vtxt
            ENDFOR
          ENDIF ELSE vavhold=va_value
          
          vs_v=STRCOMPRESS(STRSPLIT(vavhold,';',/EXTRACT,COUNT=rcnt),/REMOVE_ALL)
          IF rcnt GT 1 THEN BEGIN ;multi-dimensions
            IF (rev_vd_vs EQ 20) THEN $ ;AND (STRPOS(STRUPCASE(va_value),'DATETIME') NE -1) THEN $
              notranspose=0 ;need to transpose dataset as well
            vs_v=REVERSE(vs_v)
            FOR i=0,rcnt-1 DO $
              IF i EQ 0 THEN va_value=vs_v[i] ELSE va_value=va_value+';'+vs_v[i]
          ENDIF
        ENDIF
        IF ((STRUPCASE(va_name) EQ 'VAR_VALID_MIN') OR (STRUPCASE(va_name) EQ 'VAR_VALID_MAX') OR $
            (STRUPCASE(va_name) EQ 'VAR_FILL_VALUE')) AND (sds_datatype EQ 'BYTE') AND $
            (vavtype EQ 1) THEN va_value=vavhold
        sds[oi[j],k].va_l=PTR_NEW(va_name)
        sds[oi[j],k].va_v=PTR_NEW(va_value)
      ENDFOR
      ;This section commented out for now - Version 4.0b16
      ;Check attributes again in case there are missing GEOMS Attributes that can be filled in
      ;vnt=''
      ;FOR k=0,sds_natts-1 DO BEGIN
      ;  varattsname=NCDF_ATTNAME(fileid,j,k)
      ;  NCDF_ATTGET,fileid,j,varattsname,va_value
      ;  va_name=STRUPCASE(varattsname)
      ;  va_type=SIZE(va_value,/TYPE)

      ;  schk=WHERE(va_name EQ slabel,schkcnt)
      ;  IF ((sds_datatype EQ 'CHAR') AND (schkcnt EQ 0)) OR ((sds_datatype NE 'CHAR') AND $
      ;    ((va_type EQ 1) OR (va_type EQ 7))) THEN va_value=STRTRIM(va_value,2) $
      ;  ELSE IF (va_type EQ 1) OR (va_type EQ 7) THEN va_value=STRING(va_value)

      ;  ;Check for VAR_NOTES entry
      ;  lcname=STRLOWCASE(varattsname)
      ;  ni=WHERE(lcname EQ ncdf_desc,ncnt)
      ;  IF ncnt EQ 0 THEN ni=WHERE(lcname EQ ncdf_std,ncnt)
      ;  IF (ncnt EQ 0) AND (lcname NE 'units') THEN BEGIN ;append label and value to VAR_NOTES
      ;    mi=WHERE(attr_arr_data EQ 'VAR_NOTES')
      ;    IF vnt NE '' THEN BEGIN
      ;      IF STRMID(vnt,STRLEN(vnt)-1,1) EQ '.' THEN itxt=' ' ELSE itxt='. '
      ;    ENDIF ELSE itxt=''
      ;    vnt=vnt+itxt+STRTRIM(varattsname,2)+': '+STRTRIM(va_value,2)
      ;  ENDIF
      ;  mcnt=0
      ;  ;Check for VAR_DESCRIPTION entry (long_name)
      ;  IF lcname EQ 'long_name' THEN mi=WHERE(attr_arr_data EQ 'VAR_DESCRIPTION',mcnt)
      ;  ;Check for VAR_UNITS entry
      ;  IF lcname EQ 'units' THEN mi=WHERE(attr_arr_data EQ 'VAR_UNITS',mcnt)
      ;  IF (mcnt NE 0) AND (std_fnd[mi[0]] EQ 0) THEN sds[oi[j],mi[0]].va_v=PTR_NEW(STRTRIM(va_value,2))
      ;  ;Check for valid_range and _FillValue attributes
      ;  ni=WHERE(lcname EQ ncdf_std,ncnt)
      ;  IF ncnt NE 0 THEN BEGIN
      ;    vdt=SIZE(varattsval,/TYPE)
      ;    IF lcname EQ 'valid_range' THEN BEGIN
      ;      mi=WHERE(attr_arr_data EQ 'VAR_VALID_MIN')
      ;      IF std_fnd[mi[0]] EQ 0 THEN sds[oi[j],mi[0]].va_v=PTR_NEW(varattsval[0])
      ;      mi=WHERE(attr_arr_data EQ 'VAR_VALID_MAX')
      ;      IF std_fnd[mi[0]] EQ 0 THEN sds[oi[j],mi[0]].va_v=PTR_NEW(varattsval[1])
      ;    ENDIF ELSE BEGIN
      ;      mi=WHERE(attr_arr_data EQ 'VAR_FILL_VALUE')
      ;      IF std_fnd[mi[0]] EQ 0 THEN sds[oi[j],mi[0]].va_v=PTR_NEW(varattsval)
      ;    ENDELSE
      ;  ENDIF
      ;ENDFOR
      ;IF vnt NE '' THEN BEGIN
      ;  mi=WHERE(attr_arr_data EQ 'VAR_NOTES')
      ;  IF std_fnd[mi[0]] EQ 0 THEN sds[oi[j],mi[0]].va_v=PTR_NEW(vnt)
      ;ENDIF
      ; ;Write VAR_NAME to metadata
      ;mi=WHERE(attr_arr_data EQ 'VAR_NAME')
      ;IF std_fnd[mi[0]] EQ 0 THEN sds[oi[j],mi[0]].va_v=PTR_NEW(sds_name)
      ;catinfo[oi[j],0]=sds_name
      ;;Write VAR_DATA_TYPE to metadata
      ;mi=WHERE(attr_arr_data EQ 'VAR_DATA_TYPE')
      ;CASE 1 OF
      ;  sds_datatype EQ 'BYTE':catinfo[oi[j],1]='BYTE'
      ;  sds_datatype EQ 'CHAR':catinfo[oi[j],1]='STRING'
      ;  (sds_datatype EQ 'INT') OR (sds_datatype EQ 'SHORT'):catinfo[oi[j],1]='SHORT'
      ;  sds_datatype EQ 'LONG':catinfo[oi[j],1]='INTEGER'
      ;  sds_datatype EQ 'FLOAT':catinfo[oi[j],1]='REAL'
      ;  sds_datatype EQ 'DOUBLE':catinfo[oi[j],1]='DOUBLE'
      ;ENDCASE
      ;sds[oi[j],mi[0]].va_v=PTR_NEW(catinfo[oi[j],1])
      ;;Write VAR_DEPEND to metadata
      ;mi=WHERE(attr_arr_data EQ 'VAR_DEPEND')
      ;IF std_fnd[mi[0]] EQ 0 THEN BEGIN
      ;  vd=''
      ;  FOR k=0,sds_ndim-1 DO BEGIN
      ;    IF k EQ 0 THEN vd=vd+STRTRIM(mv_dim[sds_dim[k],0],2) $
      ;    ELSE vd=vd+';'+STRTRIM(mv_dim[sds_dim[k],0],2)
      ;  ENDFOR
      ;  sds[oi[j],mi[0]].va_v=PTR_NEW(vd)
      ;ENDIF
      ;;Determine VAR_SIZE from dataset attributes
      ;mi=WHERE(attr_arr_data EQ 'VAR_SIZE')
      ;IF std_fnd[mi[0]] EQ 0 THEN BEGIN
      ;  sds_dim=SIZE(sds_data,/DIMENSIONS)
      ;  IF sds_dim[0] EQ 0 THEN sds_dim[0]=1
      ;  vs=''
      ;  FOR k=0,N_ELEMENTS(sds_dim)-1 DO $
      ;    IF k EQ 0 THEN vs=vs+STRTRIM(sds_dim[k],2) ELSE vs=vs+';'+STRTRIM(sds_dim[k],2)
      ;  sds[oi[j],mi[0]].va_v=PTR_NEW(vs)
      ;ENDIF

    ENDIF ELSE BEGIN ;No Attributes, so can only write dataset name to structure
      val='VAR_NAME'
      sds[oi[j],0].va_l=PTR_NEW(val)
      sds[oi[j],0].va_v=PTR_NEW(sds_name)
    ENDELSE

    ;Test to see if dataset dimension ordering needs to be changed
    IF notranspose EQ 0 THEN sds_data=TRANSPOSE(sds_data)
    IF sds_datatype EQ 'CHAR' THEN sds_data=STRING(sds_data)
    sds[oi[j],0].data=PTR_NEW(sds_data)
  ENDFOR

  NCDF_CLOSE,fileid
  ;Add DATA_VARIABLES to global attributes if not already present
  gi=WHERE(STRMID(ga,0,14) EQ 'DATA_VARIABLES',gcnt)
  IF gcnt EQ 0 THEN BEGIN
    FOR j=0,n_sds-1 DO BEGIN
      IF j EQ 0 THEN dv=catinfo[j,0] ELSE dv=dv+';'+catinfo[j,0]
    ENDFOR
    ga=[ga,'DATA_VARIABLES='+dv]
  ENDIF
  dv_order=-1 & n_vn=n_sds
ENDELSE

;Array listing order may not be correct
IF (dv_order EQ 0) AND (n_vn NE 0L) THEN INFOTXT_OUTPUT_A,[6]

;If necessary write comment regarding VAR_SIZE and VAR_DEPEND dimension ordering
rev_vd_vs=rev_vd_vsh ;used for when rev_vd_vs eq 3
IF (rev_vd_vs MOD 10 EQ 0) OR (rev_vd_vs EQ 3) THEN INFOTXT_OUTPUT_A,[11,rev_vd_vs]

END ;Procedure Read_HDF_SDS



PRO output_hdf_data, ifile, ga, sds, catinfo
;Procedure to output the contents of an HDF file in ASCII form, either in a format compatible for
;conversion back to HDF, or in Row and Column format, or as a summary list
; ----------
;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org
;
;  History:
;    20050729: Original IDLCR8ASCII Routine - Version 1.0
;    20050912: Removed Common variable definition CATALOGINFO and made the variable a parameter passed
;              to the procedure; Make output filenames lower case; If data type is double and the
;              accompanying VIS_FORMAT value starts with an 'F' (e.g. F9.4), then, change the 'F' to a
;              'D' when specifying the format for output - Version 1.1
;    20061004: Common variable definition WIDGET_WIN_A added for Error calls; Display input/output
;              information in a pop-up display window if the program is opened using IDL VM - Version 2.0
;    20080302: Added code which sends the Catalog and log output to the IDLDE output window and/or an
;              external file (as determined by the dux array values); Ensure all data values are
;              separated by a space when the Dump output option is chosen - Version 3.0
;    20101122: Adopt GEOMS metadata standard; Because FORMAT attribute(s) have been dropped can no longer
;              use defined formatting values when outputing data to files; Default ASCII formatting
;              adopted - scientific notation for REAL and DOUBLE, except for DATETIME and related values,
;              which have formatting dependent on the data type - Version 4.0b1
;    20150127: Ensure all string datasets are written and saved as left justified strings (previously
;              defaulted to right justified) - Version 4.0b10
;    20151109: Convert DATETIME (and related) max, min and fill values to LONG64 data type before
;              determining the format for ASCII output to account for very large values - Version 4.0b12
;    20161130: Add checks for Metadata values written in an invalid data type (e.g. structure) and for
;              invalid attributes - Version 4.0b16
;
;  Inputs: ifile - a string containing the name of the HDF file to be read in.
;          ga - a string array containing the global attribute labels and values extracted from the HDF
;               file.
;          sds - a structure using pointers, of size [n_sds,n_atts], containing the variable attribute
;                labels and values (sds[n,m].va_l and sds[n,m].va_v) and the data (sds[n,0].data)
;                extracted from the HDF file.
;          catinfo - a string array of size [n_sds,4] (where n_sds is the number of datasets in the HDF
;                    file), containing information on the variable name, data type, data dimension, and
;                    number of attributes
;
;  Outputs: Nil
;
;  Called by: IDLCR8ASCII
;
;  Subroutines Called: None

COMMON WIDGET_WIN_A

;determine number of datasets and attributes (from sds.va)
as=SIZE(catinfo)
n_sds=as[1]
indir=FILE_DIRNAME(ifile,/MARK_DIRECTORY)
infile=FILE_BASENAME(ifile)

vdtype=[1,2,3,4,5,7,12,13,14,15] ;valid IDL data types
badlabel=[''] ;list of attribute labels where the values are not valid (to avoid repeated log messages)

IF o3[1] EQ 'C' THEN BEGIN
  IF o3[3] EQ '' THEN BEGIN
    lineno=lineno+n_sds+1
    wintxt='Listing of Var_Name Index; Var_Name; Var_Data_Type; Var_Size'
    WIDGET_CONTROL,wtxt,set_value=wintxt,/Append,Set_text_top_line=lineno
  ENDIF
  FOR i=dux[0],dux[1],dux[2] DO BEGIN
    IF i EQ -1 THEN PRINT,'Listing of Var_Name Index; Var_Name; Var_Data_Type; Var_Size' $
    ELSE PRINTF,i,'Listing of Var_Name Index; Var_Name; Var_Data_Type; Var_Size'
  ENDFOR
  FOR j=0,n_sds-1 DO BEGIN
    IF o3[3] EQ '' THEN BEGIN
      wintxt=STRTRIM(j,2)+'; '+catinfo[j,0]+'; '+catinfo[j,1]+'; '+catinfo[j,2]
      WIDGET_CONTROL,wtxt,set_value=wintxt,/Append
    ENDIF
    FOR i=dux[0],dux[1],dux[2] DO $
      IF i EQ -1 THEN PRINT,STRTRIM(j,2),'; ',catinfo[j,0],'; ',catinfo[j,1],'; ',catinfo[j,2] $
      ELSE PRINTF,i,STRTRIM(j,2),'; ',catinfo[j,0],'; ',catinfo[j,1],'; ',catinfo[j,2]
  ENDFOR
ENDIF
IF o3[0] NE '0' THEN BEGIN
  ;create output filenames
  outf1=STRMID(infile,0,STRPOS(infile,'.',/Reverse_Search))+'.meta'
  outf2=STRMID(infile,0,STRPOS(infile,'.',/Reverse_Search))+'.data'
  OPENW,lu1,indir+outf1,/GET_LUN
  OPENW,lu2,indir+outf2,/GET_LUN

  ;HDF4 and NETCDF pre-defined attributes
  ncsa=['long_name','units','format','coordsys','valid_range','_FillValue','scale_factor', $
        'scale_factor_err','add_offset','add_offset_err','calibrated_nt']

  PRINTF,lu1,'! Output from IDLcr8ASCII application v4.0'
  PRINTF,lu1,'! '+ifile & PRINTF,lu1,'!'
  PRINTF,lu1,'! Global Attributes'
  FOR i=0,N_ELEMENTS(ga)-1 DO PRINTF,lu1,ga[i]

  FOR j=0,n_sds-1 DO BEGIN
    n_atts=FIX(catinfo[j,3])
    IF n_atts NE 0 THEN BEGIN
      PRINTF,lu1,'!' & PRINTF,lu1,'! Variable Attributes'
      PRINTF,lu2,catinfo[j,0]
      FOR i=0,n_atts DO BEGIN ;number of attributes. Last loop is for Data
        IF i EQ n_atts THEN vavt=SIZE(*sds[j,0].data,/TYPE) $
        ELSE vavt=SIZE(*sds[j,i].va_v,/TYPE)
        gti=WHERE(vavt EQ vdtype,g_vavt) ;test to see if the data type is valid
        IF g_vavt EQ 1 THEN BEGIN ;it is OK to transfer the attribute value to a variable
          IF i EQ n_atts THEN vav=*sds[j,0].data ELSE vav=*sds[j,i].va_v
        ENDIF
        IF (STRPOS(STRUPCASE(catinfo[j,0]),'DATETIME') NE -1) AND ((vavt EQ 4) OR (vavt EQ 5)) THEN BEGIN
          ns=MAX(STRLEN(STRTRIM(LONG64(vav),2)))
          IF vavt EQ 4 THEN dp=6 ELSE dp=9 ;indicates the number of decimal places to use in the output
          ;If the number is very large then output as an exponent
          IF ns LT 8 THEN fmt='(D'+STRTRIM(ns+dp+1,2)+'.'+STRTRIM(dp,2)+')' ELSE fmt='(E0)'
        ENDIF ELSE IF (vavt EQ 4) OR (vavt EQ 5) THEN fmt='(E0)' $
        ELSE IF vavt EQ 7 THEN fmt='(A-)' $
        ELSE fmt='(I0)'
        IF i NE n_atts THEN BEGIN ;write out metadata in form LABEL=VALUE
          nvav=N_ELEMENTS(vav)
          vat=*sds[j,i].va_l
          ni=WHERE(STRLOWCASE(vat) EQ STRLOWCASE(ncsa),ncnt) ;check for pre-defined attributes and exclude
          IF (ncnt EQ 0) AND (g_vavt EQ 1) THEN BEGIN
            IF nvav EQ 1 THEN vat=vat+'='+STRING(FORMAT=fmt,vav[0]) $ ;'(A-'+STRTRIM(STRLEN(vav[0]),2)+')',vav[0]) $
            ELSE BEGIN
              FOR k=0,N_ELEMENTS(vav)-1 DO BEGIN
                IF k EQ 0 THEN vat=vat+'='+STRTRIM(STRING(FORMAT=fmt,vav[k]),2) $
                ELSE vat=vat+';'+STRTRIM(STRING(FORMAT=fmt,vav[k]),2)
              ENDFOR
            ENDELSE
            PRINTF,lu1,vat
          ENDIF ELSE IF g_vavt NE 1 THEN BEGIN
            bli=WHERE(vat EQ badlabel,blcnt)
            IF blcnt EQ 0 THEN BEGIN
              badlabel=[badlabel,vat]
              INFOTXT_OUTPUT_A,[18],[vat]
            ENDIF
          ENDIF
        ENDIF
      ENDFOR

      ;Write out data
      IF o3[0] EQ 'F' THEN BEGIN
        PRINTF,lu2,format=fmt,vav
      ENDIF ELSE IF o3[0] EQ 'D' THEN BEGIN
        ;Ensure there is a single character between all values
        sdssize=SIZE(vav,/Dimension)
        sdshold=STRTRIM(STRING(FORMAT=fmt,vav),2)
        maxstr=MAX(STRLEN(sdshold))
        ;use default IDL formatting rules
        sdshold=STRING(format='(A-'+STRTRIM(maxstr,2)+')',sdshold)
        ;Uncomment line below if format described by VIS_FORMAT is required
        ;sdshold=STRING(format=p_f,*sds[j].data)
        IF sdssize[0] NE 0 THEN sdshold=REFORM(sdshold,sdssize)
        PRINTF,lu2,sdshold
      ENDIF
    ENDIF ELSE BEGIN ;Variable attributes do not have an associated dataset
      INFOTXT_OUTPUT_A,[19],[catinfo[j,0]]
    ENDELSE
  ENDFOR
  PRINTF,lu1,'!' & PRINTF,lu1,'! End of output file created by IDLcr8ASCII'
  FREE_LUN,lu1 & FREE_LUN,lu2
  IF o3[3] EQ '' THEN BEGIN
    lineno=lineno+2L
    WIDGET_CONTROL,wtxt,set_value=outf1+' created!',/Append,Set_text_top_line=lineno
    WIDGET_CONTROL,wtxt,set_value=outf2+' created!',/Append,Set_text_top_line=lineno
  ENDIF
  FOR i=dux[0],dux[1],dux[2] DO BEGIN
    PRINTF,i,'  '+outf1+' created!' & PRINTF,i,'  '+outf2+' created!'
  ENDFOR
ENDIF
END ;Procedure Output_HDF_Data



PRO idlcr8ascii, ifile, ga, sds, reterr, $
                 FORMAT=o1, DUMP=o2, CATALOG=o4, POPUP=o5, LOG=o6, H4=o7, H5=o8, NC=o9
;Main IDL procedure to convert GEOMS compliant netCDF, HDF4 and HDF5 files to ASCII, Session Memory
;or another recognized Data format.
;
;Program documentation, idlcr8ascii-v4.0_Readme.pdf, available on http://avdc.gsfc.nasa.gov.
;
;Program sub-version 4.0b27 (20240927)
; ----------
;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org
;
;  History:
;    20050729: Original Release - Version 1.0
;    20050912: Removed Common variable definition CATALOGINFO; If the format of the
;              DATA_START_DATE and FILE_GENERATION_DATE values is MJD2000, then change to ISO8601,
;              unless the output option is /Dump - Version 1.1
;    20061004: Common variable definition WIDGET_WIN_A added for Error calls; Make the code suitable
;              for running on a licensed version of IDL (using the .pro and .sav versions of the code)
;              and on IDL Virtual Machine (using the .sav version of the code); Change the command
;              line keyword options. Remove the /Help option (window now opened if there are no command
;              line parameters), and /Catalog can now be called together with /AVDC or /Dump; The
;              program can now handle multiple HDF files as input (either as a string array or as a
;              file spec); Display input/output information as well as errors and warnings in a pop-up
;              display window if the program is called using IDL VM; Help window becomes an
;              Introduction window, and the user has the option of continuing to run the program with
;              file inputs; Include option to read HDF5 versions of the ground-based files
;              - Version 2.0
;    20080302: Remove HELP,/TRACEBACK call, which identified how the program was called. This was used
;              to stop output to the IDLDE output window in the event that IDL VM was used, but it is
;              not required as IDL VM ignores these print calls; Add options to send Catalog and logged
;              input/output information to a Pop-up Box (/Popup) and/or to an external file (/Log). Add
;              DIALOG_BOX to show completion of the program if program inputs are via the INTRO box, and
;              logging window option isn't requested - Version 3.0
;    20091208: Add INFORMATION text message to INFOTXT_OUTPUT_A procedure; Replace /AVDC keyword with
;              /FORMAT (note /AVDC also retained for backward compatibility purposes); Improve checks
;              on parameter and keyword inputs - Version 3.03
;    20101122: Changes made to account for new GEOMS conventions, including changing the format of the
;              structure so that numeric metadata values can be saved in their actual datatype, rather
;              than string; Add return error code option so that program returns to the calling program
;              if an error is generated - Version 4.0b1
;    20120328: Add options to convert HDF/netCDF files to other HDF/netCDF formats e.g. H4 to
;              H5 etc. Requires IDLcr8HDF in the search path to do this - Version 4.0b5
;    20130114: Add check for IDL being run in DEMO mode by using LMGR command. Replace PRINTF,-1
;              statements (i.e. when dux[0] eq -1) with PRINT (o/w causes DEMO mode to stop). Also disable
;              /LOG, /FORMAT and /DUMP keywords and stop ASCII file output in DEMO mode - Version 4.0b7
;    20150127: Add extra condition for the file extension when checking whether an input file is netCDF
;              due to the program crashing when incorrectly identifying an HDF5 file as netCDF when
;              using IDL8.3 (OK for IDL6.4) - Version 4.0b10
;    20160213: Add CATCH, /CANCEL after idlcr8hdf call - Version 4.0b13
;    20190806: Call routine FILE_FORMAT_A to determine the correct format of the input file(s)
;              - Version 4.0b22
;    20200930: Check that GEOMS file does not have filesize of zero before calling FILE_FORMAT_A
;              - Version 4.0b24
;
;  Inputs: ifile - a string array or filespec containing the name of the HDF file(s) to be read in
;
;          OPTIONS
;            FORMAT - Program generates two output files per input file (with .meta and .data
;                     extensions). The resulting files are compatible with programs that can write HDF
;                     files using these two files as input.
;            DUMP - Program generates two output files per input file (with .meta and .data extensions).
;                   Data values will be shown as indicated by the array format defined by VAR_SIZE.
;            CATALOG - Program sends the variable index, variable name, format, and dimension(s)
;                      information to a pop-up window or the IDL DE output log window.
;            POPUP - to append input/output information as well as warnings and errors to a pop-up
;                    display window
;            LOG - to append input/output/catalog information as well as errors to a log file named
;                  idlcr8ascii.log
;            H4 - outputs contents of the input file as an HDF4 file
;            H5 - outputs contents of the input file as an HDF4 file
;            NC - outputs contents of the input file as a netCDF3 file
;
;  Outputs: ga - If included as a parameter in the command line, a string array containing the global
;                attribute labels and values extracted from a single HDF file.
;           sds - If included as a parameter in the command line, a structure using pointers, of size
;                 [n_sds,n_atts], containing the variable attribute labels and values (sds[n,m].va_l
;                 and sds[n.m].va_v) and the data (sds[n,0].data) extracted from a single HDF file.
;                 The data is saved in the form defined by the variable attributes
;           reterr - optional string input that, if included, returns an error message to the calling
;                    program rather than stop the program. Note that the Pop-up option will be
;                    deselected if present together with this argument.
;           Two ASCII files per input file containing the attributes and datasets extracted from the HDF
;           file(s), if the FORMAT or DUMP options are chosen. Summary metadata information if the
;           CATALOG option is chosen. Other Hierachical data format files if H4, H5, or netCDF are chosen.
;
;  Called by: Main Routine
;
;  Subroutines Called: INTRO_A (if program called without command line parameters)
;                      FILE_FORMAT_A (to determine the format of the file(s))
;                      READ_HDF_SDS
;                      JDF_2_DATETIME
;                      OUTPUT_HDF_DATA (if output is not just to session memory only)
;                      STOP_WITH_ERROR_A (if error state detected)
;                      INFOTXT_OUTPUT_A (if program can make a change)
;    Possible Conditions for STOP_WITH_ERROR call (plus [line number] where called):
;      1. No valid HDF4, HDF5 or netCDF file selected.
;
;    Information Conditions (when the program is able to make changes):
;      1. If Session Memory option is chosen can only read in one HDF file.
;      2. /POPUP keyword cannot be used together with the 'reterr' argument.
;      3. Argument 'reterr' must be a scalar variable of type string.
;      4. /LOG, /FORMAT, and /DUMP keywords cannot be used in IDL DEMO mode
;      5. NetCDF file create feature is disabled in IDL DEMO mode

COMMON WIDGET_WIN_A

;Possible error messages for this procedure
proname='IDLCR8ASCII procedure: '
IF FLOAT(!Version.Release) GE 5.6 THEN errtxt='No valid HDF or netCDF file selected.' $
ELSE BEGIN
  errtxt='No valid HDF or netCDF file selected '
  errtxt=errtxt+'(Note: Input file can only be HDF4 or netCDF3 for IDL'+!Version.Release+').'
ENDELSE
lu=-1

ON_IOERROR,TypeConversionError ;used when checking Datetime format

demomode=LMGR(/DEMO) ;Boolean to check for IDL being run in demo mode (disable /LOG option)
IF N_PARAMS() EQ 1 THEN BEGIN ;File input only
  ;check to see what output keyword options are set - if none then intype=-3 (i.e. invalid)
  IF (~ KEYWORD_SET(o1)) AND (~ KEYWORD_SET(o2)) AND (~ KEYWORD_SET(o4)) AND $
     (~ KEYWORD_SET(o7)) AND (~ KEYWORD_SET(o8)) AND (~ KEYWORD_SET(o9)) THEN $
    intype=-3 ELSE intype=SIZE(ifile,/Type)
ENDIF ELSE IF N_PARAMS() GE 1 THEN intype=SIZE(ifile,/Type) $ ;Check that infile is a string
ELSE IF (KEYWORD_SET(o1)) OR (KEYWORD_SET(o2)) OR (KEYWORD_SET(o4)) OR $
        (KEYWORD_SET(o7)) OR (KEYWORD_SET(o8)) OR (KEYWORD_SET(o9)) THEN $
  intype=-1 $ ;need to open dialog box for input file
ELSE IF (KEYWORD_SET(o5)) OR (KEYWORD_SET(o6)) THEN intype=-3 $ ;cannot have these keyword options only
ELSE intype=-2 ;no parameter or keyword inputs in idlcr8ascii call
IF (intype NE 7) AND (intype GE 0) THEN intype=-3 ;first command line parameter not a string type
dux=[-1,-1,1] ;initialize flags for output log to IDLDE Output Window and/or to file
;dux[0] default allows output to the IDLDE output window (ignored by IDL VM)
;dux[1] is the program assigned file unit for sending log output to file
;dux[2] is the step between dux[0] and dux[1]
rerr='NA' ;initialize return error string

o3=['-1','0','0','0','0','0','0','0']
IF intype LT -1 THEN BEGIN ;either no or invalid input parameters and/or keywords
  INTRO_A,intype ;Open Intro Box and determine output options (FORMAT/DUMP, Catalog, etc)
  IF o3[0] EQ '-1' THEN BEGIN
    STOP_WITH_ERROR_A,'','',lu & RETURN
  ENDIF
  IF o3[3] EQ 'P' THEN o3[3]='' ELSE o3[3]='D_'
ENDIF ELSE BEGIN ;Set options (FORMAT/DUMP/CONVERT, Catalog, Session Memory, Popup Window, Log Output)
  IF (KEYWORD_SET(o1)) THEN o3=['F','0','0','D_','0','0','0','0'] $
  ELSE IF KEYWORD_SET(o2) THEN o3=['D','0','0','D_','0','0','0','0'] $
  ELSE o3=['0','0','0','D_','0','0','0','0']
  IF N_PARAMS() GE 3 THEN o3[2]='M'
  IF KEYWORD_SET(o4) THEN o3[1]='C'
  IF KEYWORD_SET(o5) THEN o3[3]=''
  IF KEYWORD_SET(o6) THEN o3[4]='idlcr8ascii.log'
  IF KEYWORD_SET(o7) THEN o3[5]='H4'
  IF KEYWORD_SET(o8) THEN o3[6]='H5'
  IF KEYWORD_SET(o9) THEN o3[7]='N3'
ENDELSE

;Check for reterr parameter
infoval=INTARR(4)
IF (N_PARAMS() EQ 2) OR (N_PARAMS() GE 4) THEN BEGIN
  ;If reterr included allows error output to return to a calling program
  IF N_PARAMS() EQ 2 THEN reterr=ga ;session memory option not wanted
  ;IF (ARG_PRESENT(reterr)) AND (SIZE(reterr,/Type) EQ 7) THEN BEGIN
  IF SIZE(reterr,/Type) EQ 7 THEN BEGIN
    rerr=''
    IF o3[3] EQ '' THEN BEGIN
      ;Deselect POPUP option and write INFORMATION message
      o3[3]='D_' & infoval[0]=14
    ENDIF
  ENDIF ELSE infoval[1]=15
  ;reterr input included but is of the incorrect type, or parameter cannot be returned to the calling program
ENDIF

;Do check for /LOG, /FORMAT, /DUMP, and/or /NC keywords in IDL DEMO Mode and disable if necessary
IF (demomode) AND ((o3[4] EQ 'idlcr8ascii.log') OR (o3[0] NE '0') OR (o3[7] EQ 'N3')) THEN BEGIN
  IF (o3[4] EQ 'idlcr8ascii.log') OR (o3[0] NE '0') THEN infoval[2]=16
  IF o3[7] EQ 'N3' THEN infoval[3]=17
  o3[0]='0' & o3[4]='0' & o3[7]='0'
ENDIF

IF N_PARAMS() GE 1 THEN BEGIN
  ;Check that input HDF/netCDF files exist. If not then prompt the user for the missing files
  arorfs=SIZE(ifile,/Dimensions) ;Is file input Filespec or String Array?
  IF (arorfs[0] EQ 0) THEN BEGIN ;input is filespec
    IF ifile NE '' THEN ifile=FILE_SEARCH(ifile)
    isize=SIZE(ifile,/Dimensions)
    IF isize[0] EQ 0 THEN ifile=[''] ;no files found matching filespec
  ENDIF
  IF N_ELEMENTS(arorfs) GT 1 THEN ifile=REFORM(ifile,N_ELEMENTS(ifile),/Overwrite) ;Convert to 1-D array
  file_exist=FILE_TEST(ifile,/Read)
  gi=WHERE(file_exist EQ 1,gcnt)
  IF gcnt NE 0 THEN ifile=ifile[gi] ;contains all valid filenames
ENDIF ELSE ifile=['']
gi=WHERE(ifile NE '',nfile)
IF nfile EQ 0L THEN BEGIN
  IF o3[2] EQ 'M' THEN $ ;pick one file only
    ifile=DIALOG_PICKFILE(Filter=['*.hdf','*.h5','*.nc;*nc4'],/Must_Exist, $
          Title='Select HDF4, HDF5, or netCDF GEOMS Compatible Format File') $
  ELSE $ ;multiple selection permissable
    ifile=DIALOG_PICKFILE(Filter=['*.hdf','*.h5','*.nc;*.nc4'],/Must_Exist, $
          /Multiple_Files,Title='Select HDF4, HDF5, or netCDF GEOMS Compatible Format File(s)')
ENDIF
;now check to see that files were actually selected, if not STOP!
gi=WHERE(ifile NE '',nfile)
IF nfile EQ 0L THEN BEGIN
  STOP_WITH_ERROR_A,'D_'+proname,errtxt,lu
  IF STRLEN(rerr) GT 2 THEN BEGIN
    IF N_PARAMS() EQ 2 THEN ga=rerr ELSE reterr=rerr
  ENDIF
  RETURN
ENDIF ELSE BEGIN
  ifile=ifile[gi] & dsort=SORT(ifile) & ifile=ifile[dsort]
  ifile=FILE_SEARCH(ifile,/FULLY_QUALIFY_PATH)
ENDELSE

;Set dux values according to output options
IF o3[4] NE '0' THEN BEGIN ;open idlcr8ascii.log
  o3[4]=FILE_DIRNAME(ifile[0],/Mark_Directory)+o3[4]
  res=FILE_TEST(o3[4],/Write)
  IF res EQ 0 THEN openw,du,o3[4],/GET_LUN $
  ELSE BEGIN
    OPENW,du,o3[4],/Append,/GET_LUN & FOR i=0,1 DO PRINTF,du,''
  ENDELSE
  dux[1]=du & dux[2]=dux[1]-dux[0]
ENDIF
FOR i=dux[0],dux[1],dux[2] DO $
  IF i EQ -1 THEN PRINT,'HDF/netCDF File Input/Output Log - Program Started on '+SYSTIME(0) $
  ELSE PRINTF,i,'HDF/netCDF File Input/Output Log - Program Started on '+SYSTIME(0)

IF o3[3] EQ '' THEN BEGIN
  ;Set-up output window display widget
  base=WIDGET_BASE(Title='HDF/netCDF File Input/Output Log',Units=2,yoffset=3,xoffset=3,Tlb_Frame_Attr=1,/Column) ;,Tab_Mode=1)
  wtxt=WIDGET_TEXT(base,/Scroll,xsize=100,ysize=12)
  base2=WIDGET_BASE(base)
  b3=WIDGET_BUTTON(base2,value='Finish',uvalue='DONE',frame=3,Sensitive=0) ;,Accelerator='Return')
  WIDGET_CONTROL,wtxt,/Realize
  WIDGET_CONTROL,base,/Realize
  lineno=0L
ENDIF

IF (o3[2] EQ 'M') AND (N_ELEMENTS(ifile) GT 1) THEN BEGIN
  ifile=[ifile[0]] & nfile=1L
  INFOTXT_OUTPUT_A,[7]
ENDIF

FOR i=0,N_ELEMENTS(infoval)-1 DO IF infoval[i] NE 0 THEN INFOTXT_OUTPUT_A,[infoval[i]]

FOR ndf=0L,nfile-1L DO BEGIN
  catinfo=STRARR(1,4) ;initialize catinfo array
  ifile[ndf]=FILE_SEARCH(ifile[ndf],/Fully_Qualify_Path)
  IF o3[3] EQ '' THEN BEGIN
    lineno=lineno+2L
    IF ndf NE 0 THEN WIDGET_CONTROL,wtxt,set_value='',/Append
    WIDGET_CONTROL,wtxt,set_value='Reading '+ifile[ndf],/Append,Set_text_top_line=lineno
  ENDIF
  FOR i=dux[0],dux[1],dux[2] DO BEGIN
    IF i EQ -1 THEN BEGIN
      IF ndf NE 0 THEN PRINT,''
      PRINT,'Reading '+ifile[ndf] & PRINT,''
    ENDIF ELSE BEGIN
      IF ndf NE 0 THEN PRINTF,i,''
      PRINTF,i,'Reading '+ifile[ndf] & PRINTF,i,''
    ENDELSE
  ENDFOR

  ftype='XX'
  IF (FILE_TEST(ifile[ndf])) AND (~FILE_TEST(ifile[ndf],/ZERO_LENGTH)) THEN BEGIN
    ;Identify the file format
    ftype=FILE_FORMAT_A(ifile[ndf])
    IF ftype NE 'XX' THEN catinfo[0,0]=ftype
  ENDIF
  IF ftype EQ 'XX' THEN BEGIN
    ;Either file doesn't exist or is not a valid file format
    STOP_WITH_ERROR_A,o3[3]+proname,errtxt,lu
    IF STRLEN(rerr) GT 2 THEN BEGIN
      IF N_PARAMS() EQ 2 THEN ga=rerr ELSE reterr=rerr
    ENDIF
    RETURN
  ENDIF

  ;Open and read the file
  READ_HDF_SDS,ifile[ndf],ga,sds,catinfo
  IF STRLEN(rerr) GT 2 THEN BEGIN
    reterr=rerr & RETURN
  ENDIF

  IF catinfo[0,0] EQ 'HE5' THEN BEGIN
    STOP_WITH_ERROR_A,o3[3]+proname,errtxt,lu
    IF STRLEN(rerr) GT 2 THEN BEGIN
      IF N_PARAMS() EQ 2 THEN ga=rerr ELSE reterr=rerr
    ENDIF
    RETURN
  ENDIF ELSE BEGIN ;Do checks and output standard format file
    ;Convert format of DATA_START/STOP_DATE and FILE_GENERATION_DATE if required
    ;Note: for /Dump option output will still be original format
    isodates=['DATA_START_DATE','DATA_STOP_DATE','FILE_GENERATION_DATE']
    niso=N_ELEMENTS(isodates)
    gaiso=STRARR(niso) & gih=INTARR(niso)
    FOR i=0,niso-1 DO BEGIN
      gi=WHERE(STRPOS(STRUPCASE(ga),isodates[i]) NE -1,gcnt)
      IF gcnt EQ 1 THEN BEGIN
        res=STRSPLIT(ga[gi[0]],' =',/EXTRACT)
        IF N_ELEMENTS(res) EQ 2 THEN BEGIN
          IF STRPOS(STRUPCASE(res[1]),'Z') EQ -1 THEN BEGIN ;i.e. not ISO8601
            valid=0 ;used for Type Conversion Check
            mjd=DOUBLE(res[1]) ;will jump to TypeConversionError if Res[1] is not a number
            valid=1 ;got to here so valid conversion (presumably it is MJD2000)
            TypeConversionError:
            IF valid EQ 1 THEN BEGIN
              iso='' & iso=JDF_2_DATETIME(mjd,/MJD2000,/S)
              gaiso[i]=res[0]+'='+iso
              IF o3[0] EQ 'F' THEN ga[gi[0]]=gaiso[i]
              gih[i]=gi[0]
            ENDIF
          ENDIF
        ENDIF
      ENDIF
    ENDFOR

    ;Create ASCII Output
    IF (o3[0] NE '0') OR (o3[1] NE '0') THEN OUTPUT_HDF_DATA,ifile[ndf],ga,sds,catinfo
    IF (o3[5] NE '0') OR (o3[6] NE '0') OR (o3[7] NE '0') THEN BEGIN
      ;Check for existence of TAV file in the input file directory or working directory
      indir=FILE_DIRNAME(ifile[ndf],/MARK_DIRECTORY)
      tav=FILE_SEARCH(indir+'tableattrvalue*.dat',/FULLY_QUALIFY_PATH)
      IF tav[0] EQ '' THEN INFOTXT_OUTPUT_A,[12] $ ;No valid TAV file so format conversion cannot be done
      ELSE BEGIN
        ;Convert data to a different format
        validpath=1 & ftest=5 & dferr=''
        IF demomode THEN ftestmax=6 ELSE ftestmax=7
        WHILE (validpath EQ 1) AND (ftest LE ftestmax) DO BEGIN
          IF o3[ftest] NE '0' THEN BEGIN
            CATCH, path_error ;Return program generated error if IDLcr8HDF is not in the search path
            ;Error Handler
            IF path_error NE 0 THEN BEGIN ;IDLcr8HDF is not in the IDL Search Path
              validpath=0
              INFOTXT_OUTPUT_A,[13]
              ;PRINT, 'Error index: ', path_error
              ;PRINT, 'Error message: ', !ERROR_STATE.MSG
              CATCH, /CANCEL
            ENDIF
            IF validpath EQ 1 THEN BEGIN
              ;Try to call IDLcr8HDF
              IF demomode THEN BEGIN
                CASE 1 OF
                  ftest EQ 5: IDLCR8HDF,ga,sds,tav[0],indir,dferr
                  ftest EQ 6: IDLCR8HDF,ga,sds,tav[0],indir,dferr,/H5
                ENDCASE
              ENDIF ELSE BEGIN
                CASE 1 OF
                  ftest EQ 5: IDLCR8HDF,ga,sds,tav[0],indir,dferr,/L
                  ftest EQ 6: IDLCR8HDF,ga,sds,tav[0],indir,dferr,/H5,/L
                  ftest EQ 7: IDLCR8HDF,ga,sds,tav[0],indir,dferr,/NC,/L
                ENDCASE
              ENDELSE
              CATCH, /CANCEL
              IF o3[4] NE '0' THEN BEGIN
                ;transfer contents of idlcr8hdf.log to idlcr8ascii.log
                OPENR,fu,indir+'idlcr8hdf.log',/DELETE,/GET_LUN
                dum=''
                READF,fu,dum & READF,fu,dum
                WHILE NOT EOF(fu) DO BEGIN
                  READF,fu,dum & PRINTF,du,dum
                ENDWHILE
                FREE_LUN,fu
              ENDIF ELSE FILE_DELETE,indir+'idlcr8hdf.log',/QUIET
            ENDIF
          ENDIF
          ftest++
        ENDWHILE
      ENDELSE
    ENDIF
    FOR i=0,niso-1 DO IF gaiso[i] NE '' THEN ga[gih[i]]=gaiso[i]
    IF o3[2] EQ 'M' THEN BEGIN
      IF o3[3] EQ '' THEN BEGIN
        lineno=lineno+1L
        WIDGET_CONTROL,wtxt,set_value=ifile+' array and heap structure created!',/Append,Set_text_top_line=lineno
      ENDIF
      FOR i=dux[0],dux[1],dux[2] DO $
        IF i EQ -1 THEN PRINT,'  '+ifile[ndf]+' array and heap structure created!' $
        ELSE PRINTF,i,'  '+ifile[ndf]+' array and heap structure created!'
    ENDIF
  ENDELSE
ENDFOR

IF rerr EQ '' THEN $
  IF nfile EQ 1 THEN reterr=ifile[0]+' successfully read' $
  ELSE reterr='Files successfully read'
IF N_PARAMS() EQ 2 THEN ga=reterr

FOR i=dux[0],dux[1],dux[2] DO BEGIN
  IF i EQ -1 THEN BEGIN
    PRINT,'' & PRINT,'File read completed - Program Ended on '+SYSTIME(0)
  ENDIF ELSE BEGIN
    PRINTF,i,'' & PRINTF,i,'File read completed - Program Ended on '+SYSTIME(0)
  ENDELSE
ENDFOR
IF dux[1] GT -1 THEN FREE_LUN,dux[1]
IF o3[3] EQ '' THEN BEGIN
  WIDGET_CONTROL,b3,Sensitive=1,/Input_Focus
  WIDGET_CONTROL,wtxt,set_value='',/Append,Set_text_top_line=lineno+2L
  WIDGET_CONTROL,wtxt,set_value='HDF file read completed - hit <Finish> to close program',/Append
  XMANAGER,'idlcr8ascii',base
ENDIF ELSE IF intype LT 0 THEN BEGIN ;Create Finish Dialog Box
  res=DIALOG_MESSAGE('HDF file read completed!',/Information,Title='EVDC/AVDC IDLcr8ASCII')
ENDIF

END ;procedure IDLcr8ASCII