;Main Program Version: idlcr8hdf.pro v3.0, 20080302
;  Written by Ian Boyd, NIWA-ERI for the AVDC - i.boyd@niwa.com
;Sub-versions:
;  v3.01, 20080418 - Fix bug in the check_min_max_fill procedure so that unwanted precision from
;                    all data and relevant metadata values is removed before doing QA checks.
;  v3.02, 20080430 - Change made so that any Free Attribute Metadata values that include semi-
;                    colons do not have white space removed (i.e. not treated as a sub-value).
;  v3.03, 20080523 - DIMENSIONLIST attribute in an HDF5 file, containing the object reference
;                    pointers to the variables listed in the VAR_DEPEND values, removed.
;  v3.04, 20080610 - Fix bug in the check_min_max_fill procedure which could generate an error
;                    message with incorrect information.
;  v3.05, 20081020 - Change dataset count variables from INT16 to INT32, to allow handling of
;                    datasets that contain more than 32767 values. Account for modified rules
;                    for *.BOUNDARIES variable names.

PRO idlcr8hdf_common
;Procedure to define the COMMON blocks for this program
; ----------
;Written by Ian Boyd, NIWA-ERI for the AVDC - i.boyd@niwa.com
;
;  History:
;    20050909: Introduced to IDLCR8HDF - Version 1.1
;    20061012: Variable 'ncsa' added to METADATA; 'iarr', 'larr', 'rarr', 'darr' holding Arrays
;              removed from DATA and replaced with Structure 'ds'; Variable 'vfv' added to DATA;
;              WIDGET_WIN added for common variables associated with the Graphical User Interface
;              - Version 2.0
;    20080302: tab_type integer added to TABLEDATA - Version 3.0
;
;  Input: Nil
;
;  Output: Nil
;
;  Called by: N/A
;
;  External Subroutines Called: None

COMMON TABLEDATA, tab_arr,tab_ver,tab_type
COMMON METADATA, meta_arr,attr_arr_glob,attr_arr_data,free_attr,ncsa
COMMON DATA, ds,ndm,nvn,vn,vu,vfv
COMMON WIDGET_WIN, wtxt,b1,lineno,base,o3,dux

END ;Procedure idlcr8hdf_common

PRO intro_event, ev
;Procedure to define how Events from the Start-up Introduction Window are handled
; ----------
;Written by Ian Boyd, NIWA-ERI for the AVDC - i.boyd@niwa.com
;
;  History:
;    20061012: Introduced to IDLCR8HDF - Version 2.0
;
;  Input: ev - Selected widget event structure
;
;  Output: o3 - Common string array defining the various options for program output
;
;  Called by: XMANAGER in INTRO
;
;  External Subroutines Called: None

COMMON WIDGET_WIN

;The uservalue is retrieved from a widget when an event occurs
WIDGET_CONTROL,ev.id,GET_UVALUE=uv
;Assign/Remove AVK button event to/from a variable name
IF uv EQ 'AVK' THEN IF o3[1] EQ uv THEN o3[1]='0' ELSE o3[1]=uv $
;Assign/Remove Log Output button event to/from a variable name
ELSE IF uv EQ 'idlcr8hdf.log' THEN IF o3[2] EQ uv THEN o3[2]='0' ELSE o3[2]=uv $
;Assign/Remove Pop-up window for Log Output button event to/from a variable name
ELSE IF uv EQ 'Pop' THEN IF o3[3] EQ uv THEN o3[3]='0' ELSE o3[3]=uv $
;Assign button event to a variable name
ELSE IF (uv EQ 'H4') OR (uv EQ 'H5') THEN o3[0]=uv $
ELSE o3[0]='0' ;Cancel button chosen
IF (uv NE 'AVK') AND (uv NE 'idlcr8hdf.log') AND (uv NE 'Pop') THEN WIDGET_CONTROL,ev.top,/DESTROY

END ;Intro_Event

PRO intro, intype
;Procedure which creates an Introduction Window at start-up when IDLCR8HDF 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, NIWA-ERI for the AVDC - i.boyd@niwa.com
;
;  History:
;    20061012: Introduced to IDLCR8HDF - 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 - Version 3.0
;
;  Input: intype - Integer set to -1 or -2: -1 indicates normal state; -2 indicates that input
;                  parameters at the IDLCR8HDF call were invalid.
;
;  Output: Nil
;
;  Called by: IDLCR8HDF
;
;  External Subroutines Called: INTRO_EVENT (via XMANAGER)

nhdr=39 & errtxt=STRARR(nhdr)
errtxt[1]='Welcome to IDLcr8HDF.  This program creates AVDC/NDACC/ENVISAT format HDF4 or compatible'
errtxt[2]='HDF5 files (also refer to Readme_IDLcr8HDF.pdf).'
errtxt[4]='Inputs to the program (IDL Virtual Machine (VM) and IDL Licensed (LIC) Versions):'
errtxt[5]='  METADATA TEMPLATE FILE - Containing the Global and Variable Attribute information for the site.'
errtxt[6]='  DATA FILE(s) - Multiple selection permitted.  The file(s) must show the datasets in the single'
errtxt[7]='     column format described in the documentation.'
errtxt[8]='  TAV FILE - the current non-encrypted Table Attribute Values file.'
errtxt[10]='Alternative Input Option for IDL LIC Versions:'
errtxt[11]='  GLOBAL ATTRIBUTES ARRAY (GA) - a string array containing the Global Attributes in the form'
errtxt[12]='     ''label=value''.'
errtxt[13]='  DATA STRUCTURE (DS) - a heap structure using pointers containing the Variable Attributes (DS.VA), in'
errtxt[14]='     the form ''label=value'', and the Data (DS.Data), for a single output file.'
errtxt[15]='  TAV FILE - the current non-encrypted Table Attribute Values file.'
errtxt[17]='For IDL VM, input is by ''DIALOG_BOXES''.  For IDL LIC, input can be by ''DIALOG_BOXES'' or passed'
errtxt[18]='by calling the program with one of the following command line options:'
errtxt[19]='  1. idlcr8hdf  (Opens this box, and allows the user the option to continue with file inputs).'
errtxt[20]='  2. idlcr8hdf,METAFILE,DATAFILE(s),TAVFILE[,/H5][,/AVK][,/Log][,/Popup]  or'
errtxt[21]='    idlcr8hdf,'''','''',''''[,/H5][,/AVK][,/Log][,/Popup]   (Inputs are from existing files, if no files are'
errtxt[22]='    specified then DIALOG_BOXES will prompt for input).'
errtxt[23]='  3. idlcr8hdf,GA,DS,TAVFILE[,/H5][,/AVK][,/Log][,/Popup] or idlcr8hdf,GA,DS[,/H5][,/AVK][,/Log][,/Popup]'
errtxt[24]='    (Inputs are from session memory, if no TAV file is specified then a DIALOG_BOX will prompt for input).'
errtxt[25]='    /H5, /AVK, /Log and /Popup keywords are used in place of the options given below (HDF4 is default).'
errtxt[27]='Contacts -'
errtxt[28]='  Ian Boyd, NIWA-ERI (i.boyd@niwa.com)'
errtxt[29]='  Department of Astronomy, 619 Lederle GRC, University of Massachusetts'
errtxt[30]='  710 North Pleasant St, Amherst, MA 01002, USA'
errtxt[32]='  Bojan Bojkov, AVDC Project Manager (Bojan.Bojkov@gsfc.nasa.gov)'
errtxt[33]='  NASA Goddard Space Flight Center, Code 613.3'
errtxt[34]='  Greenbelt, MD 20771, USA'
errtxt[36]='AVDC Website: Tools and documentation available from http://avdc.gsfc.nasa.gov/Overview/index.html'
errtxt[38]='To continue, please choose from the options below (Note: HDF5 only available on IDL6.2 or greater).'
errtxt='        '+errtxt

;Set-up text display widget
IF intype EQ -2 THEN xtxt=' - Command Line Input Error' ELSE xtxt=''
base=WIDGET_BASE(Title='idlcr8hdf v3.0 March 2008'+xtxt,Tlb_Frame_Attr=1,/Column) ;,Tab_Mode=1)
wtxt=WIDGET_TEXT(base,xsize=90,ysize=17,/Scroll)
base3=WIDGET_BASE(base,/Nonexclusive)
logtext='Append log output to the file ''idlcr8hdf.log'' (will create the file IF it doesn''t exist)'
poptext='Open a Pop-Up Window to display log output'
avktext='If Avg. Kernel data are present, append sentence to VAR_NOTES giving first three values for '
avktext=avktext+'lowest altitude level'
avktip='Sentence reads ''First three AVK values for the lowest altitude level are: x.xx x.xx x.xx'''
b4=WIDGET_BUTTON(base3,value=logtext,uvalue='idlcr8hdf.log',frame=3)
b5=WIDGET_BUTTON(base3,value=poptext,uvalue='Pop',frame=3)
b6=WIDGET_BUTTON(base3,value=avktext,uvalue='AVK',frame=3) ;,Tooltip=AVKTip)
base2=WIDGET_BASE(base,/Row)
tip='Left Mouse Click or Tab to entry and hit <Spacebar>'
b1=WIDGET_BUTTON(base2,value='HDF4',uvalue='H4',frame=3) ;,Tooltip=Tip)
IF FLOAT(!Version.Release) GE 6.2 THEN $
  b2=WIDGET_BUTTON(base2,value='HDF5',uvalue='H5',frame=3) $;,Tooltip=Tip) $
ELSE b2=WIDGET_BUTTON(base2,value='HDF5',Sensitive=0,frame=3)
b3=WIDGET_BUTTON(base2,value='Stop',uvalue='CANCEL',frame=3) ;,ToolTip=Tip)
WIDGET_CONTROL,base,/Realize
WIDGET_CONTROL,b4,/Input_Focus
FOR i=0,N_ELEMENTS(errtxt)-1 DO $
  WIDGET_CONTROL,wtxt,set_value=errtxt[i],/Append
XMANAGER,'intro',base

END ;Intro

PRO idlcr8hdf_event, ev
;Procedure to close the pop-up logging window after user selects 'Finish'.
; ----------
;Written by Ian Boyd, NIWA-ERI for the AVDC - i.boyd@niwa.com
;
;  History:
;    20061012: Introduced to IDLCR8HDF - Version 2.0
;
;  Input: Selected widget event structure
;
;  Output: Nil
;
;  Called by: XMANAGER in IDLCR8HDF and STOP_WITH_ERROR
;
;  External Subroutines Called: None

WIDGET_CONTROL,ev.top,/DESTROY
RETALL & HEAP_GC

END ;IDLcr8HDF_Event

PRO stop_with_error, txt1, txt2, lu, ds
;Procedure called when an error in the program inputs is detected. An error message is displayed
;and the program stopped and reset. If necessary, open files are closed, and memory associated
;with a structure is freed. 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;
;a log file (idlcr8hdf.log)
; ----------
;Written by Ian Boyd, NIWA-ERI for the AVDC - i.boyd@niwa.com
;
;  History:
;    20050802: Original IDLCR8HDF Routine - Version 1.0
;    20061012: Set-up so that the error output is displayed in the output window(s) dependent on
;              the options chosen by the user, the point in the program that the error is detected,
;              and the method that IDLCR8HDF is called. If txt1 is preceeded by 'D_' or is null,
;              the error output is to a Pop-up Dialog window. Other error output options are
;              determined by the values in the (Common) dux array - Version 2.0
;
;  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.
;          ds - Where applicable, the name of the data structure set-up by the program (dependent on
;               the point in the program that the error is detected), so that memory associated with the
;               structure can be freed.
;          dux - an integer array used to determine the output options for the error message
;
;  Output: Nil
;
;  Called by: The routine in which the error was detected. The following routines call STOP_WITH_ERROR:
;             READ_TABLEFILE; TEST_FILE_INPUT, READ_METADATA; EXTRACT_AND_TEST; CHECK_METADATA;
;             SET_UP_STRUCTURE; CHECK_MIN_MAX_FILL; EXTRACT_DATA; FIND_HDF_FILENAME; IDLCR8HDF.
;
;  External Subroutines Called: IDLCR8HDF_EVENT (via XMANAGER)

COMMON WIDGET_WIN

IF lu NE -1L THEN Free_Lun,lu

IF txt1 EQ '' THEN BEGIN ;<cancel> chosen on Intro box
  res=DIALOG_MESSAGE('IDLcr8HDF Stopped!',/Information,Title='AVDC IDLcr8HDF')
ENDIF ELSE BEGIN
  ;If necessary free up memory by destroying the heap variables pointed at by its pointer arguments
  IF N_ELEMENTS(ds) NE 0 THEN PTR_FREE,ds.data
  ;Write error to file and/or IDL output window
  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
    PRINTF,i,'  ERROR in '+txtx & PRINTF,i,'  '+txt2
    PRINTF,i,'' & PRINTF,i,'IDLcr8HDF stopped - Program Ended on '+SYSTIME(0)
  ENDFOR
  IF dux[1] GT -1 THEN FREE_LUN,dux[1]
  IF STRMID(txt1,0,2) EQ 'D_' THEN BEGIN ;write error to DIALOG Box instead of Popup window
    errtxt2=STRARR(4)
    errtxt2[0]=STRMID(txt1,2) & errtxt2[1]=txt2 & errtxt2[3]='IDLcr8HDF Stopped!'
    res=DIALOG_MESSAGE(errtxt2,/Error,Title='AVDC IDLcr8HDF Error')
  ENDIF ELSE 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 creation stopped - hit <Finish> to close program',/Append
    WIDGET_CONTROL,b1,Sensitive=1,/Input_Focus
    XMANAGER,'stop_with_error',base,Event_Handler='idlcr8hdf_event'
  ENDELSE
ENDELSE
RETALL & HEAP_GC

END ;Procedure Stop_With_Error

PRO read_tablefile, tablefile
;Procedure to identify the version number of the TAV file, read the contents of the
;LABELS/FIELDS (tab_arr), and determine the FILE_META_VERSION in the global attributes (tab_ver).
;This routine also creates a flag (tab_type) to determine whether the input file is an original
;table.dat file used by Envisat or the AVDC TAV file, with the HDF file generated accordingly.
; ----------
;Written by Ian Boyd, NIWA-ERI for the AVDC - i.boyd@niwa.com
;
;  History:
;    20050802: Original IDLCR8HDF Routine - Version 1.0
;    20061012: Bug-fix to separate semi-colons with a space when one immediately follows the other,
;              so that the number of subvalues is correctly determined by the program. Common
;              variable definition WIDGET_WIN added - Version 2.0
;    20080302: Added code to differentiate between the AVDC TAV file and original Envisat table.dat.
;              The tab_type flag is used to differentiate between the two formats. Note that some of the
;              table.dat labels are renamed to match the equivalent TAV file labels for compatibility
;              when testing Metadata entries - refer to EnviName/AVDCName arrays; Ensure that the TAV
;              Version value is correctly formatted and is version 03 or greater - Version 3.0
;
;  Input: Tablefile - Name of the file containing the Table Attributes.
;
;  Outputs: tab_ver - String containing FILE_META_VERSION value.
;           tab_arr - 2-D string array of size nf*mcnt+2, where nf=Number of Fields, and mcnt=maximum
;                     number of values detected in any one field. tab_arr(*,0) is the name of each field,
;                     and tab_arr(*,1) is the number of values in each field.
;           tab_type - 0/1 where 0 identifies an AVDC format TAV file and 1 identifies an original
;                      Envisat format table.dat file.
;
;  Called by: IDLCR8HDF
;
;  External Subroutines Called: STOP_WITH_ERROR (if error state detected)
;    Possible Conditions for STOP_WITH_ERROR call (plus [line number] where called):
;      1. Table Attribute Values file version not found [330, 332]
;      2. Envisat table.dat ASC2HDF program version not found [363]
;      3. First Envisat table.dat field value not found [370]
;      4. Table Attribute Values file version is not in a valid format [348]
;      5. Old version of the TAV file in use [351]
;
;    Warning Conditions (when the program is able to make changes):
;      1. TAV FILE_META_VERSION changed to ddRddd format [356]

COMMON TABLEDATA
COMMON WIDGET_WIN

;Possible error messages for this procedure
proname='Read_TableFile procedure: '
errtxt=STRARR(5)
errtxt[0]='Table Attribute Values file version not identified'
errtxt[1]='Envisat table.dat ASC2HDF program version not found'
errtxt[2]='First Envisat table.dat field value not found'
errtxt[3]='Table Attribute Values file version is invalid (should be ddRddd or ddRdddd): '
errtxt[4]='Old version of the Table Attribute Values file used as input. '
FOR i=0,2 do errtxt[i]=errtxt[i]+' with the search criteria used by this program'
errtxt[4]=errtxt[4]+'Please update from http://avdc.gsfc.nasa.gov/Tools: '

;Array of Envisat Field names to be changed to equivalent AVDC Field names
enviname=['_NAME','_AFFILIATION','DATA_VARIABLES_00_00','BASE_UNIT']
avdcname=['ORIGINATOR','AFFILIATION','DATA_VAR_ALL_00_00','VAR_UNITS']

ON_IOERROR,TypeConversionError
dum='' & tab_ver=''
OPENR,lu,tablefile,/GET_LUN
;determine TAV file version
REPEAT BEGIN
  READF,lu,dum
  dumup=STRUPCASE(dum)
  envitest=STRPOS(STRCOMPRESS(dumup,/Remove_all),'!TABLE.DATVERSION') NE -1
  avdctest=STRPOS(STRCOMPRESS(dumup,/Remove_all),'!VERSION') NE -1
ENDREP UNTIL (envitest) OR (avdctest) OR (EOF(lu))
IF EOF(lu) THEN STOP_WITH_ERROR,o3[3]+proname,errtxt[0]+'.',lu
res=STRSPLIT(dumup,' ',/Extract) & vi=WHERE(res EQ 'VERSION')
IF N_ELEMENTS(res) LE vi[0]+1 THEN STOP_WITH_ERROR,o3[3]+proname,errtxt[0]+': '+dum,lu
IF envitest THEN tab_type=1 ELSE tab_type=0

;Ensure Meta Version has a valid format
;i. Check third character is an 'R'
;ii. Check number of characters is 6 or 7
;iii. Check dd and ddd(d) are numeric
;iv. Check that version number is 03 or greater for AVDC file type
;v. Issue warning and convert to ddRddd if format is ddRdddd
fmv=res[vi[0]+1] ;File_Meta_Version
valid=0 ;set to test for valid string to number conversion
FOR i=0,STRLEN(fmv)-1 DO IF i NE 2 THEN fmvtest=FIX(STRMID(fmv,i,1))
fmvtest=FIX(STRMID(fmv,0,2)) ;To test for TAV version 03 or greater
valid=1 ;FILE_META_VERSION characters are numeric (except for the 'R') if program gets to here
TypeConversionError:
IF (STRMID(fmv,2,1) NE 'R') OR (STRLEN(fmv) lt 6) OR (STRLEN(fmv) gt 7) OR $
   (valid EQ 0) THEN STOP_WITH_ERROR,o3[3]+proname,errtxt[3]+fmv+'.',lu

IF (tab_type EQ 0) AND (fmvtest LE 2) THEN $
  STOP_WITH_ERROR,o3[3]+proname,errtxt[4]+'TAV Version '+fmv+'.',lu

fmv=STRMID(fmv,0,3)+STRMID(fmv,2,/Reverse_Offset) ;Ensure format is ddRddd
IF fmv NE res[vi[0]+1] THEN BEGIN
  lineno=lineno+1L
  warntxt='  WARNING: FILE_META_VERSION changed to ddRddd format: '+res[vi[0]+1]+' -> '+fmv
  IF o3[3] EQ '' THEN WIDGET_CONTROL,wtxt,set_value=warntxt,/Append,Set_text_top_line=lineno
  FOR m=dux[0],dux[1],dux[2] DO PRINTF,m,warntxt ;write out to log
ENDIF

IF envitest THEN BEGIN ;identify asc2hdf version in table.dat and read past the CHECK_ATTRIBUTE line
  WHILE (STRPOS(STRUPCASE(dum),'ASC2HDF') EQ -1) AND (not EOF(lu)) DO READF,lu,dum
  IF EOF(lu) THEN STOP_WITH_ERROR,o3[3]+proname,errtxt[1]+'.',lu
  cr8_hdf_ver=';IDLCR8HDF' ;';'+STRTRIM(strmid(dum,STRPOS(STRUPCASE(dum),'ASC2HDF')),2)
  dum=STRTRIM(dum,2)
  WHILE ((STRMID(dum,0,1) EQ '!') OR (STRMID(dum,0,1) EQ '#') OR (dum EQ '')) AND $
        (NOT EOF(lu)) DO BEGIN
    READF,lu,dum & dum=STRTRIM(dum,2)
  ENDWHILE
  IF EOF(lu) THEN STOP_WITH_ERROR,o3[3]+proname,errtxt[2]+'.',lu
  READF,lu,dum ;to get to the line after 'CHECK_ATTRIBUTE'
ENDIF ELSE cr8_hdf_ver=';IDLCR8HDF'
tab_ver=fmv+cr8_hdf_ver ;= the FILE_META_VERSION input value in the global attributes

;determine no. of FIELDS/LABELS as well as the maximum number of elements
nf=0 & mcnt=0 & firstfield=''
dum=STRTRIM(dum,2)
WHILE NOT EOF(lu) DO BEGIN
  ncnt=-1
  IF (STRMID(dum,0,1) NE '!') AND (STRMID(dum,0,1) NE '#') AND $
     (STRMID(dum,0,1) NE '=') AND (dum NE '') THEN BEGIN

    FOR i=0,N_ELEMENTS(enviname)-1 DO IF STRPOS(STRUPCASE(dum),enviname[i]) NE -1 THEN ncnt=i
    IF (ncnt EQ 0) OR (ncnt EQ 1) THEN BEGIN
      IF STRMID(STRUPCASE(dum),0,3) NE 'PI_' THEN nf=nf-1 ELSE ecnt=0
      ;This puts all PI_,DO_, and DS_NAME or AFFILIATION values into either the ORIGINATOR or AFFILIATION fields
    ENDIF ELSE ecnt=0

    nf=nf+1
    IF firstfield EQ '' THEN firstfield=dum
    IF ncnt NE -1 THEN dum=avdcname[ncnt] ;change the name of the Envisat field to AVDC equivalent
    REPEAT BEGIN
      READF,lu,dum & dum=STRTRIM(dum,2)
      IF STRMID(dum,0,1) EQ '=' THEN ecnt=ecnt+1
    ENDREP UNTIL (STRMID(dum,0,1) NE '=') OR (EOF(lu))
    IF ecnt GT mcnt THEN mcnt=ecnt
  ENDIF ELSE IF NOT EOF(lu) THEN BEGIN
    READF,lu,dum & dum=STRTRIM(dum,2)
  ENDIF
ENDWHILE
FREE_LUN,lu

;read in the contents of the file
tab_arr=STRARR(nf,mcnt+2) ;note tab_arr(*,0) EQ FIELD/LABEL name and
;tab_arr(*,1) EQ N_ELEMENTS under each FIELD/LABEL
tab_hold=STRARR(mcnt)

OPENR,lu,tablefile,/GET_LUN
READF,lu,dum & dum=STRTRIM(dum,2)
WHILE dum NE firstfield DO BEGIN
  READF,lu,dum & dum=STRTRIM(dum,2)
ENDWHILE
i=0
WHILE i LE nf-1 DO BEGIN
  ncnt=-1
  FOR j=0,N_ELEMENTS(enviname)-1 DO IF STRPOS(STRUPCASE(dum),enviname[j]) NE -1 THEN ncnt=j
  IF (ncnt EQ 0) OR (ncnt EQ 1) THEN BEGIN
    IF STRMID(STRUPCASE(dum),0,3) NE 'PI_' THEN i=i-1 ELSE ecnt=0
  ENDIF ELSE ecnt=0
  IF ncnt NE -1 THEN dum=avdcname[ncnt] ;change the name of the Envisat field to AVDC equivalent
  tab_arr[i,0]=dum
  REPEAT BEGIN
    READF,lu,dum & dum=STRTRIM(dum,2)
    IF STRMID(dum,0,1) EQ '=' THEN BEGIN
      tab_hold[ecnt]=STRMID(dum,1) ;strip the '=' sign
      ;add space between adjacent semi-colons so StrSplit finds correct number of sub-values
      REPEAT BEGIN
        cpos=STRPOS(tab_hold[ecnt],';;')
        IF cpos NE -1 THEN tab_hold[ecnt]=STRMID(tab_hold[ecnt],0,cpos+1)+ $
          ' '+STRMID(tab_hold[ecnt],cpos+1)
      ENDREP UNTIL cpos EQ -1
      ecnt=ecnt+1
    ENDIF
  ENDREP UNTIL (STRMID(dum,0,1) NE '=') OR (EOF(lu))
  tab_arr[i,1]=STRTRIM(ecnt,2)
  tab_arr[i,2:ecnt+1]=tab_hold[0:ecnt-1]
  IF i NE nf-1 THEN BEGIN
    WHILE (STRMID(dum,0,1) EQ '!') OR (STRMID(dum,0,1) EQ '#') OR (dum EQ '') DO BEGIN
      READF,lu,dum & dum=STRTRIM(dum,2)
    ENDWHILE
  ENDIF
  i=i+1
ENDWHILE
FREE_LUN,lu

END ;Procedure Read_TableFile

PRO test_file_input,aname,fentry
;Procedure to test that a file name given as an entry to a free text attribute is valid, based on Envisat
;Metadata Guidelines (not currently used by AVDC)
; ----------
;Written by Ian Boyd, NIWA-ERI for the AVDC - i.boyd@niwa.com
;
;  History:
;    20050802: Original IDLCR8HDF Routine - Version 1.0
;    20061012: Common variable definition WIDGET_WIN added - Version 2.0
;
;  Inputs: aname - Global or Variable Attribute Label
;          fentry - Filename entry used as the Global or Variable Attribute value
;
;  Outputs: None
;
;  Called by: READ_METADATA
;
;  External Subroutines Called: STOP_WITH_ERROR (if error state detected)
;    Possible Conditions for STOP_WITH_ERROR call (plus [line number] where called):
;      1. Syntax of Filename entry incorrect [485]
;      2. Filename entry not found or not usable [488]
;      3. File size is too large [491]

COMMON WIDGET_WIN

sfile=4096 ;maximum permitted file size

;possible error message for this procedure
proname='Test_File_Input procedure: '
errtxt2=STRARR(3) & lu=-1
errtxt2[0]='Syntax of Filename entry incorrect: '
errtxt2[1]='Filename entry not found or not usable: '
errtxt2[2]='File size is too large (maximum permitted: '+STRTRIM(sfile,2)+' bytes)'

si=STRPOS(fentry,'"')+1 & ei=STRPOS(fentry,'"',/Reverse_Search)-si
IF ei EQ si THEN STOP_WITH_ERROR,o3[3]+proname+aname+': ',errtxt2[0]+fentry,lu
faname=STRMID(fentry,si,ei)
ftest=FILE_TEST(faname,/Read,/Regular)
IF ftest EQ 0 THEN STOP_WITH_ERROR,o3[3]+proname+aname+': ',errtxt2[1]+faname,lu
OPENR,fu,faname,/GET_LUN
ftest=FSTAT(fu) & FREE_LUN,fu
IF ftest.size GT sfile THEN STOP_WITH_ERROR,o3[3]+proname+AName+'='+fentry+': ',errtxt2[2],lu

END ;procedure Test_File_Input

PRO read_metadata, metafile, inf
;Procedure to check Metadata contents and return as meta_arr - also:
;1. Ensures uniform input (making ATTRIBUTE_NAME uppercase, deleting unwanted spaces etc).
;2. Checks that attribute length falls within allowable limits
;3. Checks for filenames for the Free Text Attributes. If so, then checks that the file exists,
;   and that the file size is within allowable limits
;4. Update FILE_META_VERSION with TAV and software version (tab_ver)

;Please also note the following:
;1. The Global Attribute Label DATA_TYPE will be changed to DATA_LEVEL (or vice versa) depending on
;   the tab_type value.
;2. Any extra Global Attributes will be included (and added to the attr_arr_glob array).
;3. If the Dataset Attribute VAR_MONOTONE is detected it will be omitted from the Metadata written to
;   the HDF file if an AVDC style TAV file has been read in.
; ----------
;Written by Ian Boyd, NIWA-ERI for the AVDC - i.boyd@niwa.com
;
;  History:
;    20050802: Original IDLCR8HDF Routine - Version 1.0
;    20061012: Error messages modified; Allow VAR_MONOTONE as a Variable Attribute if it is present in the
;              Metadata; If DATA_TYPE is present in the Metadata then change it to DATA_LEVEL; Allow
;              additional Global Attributes; Puts Variable Attribute names in the same order as
;              attr_arr_data; Common variable definition WIDGET_WIN added - Version 2.0
;    20080302: VAR_MONOTONE not included in the output Metadata if it is present in the input Metadata
;              and an AVDC style TAV file is read in; if DATA_TYPE is present in the Metadata it is changed
;              to DATA_LEVEL if an AVDC style TAV file is read in, and vice versa if an original style
;              Envisat table.dat file read in; Warnings added if VAR_MONOTONE attribute is omitted, when
;              changing DATA_TYPE to DATA_LEVEL (or vice versa), and if extra Global Attributes are
;              included in the Metadata; Checks for missing or repeated Global Attributes; Global
;              Attributes can now be listed in any order in the Metadata (will be written to the HDF file
;              in a standard order) - Version 3.0
;
;  Inputs: metafile - Either, the filename of the input file containing the Metadata or, if program
;                     input is via string array and Structure, a string array containing the Global
;                     and Variable Attributes.
;          inf - flag, where 0=Metafile is an array; 1=Metafile is a file.
;
;  Output: meta_arr - a string array containing the Global and Variable Attributes, which have been
;                     checked and uniformally formatted.
;
;  Called by: IDLCR8HDF
;
;  External Subroutines Called: TEST_FILE_INPUT
;                               STOP_WITH_ERROR (if error state detected);
;    Possible Conditions for STOP_WITH_ERROR call (plus [line number] where called):
;      1. Metadata line entry not valid ('=' not detected) [650]
;      2. Metadata value contains too many characters [683, 747]
;      3. Incorrect total number of Variable/Visualization Attributes [730]
;      4. Variable/Visualization Attribute Label is not valid [734]
;      5. No valid Metadata in command line parameter file or arrays [640]
;      6. The same attribute label has already been read in for a particular VAR_NAME [738]
;      7. A Variable/Visualization Attribute read in while still expecting to read Global Attributes [695]
;      8. A required Global Attribute Label is not present in the Metadata [721]
;      9. A required Global Attribute Label is repeated in the Metadata [723]
;
;    Warning Conditions (when the program is able to make changes):
;      1. VAR_MONOTONE omitted from the Variable Attributes [629]
;      2. DATA_LEVEL relabeled DATA_TYPE or vice versa depending on the style of the TAV file [671]
;      3. New Global Attribute added [704]

COMMON TABLEDATA
COMMON METADATA
COMMON WIDGET_WIN

attr_arr_glob=['PI_NAME','PI_AFFILIATION','PI_ADDRESS','PI_EMAIL',$
               'DO_NAME','DO_AFFILIATION','DO_ADDRESS','DO_EMAIL',$
               'DS_NAME','DS_AFFILIATION','DS_ADDRESS','DS_EMAIL',$
               'DATA_DESCRIPTION','DATA_DISCIPLINE','DATA_GROUP',$
               'DATA_LOCATION','DATA_SOURCE','DATA_LEVEL',$
               'DATA_VARIABLES','DATA_START_DATE','DATA_FILE_VERSION',$
               'DATA_MODIFICATIONS','DATA_CAVEATS','DATA_RULES_OF_USE',$
               'DATA_ACKNOWLEDGEMENT','FILE_NAME','FILE_GENERATION_DATE',$
               'FILE_ACCESS','FILE_PROJECT_ID','FILE_ASSOCIATION',$
               'FILE_META_VERSION']

attr_arr_data=['VAR_NAME','VAR_DESCRIPTION','VAR_NOTES','VAR_DIMENSION',$
               'VAR_SIZE','VAR_DEPEND','VAR_DATA_TYPE','VAR_UNITS',$
               'VAR_SI_CONVERSION','VAR_VALID_MIN','VAR_VALID_MAX',$
               'VAR_AVG_TYPE','VAR_FILL_VALUE','VIS_LABEL',$
               'VIS_FORMAT','VIS_PLOT_TYPE','VIS_SCALE_TYPE',$
               'VIS_SCALE_MIN','VIS_SCALE_MAX']

free_attr=['DATA_DESCRIPTION','DATA_MODIFICATIONS','DATA_CAVEATS',$
           'DATA_RULES_OF_USE','DATA_ACKNOWLEDGEMENT',$
           'VAR_DESCRIPTION','VAR_NOTES']

;To check that NCSA attributes are not included if present in the metadata
ncsa=['long_name','units','format','coordsys','valid_range','_fillvalue']

nchar=4096 ;limit on number of characters

;possible error messages for this procedure
errtxt=STRARR(9)
proname='Read_Metadata procedure: '
errtxt[0]='Metadata line entry not valid (''='' not detected): '
errtxt[1]='Value contains too many characters (maximum permitted: '+STRTRIM(nchar,2)+')'
errtxt[2]='Incorrect total number of Variable/Visualization Attributes: '
errtxt[3]='Variable/Visualization Attribute Label is not valid: '
errtxt[4]='No valid Metadata in command line parameter file or arrays.'
errtxt[5]='This attribute label already read in for VAR_NAME='
errtxt[6]=' Attribute read in while still expecting to read Global Attributes'
errtxt[7]='Global Attribute Label not present in the Metadata: '
errtxt[8]='Global Attribute Label repeated in the Metadata: '

dum='' & nrline=0 & res=STRARR(2)
dc=N_ELEMENTS(attr_arr_data) & di=0 & diloop=0
;Change 'DATA_LEVEL' to 'DATA_TYPE' in attr_arr_glob and add VAR_MONOTONE to attr_arr_data
;if original style Envisat table.dat read in
IF tab_type EQ 1 THEN BEGIN
  dli=WHERE(attr_arr_glob EQ 'DATA_LEVEL')
  attr_arr_glob[dli[0]]='DATA_TYPE'
  attr_arr_data=[attr_arr_data[0:10],'VAR_MONOTONE',attr_arr_data[11:dc-1]]
  dc=dc+1
ENDIF

IF inf EQ 1 THEN BEGIN ;count number of lines in the file
  OPENR,lu,metafile,/GET_LUN
  WHILE NOT EOF(lu) DO BEGIN
    READF,lu,dum & nrline=nrline+1
  ENDWHILE
  FREE_LUN,lu
  mh=STRARR(nrline)
  OPENR,lu,metafile,/GET_LUN
  READF,lu,mh
  FREE_LUN,lu
ENDIF ELSE mh=metafile ;metadata is already in an array
mh=STRTRIM(mh,2)

;check for comment lines, NCSA attributes and, if AVDC style TAV file, VAR_MONOTONE Attribute label
fai=WHERE((STRMID(STRUPCASE(mh),0,12) EQ 'VAR_MONOTONE') AND (tab_type EQ 0),facnt)
IF facnt NE 0 THEN BEGIN
  lineno=lineno+1L
  warntxt='  WARNING: VAR_MONOTONE omitted from the Variable Attributes to be saved to the HDF file'
  IF o3[3] EQ '' THEN WIDGET_CONTROL,wtxt,set_value=warntxt,/Append,Set_text_top_line=lineno
  FOR m=dux[0],dux[1],dux[2] DO PRINTF,m,warntxt ;write out to log
  test1=(STRMID(mh,0,1) NE '!') AND (STRMID(mh,0,1) NE '#') AND (STRTRIM(mh,2) NE '') AND $
        (STRMID(STRUPCASE(mh),0,12) NE 'VAR_MONOTONE')
ENDIF ELSE test1=(STRMID(mh,0,1) NE '!') AND (STRMID(mh,0,1) NE '#') AND (STRTRIM(mh,2) NE '')
test2=((STRMID(STRLOWCASE(mh),0,9) NE ncsa[0]) AND (STRMID(STRLOWCASE(mh),0,5) NE ncsa[1]) and $
       (STRMID(STRLOWCASE(mh),0,6) NE ncsa[2]) AND (STRMID(STRLOWCASE(mh),0,8) NE ncsa[3]) and $
       (STRMID(STRLOWCASE(mh),0,11) NE ncsa[4]) AND (STRMID(STRLOWCASE(mh),0,10) NE ncsa[5]))

nri=WHERE((test1) AND (test2),nrline)
IF nrline EQ 0 THEN STOP_WITH_ERROR,o3[3]+proname,errtxt[4],lu
mh=mh[nri] & meta_arr=STRARR(nrline) & lu=-1

dorder=INTARR(dc)-1 ;actual order of the variable attribute labels
;initialize count variable
i=0 & ng=N_ELEMENTS(attr_arr_glob) & extraga=0

WHILE i LE nrline-1 DO BEGIN
  ;separate out the Meta entry into two components
  eqpos=STRPOS(mh[i],'=')
  IF eqpos LE 0 THEN STOP_WITH_ERROR,o3[3]+proname,errtxt[0]+STRMID(mh[i],0,20),lu
  ;ensure Attribute name is in uppercase and delete unwanted spaces
  res[0]=STRMID(mh[i],0,eqpos) & res[0]=STRTRIM(STRUPCASE(res[0]),2)
  ;delete any lead and trailing spaces from the Attribute entry
  IF eqpos EQ STRLEN(mh[i])-1 THEN res[1]='' $ ;ie. the last character is the '='
  ELSE BEGIN
    res[1]=STRMID(mh[i],eqpos+1) & res[1]=STRTRIM(res[1],2)
  ENDELSE

  ;check to see if res[0] EQ Var/Vis Attribute Label
  aadi=WHERE(res[0] EQ attr_arr_data,aadic)

  IF (i LE ng-1) OR ((i EQ ng) AND (aadic EQ 0)) THEN BEGIN
    ;carry out checks on Global Attributes
    IF i LE ng-1 THEN BEGIN
      IF ((res[0] EQ 'DATA_TYPE') AND (tab_type EQ 0)) OR $
         ((res[0] EQ 'DATA_LEVEL') AND (tab_type EQ 1)) THEN BEGIN
        ;Reverse the Attribute Name and generate a warning
        IF tab_type EQ 0 THEN stxt=['LEVEL','TYPE'] ELSE stxt=['TYPE','LEVEL']
        res[0]='DATA_'+stxt[0]
        lineno=lineno+1L
        warntxt='  WARNING: Global Attribute DATA_'+stxt[1]+' renamed DATA_'+stxt[0]
        IF o3[3] EQ '' THEN WIDGET_CONTROL,wtxt,set_value=warntxt,/Append,Set_text_top_line=lineno
        FOR m=dux[0],dux[1],dux[2] DO PRINTF,m,warntxt ;write out to log
      ENDIF
      gi=WHERE(res[0] EQ attr_arr_glob,gcnt)
      IF gcnt NE 0 THEN BEGIN ;valid Global Attribute found
        fai=WHERE((res[0] EQ free_attr) OR (res[0] EQ 'DATA_VARIABLES'),facnt)
        IF facnt NE 0 THEN BEGIN ;check for filename input
          IF STRMID(STRUPCASE(res[1]),0,6) EQ 'FILE("' THEN BEGIN
            TEST_FILE_INPUT,res[0],res[1]
          ENDIF ELSE BEGIN
            IF STRLEN(res[1]) GT nchar THEN $
              STOP_WITH_ERROR,o3[3]+proname+res[0]+': ',errtxt[1],lu
              ;Entry contains too many characters
          ENDELSE
          IF res[0] EQ 'DATA_VARIABLES' THEN dv=STRSPLIT(res[1],' ;',/Extract)
        ENDIF
        IF res[0] EQ 'FILE_META_VERSION' THEN meta_arr[i]=res[0]+'='+tab_ver $
        ELSE meta_arr[i]=res[0]+'='+res[1]
      ENDIF ELSE extraga=1
    ENDIF ELSE extraga=1
    IF extraga EQ 1 THEN BEGIN
      ;Don't add an extra Global Attribute if it is actually a Variable Attribute
      IF (aadic NE 0) AND (i LT ng-1) THEN $
        STOP_WITH_ERROR,o3[3]+proname,mh[i]+errtxt[6],lu $
      ELSE IF aadic EQ 0 THEN BEGIN
        ;unknown global attribute so add it to Attr_Arr_Glob and meta_arr arrays
        meta_arr[i]=res[0]+'='+res[1]
        IF i EQ ng THEN attr_arr_glob=[attr_arr_glob,res[0]] $
        ELSE IF i EQ 0 THEN attr_arr_glob=[res[0],attr_arr_glob] $
        ELSE attr_arr_glob=[attr_arr_glob[0:i-1],res[0],attr_arr_glob[i:ng-1]]
        ng=ng+1 & extraga=0
        lineno=lineno+1L
        warntxt='  WARNING: New Global Attribute added: '+res[0]
        IF o3[3] EQ '' THEN WIDGET_CONTROL,wtxt,set_value=warntxt,/Append,Set_text_top_line=lineno
        FOR m=dux[0],dux[1],dux[2] DO PRINTF,m,warntxt ;write out to log
      ENDIF
    ENDIF
  ENDIF ELSE BEGIN ;Sort Global Attributes and/or do checks on Var and Vis Attributes
    IF i EQ ng THEN BEGIN
      ;Sort Global Attributes in meta_arr into the same order as attr_arr_glob.
      ;Also ensure that all required Global Attributes are present, and are listed only once
      ;and check that the number of remaining lines in the metadata is correct
      marrhold=STRARR(ng) & gav=marrhold
      FOR i=0,ng-1 DO BEGIN
        dum=STRMID(meta_arr[i],0,STRPOS(meta_arr[i],'='))
        gav[i]=dum
      ENDFOR
      FOR i=0,ng-1 DO BEGIN
        gi=WHERE(gav eq attr_arr_glob[i],gcnt)
        IF gcnt EQ 0 THEN STOP_WITH_ERROR,o3[3]+proname,errtxt[7]+attr_arr_glob[i],lu $
        ELSE IF gcnt NE 1 THEN $
          STOP_WITH_ERROR,o3[3]+proname,errtxt[8]+attr_arr_glob[i],lu $
        ELSE marrhold[i]=meta_arr[gi[0]]
      ENDFOR
      meta_arr[0:ng-1]=marrhold
      ;check that the number of lines in the file is correct
      dvi=N_ELEMENTS(dv) & nrchk=dvi*dc
      IF nrchk NE nrline-ng THEN $
        STOP_WITH_ERROR,o3[3]+proname,errtxt[2]+STRTRIM(FIX(nrline-ng),2)+ $
        ' (Should be '+STRTRIM(dc,2)+' Attributes * '+STRTRIM(dvi,2)+' DATA_VARIABLES)',lu
        ;Incorrect total number of Variable/Visualization Attributes
    ENDIF
    IF aadic EQ 0 THEN STOP_WITH_ERROR,o3[3]+proname,errtxt[3]+res[0],lu
    ;Variable/Visualization Attribute Label is not valid
    aadx=WHERE(aadi[0] EQ dorder,aadxc)
    IF aadxc EQ 0 THEN dorder[di]=aadi[0] $
    ELSE STOP_WITH_ERROR,o3[3]+proname+res[0]+': ',errtxt[5]+dv[diloop],lu
    ;This Attribute Label already read in for VAR_NAME
    ;Check for Filename as input
    fai=WHERE(res[0] EQ free_attr,facnt)
    IF facnt NE 0 THEN BEGIN ;check for filename input
      IF STRMID(STRUPCASE(res[1]),0,6) EQ 'FILE("' THEN BEGIN
        TEST_FILE_INPUT,res[0],res[1]
      ENDIF ELSE BEGIN
        IF STRLEN(res[1]) GT nchar THEN $
          STOP_WITH_ERROR,o3[3]+proname+res[0]+': ',errtxt[1],lu
          ;Entry contains too many characters
      ENDELSE
    ENDIF
    ;Put the Variable Attribute Name in the correct order (as per attr_arr_data)
    meta_arr[i+(dorder[di]-di)]=res[0]+'='+res[1]
    IF di EQ dc-1 THEN BEGIN
      di=0 & diloop=diloop+1 & dorder[*]=-1
    ENDIF ELSE di=di+1
  ENDELSE
  i=i+1
ENDWHILE

END ;Procedure Read_Metadata

PRO extract_and_test, dentry, cpos, nel, ta1, ta2, taname, aname, ti
;Procedure called by Check_Metadata to extract the relevant parts of tab_arr and test against a corresponding
;Metadata value.
; ----------
;Written by Ian Boyd, NIWA-ERI for the AVDC - i.boyd@niwa.com
;
;  History:
;    20050802: Original IDLCR8HDF Routine - Version 1.0
;    20050909: Make VAR_SI_CONVERSION values match the VAR_DATA_TYPE, and add fix for VAR_SI_CONVERSION
;              calculation rules e.g. if VAR_UNITS=cm m-3 then VAR_SI_CONVERSION=0;0.01;m-2 - Version 1.1
;    20061012: Procedure simplified, with all portions of the code related to the VAR_UNITS and
;              VAR_SI_CONVERSION calculations moved to the Var_Units_Test routine; Common variable
;              definition WIDGET_WIN added - Version 2.0
;
;  Inputs: dentry - the Metadata entry to be checked (everything after the '=')
;          cpos - a pattern used by StrSplit on DEntry to split it into its component parts e.g. ';' or '_'
;          nel - the number of sub-values in the set of tab_arr entries being checked (ta1 only)
;          ta1 - the set of tab_arr values used to check against the Metadata contents
;          ta2 - a second set of tab_arr values used for DATA_SOURCE checks, o/w set to ['']
;          taname - a combination of the Metadata Attribute name and Table Field/Label used for the
;                   error messages
;          aname - the Metadata attribute name to be used for the error message
;          ti - the index value of the ta2 values to be checked. Used for DATA_SOURCE checks only
;
;  Output: ti - the index value(s) of tab_arr which match the Metadata value (used in Check_Metadata
;               to do further checks on the ORIGINATOR values)
;
;  Called by: CHECK_METADATA
;
;  External Subroutines Called: STOP_WITH_ERROR (if error state detected)
;    Possible Conditions for STOP_WITH_ERROR call (plus [line number] where called):
;      1. Incorrect number of (sub-)values for a particular Metadata attribute [809]
;      2. Metadata value does not match any values in the Table Attribute Values file [836]

COMMON TABLEDATA
COMMON WIDGET_WIN

proname='Check_Metadata/Extract_And_Test procedures: ' & lu=-1L
errtxt2=STRARR(2)
errtxt2[0]='Number of (sub-)values for this attribute should be '
errtxt2[1]='Doesn''t match any values in Table Attribute Values file under ATTRIBUTE: '

;Check number of Metadata entry components is as expected
achk=STRSPLIT(dentry,cpos,/Extract)
IF N_ELEMENTS(achk) NE nel THEN $
  STOP_WITH_ERROR,o3[3]+proname+taname+'='+dentry+': ',errtxt2[0]+STRTRIM(nel,2)+'.',lu

;check Metadata entry with relevant TAV entry or entries
IF cpos EQ '_' THEN BEGIN ;DATA_SOURCE entry so need to treat components separately
  achk=STRCOMPRESS(achk,/Remove_All) & nel=1
  ;strip the last three characters (instrument ID) off achk(1)
  achk[1]=STRMID(achk[1],0,STRLEN(achk[1])-3)
ENDIF ELSE achk=STRCOMPRESS(dentry,/Remove_All)
FOR j=0,N_ELEMENTS(ta1)-1 DO BEGIN
  ;extract valid portion of the entries (depending on ATTRIBUTE name)
  tchk=STRSPLIT(ta1[j],';',/Extract)
  ta1[j]=STRCOMPRESS(tchk[0],/Remove_All)
  IF nel GT 1 THEN $
    FOR k=1,nel-1 DO ta1[j]=ta1[j]+';'+STRCOMPRESS(tchk[k],/Remove_All)
ENDFOR
IF ta2[0] NE '' THEN BEGIN
  FOR j=0,N_ELEMENTS(ta2)-1 DO BEGIN
    ;extract valid portion of the entries (depending on ATTRIBUTE name)
    tchk=STRSPLIT(ta2[j],';',/Extract)
    ta2[j]=STRCOMPRESS(tchk[ti],/Remove_All)
  ENDFOR
ENDIF
IF cpos EQ '_' THEN BEGIN
  gi=WHERE(ta1 EQ achk[0],gcnt)
  IF gcnt NE 0 THEN gi=WHERE(ta2 EQ achk[1],gcnt)
ENDIF ELSE gi=WHERE(ta1 EQ achk,gcnt)

IF gcnt EQ 0 THEN STOP_WITH_ERROR,o3[3]+proname+aname+': ',errtxt2[1]+taname,lu
ti=gi ;ti returned to the Check_MetaData procedure to check AFFILIATION, ADDRESS and EMAIL values

END ;procedure Extract_and_Test

PRO check_metadata
;Procedure to check the validity of Metadata values by checking them against the corresponding
;entries in the Table Attribute Values File
; ----------
;Written by Ian Boyd, NIWA-ERI for the AVDC - i.boyd@niwa.com
;
;  History:
;    20050802: Original IDLCR8HDF Routine - Version 1.0
;    20050909: 'rd' flag included to convert VAR_SI_CONVERSION values to floating point if the
;              corresponding VAR_DATA_TYPE is real or double; Section included to check for
;              repeated SI units in the calculated VAR_SI_CONVERSION - Version 1.1
;    20061012: Section added to do simple checks on Metadata ORIGINATOR values if the information
;              is not otherwise available in the TAV file; Warning messages written to output
;              windows and/or log file depending on the options chosen by the user; Section relating
;              to checks on VAR_UNITS and VAR_SI_CONVERSION moved to the VAR_UNITS_TEST routine;
;              Common variable definition WIDGET_WIN added - Version 2.0
;    20080302: Sections included to handle checks when an Envisat table.dat file is used, including;
;              ensuring metadata labels DATA_TYPE and VAR_DATA_TYPE are not both found when using the
;              Strpos procedure; Handling different table.dat format of ORIGINATOR attributes; using
;              DATA_SOURCE_02 instead of AFFILIATION for the DATA_SOURCE checks. Section which puts
;              TAV file VAR_UNITS and UNIT_PREFIX values into arrays for the VAR_UNITS_TEST call is
;              no longer needed and is removed; Change made so that Free Attribute Metadata values
;              which include semi-colons do not have white space removed (i.e. the semi-colons are
;              not defined as separating sub-values) - Version 3.0
;
;  Inputs: meta_arr - a string array containing the Global and Variable Attributes
;          tab_arr - a string array containing the TAV or table.dat file attributes

;  Output: meta_arr - some meta_arr values may be corrected or calculated in this routine. White space
;                     between metadata sub-values is also removed.
;
;  Called by: IDLCR8HDF
;
;  External Subroutines Called: EXTRACT_AND_TEST
;                               VAR_UNITS_TEST
;                               STOP_WITH_ERROR (if error state detected)
;    Possible Conditions for STOP_WITH_ERROR call (plus [line number] where called):
;      1. An attribute value is expected but is absent or invalid [955, 977, 1014]
;      2. Incorrect number of (sub-)values for a particular Metadata attribute [950, 1060, 1097]
;      3. NAME and AFFILIATION values don't match (only applies if reading in the full AVDC
;         TAV file) [1009]
;      4. AFFILIATION (AVDC) or DATA_SOURCE_02 (Envisat) field not found in the TAV file [1028]
;      5. There is no match between a given VAR_UNIT, and the list of valid units and prefixes
;         present in the TAV file [1068]
;
;    Warning Conditions (when the program is able to make changes):
;      1. ORIGINATOR values for ADDRESS or EMAIL replaced with those from the TAV file [994]
;      2. VAR_SI_CONVERSION values replaced with those calculated by the program [1078]

COMMON TABLEDATA
COMMON METADATA
COMMON WIDGET_WIN

;Possible error messages for this procedure
proname='Check_Metadata procedure: ' & lu=-1L
errtxt=STRARR(4)
errtxt[0]='No or Invalid value for this Attribute: '
errtxt[1]='Number of (sub-)values for this Attribute should be '
errtxt[2]='AFFILIATION value doesn''t match NAME value: '
errtxt[3]=' field not found in the input Table Attribute Values file.

;Variable Attributes which have user defined values
free_attr=['DATA_DESCRIPTION','DATA_MODIFICATIONS','DATA_CAVEATS',$
           'DATA_RULES_OF_USE','DATA_ACKNOWLEDGEMENT',$
           'VAR_DESCRIPTION','VAR_NOTES']

;originator values that need to be checked - ADDRESS and EMAIL derived automatically
orig_attr=['PI_','DO_','DS_','NAME','AFFILIATION']
;initiate variables for VAR_UNITS/VAR_SI_CONVERSION checks
firstvu=0 & tchk='' & rd=1
xval=[2,2,3,1] ;expected number of sub-values for the four originator attributes

FOR i=0,N_ELEMENTS(meta_arr)-1 DO BEGIN
  res=STRSPLIT(meta_arr[i],'=',/Extract) & res0=res[0]
  FOR j=0,2 DO FOR k=3,4 DO $
    ;need to change ORIGINATOR NAME ATTRIBUTE names to match TAV File
    IF res[0] EQ orig_attr[j]+orig_attr[k] THEN BEGIN
      IF k EQ 3 THEN res[0]='ORIGINATOR' ELSE res[0]='AFFILIATION'
      jh=j & kh=k
    ENDIF
  ;Test whether metadata label is also a TAV field
  ;First test checks for exact match between metadata label and TAV field
  ;(This avoids possible problem using STRPOS (e.g. DATA_TYPE won't also pick up VAR_DATA_TYPE))
  mi=WHERE(tab_arr[*,0] EQ res[0],mcnt)
  ;Back-up test in the event that label has more than one TAV field (e.g. DATA_VARIABLES)
  IF mcnt EQ 0 THEN mi=WHERE(STRPOS(tab_arr[*,0],res[0]) NE -1,mcnt)

  IF res[0] EQ 'AFFILIATION' THEN BEGIN
    ;If tab_type EQ 0 (AVDC) then perform AFFILIATION checks below only if the ORIGINATOR field in the
    ;TAV file is missing. If tab_type EQ 1 (original Envisat) then perform AFFILIATION checks below.
    IF tab_type EQ 0 THEN BEGIN ;AVDC type TAV file
      ai=WHERE(STRPOS(tab_arr[*,0],'ORIGINATOR') NE -1,acnt)
      IF acnt NE 0 THEN BEGIN
        ;ORIGINATOR field present in the TAV file so no need to do separate checks
        ;using the AFFILIATION field as all the information is in the ORIGINATOR field.
        res[0]=orig_attr[jh]+orig_attr[kh]
        mi=WHERE(STRPOS(tab_arr[*,0],res[0]) NE -1,mcnt)
      ENDIF
    ENDIF
  ENDIF

  IF (res[0] EQ 'ORIGINATOR') AND ((mcnt EQ 0) OR (tab_type EQ 1)) THEN BEGIN
    ;Do simple checks on _NAME, _AFFILIATION, _ADDRESS, and _EMAIL sub-values
    FOR k=0,3 DO BEGIN
      resx=STRSPLIT(meta_arr[i+k],'=;')
      nel=N_ELEMENTS(resx)-1
      IF (nel NE xval[k]) OR (STRMID(meta_arr[i+k],STRLEN(meta_arr[i+k])-1,1) EQ ';') THEN $
        STOP_WITH_ERROR,o3[3]+proname+meta_arr[i+k]+': ',errtxt[1]+STRTRIM(xval[k],2)+'.',lu
    ENDFOR
  ENDIF

  IF mcnt NE 0 THEN BEGIN ;check Metadata value(s) against TAV file entries
    IF N_ELEMENTS(res) NE 2 THEN STOP_WITH_ERROR,o3[3]+proname,errtxt[0]+res0,lu
    IF res[0] EQ 'ORIGINATOR' THEN BEGIN
      ;perform checks on the ORIGINATOR attributes
      res[0]=orig_attr[jh]+orig_attr[3]
      ti=-1
      ta1=tab_arr[mi[0],2:tab_arr[mi[0],1]+1] ;subset of relevant FIELD ENTRIES
      taname=tab_arr[mi[0],0]+'/'+res[0] ;name combination for error message
      EXTRACT_AND_TEST,res[1],';',xval[0],ta1,[''],taname,res[1],ti
      IF tab_type EQ 0 THEN BEGIN
        ;check/update AFFILIATION, ADDRESS and EMAIL values according to returned tab_arr index(es) (ti)
        FOR j=1,3 DO BEGIN
          ;extract attribute value(s) from Metadata file
          res=STRSPLIT(meta_arr[i+j],'=',/Extract)
          IF N_ELEMENTS(res) EQ 2 THEN BEGIN
            ;remove leading and trailing blanks from each sub-value
            achk2=STRSPLIT(res[1],';',/Extract)
            nel=N_ELEMENTS(achk2)
            res[1]=STRTRIM(achk2[0],2)
            IF nel GT 1 THEN FOR k=1,nel-1 DO res[1]=res[1]+';'+STRTRIM(achk2[k],2)
          ENDIF
          ;check against the TAV(s) returned for the NAME attribute
          IF j EQ 1 THEN BEGIN ;AFFILIATION
            IF N_ELEMENTS(res) NE 2 THEN STOP_WITH_ERROR,o3[3]+proname,errtxt[0]+res[0],lu
            si=2 & kh=-1
          ENDIF ELSE IF j EQ 2 THEN si=4 $;ADDRESS
          ELSE si=7 ;EMAIL
          ;make up correct set of values from the table attribute file
          FOR k=0,N_ELEMENTS(ti)-1 DO BEGIN ;in the event that the NAME is listed twice
            IF (kh EQ k) OR (j EQ 1) THEN BEGIN ;Extract values from the selected TAV input(s)
              achk2=STRSPLIT(tab_arr[mi[0],ti[k]+2],';',/Extract)
              achk=STRTRIM(achk2[si],2)
              IF xval[j] GT 1 THEN FOR m=si+1,si+xval[j]-1 DO achk=achk+';'+STRTRIM(achk2[m],2)
              IF (j EQ 1) AND (STRUPCASE(achk) EQ STRUPCASE(res[1])) THEN BEGIN
                kh=k & meta_arr[i+j]=res[0]+'='+achk
              ENDIF ELSE IF j GE 2 THEN BEGIN
                ;Check that metadata ADDRESS and EMAIL values are valid, if present
                IF N_ELEMENTS(res) EQ 2 THEN BEGIN
                  IF res[1] NE achk THEN BEGIN
                    warntxt=STRARR(3) & lineno=lineno+3L
                    warntxt[0]='  WARNING: '+res[0]+' values in Metadata replaced with those from the '
                    warntxt[0]=warntxt[0]+'Table Attribute Value file:'
                    warntxt[1]='  '+res[1]+' -> '
                    warntxt[2]='  '+achk
                    FOR m=0,2 DO BEGIN
                      IF o3[3] EQ '' THEN WIDGET_CONTROL,wtxt,set_value=warntxt[m],/Append,Set_text_top_line=lineno
                      FOR n=dux[0],dux[1],dux[2] DO PRINTF,n,warntxt[m] ;write out to log
                    ENDFOR
                  ENDIF
                ENDIF
                meta_arr[i+j]=res[0]+'='+achk
              ENDIF
            ENDIF
          ENDFOR
          IF kh EQ -1 THEN $ ;metadata AFFILIATION not valid
              STOP_WITH_ERROR,o3[3]+proname+res[1]+': ',errtxt[2]+meta_arr[i],lu
        ENDFOR
      ENDIF
    ENDIF ELSE IF res[0] EQ 'AFFILIATION' THEN BEGIN
      res[0]=orig_attr[jh]+orig_attr[4]
      IF N_ELEMENTS(res) NE 2 THEN STOP_WITH_ERROR,o3[3]+proname,errtxt[0]+res[0],lu
      ta1=tab_arr[mi[0],2:tab_arr[mi[0],1]+1] ;subset of relevant FIELD ENTRIES
      taname=tab_arr[mi[0],0]+'/'+res[0] ;name combination for error message
      EXTRACT_AND_TEST,res[1],';',xval[1],ta1,[''],taname,res[1],0
    ENDIF ELSE IF res[0] EQ 'DATA_SOURCE' THEN BEGIN
      ;need to test against 'DATA_SOURCE' and 'AFFILIATION' (AVDC) or 'DATA_SOURCE_02'
      ;(original Envisat) entries
      ta1=tab_arr[mi[0],2:tab_arr[mi[0],1]+1] ;DATA_SOURCE ENTRIES
      IF tab_type EQ 0 THEN BEGIN
        atxt='AFFILIATION' & atx='' & ti=1
      ENDIF ELSE BEGIN
        atxt='DATA_SOURCE_02' & atx='_01' & ti=0
      ENDELSE
      ai=WHERE(STRPOS(tab_arr[*,0],atxt) NE -1,acnt)
      IF acnt EQ 0 THEN STOP_WITH_ERROR,o3[3]+proname,atxt+errtxt[3],lu
      ta2=tab_arr[ai[0],2:tab_arr[ai[0],1]+1] ;AFFILIATION/DATA_SOURCE_02 ENTRIES
      taname=res[0]+atx+'/'+tab_arr[ai[0],0] ;name combination for error message
      EXTRACT_AND_TEST,res[1],'_',2,ta1,ta2,taname,res[1],ti
    ENDIF ELSE IF res[0] EQ 'DATA_VARIABLES' THEN BEGIN
      ;make into 2-D array and separate out the components of the VAR_NAMES
      achk=STRSPLIT(res[1],';',/Extract)
      nel=N_ELEMENTS(achk)
      achk2=STRARR(mcnt,nel)
      FOR j=0,nel-1 DO BEGIN
        dv=STRSPLIT(achk[j],'_',/Extract)
        achk2[0:N_ELEMENTS(dv)-1,j]=dv
      ENDFOR
      FOR j=0,nel-1 DO BEGIN
        FOR k=0,mcnt-1 DO BEGIN
          IF achk2[k,j] NE '' THEN BEGIN
            taname=tab_arr[mi[k],0]
            ta1=tab_arr[mi[k],2:tab_arr[mi[k],1]+1] ;subset of DATA_VARIABLES entries
            EXTRACT_AND_TEST,achk2[k,j],';',1,ta1,[''],taname,achk[j],0
          ENDIF
        ENDFOR
      ENDFOR
    ENDIF ELSE IF res[0] EQ 'FILE_ACCESS' THEN BEGIN
      ;need to check each Metadata entry individually
      achk=STRSPLIT(res[1],';',/Extract)
      nel=N_ELEMENTS(achk)
      ta1=tab_arr[mi[0],2:tab_arr[mi[0],1]+1] ;FILE_ACCESS entries
      FOR j=0,nel-1 DO EXTRACT_AND_TEST,achk[j],';',1,ta1,[''],res[0],achk[j],0
    ENDIF ELSE IF res[0] EQ 'FILE_META_VERSION' THEN BEGIN
      ;only need to look at the second value
      achk=STRSPLIT(res[1],';',/Extract)
      IF N_ELEMENTS(achk) NE 2 THEN $
        STOP_WITH_ERROR,o3[3]+proname+meta_arr[i]+': ',errtxt[1]+'2.',lu
      achk=[STRTRIM(achk[1],2)]
      ta1=tab_arr[mi[0],2:tab_arr[mi[0],1]+1] ;FILE_META_VERSION entries
      EXTRACT_AND_TEST,achk[0],';',1,ta1,[''],res[0],achk[0],0
    ENDIF ELSE IF res[0] EQ 'VAR_UNITS' THEN BEGIN ;VAR_UNITS and VAR_SI_CONVERSION entries
      errstate=''
      VAR_UNITS_TEST,res[1],rd,tab_type,tchk,errstate
      IF errstate NE '' THEN $ VAR_UNIT value not valid
        STOP_WITH_ERROR,o3[3]+proname+STRMID(errstate,0,STRPOS(errstate,':')+2), $
                        STRMID(errstate,STRPOS(errstate,':')+2),lu
      ;Find VAR_SI_CONVERSION meta attribute
      k=1
      WHILE STRMID(meta_arr[i+k],0,17) NE 'VAR_SI_CONVERSION' DO k=k+1
      achk=STRSPLIT(meta_arr[i+k],'=',/Extract) ;VAR_SI_CONVERSION values
      IF N_ELEMENTS(achk) GE 2 THEN BEGIN
        ;check calculated VAR_SI_VALUE against existing value in the metadata
        IF achk[1] NE tchk THEN BEGIN
          warntxt=STRARR(2) & lineno=lineno+2L
          warntxt[0]='  WARNING: VAR_SI_CONVERSION values in Metadata replaced with'
          warntxt[1]='  those from the Table Attribute Value file: '+achk[1]+' -> '+tchk
          FOR j=0,1 DO BEGIN
            IF o3[3] EQ '' THEN WIDGET_CONTROL,wtxt,set_value=warntxt[j],/Append,Set_text_top_line=lineno
            FOR m=dux[0],dux[1],dux[2] DO PRINTF,m,warntxt[j] ;write out to log
          ENDFOR
        ENDIF
      ENDIF
      meta_arr[i+k]=achk[0]+'='+tchk
    ENDIF ELSE BEGIN ;perform checks on the remaining ATTRIBUTE_NAMES
      achk=STRSPLIT(res[1],';',/Extract)
      achk=STRTRIM(achk,2)
      nel=N_ELEMENTS(achk) & ta2=['']
      IF res[0] EQ 'VAR_DATA_TYPE' THEN BEGIN
        ;to convert VAR_SI_CONVERSION values to floating point if necessary
        IF (STRUPCASE(res[1]) EQ 'REAL') OR (STRUPCASE(res[1]) EQ 'DOUBLE') THEN rd=-1 $
        ELSE rd=1
      ENDIF
      IF (nel NE mcnt) OR (STRMID(meta_arr[i],STRLEN(meta_arr[i])-1,1) EQ ';') THEN $
        STOP_WITH_ERROR,o3[3]+proname+meta_arr[i]+': ',errtxt[1]+STRTRIM(mcnt,2)+'.',lu
      nel=1
      FOR j=0,mcnt-1 DO BEGIN
        ta1=tab_arr[mi[j],2:tab_arr[mi[j],1]+1] ;subset of relevant ENTRIES
        EXTRACT_AND_TEST,achk[j],';',nel,ta1,ta2,tab_arr[mi[j],0],achk[j],mi[j]
        IF j EQ 0 THEN ta2[0]=''
      ENDFOR
    ENDELSE
  ENDIF
  ;Check for and remove white spaces between sub-values (except for the Free_Attributes)
  eqpos=STRPOS(meta_arr[i],'=')
  lab=STRMID(meta_arr[i],0,eqpos)
  IF eqpos NE STRLEN(meta_arr[i])-1 THEN BEGIN
    val=STRMID(meta_arr[i],eqpos+1) & val=STRTRIM(val,2)
    fi=WHERE(lab EQ free_attr,fcnt)
    IF fcnt EQ 0 THEN BEGIN ;ie. is not a Free Attribute
      res=STRSPLIT(val,';',/Extract)
      res=STRTRIM(res,2)
      meta_arr[i]=lab+'='+res[0]
      IF N_ELEMENTS(res) GT 1 THEN FOR j=1,N_ELEMENTS(res)-1 DO meta_arr[i]=meta_arr[i]+';'+res[j]
    ENDIF ELSE meta_arr[i]=lab+'='+val
  ENDIF
ENDFOR

END ;Procedure Check_Metadata

PRO extract_data, sds, inf, vc, dtest, ndl
;Procedure to extract the set of data corresponding to a particular Variable Attribute
; ----------
;Written by Ian Boyd, NIWA-ERI for the AVDC - i.boyd@niwa.com
;
;  History:
;    20050802: Introduced to IDLCR8HDF, originally part of the READ_DATA procedure - Version 1.0
;    20050909: After reading in a data segment according to the VAR_SIZE values, check the next line to
;              ensure it is not a further data point (i.e. there are more data points than specified by
;              VAR_SIZE); If sds is a structure, and there is only one data point then return dtest as
;              an array rather than a scalar - Version 1.1
;    20061012: The portion of READ_DATA dealing with extracting a specific data segment renamed
;              EXTRACT_DATA - Version 2.0
;    20080302: The sets of data values in a file can now be in any order, rather than the same order as
;              that given by the DATA_VARIABLES attribute; dtest used as an input flag to indicate
;              whether the procedure call is from SET_UP_STRUCTURE [-1] or READ_DATA [1]. If from
;              SET_UP_STRUCTURE then only the number of data lines (ndl) is determined by the routine;
;              an error is generated in the case where the same VAR_NAME is repeated in the data file;
;              Error messages clarified when an incorrect number of data values are found in the
;              input data file - Version 3.0
;
;  Inputs: sds - Either a structure containing the Variable Attributes and Data, or the input
;                data file
;          inf - flag identifying sds type where, 0=structure; 1=data file
;          vc - the index value of the vn array holding the VARIABLE_NAME being searched for in the data
;               file, or the index value of the sds structure which holds the data
;          dtest - a single string array to show which procedure called this routine, either
;                  SET_UP_STRUCTURE [-1], or READ_DATA [1]
;
;  Outputs: dtest - a string array of size ndl, which holds the data values
;           ndl - The total number of data lines i.e. the product of the VAR_SIZE values
;
;  Called by: SET_UP_STRUCTURE; READ_DATA
;
;  External Subroutines Called: STOP_WITH_ERROR (if error state detected)
;    Possible Conditions for STOP_WITH_ERROR call (plus [line number] where called):
;      1. EOF encountered when trying to find the VAR_NAME or while attempting to read in data
;         [1194, 1259, 1260]
;      2. Invalid data point found in the dataset [1238]
;      3. Two instances of the same VAR_NAME found in the input data file [1247]
;      4. Too many data values present in the dataset or the next line in the file is invalid [1252]
;      5. Number of data values does not match product of VAR_SIZE [1275]
;      6. ISO8601 datetime format not valid [1289]

COMMON DATA
COMMON WIDGET_WIN

;possible error messages for this procedure
errtxt=STRARR(9)
errtxt[0]='EOF encountered but not expected'
errtxt[1]=' when trying to find VAR_NAME.'
errtxt[2]=' when trying to determine VAR_SIZE for VAR_NAME='
errtxt[3]=' - Number of data lines should be '
errtxt[4]='Invalid data point found in the dataset at line '
errtxt[5]='Two instances of this VAR_NAME present in the Data File.'
errtxt[6]='Too many data values present in the dataset or invalid data line entry following the dataset'
errtxt[7]='Number of data values does not match product of VAR_SIZE: '
errtxt[8]='ISO8601 format not valid: '
proname='Extract_Data procedure: '

ndl=1L ;initialize data line count

IF inf EQ 1 THEN BEGIN ;Find the first data value for the requested VAR_NAME
  ON_IOERROR,TypeConversionError
  dum=''
  OPENR,lu,sds,/GET_LUN
  IF NOT EOF(lu) THEN $ ;read through data file until the correct Variable Name is found
    REPEAT READF,lu,dum UNTIL (EOF(lu)) OR (STRTRIM(STRUPCASE(dum),2) EQ STRUPCASE(vn[vc]))
  IF STRTRIM(STRUPCASE(dum),2) NE STRUPCASE(vn[vc]) THEN $
    STOP_WITH_ERROR,o3[3]+proname+vn[vc],errtxt[0]+errtxt[1],lu,ds
  ;Find first data point
  dum=''
  valid=-1 ;set to test for successful line read
  WHILE (STRMID(dum,0,1) EQ '!') OR (STRMID(dum,0,1) EQ '#') OR (dum EQ '') DO BEGIN
    READF,lu,dum & dum=STRTRIM(dum,2)
  ENDWHILE
  valid=1 ;i.e. got to here without prematurely reaching the eof so first data point found OK

  IF dtest[0] EQ '-1' THEN BEGIN ;need to determine the total number of data lines only (ndl)
    IF NOT EOF(lu) THEN BEGIN
      ;read through until eof or comment line, null line, or another VAR_NAME is reached
      REPEAT BEGIN
        READF,lu,dum
        dum=STRTRIM(STRUPCASE(dum),2)
        bi=WHERE(dum EQ STRUPCASE(vn))
        IF (STRMID(dum,0,1) NE '!') AND (STRMID(dum,0,1) NE '#') AND (dum NE '') AND (bi[0] EQ -1) THEN incnt=1L $
        ELSE incnt=0L
        ndl=ndl+incnt ;presume it is a valid data line so add to total
      ENDREP UNTIL (incnt EQ 0L) OR (EOF(lu))
    ENDIF
  ENDIF ELSE BEGIN ;return dtest and ndl
    di=WHERE(ndm[vc,0:7] NE 0L,ndim)
    ;multiply together the array dimensions to determine expected total number of datalines
    FOR i=0,ndim-1 DO ndl=ndl*ndm[vc,di[i]]
    ;attempt to read any remaining data values
    IF ndl GT 1L THEN BEGIN
      valid=0 ;set to test for successful block read
      dtest=STRARR(ndl-1) & READF,lu,dtest
      valid=1 ;i.e. got to here without prematurely reaching the EOF so block read was OK
      dtest=[dum,dtest] ;add first value to dtest
    ENDIF ELSE dtest=[dum]
    dtest=STRTRIM(STRUPCASE(dtest),2)
    ;Test to see if any of the 'data' read in is a VAR_NAME - if so bcnt will NE 0 (and will generate error message)
    bcnt=0 & i=0
    WHILE (bcnt EQ 0) AND (i LE nvn-1) DO BEGIN
      bi=WHERE(dtest EQ STRUPCASE(vn[i]),bcnt)
      IF bcnt EQ 0 THEN i=i+1
    ENDWHILE
    ;Test to see if any of the 'data' read in is not a valid data point - if so bcnt will NE 0
    IF bcnt EQ 0 THEN $
      bi=WHERE((STRMID(dtest,0,1) EQ '!') OR (STRMID(dtest,0,1) EQ '#') OR (dtest EQ ''),bcnt)

    IF bcnt NE 0 THEN $ Non-valid data point found in the dataset
      STOP_WITH_ERROR,o3[3]+proname+vn[vc]+': ',errtxt[4]+STRTRIM(bi[0]+1,2)+errtxt[3]+ $
                      STRTRIM(ndl,2),lu,ds

    ;Read next line if not EOF to test for the correct number of data lines
    IF NOT EOF(lu) THEN READF,lu,dum ELSE dum=''
    dum=STRTRIM(STRUPCASE(dum),2)
    ;Test to see if the next line read in is another VAR_NAME - if not then bi[0] will equal -1
    bi=WHERE(dum EQ STRUPCASE(vn))
    IF bi[0] EQ vc THEN $ ;Same VAR_NAME found twice in the data file
      STOP_WITH_ERROR,o3[3]+proname+vn[vc]+': ',errtxt[5],lu,ds
    ;Test to check if the next line read in is another data point - if so bcnt will be set to 1
    IF (STRMID(dum,0,1) NE '!') AND (STRMID(dum,0,1) NE '#') AND (dum NE '') AND (bi[0] EQ -1) THEN $
      bcnt=1
    IF bcnt NE 0 THEN $ ;not enough data lines read in or next line in the file is invalid
      STOP_WITH_ERROR,o3[3]+proname+vn[vc]+': ',errtxt[6]+errtxt[3]+STRTRIM(ndl,2),lu,ds
  ENDELSE
  FREE_LUN,lu

  TypeConversionError:
  IF valid LE 0 THEN BEGIN ;EOF encountered but not expected
    IF (dtest[0] EQ '-1') AND (valid EQ -1) THEN $
      STOP_WITH_ERROR,o3[3]+proname,errtxt[0]+errtxt[2]+vn[vc],lu,ds $
    ELSE STOP_WITH_ERROR,o3[3]+proname+vn[vc]+': ',errtxt[0]+errtxt[3]+STRTRIM(ndl,2),lu,ds
  ENDIF
ENDIF ELSE BEGIN ;sds is a structure
  IF dtest[0] EQ '-1' THEN BEGIN ;determine the total number of data lines only
    dtest=*sds[vc].data
    ndl=LONG(N_ELEMENTS(dtest))
    dtest=['-1']
  ENDIF ELSE BEGIN ;return dtest and ndl
    lu=-1
    di=WHERE(ndm[vc,0:7] NE 0L,ndim)
    ;multiply together the array dimensions to determine expected total number of datalines
    FOR i=0,ndim-1 DO ndl=ndl*ndm[vc,di[i]]
    dtest=*sds[vc].Data
    ;check structure contains the correct number of elements
    IF N_ELEMENTS(dtest) NE ndl THEN $
      STOP_WITH_ERROR,o3[3]+proname+vn[vc]+': ',errtxt[7]+STRTRIM(N_ELEMENTS(dtest),2)+'/'+STRTRIM(ndl,2),lu,ds
    stest=SIZE(dtest) ;test to see if dtest is an array or scalar
    IF stest[0] EQ 0 THEN dtest=[dtest] ELSE dtest=REFORM(dtest,ndl,/Overwrite) ;convert to a 1-D array if necessary
  ENDELSE
ENDELSE

;Note: this conditional will only be satisfied if the routine is called by READ_DATA
;(vu still not assigned VAR_UNITS value if called by SET_UP_STRUCTURE)
IF STRUPCASE(vu[vc]) EQ 'MJD2000' THEN BEGIN
  ;Convert time from ISO8601 to MJD2000 format if necessary
  FOR i=0L,ndl-1L DO BEGIN
    IF STRPOS(STRUPCASE(dtest[i]),'Z') NE -1 THEN BEGIN
      mjd2000=JULIAN_DATE(dtest[i],/I,/M) ;return time in MJD2000 format
      IF mjd2000 EQ -99999.d THEN $ ;conversion error
        STOP_WITH_ERROR,o3[3]+proname+vn[vc]+': ',errtxt[8]+dtest[i],lu,ds
      dtest[i]=STRING(format='(d18.9)',mjd2000)
    ENDIF
  ENDFOR
ENDIF

END ;Procedure Extract_Data

PRO set_up_structure, sds, inf
;Procedure to do additional checks on VAR_NAME, VAR_DIMENSION, VAR_DEPEND, VAR_DATA_TYPE,
;VAR_UNITS, and VAR_FILL_VALUE metadata attributes and set up a structure which will be used to
;hold the data values.
; ----------
;Written by Ian Boyd, NIWA-ERI for the AVDC - i.boyd@niwa.com
;
;  History:
;    20050802: Introduced to IDLCR8HDF, as Determine_Arr_Sizes. Data originally stored in 4
;              arrays according to the data type of the variable attributes - Version 1.0
;    20050909: Additional checks performed on the metadata attributes as follows: that the
;              VAR_DEPEND variable name(s), themselves, have a VAR_DEPEND value of CONSTANT or
;              INDEPENDENT, and matching VAR_SIZE values; that VAR_DATA_TYPE is DOUBLE for
;              attributes with VAR_UNITS=MJD2000; that VAR_UNITS=MJD2000 when VAR_NAME=DATETIME
;              - Version 1.1
;    20061012: Procedure renamed Set_Up_Structure. A structure using pointers used to store the
;              data instead of arrays. VAR_FILL_VALUE value saved in common array and, if
;              VAR_UNITS=MJD2000, VAR_FILL_VALUE converted to its MJD2000 form if it is in
;              ISO8601 format. Common variable definition WIDGET_WIN added - Version 2.0
;    20080302: Can calculate the VAR_SIZE value(s) from the input data or from the VAR_SIZE
;              value(s) of the VAR_DEPEND variable name(s), if they are not present in the
;              metadata. Section added to account for the new variables, ALTITUDE/ALTITUDE.GPH/
;              PRESSURE.BOUNDARIES; Additional checks added for VIS_PLOT_TYPE, VIS_SCALE_TYPE,
;              VIS_SCALE_MIN, and VIS_SCALE_MAX values - Version 3.0
;
;  Inputs: sds - Either a structure containing the Variable Attributes and Data, or the input
;                data file
;          inf - flag identifying SDS type where, 0=structure; 1=data file
;          meta_arr - a string array containing the Global and Variable Attributes
;
;  Outputs: meta_arr - some meta_arr values may be corrected or calculated in this routine, including:
;                      VAR_DIMENSION, VAR_SIZE, and VAR_FILL_VALUE
;           nvn - an integer giving the number of Variable Attributes present in the input
;           vn - a string array of size nvn containing the VAR_NAME values
;           vu - a string array of size nvn containing the VAR_UNIT values
;           vfv - a string array of size nvn containing the VAR_FILL_VALUE values
;           ndm - a (nvn,9) long array, where ndm(*,0:7) describes the dimensions of each dataset
;                 (maximum of 8 dimensions allowed), and ndm(*,8) describes the VAR_DATA_TYPE
;           ds - the structure that will hold the data
;
;  Called by: IDLCR8HDF
;
;  External Subroutines Called: EXTRACT_DATA (if it is necessary to determine VAR_SIZE from the input data)
;                               STOP_WITH_ERROR (if error state detected)
;    Possible Conditions for STOP_WITH_ERROR call (plus [line number] where called):
;      1. Program expects to find the VAR_NAME in the metadata in the same order as that listed under
;         DATA_VARIABLES [1409]
;      2. The VAR_DIMENSION value is greater than 8 [1432]
;      3. Number of VAR_DEPEND or VAR_SIZE (sub-)values does not match the number of dimensions [1434, 1445]
;      4. The VAR_DEPEND value is not 'CONSTANT' or 'INDEPENDENT', and doesn't match a previous
;         VAR_NAME [1463]
;      5. The VAR_DEPEND variable name must, itself, have a VAR_DEPEND value of 'CONSTANT' or
;         'INDEPENDENT' [1474]
;      6. The VAR_DEPEND variable name must have a VAR_SIZE value matching the current VAR_NAME [1482]
;      7. VAR_DATA_TYPE not INTEGER, LONG, REAL, or DOUBLE [1425]
;      8. Unexpected number of ATTRIBUTE values extracted [1413, 1535, 1555]
;      9. VAR_DATA_TYPE should be DOUBLE for VAR_UNITS=MJD2000 [1525]
;     10. VAR_UNITS should be MJD2000 for VAR_NAME=DATETIME [1528]
;     11. VAR_FILL_VALUE not a valid value - occurs on an unsuccessful call to JULIAN_DATE [1543]
;     12. VAR_DEPEND values not valid - occurs when INDEPENDENT or CONSTANT is a VAR_DEPEND value
;         when it should not be [1455, 1494, 1500]
;     13. Second VAR_SIZE value is not '2' when VAR_NAME=ALTITUDE/ALTITUDE.GPH/PRESSURE.BOUNDARIES [1497]
;     14. If VIS_PLOT_TYPE=NONE then accompanying VIS_SCALE_TYPE and VIS_SCALE_MIN/MAX values must
;         also be set to NONE [1563]
;     15. If VIS_PLOT_TYPE NE NONE then accompanying VIS_SCALE_TYPE and VIS_SCALE_MIN/MAX values
;         cannot be set to NONE [1568]
;     16. Type conversion error. Called when trying to convert a string, which is expected to be
;         numeric, to a number [1581]

COMMON METADATA
COMMON DATA
COMMON WIDGET_WIN

;possible error messages for this procedure
errtxt=STRARR(16) & lu=-1L
errtxt[0]='Does not match the equivalent VAR_NAME in DATA_VARIABLES: '
errtxt[1]='Number of dimensions too high to make HDF file (max. allowed 8).'
errtxt[2]='Number of attribute values does not match VAR_DIMENSION: '
errtxt[3]='VAR_DEPEND value not ''CONSTANT/INDEPENDENT'', and doesn''t match a previous VAR_NAME: '
errtxt[4]='VAR_DEPEND variable name must have VAR_DEPEND value of ''CONSTANT/INDEPENDENT'': '
errtxt[5]='VAR_DEPEND variable name must have matching VAR_SIZE value (= '
errtxt[6]='Data type not INTEGER, LONG, REAL, or DOUBLE: '
errtxt[7]='Unexpected number of ATTRIBUTE values extracted: '
errtxt[8]='VAR_DATA_TYPE should be DOUBLE for VAR_UNITS=MJD2000: '
errtxt[9]='VAR_UNITS should be MJD2000 for VAR_NAME=DATETIME: '
errtxt[10]='VAR_FILL_VALUE not a valid value: '
errtxt[11]='VAR_DEPEND value(s) not valid'
errtxt[12]='Second VAR_SIZE value should be ''2'' for this VAR_NAME.'
errtxt[13]=' Attribute Value(s) incorrect for VIS_PLOT_TYPE=NONE - should be: '
errtxt[14]=' Attribute Value(s) cannot be ''NONE'' for VIS_PLOT_TYPE='
errtxt[15]='Type conversion error.  Entry is not a valid number: '
proname='Set_Up_Structure procedure: '

lc=0 & vc=0
ALLOWABLE_DATA_TYPES=['INTEGER','LONG','REAL','DOUBLE']
ON_IOERROR,TypeConversionError

WHILE lc LT N_ELEMENTS(meta_arr) DO BEGIN
  valid=0 ;set to test for successful string conversion to a number
  IF STRMID(meta_arr[lc],0,14) EQ 'DATA_VARIABLES' THEN BEGIN ;determine number of variables reported
    vn=STRSPLIT(meta_arr[lc],' =;',/Extract) ;string containing reported variables
    nvn=N_ELEMENTS(vn)-1 ;total number of variables reported
    vn=vn[1:nvn] ;remove the DATA_VARIABLES label
    vu=STRARR(nvn) & vfv=STRARR(nvn) ;holding arrays for VAR_UNITS and VAR_FILL_VALUE
    ndm=LONARR(nvn,9) ;to contain the dimensions and data type for each dataset
  ENDIF
  IF STRMID(meta_arr[lc],0,8) EQ 'VAR_NAME' THEN BEGIN
    vname=meta_arr[lc]+': ' ;used in case of error
    res=STRSPLIT(meta_arr[lc],' =',/Extract)
    IF N_ELEMENTS(res) NE 2 THEN res=[res[0],'-1']
    IF res[1] NE vn[vc] THEN STOP_WITH_ERROR,o3[3]+proname+vname,errtxt[0]+vn[vc],lu
    ;Does not match the equivalent VAR_NAME in DATA_VARIABLES
    REPEAT lc=lc+1 UNTIL STRMID(meta_arr[lc],0,13) EQ 'VAR_DIMENSION'
    res=STRSPLIT(meta_arr[lc],' =',/Extract)
    IF N_ELEMENTS(res) NE 2 THEN STOP_WITH_ERROR,o3[3]+proname+vname,errtxt[7]+meta_arr[lc],lu
    ;Unexpected number of ATTRIBUTE values extracted
    lcx=lc ;in case of type conversion error
    nd=FIX(res[1]) & hold=LONARR(nd)
    meta_arr[lc]=res[0]+'='+STRTRIM(nd,2) ;to ensure that there are no extra characters after the number
    REPEAT lc=lc+1 UNTIL STRMID(meta_arr[lc],0,8) EQ 'VAR_SIZE'
    holdvs=meta_arr[lc] & lcx=lc ;do checks after determining VAR_DATA_TYPE
    REPEAT lc=lc+1 UNTIL STRMID(meta_arr[lc],0,10) EQ 'VAR_DEPEND'
    holdvd=meta_arr[lc] ;do checks after determining VAR_DATA_TYPE
    REPEAT lc=lc+1 UNTIL STRMID(meta_arr[lc],0,13) EQ 'VAR_DATA_TYPE'
    res=STRSPLIT(STRUPCASE(meta_arr[lc]),' =;',/Extract)
    gi=WHERE(res[1] EQ ALLOWABLE_DATA_TYPES,gcnt) ;Check VAR_DATA_TYPE is valid
    IF gcnt NE 1 THEN STOP_WITH_ERROR,o3[3]+proname+vname,errtxt[6]+meta_arr[lc],lu
    ndm[vc,8]=gi[0] ;index number representing the data type

    ;Do VAR_SIZE checks
    resv=STRSPLIT(holdvs,' =;',/Extract)
    IF N_ELEMENTS(resv) NE 1 THEN BEGIN
      IF nd GT 8 THEN $
        STOP_WITH_ERROR,o3[3]+proname+vname,errtxt[1],lu
      ;Number of dimensions too high to make HDF file (max. allowed 8)
      IF N_ELEMENTS(resv) NE nd+1 THEN STOP_WITH_ERROR,o3[3]+proname+vname,errtxt[2]+holdvs,lu
      ;Number of elements does not match VAR_DIMENSION
      hold=LONG(resv[1:nd])
      ndm[vc,0:nd-1]=hold ;EQ array dimensions from VAR_SIZE
      ndmok=1
    ENDIF ELSE ndmok=0
    ;if there are no VAR_SIZE value entries, need to determine from Data file or structure or
    ;from the VAR_SIZE values of the VAR_DEPEND dependencies

    ;Do VAR_DEPEND checks
    resv=STRSPLIT(holdvd,' =;',/Extract) ;VAR_DEPEND entries
    IF N_ELEMENTS(resv) NE nd+1 THEN STOP_WITH_ERROR,o3[3]+proname+vname,errtxt[2]+holdvd,lu
    ;Number of elements does not match VAR_DIMENSION (or formatting problem)
    vshold=LONARR(nd) ;to hold VAR_SIZE of the dependencies

    IF STRPOS(vn[vc],'.BOUNDARIES') NE -1 THEN BEGIN
      ;Check that the VAR_DEPEND value(s) are valid given VAR_NAME=XXX.BOUNDARIES
      IF nd GE 2 THEN test1=resv[2] NE 'INDEPENDENT' ELSE test1=0
      firstvdsplt=STRSPLIT(resv[1],'.',/Extract)
      test2=STRPOS(vn[vc],firstvdsplt[0]) EQ -1
      IF (test1) OR (test2) THEN $
        STOP_WITH_ERROR,o3[3]+proname+vname+holdvd+': ',errtxt[11]+' for the given VAR_NAME.',lu
        ;VAR_DEPEND value(s) not valid for the given VAR_NAME
    ENDIF

    FOR i=1,nd DO BEGIN
      ;Check for valid dependencies, and hold VAR_SIZE values of the dependencies if required
      test1=(STRUPCASE(resv[i]) NE 'CONSTANT') AND (STRUPCASE(resv[i]) NE 'INDEPENDENT')
      IF vc GE 1 THEN gi=WHERE(resv[i] EQ vn[0:vc-1],gcnt) ELSE gcnt=0
      IF (gcnt EQ 0) AND (test1) THEN STOP_WITH_ERROR,o3[3]+proname+vname,errtxt[3]+resv[i],lu
      ;VAR_DEPEND entry not 'CONSTANT/INDEPENDENT', and doesn't match a previous VAR_NAME
      IF gcnt NE 0 THEN BEGIN ;do remaining checks on VAR_DEPEND values
        errfound=0
        gi=WHERE(meta_arr EQ 'VAR_NAME='+resv[i])
        ;check VAR_DEPEND value of the VAR_DEPEND variable name
        vi=WHERE(STRPOS(meta_arr[gi[0]:N_ELEMENTS(meta_arr)-1],'VAR_DEPEND') NE -1)
        vex=STRSPLIT(STRUPCASE(meta_arr[gi[0]+vi[0]]),' =;',/Extract)
        IF N_ELEMENTS(vex) NE 2 THEN errfound=1 $
        ELSE IF (vex[1] NE 'CONSTANT') AND (vex[1] NE 'INDEPENDENT') THEN errfound=1
        IF errfound EQ 1 THEN $
          STOP_WITH_ERROR,o3[3]+proname+vname+'Dependency='+resv[i]+': ',errtxt[4]+meta_arr[gi[0]+vi[0]],lu
          ;VAR_DEPEND variable name must have VAR_DEPEND value of 'CONSTANT/INDEPENDENT'
        ;check VAR_SIZE value of the VAR_DEPEND variable name
        vi=WHERE(STRPOS(meta_arr[gi[0]:N_ELEMENTS(meta_arr)-1],'VAR_SIZE') NE -1)
        vex=STRSPLIT(meta_arr[gi[0]+vi[0]],' =;',/Extract)
        IF N_ELEMENTS(vex) NE 2 THEN errfound=1 $
        ELSE IF (ndmok EQ 1) AND (LONG(vex[1]) NE ndm[vc,i-1]) THEN errfound=1
        IF errfound EQ 1 THEN BEGIN
          STOP_WITH_ERROR,o3[3]+proname+vname+'VAR_DEPEND='+resv[i]+': ', $
          errtxt[5]+STRTRIM(ndm[vc,i-1],2)+'): '+meta_arr[gi[0]+vi[0]],lu
          ;VAR_DEPEND variable name must have matching VAR_SIZE value
        ENDIF ELSE vshold[i-1]=LONG(vex[1])
      ENDIF
      IF (test1 EQ 0) AND (i GT 1) THEN BEGIN
        ;VAR_DEPEND value is CONSTANT or INDEPENDENT and it is not the first VAR_DEPEND value.
        ;If i eq 2 and value is INDEPENDENT then check for ALTITUDE/PRESSURE.BOUNDARIES variable name,
        ;and that the corresponding VAR_SIZE value is 2 (if present), o/w generate errors
        IF (i EQ 2) AND (STRUPCASE(resv[i]) EQ 'INDEPENDENT') THEN BEGIN
          firstvdsplt=STRSPLIT(resv[1],'.',/Extract)
          IF (STRPOS(vn[vc],firstvdsplt[0]) EQ -1) AND (STRPOS(vn[vc],'.BOUNDARIES') EQ -1) THEN $
            STOP_WITH_ERROR,o3[3]+proname+vname+holdvd+': ',errtxt[11]+' for the given VAR_NAME.',lu
            ;VAR_DEPEND value(s) not valid for the given VAR_NAME
          IF (ndmok EQ 1) AND (ndm[vc,i-1] NE 2L) THEN $
            STOP_WITH_ERROR,o3[3]+proname+vname+holdvs+': ',errtxt[12],lu
            ;Second VAR_SIZE value should be '2' for this VAR_NAME
          vshold[i-1]=2L ;set VAR_SIZE value in the event that it is not part of the Metadata
        ENDIF ELSE STOP_WITH_ERROR,o3[3]+proname+vname+holdvd+': ',errtxt[11]+'.',lu
       ;VAR_DEPEND values not valid
      ENDIF
    ENDFOR

    IF ndmok EQ 0 THEN BEGIN ;no VAR_SIZE value so need to determine from the data or the vshold array
      IF vshold[0] EQ 0L THEN BEGIN
        ;VAR_DEPEND is INDEPENDENT or CONSTANT so determine VAR_SIZE from the Data file or structure
        dtest=['-1'] ;-1 identifies to the EXTRACT_DATA routine that ndl is all that is required
        EXTRACT_DATA,SDS,inf,vc,dtest,ndl
        meta_arr[lcx]='VAR_SIZE='+STRTRIM(ndl,2)
        ndm[vc,0]=ndl
      ENDIF ELSE BEGIN ;can determine VAR_SIZE from the vshold array
        meta_arr[lcx]='VAR_SIZE='
        FOR i=0,nd-1 DO BEGIN
          IF i EQ 0 THEN meta_arr[lcx]=meta_arr[lcx]+STRTRIM(vshold[i],2) $
          ELSE meta_arr[lcx]=meta_arr[lcx]+';'+STRTRIM(vshold[i],2)
          ndm[vc,i]=vshold[i] ;EQ array dimensions from VAR_SIZE of the dependencies
        ENDFOR
      ENDELSE
    ENDIF

    REPEAT lc=lc+1 UNTIL STRMID(meta_arr[lc],0,9) EQ 'VAR_UNITS'
    resv=STRSPLIT(meta_arr[lc],' =',/Extract)
    IF (STRUPCASE(resv[1]) EQ 'MJD2000') AND (res[1] NE 'DOUBLE') THEN $
      STOP_WITH_ERROR,o3[3]+proname+vname,errtxt[8]+res[0]+'='+res[1],lu
      ;VAR_DATA_TYPE should be DOUBLE for VAR_UNITS=MJD2000
    IF (vn[vc] EQ 'DATETIME') AND (STRUPCASE(resv[1]) NE 'MJD2000') THEN $
      STOP_WITH_ERROR,o3[3]+proname,errtxt[9]+meta_arr[lc],lu
      ;VAR_UNITS should be MJD2000 for VAR_NAME=DATETIME
    vu[vc]=resv[1] ;used to check for MJD2000 units when reading in data as well as NCSA units value
    IF N_ELEMENTS(resv) GT 2 THEN $
      FOR i=2,N_ELEMENTS(resv)-1 DO vu[vc]=vu[vc]+' '+resv[i]
    REPEAT lc=lc+1 UNTIL STRMID(meta_arr[lc],0,14) EQ 'VAR_FILL_VALUE'
    resv=STRSPLIT(meta_arr[lc],' =;',/Extract)
    IF N_ELEMENTS(resv) NE 2 THEN STOP_WITH_ERROR,o3[3]+proname+vname,errtxt[7]+meta_arr[lc],lu
    ;Unexpected number of ATTRIBUTE values extracted
    ;Check that the VAR_FILL_VALUE is numeric
    lcx=lc & holdvfv=DOUBLE(resv[1])
    vfv[vc]=resv[1] ;used to check for valid data values when reading in data
    ;If VAR_UNITS=MJD2000 then check for ISO8601 format and change to MJD2000 if necessary
    IF (STRUPCASE(vu[vc]) EQ 'MJD2000') AND (STRPOS(STRUPCASE(vfv[vc]),'Z') NE -1) THEN BEGIN
      mjd2000=JULIAN_DATE(vfv[vc],/I,/M) ;return time in MJD2000 format
      IF mjd2000 EQ -99999.d THEN STOP_WITH_ERROR,o3[3]+proname+vn[vc]+': ',errtxt[10]+meta_arr[lc],lu
      vfv[vc]=STRTRIM(STRING(format='(d18.9)',mjd2000),2)
      meta_arr[lc]=resv[0]+'='+vfv[vc]
    ENDIF

    ;Do checks on VIS_PLOT_TYPE, VIS_SCALE_TYPE, and VIS_SCALE_MIN/MAX
    resh=STRARR(4) ;holding array for attribute values
    attrh=['VIS_PLOT_TYPE','VIS_SCALE_TYPE','VIS_SCALE_MIN','VIS_SCALE_MAX']
    ;Place metadata attribute values into resh array
    FOR i=0,3 DO BEGIN
      REPEAT lc=lc+1 until STRMID(meta_arr[lc],0,13) EQ STRMID(attrh[i],0,13)
      resv=STRSPLIT(meta_arr[lc],' =',/Extract)
      IF N_ELEMENTS(resv) NE 2 THEN STOP_WITH_ERROR,o3[3]+proname+vname,errtxt[7]+meta_arr[lc],lu
      ;Unexpected number of ATTRIBUTE values extracted
      resh[i]=resv[1]
    ENDFOR
    IF STRUPCASE(resh[0]) eq 'NONE' THEN BEGIN ;check that the following attribute values are also NONE
      FOR i=1,3 DO BEGIN
        IF i EQ 1 THEN vchk='NONE;NONE' ELSE vchk='NONE'
        IF resh[i] NE vchk THEN $
          STOP_WITH_ERROR,o3[3]+proname+vname,attrh[i]+errtxt[13]+attrh[i]+'='+vchk,lu
      ENDFOR
    ENDIF ELSE BEGIN ;check that the following attribute values are not NONE
      FOR i=1,3 DO BEGIN
        IF STRPOS(STRUPCASE(resh[i]),'NONE') NE -1 THEN $
          STOP_WITH_ERROR,o3[3]+proname+vname,attrh[i]+errtxt[14]+resh[0],lu
      ENDFOR
    ENDELSE
    vc=vc+1
  ENDIF
  lc=lc+1 & valid=1
ENDWHILE

;Define the HDF storage structure
ds_set={data: PTR_NEW()} ;SDS data array
ds=REPLICATE(ds_set,nvn) ;Dimension the structure to the size of the SDS datasets

TypeConversionError:
IF valid EQ 0 THEN STOP_WITH_ERROR,o3[3]+proname+vname,errtxt[15]+meta_arr[lcx],lu
END ;Procedure Set_Up_Structure

PRO check_min_max_fill, vc, dtest, ndl, vfsx
;Procedure to check that the data, VAR_VALID_MIN/MAX, VIS_SCALE_MIN/MAX, and VAR_FILL_VALUE fit
;within the given format and data type. That the VAR_FILL_VALUE is outside the valid minimum and
;maximum values and that the data values are within the valid minimum and maximum values. That the
;VIS_FORMAT value is valid. All values are assigned the precision described by VIS_FORMAT. The data
;values are returned in the data type given by VAR_DATA_TYPE.
; ----------
;Written by Ian Boyd, NIWA-ERI for the AVDC - i.boyd@niwa.com
;
;  History:
;    20050802: Original IDLCR8HDF routine - Version 1.0
;    20050909: If VAR_NAME=DATETIME then checks that the first data value is not a fill value, and is
;              the lowest value; Change the methods for determining if a given data value falls within
;              the range allowed by integer or long data types; Add fix for the case where a dataset
;              consists of only fill values - Version 1.1
;    20051107: Include VIS_SCALE_MIN/MAX values in the checks; Add fix to remove unwanted precision (to
;              prevent rounding errors), by converting values to formatted strings then back to numeric
;              values - Version 1.11
;    20061012: Check that all DATETIME values are in chronological order (if more than one value), and
;              contain no fill values; ISO8601 DATETIME conversions to MJD2000 format (if necessary) moved
;              to the READ_DATA routine; Add additional checks when extracting the VIS_FORMAT values for
;              testing; Common variable definition WIDGET_WIN added - Version 2.0
;    20080302: Fix bug so that unwanted precision from all data and relevant metadata values is removed
;              before doing QA checks, to avoid unnecessary error calls - Version 3.0
;
;  Inputs: meta_arr - a string array containing the Global and Variable Attributes
;          vc - the index value of the vn array holding the VARIABLE_NAME being searched for in the data
;               file, or the index value of the sds structure which holds the data
;          dtest - the set of data values to be tested
;          ndl - The total number of data values i.e. the product of the VAR_SIZE values
;
;  Outputs: meta_arr - in the event that the VIS_FORMAT value string or the precision of the attribute
;                      values being tested are changed, the array will be updated
;           vfsx - The VIS_FORMAT value. Used in READ_DATA to format the first three Averaging Kernel
;                  values when adding them, as part of a comment, to VAR_NOTES (if this option is chosen)
;
;  Called by: READ_DATA
;
;  External Subroutines Called: STOP_WITH_ERROR (if error state detected)
;    Possible Conditions for STOP_WITH_ERROR call (plus [line number] where called):
;      1. The format (label=value) of the metadata entry is not correct, or the format does not
;         match the data type (e.g. VAR_DATA_TYPE=INTEGER or LONG requires VIS_FORMAT=Iw, where
;         w is a number) [1718, 1786, 1788]
;      2. A data value falls outside the range given by VAR_VALID_MIN or VAR_VALID_MAX [1852, 1855]
;      3. The VAR_FILL_VALUE is not outside the valid minimum or maximum values [1744]
;      4. The metadata or data value being tested is outside the data type range. For example,
;         if a data value is 40000, but VAR_DATA_TYPE=INTEGER (maximum allowable value of 32767)
;         [1697, 1703, 1708, 1713]
;      5. The metadata or data value is outside the range given by VIS_FORMAT. For example, when
;         a fill value is -90000.00 and the VIS_FORMAT value is F7.2 [1814]
;      6. The difference between D/E/F[w.d] values is too small. By IDL/Fortran definition, the
;         w and d values must be sufficiently different, depending on the format and, in the case of
;         'E', the operating system [1779]
;      7. The DATETIME value cannot be a VAR_FILL_VALUE. Because DATETIME is an Independent variable
;         it should not contain Fill Values [1866]
;      8. The DATETIME values are not in chronological order [1870]
;      9. Type conversion error. The program cannot convert a data value or the numeric part of the
;         VIS_FORMAT to a valid number [1875]

COMMON METADATA
COMMON DATA
COMMON WIDGET_WIN

;Error messages for this procedure
ON_IOERROR,TypeConversionError
proname='Check_Min_Max_Fill procedure: '
errtxt2=STRARR(9) & lu=-1
errtxt2[0]=' not valid, or does not match VAR_DATA_TYPE: '
errtxt2[1]=' data value outside VAR_VALID_'
errtxt2[2]='Fill data value not outside VAR_VALID_MIN or VAR_VALID_MAX: '
errtxt2[3]=' outside the data type range: '
errtxt2[4]=' outside the range given by VIS_FORMAT '
errtxt2[5]='Difference between D/E/F[w.d] values too small (needs to be at least '
errtxt2[6]='DATETIME value cannot be a VAR_FILL_VALUE. '
errtxt2[7]='DATETIME values not in chronological order.'
errtxt2[8]='Type conversion error.  Entry is not a valid value/format code: '

vi=WHERE(meta_arr EQ 'VAR_NAME='+vn[vc])

;Determine Var_Fill_Value, Var_Valid_Min, Var_Valid_Max, Vis_Scale_Min, Vis_Scale_Max
;and data values, and check that they fall within the range allowed by VAR_DATA_TYPE
label=['VAR_FILL_VALUE','VAR_VALID_MIN','VAR_VALID_MAX']
label=[label,'VIS_SCALE_MIN','VIS_SCALE_MAX','Data value']
FOR i=0L,ndl+4L DO BEGIN
  IF i LE 4 THEN BEGIN
    lin=i ;label index
    li=WHERE(STRPOS(meta_arr[vi[0]:N_ELEMENTS(meta_arr)-1],label[lin]) NE -1)
    lvi=li[0]+vi[0]
    res=STRSPLIT(meta_arr[lvi],'=',/Extract)
    IF N_ELEMENTS(res) EQ 2 THEN BEGIN
      lv=res[1]
      IF (i EQ 3) OR (i EQ 4) THEN BEGIN ;VIS_SCALE_MIN/MAX
        ;if necessary change non-numeric value to the fill value to pass tests
        btest=BYTE(lv)
        test1=(btest[0] LT 43) OR (btest[0] GT 57)
        test2=(btest[0] EQ 44) OR (btest[0] EQ 46) OR (btest[0] EQ 47)
        IF (test1) OR (test2) THEN lv=STRTRIM(fv,2)
      ENDIF
    ENDIF
    tcerr=meta_arr[lvi] ;used for type conversion error
  ENDIF ELSE BEGIN
    res=['',''] & lin=5 ;label index
    tcerr=STRTRIM(dtest[i-5],2) ;used for type conversion error
    lv=dtest[i-5] ;data value
  ENDELSE
  IF N_ELEMENTS(res) EQ 2 THEN BEGIN
    valid=0 ;used to test for type conversion
    CASE ndm[vc,8] OF
      0:BEGIN
          ;do difference test to account for possible rounding errors
          IF abs(DOUBLE(lv)-DOUBLE(FIX(lv))) GE 1. THEN $
            STOP_WITH_ERROR,o3[3]+proname+vn[vc]+' (INTEGER): ',label[lin]+errtxt2[3]+STRTRIM(lv,2),lu,ds
          lv=FIX(lv)
        END
      1:BEGIN
          ;do difference test to account for possible rounding errors
          IF ABS(DOUBLE(lv)-DOUBLE(LONG(lv))) GE 1. THEN $
            STOP_WITH_ERROR,o3[3]+proname+vn[vc]+' (LONG): ',label[lin]+errtxt2[3]+STRTRIM(lv,2),lu,ds
          lv=LONG(lv)
        END
      2:BEGIN
          IF NOT FINITE(FLOAT(lv)) THEN $
            STOP_WITH_ERROR,o3[3]+proname+vn[vc]+' (REAL): ',label[lin]+errtxt2[3]+STRTRIM(lv,2),lu,ds
          lv=FLOAT(lv)
        END
      3:BEGIN
          IF NOT FINITE(DOUBLE(lv)) THEN $
            STOP_WITH_ERROR,o3[3]+proname+vn[vc]+' (DOUBLE): ',label[lin]+errtxt2[3]+STRTRIM(lv,2),lu,ds
          lv=DOUBLE(lv)
        END
    ENDCASE
    valid=1 ;type conversion OK if got to here
  ENDIF ELSE STOP_WITH_ERROR,o3[3]+proname+vn[vc]+': ',label[lin]+errtxt2[0]+tcerr,lu,ds
  CASE i OF
    0:BEGIN
        fv=lv & fvi=lvi ;VAR_FILL_VALUE value
      END
    1:BEGIN
        mnv=lv & mnvi=lvi ;VAR_VALID_MIN value
      END
    2:BEGIN
        mxv=lv & mxvi=lvi ;VAR_VALID_MAX value
      END
    3:BEGIN
        mnvs=lv & mnvsi=lvi ;VIS_SCALE_MIN value
      END
    4:BEGIN
        mxvs=lv & mxvsi=lvi ;VIS_SCALE_MAX value
      END
    ELSE:
  ENDCASE
ENDFOR

;Check that fill value is outside the VAR_VALID_MIN/MAX values and,
;if so, assign the fill value to the valid minimum or maximum value
mnvhold=mnv & mxvhold=mxv ;holding variables for VAR_VALID_MIN/MAX checks
IF fv LT mnv THEN mnv=fv $
ELSE IF fv GT mxv THEN mxv=fv $
ELSE STOP_WITH_ERROR,o3[3]+proname+vn[vc]+': ',errtxt2[2]+meta_arr[fvi]+'.',lu,ds

;Determine minimum and maximum values that can be printed using the VIS_FORMAT settings
fi=WHERE(STRPOS(meta_arr[vi[0]:N_ELEMENTS(meta_arr)-1],'VIS_FORMAT') NE -1)
vfi=fi[0]+vi[0]
res=STRSPLIT(meta_arr[vfi],' =',/Extract)
tcerr=meta_arr[vfi] ;used for type conversion error
IF N_ELEMENTS(res) EQ 2 THEN BEGIN
  vfs=STRUPCASE(res[1])
  test1=STRMID(vfS,0,1) & test2=STRMID(vfs,1)
  valid=0 ;test for type conversion
  CASE 1 OF
    ndm[vc,8] LE 1:BEGIN
        ok=test1 EQ 'I'
        wp=FIX(test2)
        IF wp EQ 0 THEN ok=0 $
        ELSE maxp=10L^wp-1 & minp=(10L^(wp-1)-1)*(-1)
        IF STRPOS(test2,'.') NE -1 THEN $ ;remove everything after decimal point
          vfs=STRMID(vfs,0,STRPOS(vfs,'.'))
      END
    ndm[vc,8] GE 2:BEGIN
        ok=(test1 EQ 'D') OR (test1 EQ 'E') OR (test1 EQ 'F')
        wp=FIX(test2)
        IF STRMID(test2,STRLEN(test2)-1,1) EQ '.' THEN BEGIN
          wd=0 & vfs=vfs+'0' & test2=test2+'0'
        ENDIF ELSE IF STRPOS(test2,'.') EQ -1 THEN BEGIN
          wd=0 & vfs=vfs+'.0' & test2=test2+'.0'
        ENDIF ELSE IF STRPOS(test2,'.') NE -1 THEN $
          wd=FIX(STRMID(test2,STRPOS(test2,'.')+1))
        IF test1 EQ 'E' THEN BEGIN
          mnd=8
          ;note that windows OS requires a minimum difference of eight spaces between w.d (e.g. e10.2)
          IF mnv GE 0. THEN mnd=mnd-1 ;all numbers are positive so do not need to account for '-' sign
        ENDIF ELSE IF mnv GE 0. THEN mnd=2 ELSE mnd=3
        IF wp-wd LT mnd THEN $
          STOP_WITH_ERROR,o3[3]+proname+vn[vc]+': ',errtxt2[5]+STRTRIM(mnd,2)+'): '+res[1],lu,ds
        mp=wp-(wd+1)
        IF (test1 EQ 'D') OR (test1 EQ 'F') THEN $
          maxp=(10.D^mp)-(10.D^(-wd)) & minp=((10.D^(mp-1))-(10.D^(-wd)))*(-1.)
      END
  ENDCASE
  valid=1 ;type conversion OK
  IF ok EQ 0 THEN STOP_WITH_ERROR,o3[3]+proname+vn[vc]+': ',res[0]+errtxt2[0]+meta_arr[vfi],lu,ds
  meta_arr[vfi]=res[0]+'='+vfs ;in the event that vfS value has changed in the above section
ENDIF ELSE STOP_WITH_ERROR,o3[3]+proname+vn[vc]+': ',res[0]+errtxt2[0]+meta_arr[vfi],lu,ds

;Convert all data values plus VAR_VALID_MIN/MAX, VIS_SCALE_MIN/MAX, and
;FILL_VALUE to formatted strings then back to numeric values to provide
;the required precision (to prevent rounding errors).
;For double precision convert to string using 'D' instead of 'F'
IF (ndm[vc,8] EQ 3) AND (test1 EQ 'F') THEN vfSx='(D'+test2+')' ELSE vfSx='('+vfS+')'
FOR lin=0,5 DO BEGIN
  CASE lin OF
    0:vhold=fv      ;VAR_FILL_VALUE
    1:vhold=mnvhold ;VAR_VALID_MIN
    2:vhold=mxvhold ;VAR_VALID_MAX
    3:vhold=mnvs    ;VIS_SCALE_MIN
    4:vhold=mxvs    ;VIS_SCALE_MAX
    5:BEGIN         ;Data values
        CASE ndm[vc,8] OF
          0:vhold=FIX(dtest)
          1:vhold=LONG(dtest)
          2:vhold=FLOAT(dtest)
          3:vhold=DOUBLE(dtest)
        ENDCASE
      END
  ENDCASE
  IF test1 NE 'E' THEN BEGIN ;check values can be printed using VIS_FORMAT
    IF (maxp LT MAX(vhold)) OR (minp GT MIN(vhold)) THEN BEGIN
      IF maxp LT MAX(vhold) THEN vhx=STRTRIM(MAX(vhold),2) ELSE vhx=STRTRIM(MIN(vhold),2)
      STOP_WITH_ERROR,o3[3]+proname+vn[vc]+': ',label[lin]+errtxt2[4]+'('+vfs+'): '+vhx,lu,ds
    ENDIF
  ENDIF
  ;convert to formatted string
  vholds=STRING(format=vfsx,vhold)
  ;convert back to numeric representation (with correct precision)
  CASE ndm[vc,8] OF
    0:vhold=FIX(vholds)
    1:vhold=LONG(vholds)
    2:vhold=FLOAT(vholds)
    3:vhold=DOUBLE(vholds)
  ENDCASE

  ;convert variable attributes and data to correct format and convert data to correct data type
  CASE lin OF
    0:BEGIN
        fvhold=vhold ;hold fill value to test VIS_SCALE_MIN/MAX
        meta_arr[fvi]=STRMID(meta_arr[fvi],0,STRPOS(meta_arr[fvi],'=')+1)+STRTRIM(vholds,2)
      END
    1:BEGIN
        mnvhold=vhold
        meta_arr[mnvi]=STRMID(meta_arr[mnvi],0,STRPOS(meta_arr[mnvi],'=')+1)+STRTRIM(vholds,2)
      END
    2:BEGIN
        mxvhold=vhold
        meta_arr[mxvi]=STRMID(meta_arr[mxvi],0,STRPOS(meta_arr[mxvi],'=')+1)+STRTRIM(vholds,2)
      END
    3:IF vhold NE fvhold THEN $ ;o/w VIS_SCALE_MIN value is not a number
        meta_arr[mnvsi]=STRMID(meta_arr[mnvsi],0,STRPOS(meta_arr[mnvsi],'=')+1)+STRTRIM(vholds,2)
    4:IF vhold NE fvhold THEN $ ;o/w VIS_SCALE_MAX value is not a number
        meta_arr[mxvsi]=STRMID(meta_arr[mxvsi],0,STRPOS(meta_arr[mxvsi],'=')+1)+STRTRIM(vholds,2)
    5:BEGIN
        dtest=vhold
        ;determine minimum and maximum data values are within VAR_VALID_MIN/MAX
        nfvi=WHERE(dtest NE fvhold,nfvcnt)
        IF nfvcnt NE 0 THEN BEGIN ;i.e. there are non-fill values in the data
          minv=MIN(dtest(nfvi),minvi,MAX=maxv,SUBSCRIPT_MAX=maxvi)
          IF minv LT mnvhold THEN $ ;Minimum data value is less than VAR_VALID_MIN
            STOP_WITH_ERROR,o3[3]+proname+vn[vc]+': ','Minimum'+errtxt2[1]+ $
                            'MIN: '+STRTRIM(vholds(nfvi(minvi)),2)+' ('+meta_arr[mnvi]+').',lu,ds
          IF maxv GT mxvhold THEN $ ;Maximum data value is greater than VAR_VALID_MAX
            STOP_WITH_ERROR,o3[3]+proname+vn[vc]+': ','Maximum'+errtxt2[1]+ $
                            'MAX: '+STRTRIM(vholds(nfvi(maxvi)),2)+' ('+meta_arr[mxvi]+').',lu,ds
        ENDIF
      END
  ENDCASE
ENDFOR

;Do Checks with DATETIME attribute
IF vn[vc] EQ 'DATETIME' THEN BEGIN
  ;Does DATETIME contain any fill values?
  di=WHERE(dtest EQ fv,dcnt)
  IF dcnt NE 0 THEN STOP_WITH_ERROR,o3[3]+proname,errtxt2[6]+STRTRIM(dtest[di[0]],2),lu,ds
  IF N_ELEMENTS(dtest) GT 1 THEN BEGIN
    ;Are DATETIME values in chronological order?
    dsort=SORT(dtest)
    IF ARRAY_EQUAL(dtest,dtest[dsort]) EQ 0 THEN STOP_WITH_ERROR,o3[3]+proname,errtxt2[7],lu,ds
  ENDIF
ENDIF

TypeConversionError:
IF valid EQ 0 THEN STOP_WITH_ERROR,o3[3]+proname+vn[vc]+': ',errtxt2[8]+tcerr,lu,ds
END ;procedure Check_Min_Max_Fill

PRO read_data, sds, inf
;Procedure to perform checks on the data file or structure. It looks for missing or invalid
;VAR_VALID_MIN/MAX and VIS_SCALE_MIN/MAX attribute values for datasets which have
;VAR_UNITS=MJD2000 (including start and stop times), and determines these values according to
;values in the data.  It also adds a comment to VAR_NOTES if averaging kernel data is present,
;to indicate the proper array order (if this option is chosen on program start-up).
; ----------
;Written by Ian Boyd, NIWA-ERI for the AVDC - i.boyd@niwa.com
;
;  History:
;    20050802: Original IDLCR8HDF routine - Version 1.0
;    20050909: Check that the input data file is not empty; test for the correct number of
;              datalines in the dataset being searched (e.g. there is not an additional data
;              line in the input file); Bug fix to save dtest as an array when there is only
;              one data value, and the input data is in a structure - Version 1.1
;    20061012: Portion of routine originally extracting a dataset from the file or structure
;              moved to the EXTRACT_DATA routine; Code added to determine missing or invalid
;              minimum or maximum metadata values for DATETIME, or START/STOP.TIME attributes;
;              Code added to append a comment to VAR_NOTES if averaging kernel data is present,
;              to indicate proper array order (if /AVK option selected); Checked data written
;              to the ds structure instead of a series of arrays set-up according to data type;
;              Common variable definition WIDGET_WIN added - Version 2.0
;    20080302: dtest initialized for the EXTRACT_DATA call - Version 3.0
;
;  Inputs: sds - Either a structure containing the Variable Attributes and Data, or the input
;                data file
;          inf - flag identifying SDS type where, 0=structure; 1=data file
;          meta_arr - a string array containing the Global and Variable Attributes
;
;  Outputs: meta_arr - completes missing or invalid metadata values for attributes with
;                      VAR_UNITS=MJD2000, as required, and also adds comments to VAR_NOTES
;                      for averaging kernel data to indicate proper array order, if this
;                      option is chosen
;           ds - checked datasets are written to the ds structure with correct dimensions and
;                data type
;
;  Called by: IDLCR8HDF
;
;  External Subroutines Called: EXTRACT_DATA
;                               CHECK_MIN_MAX_FILL

COMMON METADATA
COMMON DATA
COMMON WIDGET_WIN

ON_IOERROR,TypeConversionError ;used when checking Min and Max values, when VAR_UNITS=MJD2000

;Go through variable names and check for DATETIME, XX_XX_START.TIME, or XX_XX_STOP.TIME
;values and determine representative minimum and maximum datetime values
mint=90000.d & maxt=-90000.d ;initialize min and max datetimes
vchk=['VAR_VALID_MIN','VAR_VALID_MAX','VIS_SCALE_MIN','VIS_SCALE_MAX']
nvchk=N_ELEMENTS(vchk) & mmi=INTARR(nvchk)
FOR i=0,nvchk-1 DO BEGIN
  mi=WHERE(attr_arr_data EQ vchk[i]) & mmi[i]=mi[0]
ENDFOR
vavk=INTARR(nvn) ;holding array for AVK attribute (used below)
FOR vc=0,nvn-1 DO BEGIN
  dtest=['0'] ;initializing dtest array for EXTRACT_DATA call
  vnspl=STRSPLIT(vn[vc],'_',/Extract)
  IF N_ELEMENTS(vnspl) LT 3 THEN vnspl=[vnspl,'x','x'] ;add dummy values if necessary
  IF vnspl[2] EQ 'AVK' THEN vavk[vc]=1 ;identifies a dataset containing AVK data
  IF ((vn[vc] EQ 'DATETIME') OR (vnspl[2] EQ 'START.TIME') OR $
     (vnspl[2] EQ 'STOP.TIME')) AND (vu[vc] EQ 'MJD2000') THEN BEGIN
    EXTRACT_DATA,SDS,inf,vc,dtest ;return the dataset
    gi=WHERE(DOUBLE(dtest) NE DOUBLE(vfv[vc]),gcnt) ;edit out fill values
    IF gcnt NE 0 THEN BEGIN
      minth=MIN(DOUBLE(dtest[gi])) & maxth=MAX(DOUBLE(dtest[gi]))
      IF minth LT mint THEN mint=minth
      IF maxth GT maxt THEN maxt=maxth
      IF vnspl[2] EQ 'START.TIME' THEN mint=minth $
      ELSE IF vnspl[2] EQ 'STOP.TIME' THEN maxt=maxth
    ENDIF
  ENDIF
ENDFOR
;Add minimum and maximum values to Metadata as required
di=WHERE(STRUPCASE(vu) EQ 'MJD2000',dcnt)
IF dcnt NE 0 THEN BEGIN
  FOR vc=0,dcnt-1 DO BEGIN
    mi=WHERE(meta_arr EQ 'VAR_NAME='+vn[di[vc]])
    FOR i=0,nvchk-1 DO BEGIN
      dtadd=0 ;default - it is not necessary to add a Min or Max Datetime value
      res=STRSPLIT(meta_arr[mi[0]+mmi[i]],'=',/Extract) & nres=N_ELEMENTS(res)
      IF nres EQ 1 THEN dtadd=1 $ ;no value so add Min or Max value to this attribute
      ELSE BEGIN ;check that the value is valid
        ;Check for ISO8601 format and change to MJD2000 if necessary
        IF STRPOS(STRUPCASE(res[1]),'Z') NE -1 THEN BEGIN
          mjd2000=JULIAN_DATE(res[1],/I,/M) ;return time in MJD2000 format
          IF mjd2000 EQ -99999.d THEN res[1]='X'+res[1] $ ;conversion error so add dummy character
          ELSE BEGIN
            res[1]=STRTRIM(STRING(format='(d18.9)',mjd2000),2)
            meta_arr[mi[0]+mmi[i]]=res[0]+'='+res[1]
          ENDELSE
        ENDIF
        valid=0 ;to check for Type Conversion Error
        ci=DOUBLE(res[1])
        valid=1 ;if here then Type Conversion was OK, o/w TypeConversionError
        TypeConversionError:
        IF valid EQ 0 THEN BEGIN
          IF (i LE 1) OR ((i GE 2) AND (STRUPCASE(res[1]) NE 'NONE')) THEN dtadd=1
          ;not a valid value so add Min or Max value
        ENDIF
      ENDELSE
      IF dtadd EQ 1 THEN BEGIN ;add Min or Max value to Metadata label
        IF i MOD 2 EQ 0 THEN BEGIN
          IF mint NE 90000.d THEN meta_arr[mi[0]+mmi[i]]=vchk[i]+'='+STRTRIM(STRING(format='(d18.9)',mint),2)
        ENDIF ELSE IF maxt NE -90000.d THEN $
          meta_arr[mi[0]+mmi[i]]=vchk[i]+'='+STRTRIM(STRING(format='(d18.9)',maxt),2)
      ENDIF
    ENDFOR
  ENDFOR
ENDIF

IF o3[1] EQ 'AVK' THEN BEGIN ;append sentence to VAR_NOTES in the AVK attribute
  di=WHERE(vavk EQ 1,dcnt)
  vnc=STRARR(nvn) ;holding array for comment
  IF dcnt NE 0 THEN BEGIN
    FOR vc=0,dcnt-1 DO BEGIN
      ;find relevant VAR_NOTES attribute label
      mi=WHERE(meta_arr EQ 'VAR_NAME='+vn[di[vc]])
      xi=WHERE(attr_arr_data EQ 'VAR_NOTES')
      eqpos=STRPOS(meta_arr[mi[0]+xi[0]],'=')
      vnc[di[vc]]=STRTRIM(STRMID(meta_arr[mi[0]+xi[0]],eqpos+1),2) ;existing VAR_NOTES comment, if any
      ;if comment exists, check to see if it ends with a period, if not add one
      IF vnc[di[vc]] NE '' THEN BEGIN
        IF STRMID(vnc[di[vc]],STRLEN(vnc[di[vc]])-1,1) NE '.' THEN $
          vnc[di[vc]]=vnc[di[vc]]+'. ' ELSE vnc[di[vc]]=vnc[di[vc]]+' '
      ENDIF
      ;determine the number of dimensions
      ni=WHERE(ndm[di[vc],0:7] NE 0,ndim)
      IF ndim GT 2 THEN vncx='for the first measurement are:' ELSE vncx='are:'
      ;append comment
      vnc[di[vc]]=vnc[di[vc]]+'First three AVK values for the lowest altitude level '+vncx
      ;append meta_arr index to comment
      vnc[di[vc]]=STRTRIM(mi[0]+xi[0],2)+'_'+vnc[di[vc]]
    ENDFOR
  ENDIF
ENDIF

vfsx='' ;returns VIS_FORMAT string in CHECK_MIN_MAX_FILL
FOR vc=0,nvn-1 DO BEGIN
  dtest=['0'] ;initializing dtest array for EXTRACT_DATA call
  ;Extract the dataset
  EXTRACT_DATA,sds,inf,vc,dtest,ndl
  ;perform checks on the data values
  CHECK_MIN_MAX_FILL,vc,dtest,ndl,vfsx

  ;if AVK dataset extracted then add comment to VAR_NOTES if required
  IF o3[1] EQ 'AVK' THEN BEGIN
    IF (vnc[vc] NE '') AND (ndm[vc,0] GE 3) THEN BEGIN
      res=STRSPLIT(vnc[vc],'_',/Extract) & mi=FIX(res[0])
      meta_arr[mi]='VAR_NOTES='+res[1]
      FOR i=0,2 DO meta_arr[mi]=meta_arr[mi]+' '+STRTRIM(STRING(format=vfsx,dtest[i]),2)
    ENDIF
  ENDIF

  ;Copy data to the data structure with correct dimensions and data type
  arrchk=SIZE(dtest) ;put it into array form if it is a scalar
  IF arrchk[0] EQ 0 THEN dtest=[dtest]
  gi=WHERE(ndm[vc,0:7] NE 0)
  vs=TRANSPOSE(ndm[vc,gi])
  dtest=REFORM(dtest,vs,/Overwrite)
  ds[vc].data=PTR_NEW(dtest)
ENDFOR

END ;Procedure Read_Data

PRO find_hdf_filename, hdffilename
;Procedure to do the following:
;1. Compare the DATA_START_DATE (if present) with the first entry under DATETIME. If necessary
;   create/change DATA_START_DATE to match the DATETIME entry, and put in ISO8601 format.
;2. Compare filename created by the program from the DATASET_ATTRIBUTES with that under the
;   FILE_NAME entry (if present). If necessary create/change the FILE_NAME entry to match the
;   created filename
;3. Create/change the FILE_GENERATION_DATE and ensure it is in ISO8601 format.
; ----------
;Written by Ian Boyd, NIWA-ERI for the AVDC - i.boyd@niwa.com
;
;  History:
;    20050802: Original IDLCR8HDF routine - Version 1.0
;    20050909: Remove requirement for a 'v' to be at the start of the DATA_FILE_VERSION
;              (e.g. v1.0 or 1.0 acceptable); Change the format that DATA_START_DATE and
;              FILE_GENERATION_DATE values are saved in the HDF file from MJD2000 to
;              ISO8601 - Version 1.1
;    20051107: Move VIS_SCALE_MIN/MAX checks, for correct format of DATETIME values, to the
;              CHECK_MIN_MAX_FILL routine - Version 1.11
;    20061012: Move the FILE_GENERATION_DATE checks to the main loop which determines the file
;              name to facilitate the future option of including the file generation date in the
;              file name (this option is not currently implemented by the AVDC); Common variable
;              definition WIDGET_WIN added - Version 2.0
;    20080302: Do search for either DATA_LEVEL or DATA_TYPE, depending on whether an AVDC style
;              TAV file or original Envisat table.dat file has been read in; Common variable
;              definition TABLEDATA added (tab_type used to differentiate between the databases)
;              - Version 3.0
;
;  Inputs: meta_arr - a string array containing the Global and Variable Attributes
;          hfdfilename - a string holding the extension of the eventual HDF filename, either
;                        '.hdf' for HDF4 or '.h5' for HDF5
;
;  Outputs: meta_arr - DATA_START_DATE, FILE_GENERATION_DATE and FILE_NAME values will be
;                      (re)written to the array based on values computed by the routine
;           hdffilename - a string holding the full HDF file name determined by the routine
;
;  Called by: IDLCR8HDF
;
;  External Subroutines Called: STOP_WITH_ERROR (if error state detected)
;    Possible Conditions for STOP_WITH_ERROR call (plus [line number] where called):
;      1. An incorrect number of sub-values found in meta_arr for DATA_DISCIPLINE,
;         DATA_SOURCE, DATA_LOCATION, or DATA_FILE_VERSION [2134]
;      2. Error encountered when converting ISO8601 datetime values to MJD2000 format [2141, 2174]
;      3. Type conversion error encountered when a datetime value is expected to be in
;         MJD2000 format, but is not numeric [2210]
;
;    Warning Conditions (when the program is able to make changes):
;      1. DATA_START_DATE changed to match first DATETIME entry [2156]
;      2. Filename determined from DATASETS attributes [2197]

COMMON TABLEDATA
COMMON METADATA
COMMON DATA
COMMON WIDGET_WIN

;Possible error messages for this procedure
ON_IOERROR,TypeConversionError
errtxt=STRARR(4) & lu=-1L
proname='Find_HDF_Filename procedure: '
errtxt[0]='Incorrect number of sub-values under DATA_'
errtxt[1]=' (Should be '
errtxt[2]=' ISO8601 format not valid: '
errtxt[3]='Type conversion error.  MJD2000 entry is not valid: '

fhold='' & iso='' & mjd2000=0.d
dtmjd=0.d & dtiso=''
valid=0 ;test for Type conversion errors
FOR i=0,6 DO BEGIN
  CASE i OF
    0:dt='DISCIPLINE'
    1:dt='SOURCE'
    2:dt='LOCATION'
    3:IF tab_type EQ 0 THEN dt='LEVEL' ELSE dt='TYPE'
    4:dt='START_DATE'
    5:dt='FILE_GENERATION_DATE'
    6:dt='FILE_VERSION'
  ENDCASE
  IF i EQ 0 THEN BEGIN
    nei=4 & nes='3)'
  ENDIF ELSE BEGIN
    nei=2 & nes='1)'
  ENDELSE
  IF i EQ 5 THEN ai=WHERE(attr_arr_glob EQ dt) $
  ELSE ai=WHERE(attr_arr_glob EQ 'DATA_'+dt)
  res=STRSPLIT(meta_arr[ai[0]],' =;',/Extract)
  IF (N_ELEMENTS(res) NE nei) AND (i NE 4) AND (i NE 5) THEN $
    STOP_WITH_ERROR,o3[3]+proname+meta_arr[ai[0]]+': ',errtxt[0]+dt+errtxt[1]+nes,lu,ds
  IF i EQ 4 THEN BEGIN ;calculate the ISO or MJD2000 value as required
    IF N_ELEMENTS(res) EQ nei THEN BEGIN ;a date/time value is present
      IF STRPOS(STRUPCASE(res[1]),'Z') NE -1 THEN BEGIN
        res[1]=STRUPCASE(res[1])
        mjd2000=JULIAN_DATE(res[1],/I,/M) ;return ISO8601 time in MJD2000 format
        IF mjd2000 EQ -99999.d THEN $ ;conversion error
          STOP_WITH_ERROR,o3[3]+proname,res[0]+errtxt[2]+res[1],lu,ds
      ENDIF ELSE BEGIN
        mjd2000=DOUBLE(res[1])
        iso=JDF_2_DATETIME(mjd2000,/M,/S) ;return MJD2000 time in ISO8601 format
        res[1]=iso
      ENDELSE
    ENDIF ELSE mjd2000=-999999.0d
    ;check that DATA_START_DATE matches the first value under DATETIME
    di=WHERE(vn EQ 'DATETIME')
    darr=*ds[di[0]].data ;extract the DATETIME values
    dtmjd=darr[0]
    dtiso=JDF_2_DATETIME(dtmjd,/M,/S) ;returns datetime in ISO8601 format
    IF ABS(mjd2000-dtmjd) GE 1./86400. THEN BEGIN
      IF N_ELEMENTS(res) EQ nei THEN BEGIN
        warntxt=STRARR(3) & lineno=lineno+3L
        warntxt[0]='  WARNING: Date in DATA_START_DATE does not match first DATETIME entry'
        warntxt[1]='  '+res[1]+' -> '+dtiso
        warntxt[2]='  DATA_START_DATE changed to match first DATETIME entry'
        FOR j=0,2 DO BEGIN
          IF o3[3] EQ '' THEN WIDGET_CONTROL,wtxt,set_value=warntxt[j],/Append,Set_text_top_line=lineno
          FOR m=dux[0],dux[1],dux[2] DO PRINTF,m,warntxt[j] ;write out to log
        ENDFOR
      ENDIF
      res=[res[0],dtiso] ;new ISO time for filename
    ENDIF
    meta_arr[ai[0]]=res[0]+'='+res[1] ;rewrite (correct) value in meta_arr in ISO8601 format
  ENDIF
  IF i EQ 5 THEN BEGIN
    ;check format of FILE_GENERATION_DATE value and create/change if necessary
    IF N_ELEMENTS(res) EQ 1 THEN res=[res,'-1']
    IF STRPOS(STRUPCASE(res[1]),'Z') NE -1 THEN BEGIN
      mjd2000=JULIAN_DATE(res[1],/I,/M) ;return time in MJD2000 format
      IF mjd2000 EQ -99999.d THEN $ ;conversion error
        STOP_WITH_ERROR,o3[3]+proname,res[0]+errtxt[2]+res[1],lu,ds
      meta_arr[ai[0]]=res[0]+'='+STRTRIM(STRUPCASE(res[1]),2) ;rewrite date in ISO8601 format
    ENDIF ELSE BEGIN ;FILE_GENERATION_DATE not present or in MJD2000 format
      IF res[1] EQ '-1' THEN mjd2000=SYSTIME(/Julian,/UTC)-2451544.5D $
      ELSE mjd2000=DOUBLE(res[1]) ;will cause type conversion error if it is not a number
      mjds=JDF_2_DATETIME(mjd2000,/M,/S) ;returns datetime in ISO8601 format
      meta_arr[ai[0]]=res[0]+'='+mjds ;write date in ISO8601 format
      res[1]=mjds
    ENDELSE
  ENDIF
  IF i NE 5 THEN BEGIN ;i.e. don't include file generation date in the filename
    IF fhold EQ '' THEN fhold=fhold+STRLOWCASE(res[nei-1]) $
    ELSE fhold=fhold+'_'+STRLOWCASE(res[nei-1])
  ENDIF
ENDFOR
hdffilename=fhold+hdffilename
;check to see if the resulting filename is the same as that under FILE_NAME
ai=WHERE(attr_arr_glob EQ 'FILE_NAME')
res=STRSPLIT(meta_arr[ai[0]],' =',/Extract)
IF N_ELEMENTS(res) EQ 1 THEN res=[res,'-1']
IF hdffilename NE STRLOWCASE(res[1]) THEN BEGIN
  IF res[1] NE '-1' THEN BEGIN
    warntxt=STRARR(3) & lineno=lineno+3L
    warntxt[0]='  WARNING: Filename determined from DATASET ATTRIBUTES does not match FILE_NAME entry under FILE ATTRIBUTES'
    warntxt[1]='  '+hdffilename+' -> '+STRLOWCASE(res[1])
    warntxt[2]='  Filename determined from DATASET ATTRIBUTES used'
    FOR j=0,2 DO BEGIN
      IF o3[3] EQ '' THEN WIDGET_CONTROL,wtxt,set_value=warntxt[j],/Append,Set_text_top_line=lineno
      FOR m=dux[0],dux[1],dux[2] DO PRINTF,m,warntxt[j] ;write out to log
    ENDFOR
  ENDIF
  meta_arr[ai[0]]=res[0]+'='+hdffilename ;change FILE_NAME entry
ENDIF
valid=1 ;Type conversions performed OK

TypeConversionError:
IF valid EQ 0 THEN STOP_WITH_ERROR,o3[3]+proname+res[0]+': ',errtxt[3]+res[1],lu,ds
END ;Procedure Find_HDF_Filename

PRO makean, hdf_fn, obj_id, obj_type, label, value
;Procedure to read the contents of a file given as an attribute value and write
;it directly to the HDF Scientific Dataset or to the annotation file description
;section (depending on its size). Currently set up to always write the contents
;of the file to HDF_SD as the text file size limit is also 4096 bytes.
; ----------
;Written by Ian Boyd, NIWA-ERI for the AVDC - i.boyd@niwa.com
;
;  History:
;    20050802: Original IDLCR8HDF routine - Version 1.0
;
;  Inputs: hdf_fn - The file handle identifier for the HDF file
;          obj_id - The Scientific Dataset (SD) identifier of the HDF file or a newly
;                   created dataset
;          obj_type - a string identifying the type of annotation (set to 'FILE')
;          label - a string containing the Attribute label from the metadata
;          value - a string containing the Attribute value from the metadata
;
;  Output: Nil
;
;  Called by: AVDC_HDF_WRITE
;
;  External Subroutines Called: None

nchar=4096 ;strlen maximum for writing directly to SD (instead of AN)

;Extract name of file
pos1=STRPOS(value,'"')
pos2=STRPOS(value,'"',/REVERSE_SEARCH)
data_notes_file=STRMID(value,pos1+1,pos2-pos1-1)
OPENR,lu,data_notes_file,/GET_LUN

;read each line and count number of characters in file
dum='' & text_in_file=''
i=0
WHILE NOT EOF(lu) DO BEGIN
  READF,lu,dum
  text_in_file=text_in_file+dum+STRING(10B)  ;+STRING(13B)
  i=i+1
ENDWHILE
FREE_LUN,lu
n1=STRLEN(text_in_file)

;The text-buffer contains n1 characters which will be written either
;to the annotation file description section or directly to the HDF file as
;the value of the current attribute
IF n1 LE nchar THEN HDF_SD_ATTRSET,obj_id,label,text_in_file,n1,/DFNT_CHAR $
ELSE BEGIN
  ;open the HDF output file for AN write operations
  ;Initialize the HDF AN interface for the specified file
  an_id=HDF_AN_START(hdf_fn)

  IF obj_type EQ 'FILE' THEN BEGIN
    ;Get id for the file label annotation
    an_labl_id=HDF_AN_CREATEF(an_id,2)
    ;Get id for the file description annotation
    an_desc_id=HDF_AN_CREATEF(an_id,3)
  ENDIF ELSE BEGIN
    ;Get id for the data object label annotation
    an_labl_id=HDF_AN_CREATE(an_id,DFTAG_NDG,HDF_SD_IDTOREF(obj_id),0)
    ;Get id for the data object description annoatation
    an_desc_id=HDF_AN_CREATE(an_id,DFTAG_NDG,HDF_SD_IDTOREF(obj_id),1)
  ENDELSE

  ;Write the label annotation
  status=HDF_AN_WRITEANN(an_labl_id,label)
  ;Terminate access to the label annotation
  HDF_AN_ENDACCESS,an_labl_id

  ;Write the description annotation
  status=HDF_AN_WRITEANN(an_desc_id,text_in_file)
  ;Terminate access to the description annotation
  HDF_AN_ENDACCESS,an_desc_id

  ;Terminate access to the AN interface
  HDF_AN_END,an_id
ENDELSE

END ;Procedure MakeAN

PRO avdc_hdf5_write, hdffilename, natts, av, var_depend
;IDL subroutine to write AVDC-type global attributes and datasets to the Root
;Group in an HDF5 file.
;##########################################################################
;Note: In versions of IDL earlier than 6.2, this procedure may not compile
;and the HDF5 write option will not be available.
;##########################################################################
; ----------
;Written by Ian Boyd, NIWA-ERI for the AVDC - i.boyd@niwa.com
;
;  History:
;    20061012: Introduced to IDLCR8HDF - Version 2.0
;    20080302: Removed portion of the code which created the DIMENSIONLIST attribute
;              containing the object reference pointers to the variables referred to
;              in the VAR_DEPEND values - Version 3.0
;
;  Inputs: hdffilename - a string holding the name of the HDF file being created
;                        and written to
;          natts - an integer giving the number of Variable Attributes in each
;                  dataset (i.e. the number of elements in attr_arr_data)
;          av - a string array of size (nvn,natts+7) containing the attribute and
;               NCSA values for each dataset
;          var_depend - a string array of size (nvn) holding the VAR_DEPEND attribute
;                       values, used to assign DIM information
;          ds - structure containing the data
;          meta_arr - a string array containing the Global and Variable Attributes
;          vn - a string array containing the VAR_NAME values
;          ndm - a long array describing the dimensions of each dataset and the
;                VAR_DATA_TYPE
;
;  Outputs: An HDF5 file with the name hdffilename
;
;  Called by: AVDC_HDF_WRITE
;
;  External Subroutines Called: None

COMMON METADATA
COMMON DATA

;Create an HDF5 file
hdf_file_id=H5F_CREATE(hdffilename)
H5F_CLOSE, hdf_file_id

;open the HDF5 file for writing.
hdf_file_id=H5F_OPEN(hdffilename,/WRITE)
;The H5G_OPEN function opens an existing group within an HDF5 file
sd_id=H5G_OPEN(hdf_file_id,'/')

;Write the Global Attributes to file
;Section 4.1 (Bojkov et al., 2002): Originator attributes
;Section 4.2 (Bojkov et al., 2002): Dataset attributes
;Section 4.3 (Bojkov et al., 2002): File attributes
FOR i=0,N_ELEMENTS(attr_arr_glob)-1 DO BEGIN
  ;separate out the Meta Attribute entry
  eqpos=STRPOS(meta_arr[i],'=')
  IF eqpos EQ STRLEN(meta_arr[i])-1 THEN attribute_value=' ' $
  ELSE attribute_value=STRMID(meta_arr[i],eqpos+1)
  ;get attribute type and space, needed to create the attribute
  atype_id=H5T_IDL_CREATE(attribute_value)
  aspace_id=H5S_CREATE_SCALAR()
  ;create attribute in the output file
  aset_id=H5A_CREATE(sd_id,attr_arr_glob[i],atype_id,aspace_id)
  ;write attribute to dataset
  H5A_WRITE,aset_id,attribute_value
  ;close all open identifiers
  H5A_CLOSE,aset_id
  H5S_CLOSE,aspace_id
  H5T_CLOSE,atype_id
ENDFOR

;Write the Datasets (SDS) to file
;Section 5.1 (Bojkov et al, 2002): Variable description attributes.
;Section 5.2 (Bojkov et al, 2002): Variable visualisation attributes.
al=STRARR(natts+6)
al[0:natts-1]=attr_arr_data & al[natts:natts+5]=NCSA
FOR i=0,nvn-1 DO BEGIN
  CASE ndm[i,8] OF ;identifies the VAR_DATA_TYPE
    0:BEGIN
        valid_range=[FIX(av[i,natts+4]),FIX(av[i,natts+5])]
        fill_value=FIX(av[i,natts+6])
      END
    1:BEGIN
        valid_range=[LONG(av[i,natts+4]),LONG(av[i,natts+5])]
        fill_value=LONG(av[i,natts+6])
      END
    2:BEGIN
        valid_range=[FLOAT(av[i,natts+4]),FLOAT(av[i,natts+5])]
        fill_value=FLOAT(av[i,natts+6])
      END
    3:BEGIN
        valid_range=[DOUBLE(av[i,natts+4]),DOUBLE(av[i,natts+5])]
        fill_value=DOUBLE(av[i,natts+6])
      END
  ENDCASE

  ;Make up data array for writing to HDF5 file
  data=*ds[i].data
  ;may have lost array VAR_SIZE information if the last value is '1' e.g. 41,1
  gi=WHERE(ndm[i,0:7] NE 0) & vs=TRANSPOSE(ndm[i,gi])
  ;get dataset type and space, needed to create the dataset
  dtype_id=H5T_IDL_CREATE(data)
  dspace_id=H5S_CREATE_SIMPLE(vs)
  ;create dataset in the output file
  dset_id=H5D_CREATE(sd_id,vn[i],dtype_id,dspace_id)
  ;write data to dataset
  H5D_WRITE,dset_id,data

  ;write out variable attributes
  FOR j=0,natts-1 DO BEGIN ;natts+5 do begin ;use commented bit if keeping the HDF4 predefined attributes
    IF j EQ natts+4 THEN athold=valid_range $
    ELSE IF j EQ natts+5 THEN athold=[fill_value] $
    ELSE athold=av[i,j]
    ;get attribute type and space, needed to create the attribute
    atype_id=H5T_IDL_CREATE(athold)
    IF j LE natts+3 THEN aspace_id=H5S_CREATE_SCALAR() $
    ELSE aspace_id=H5S_CREATE_SIMPLE(SIZE(athold,/DIMENSIONS))
    ;create attribute in the output file
    aset_id=H5A_CREATE(dset_id,al[j],atype_id,aspace_id)
    ;write attribute to dataset
    H5A_WRITE,aset_id,athold
    ;close all open attribute identifiers
    H5A_CLOSE,aset_id
    H5S_CLOSE,aspace_id
    H5T_CLOSE,atype_id
  ENDFOR
  ;close all open dataset identifiers
  H5D_CLOSE,dset_id
  H5S_CLOSE,dspace_id
  H5T_CLOSE,dtype_id
ENDFOR

;The H5G_CLOSE procedure closes the specified group and releases resources used by it.
H5G_CLOSE,sd_id
;The H5F_CLOSE procedure closes the HDF file associated with the given file handle.
H5F_CLOSE,hdf_file_id

END ;Procedure AVDC_HDF5_Write

PRO avdc_hdf_write, hdffilename
;IDL subroutine to write AVDC-type global attributes and datasets to a Scientific
;Dataset (SDS) in HDF4 (Bojkov et al.,2004) or HDF5. This routine creates the HDF4
;file if requested, and calls the AVDC_HDF5_WRITE routine to create the HDF5 file if
;that option is chosen.
; ----------
;Written by Ian Boyd, NIWA-ERI for the AVDC - i.boyd@niwa.com
;
;  History:
;    20050802: Originally split into two routines to separately write the Global
;              attributes (AVDC_HDF_GLOBAL_ATTRIBUTES) and the Variable attributes
;              and data (AVDC_HDF_SDS_VARIABLES) to an HDF4 file - Version 1.0
;    20061012: Two routines combined into the AVDC_HDF_WRITE routine; Call to the
;              AVDC_HDF5_WRITE routine if HDF5 file option is chosen; HDF4 file not
;              closed between writing the global and variable attributes to file;
;              Data now written to file from the ds structure instead of from arrays
;              made according to VAR_DATA_TYPE; the av array made to hold attribute
;              values to write to the HDF file, instead of writing the values to
;              file directly from the meta_arr array (to avoid duplication of code
;              in AVDC_HDF5_WRITE) - Version 2.0
;
;  Inputs: hdffilename - a string holding the name of the HDF file being created
;                        and written to
;          ds - structure containing the data
;          meta_arr - a string array containing the Global and Variable Attributes
;          vn - a string array containing the VAR_NAME values
;          ndm - a long array describing the dimensions of each dataset and the
;                VAR_DATA_TYPE
;
;  Outputs: An HDF4 file with the name hdffilename
;
;  Called by: IDLCR8HDF
;
;  External Subroutines Called: MAKEAN
;                               AVDC_HDF5_WRITE (if HDF5 option is chosen)

COMMON METADATA
COMMON DATA

;Determine HDF format (ftype is either 'hdf' or 'h5')
ftype=STRMID(hdffilename,STRPOS(hdffilename,'.',/Reverse_Search)+1)

;List of dim. attributes to be assigned to the SDS dimensions.
da_list=['VIS_FORMAT','VIS_LABEL','VAR_NAME','VAR_UNITS']
da_att=INDGEN(N_ELEMENTS(da_list),/String) & natts=N_ELEMENTS(attr_arr_data)
av=STRARR(nvn,natts+7) ;array containing attribute and NCSA values for each dataset
var_depend=STRARR(nvn) ;holding array for the VAR_DEPEND attributes, used to assign DIM information

;Write all Dataset Attribute values to an array
FOR i=0,nvn-1 DO BEGIN ;nvn EQ number of variable names
  mi=WHERE(meta_arr EQ attr_arr_data[0]+'='+vn[i])
  av[i,natts+1]=vu[i]
  FOR j=0,natts-1 DO BEGIN
    ma=mi[0]+j
    ;separate out the Meta entry into two components
    eqpos=STRPOS(meta_arr[ma],'=')
    res=STRMID(meta_arr[ma],0,eqpos)
    IF eqpos EQ STRLEN(meta_arr[ma])-1 THEN av[i,j]=' ' $
    ELSE av[i,j]=STRMID(meta_arr[ma],eqpos+1)
    IF res EQ 'VAR_DEPEND' THEN var_depend[i]=av[i,j]
    ;collect additional information for HDF_SD_SETINFO
    IF res EQ 'VAR_VALID_MIN' THEN av[i,natts+4]=av[i,j]
    IF res EQ 'VAR_VALID_MAX' THEN av[i,natts+5]=av[i,j]
    IF res EQ 'VAR_FILL_VALUE' THEN av[i,natts+6]=av[i,j]
    IF res EQ 'VIS_LABEL' THEN av[i,natts]=av[i,j]
    IF res EQ 'VIS_FORMAT' THEN av[i,natts+2]=av[i,j]
    IF res EQ 'VIS_SCALE_TYPE' THEN av[i,natts+3]=av[i,j]
  ENDFOR
ENDFOR

IF ftype EQ 'hdf' THEN BEGIN
  ;Create and open the HDF4 file for writing
  hdf_file_id=HDF_OPEN(hdffilename,/ALL)
  ;The HDF_SD_START function opens or creates an HDF4 file and initializes the SD interface.
  sd_id=HDF_SD_START(hdffilename,/RDWR)

  ;Write the Global Attributes to file
  ;Section 4.1 (Bojkov et al., 2002): Originator attributes
  ;Section 4.2 (Bojkov et al., 2002): Dataset attributes
  ;Section 4.3 (Bojkov et al., 2002): File attributes
  FOR i=0,N_ELEMENTS(attr_arr_glob)-1 DO BEGIN
    ;separate out the Meta Attribute entry
    eqpos=STRPOS(meta_arr[i],'=')
    IF eqpos EQ STRLEN(meta_arr[i])-1 THEN attribute_value=' ' $
    ELSE attribute_value=STRMID(meta_arr[i],eqpos+1)
    ;check for Free text attribute, and filename entry
    res=STRMID(meta_arr[i],0,eqpos)
    fai=WHERE(res EQ free_attr,facnt)
    IF (facnt NE 0) AND (STRMID(STRUPCASE(attribute_value),0,6) EQ 'FILE("') THEN $
      MAKEAN,hdf_file_id,sd_id,'FILE',res,attribute_value $
    ELSE HDF_SD_ATTRSET,sd_id,attr_arr_glob[i],attribute_value
  ENDFOR

  ;Write the Datasets (SDS) to file
  ;Section 5.1 (Bojkov et al, 2002): Variable description attributes.
  ;Section 5.2 (Bojkov et al, 2002): Variable visualisation attributes.
  FOR i=0,nvn-1 DO BEGIN ;nvn EQ number of variable names
    ;create data array from structure and create and define dataset for an HDF4 file
    data=*ds[i].data
    ;may have lost array VAR_SIZE information if the last value is '1' e.g. 41,1
    gi=WHERE(ndm[i,0:7] NE 0) & vs=TRANSPOSE(ndm[i,gi])
    type=HDF_IDL2HDFTYPE(SIZE(data,/Type))
    sds_id_1=HDF_SD_CREATE(sd_id,vn[i],vs,HDF_TYPE=type)

    mi=WHERE(meta_arr EQ attr_arr_data[0]+'='+vn[i])
    FOR j=0,natts-1 DO BEGIN
      ma=mi[0]+j
      ;Extract Attribute Label
      eqpos=STRPOS(meta_arr[ma],'=')
      res=STRMID(meta_arr[ma],0,eqpos)
      ;check for Free text attribute, and filename entry
      fai=WHERE(res EQ free_attr,facnt)
      IF (facnt NE 0) AND (STRMID(STRUPCASE(av[i,j]),0,6) EQ 'FILE("') THEN $
        MAKEAN,hdf_file_id,sds_id_1,'FILE',res,av[i,j] $
      ELSE HDF_SD_ATTRSET,sds_id_1,attr_arr_data[j],av[i,j]
    ENDFOR
    CASE ndm[i,8] OF ;identifies the VAR_DATA_TYPE
      0:BEGIN
          valid_range=[FIX(av[i,natts+4]),FIX(av[i,natts+5])]
          fill_value=FIX(av[i,natts+6])
        END
      1:BEGIN
          valid_range=[LONG(av[i,natts+4]),LONG(av[i,natts+5])]
          fill_value=LONG(av[i,natts+6])
        END
      2:BEGIN
          valid_range=[FLOAT(av[i,natts+4]),FLOAT(av[i,natts+5])]
          fill_value=FLOAT(av[i,natts+6])
        END
      3:BEGIN
          valid_range=[DOUBLE(av[i,natts+4]),DOUBLE(av[i,natts+5])]
          fill_value=DOUBLE(av[i,natts+6])
        END
    ENDCASE
    ;Set information about the dataset
    HDF_SD_SETINFO,sds_id_1,label=av[i,natts],unit=vu[i],format=av[i,natts+2], $
      coordsys=av[i,natts+3],range=valid_range,fill=fill_value

    ;Assign the VAR_DEPEND dimension(s) attribute information
    vd_out=STRSPLIT(var_depend[i],' ;',/Extract) ;Read the variable dependencies
    vdim=N_ELEMENTS(vd_out)
    vd_out=STRTRIM(vd_out,2)
    ;Start search of information
    FOR j=0,vdim-1 DO BEGIN
      IF (STRUPCASE(vd_out[j]) NE 'CONSTANT') AND (STRUPCASE(vd_out[j]) NE 'INDEPENDENT') THEN BEGIN
        ;Return the index of the var_depend array SDS dataset
        index=HDF_SD_NAMETOINDEX(sd_id,vd_out[j])
        ;Access the dataset
        sds_id_tmp=HDF_SD_SELECT(sd_id,index)
        FOR k=0,N_ELEMENTS(da_list)-1 DO BEGIN
          ;Find the dataset attribute in da_list
          dindex=HDF_SD_ATTRFIND(sds_id_tmp,da_list[k])
          ;Read attribute info and assign to da_att
          HDF_SD_ATTRINFO,sds_id_tmp,dindex,DATA=att_data
          da_att[k]=att_data
        ENDFOR
        ;End access of SDS search
        HDF_SD_ENDACCESS,sds_id_tmp

        ;Assign dimension to the current SDS
        dim_id=HDF_SD_DIMGETID(sds_id_1,j)
        HDF_SD_DIMSET,dim_id,/BW_INCOMP,FORMAT=da_att[0],LABEL=da_att[1],$
          NAME=da_att[2],UNIT=da_att[3]
      ENDIF
    ENDFOR

    ;Write data to HDF4 file
    HDF_SD_ADDDATA,sds_id_1,data
    HDF_SD_ENDACCESS,sds_id_1
  ENDFOR

  ;The HDF_SD_END function closes the SD interface to an HDF4 file.
  HDF_SD_END,sd_id
  ;The HDF_CLOSE procedure closes the HDF4 file associated with the given file handle.
  HDF_CLOSE,hdf_file_id
ENDIF ELSE AVDC_HDF5_WRITE,hdffilename,natts,av,var_depend

END ;Procedure AVDC_HDF_Write

PRO idlcr8hdf, ga, sds, tav, H5=o1, AVK=o2, LOG=o4, POPUP=o5
;Main IDL program to create HDF4 or HDF5 format files for submission to the AVDC, NDACC, or
;NILU (ESA Envisat) databases.
;
;Program documentation, Readme_IDLcr8HDF.pdf, available from http://avdc.gsfc.nasa.gov.
;
;Program sub-version 3.05 (20081020)
; ----------
;Written by Ian Boyd, NIWA-ERI for the AVDC - i.boyd@niwa.com
;
;  History:
;    20050802: Original Release - Version 1.0
;    20050909: No change to routine - Version 1.1
;    20051107: No change to routine - Version 1.11
;    20061012: 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 command line keyword options, remove the /Help (window now
;              opened if there are no command line parameters) and /File options (now automatically
;              identifies whether input is from session memory or files), and add the /H5 (write
;              HDF5), /AVK (for Averaging Kernel Datasets add sentence to VAR_NOTES indicating
;              averaging kernel order), /Log (append input/output information to a log file), and
;              /Popup options (append input/output, error and warning information to a pop-up
;              display window); If input is from files, the program can now handle multiple data
;              files (either as a string array or as a file spec), together with a Metadata template
;              file and the TAV file; Input/output information as well as errors and warnings
;              included in the IDL DE output log window and/or to a log file; The user has the
;              option of continuing to run the program with file inputs from the 'Introduction'
;              window; Common variable definition WIDGET_WIN added - 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 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
;
;  Inputs: DIALOG PROMPTS WITH IDL VIRTUAL MACHINE (File input only)
;            Metadata template file - The program will fill in missing spaces based on data file and
;                                     TAV inputs
;            Data file(s) - the user has the option of picking multiple data files
;            tav - the current Table Attribute Values (TAV) file.
;
;          COMMAND LINE PARAMETER OPTION AVAILABLE WITH FULL IDL VERSION
;            ga - either a string array containing the Global Attributes, or a Metadata template file
;                 (as above).
;            sds - either a heap structure using pointers containing the Data (DS.Data) and the Variable
;                  Attributes (DS.VA) for a single file, or a string array or file spec of file(s)
;                  containing the data (as above).
;            tav - the current Table Attribute Values (TAV) file.
;
;          OPTIONS
;            H5 - output will be as an HDF5 file instead of the standard HDF4
;            AVK - to add a sentence to VAR_NOTES indicating array order for Averaging Kernel
;                  datasets
;            Log - to append input/output information as well as warnings and errors to a log file
;                  named idlcr8hdf.log
;            Popup - to append input/output information as well as warnings and errors to a pop-up
;                    display window
;
;  Output: An HDF file formatted according to AVDC or Envisat guidelines
;
;  Called by: Main Routine
;
;  External Subroutines Called: INTRO (if program called without command line parameters)
;                               READ_TABLEFILE
;                               READ_METADATA
;                               CHECK_METADATA
;                               SET_UP_STRUCTURE
;                               READ_DATA
;                               FIND_HDF_FILENAME
;                               AVDC_HDF_WRITE
;                               STOP_WITH_ERROR (if error state detected)
;    Possible Conditions for STOP_WITH_ERROR call (plus [line number] where called):
;      1. If input is via files and the Metadata or Data file selection is not valid [2778]
;      2. The Table Attribute Values file selection is not valid [2814]
;      3. If input is via session memory and the array holding the global attributes
;         is not of type string, or is not one dimensional [2786]
;      4. If input is via session memory and the input heap structure is not valid
;         [2789, 2793, 2797, 2800]
;      5. If input is via session memory and the number of Datasets does not match
;         the number of Variable Attributes in the heap structure [2802]
;      6. If the H5 option is chosen and the IDL version does not support HDF5 write
;         routines (less than v6.2) [2732]

COMMON TABLEDATA
COMMON METADATA
COMMON DATA
COMMON WIDGET_WIN

;Possible error message for this procedure
proname='IDLcr8HDF procedure: ' & lu=-1L
errtxt=STRARR(6)
errtxt[0]='Metadata, Data and/or Table Attribute Values file selection not valid.'
errtxt[1]='No valid Table Attribute Values file selected.'
errtxt[2]='Global Attributes Array is not of type string, or is not one dimensional.'
errtxt[3]='structure is not valid ('
errtxt[4]='Number of Datasets does not match the number of Variable Attributes in the heap structure: '
errtxt[5]=' does not support HDF5 Write Routines.'

IF N_PARAMS() GE 2 THEN intype=SIZE(SDS,/Type) $ ;Is SDS a structure (8) or a string (7)?
ELSE intype=-1
IF (intype NE 7) AND (intype NE 8) AND (intype NE -1) THEN intype=-2
;Command line parameter neither string nor structure
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]
lineno=0L ;count variable to put curser at end of text pop-up window

IF intype LT 0 THEN BEGIN ;either no input parameters or invalid second parameter
  o3=['0','0','0','0']
  INTRO,Intype ;Open Intro Box and determine HDF output format (HDF4 or HDF5)
  IF o3[0] EQ '0' THEN STOP_WITH_ERROR,'','',lu
  IF o3[3] EQ 'Pop' THEN o3[3]='' ELSE o3[3]='D_'
ENDIF ELSE BEGIN ;Set options (HDF4, HDF5, AVK, LOGFILE, and POP-UP)
  IF (KEYWORD_SET(o1)) AND (FLOAT(!Version.Release) LT 6.2) THEN $
    STOP_WITH_ERROR,'D_'+proname,'IDL Version '+!Version.Release+errtxt[5],lu
  IF KEYWORD_SET(o1) THEN o3=['H5','0','0','D_'] ELSE o3=['H4','0','0','D_']
  IF KEYWORD_SET(o2) THEN o3[1]='AVK'
  IF KEYWORD_SET(o4) THEN o3[2]='idlcr8hdf.log'
  IF KEYWORD_SET(o5) THEN o3[3]=''
ENDELSE

IF (intype LT 0) OR (intype EQ 7) THEN BEGIN ;Input via files
  metafile='' & datafile=[''] & tablefile='' & inf=1
  IF N_PARAMS() GE 2 THEN BEGIN
    ;Check that input files exist. If not then prompt the user for the missing files
    IF SIZE(ga,/Dimensions) EQ 0 THEN BEGIN
      file_exist=FILE_TEST(ga,/Read)
      IF (file_exist EQ 1) AND (ga NE '') THEN metafile=ga
    ENDIF
    arorfs=SIZE(sds,/Dimensions) ;Is datafile input Filespec or String Array?
    IF arorfs[0] EQ 0 THEN BEGIN ;input is filespec (scalar)
      IF sds NE '' THEN sds=FINDFILE(sds)
      isize=SIZE(sds,/Dimensions)
      IF isize[0] EQ 0 THEN sds=[''] ;no files found matching filespec
    ENDIF
    IF N_ELEMENTS(arorfs) GT 1 THEN sds=REFORM(sds,N_ELEMENTS(sds),/Overwrite) ;Convert to 1-D array
    file_exist=FILE_TEST(sds,/Read)
    gi=WHERE(file_exist EQ 1,gcnt)
    IF gcnt NE 0 THEN datafile=sds(gi) ;contains all valid datafiles
    IF N_PARAMS() EQ 3 THEN BEGIN ;check TAV file
      IF SIZE(tav,/Dimensions) EQ 0 THEN BEGIN
        file_exist=FILE_TEST(tav,/Read)
        IF (file_exist EQ 1) AND (tav NE '') THEN tablefile=tav
      ENDIF
    ENDIF
  ENDIF
  IF metafile EQ '' THEN $
    metafile=DIALOG_PICKFILE(Filter=['meta*.txt','Meta*.txt','*.meta','*.txt'], $
             /Must_Exist,Title='Select Metadata Template File')
  gi=WHERE(datafile NE '',nfile)
  IF nfile EQ 0 THEN $
    datafile=DIALOG_PICKFILE(Filter=['*.dat','*.data','*.txt'], $
             /Must_Exist,/Multiple_Files,Title='Select Data File(s)')
  IF tablefile EQ '' THEN $
    tablefile=DIALOG_PICKFILE(Filter=['table*.dat','*.dat'], $
              /Must_Exist,Title='Select Table Attribute Values File')

  ;now check to see that files were actually selected, if not STOP!
  gi=WHERE(datafile NE '',nfile)
  IF (metafile EQ '') OR (nfile EQ 0) OR (tablefile EQ '') THEN $
    STOP_WITH_ERROR,'D_'+proname,errtxt[0],lu $
  ELSE BEGIN
    datafile=datafile[gi] & dsort=SORT(datafile) & sds=datafile[dsort]
  ENDELSE
ENDIF ELSE BEGIN ;SDS is a data structure, so do initial checks on parameters
  ;check GA array
  inf=0 & tablefile='' & nfile=1
  as=SIZE(ga)
  IF (as[0] NE 1) OR (as[2] NE 7) THEN STOP_WITH_ERROR,'D_'+proname,errtxt[2],lu
  ;check pointer array (heap structure)
  IF N_TAGS(sds) EQ 0 THEN $
    STOP_WITH_ERROR,'D_'+proname,'Input heap '+errtxt[3]+'expecting VA and DATA tags).',lu
  tagnames=STRUPCASE(TAG_NAMES(sds))
  si=WHERE((tagnames EQ 'VA') OR (tagnames EQ 'DATA'),scnt)
  IF scnt NE 2 THEN $
    STOP_WITH_ERROR,'D_'+proname,'Input heap '+errtxt[3]+'missing VA and/or DATA tags).',lu
  pchk1=PTR_VALID(sds.va) & pchk2=PTR_VALID(sds.data)
  pi=WHERE(pchk1 NE 1,pcnt)
  IF pcnt NE 0 THEN $
    STOP_WITH_ERROR,'D_'+proname,'SDS.VA '+errtxt[3]+'invalid pointer arguments).',lu
  pi=WHERE(pchk2 NE 1,pcnt)
  IF pcnt NE 0 THEN $
    STOP_WITH_ERROR,'D_'+proname,'SDS.Data '+errtxt[3]+'invalid pointer arguments).',lu
  IF N_ELEMENTS(pchk1) NE N_ELEMENTS(pchk2) THEN $
    STOP_WITH_ERROR,'D_'+proname,errtxt[4]+STRTRIM(N_ELEMENTS(pchk1),2)+'/'+ $
      STRTRIM(N_ELEMENTS(pchk2),2)+'.',lu
  ;check TAV file
  IF N_PARAMS() EQ 3 THEN BEGIN
    IF SIZE(tav,/Dimensions) EQ 0 THEN BEGIN
      file_exist=FILE_TEST(tav,/Read)
      IF (file_exist EQ 1) AND (tav NE '') THEN tablefile=tav
    ENDIF
  ENDIF
  IF tablefile EQ '' THEN $
    tablefile=DIALOG_PICKFILE(File='Table*.dat',Filter=['*.dat'], $
              /Must_Exist,Title='Select Table Attribute Values File')
  IF tablefile EQ '' THEN STOP_WITH_ERROR,'D_'+proname,errtxt[1],lu
  ;create Metafile by combining global and variable attributes
  vatot=0L ;to count the total number of Variable Attribute lines
  FOR i=0,N_ELEMENTS(sds.va)-1 DO BEGIN
    as=SIZE(*sds[i].va) & vatot=vatot+as[N_ELEMENTS(as)-1]
  ENDFOR
  metafile=STRARR(N_ELEMENTS(ga)+vatot)
  metafile[0:N_ELEMENTS(ga)-1]=ga
  pcnt=N_ELEMENTS(ga)
  FOR i=0,N_ELEMENTS(sds.va)-1 DO BEGIN
    vat=*sds[i].va
    metafile[pcnt:pcnt+N_ELEMENTS(vat)-1]=vat
    pcnt=pcnt+N_ELEMENTS(vat)
  ENDFOR
ENDELSE

;Set dux values according to output options
IF o3[2] NE '0' THEN BEGIN ;open idlcr8hdf.log
  res=FILE_TEST(o3[2],/Write)
  IF res EQ 0 THEN openw,du,o3[2],/GET_LUN $
  ELSE BEGIN
    openw,du,o3[2],/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 PRINTF,i,'HDF File Input/Output Log - Program Started on '+SYSTIME(0)

IF o3[3] EQ '' THEN BEGIN
  ;Set-up output file display widget and log file
  base=WIDGET_BASE(Title='HDF 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)
  b1=WIDGET_BUTTON(base2,value='Finish',uvalue='DONE',frame=3,Sensitive=0) ;Accelerator='Return')
  WIDGET_CONTROL,wtxt,/Realize
  WIDGET_CONTROL,base,/Realize
  WIDGET_CONTROL,wtxt,set_value='',/Append
  WIDGET_CONTROL,wtxt,set_value='Input TAV File: '+tablefile,/Append
ENDIF

;Call the procedure to read the TAV file (returns tab_arr)
FOR i=dux[0],dux[1],dux[2] DO BEGIN
  PRINTF,i,'' & PRINTF,i,'Input TAV File: '+tablefile
ENDFOR
READ_TABLEFILE,Tablefile

FOR ndf=0,nfile-1 DO BEGIN
  IF o3[3] EQ '' THEN BEGIN
    lineno=lineno+3L
    WIDGET_CONTROL,wtxt,set_value='',/Append
  ENDIF
  FOR i=dux[0],dux[1],dux[2] DO PRINTF,i,''
  IF inf EQ 1 THEN BEGIN
    IF o3[3] EQ '' THEN BEGIN
      lineno=lineno+2L
      WIDGET_CONTROL,wtxt,set_value='Input Metadata File: '+metafile,/Append
      WIDGET_CONTROL,wtxt,set_value='Input Data File: '+sds[ndf],/Append
    ENDIF
    FOR i=dux[0],dux[1],dux[2] DO BEGIN
      PRINTF,i,'Input Metadata File: '+metafile
      PRINTF,i,'Input Data File: '+sds[ndf]
    ENDFOR
  ENDIF ELSE BEGIN
    IF o3[3] EQ '' THEN $
      WIDGET_CONTROL,wtxt,set_value='Reading input Global Attribute array and Data Structure',/Append
    FOR i=dux[0],dux[1],dux[2] DO $
      PRINTF,i,'Reading input Global Attribute array and Data Structure'
  ENDELSE
  ;Call the procedure to read the metadata (returns meta_arr)
  READ_METADATA,metafile,inf
  ;Call Procedure to check inputs in meta_arr with those from the TAV file
  CHECK_METADATA
  ;Call the procedure to set up the data structure and assign arrays for VAR_NAME and VAR_UNITS
  IF inf EQ 0 THEN SET_UP_STRUCTURE,sds,inf ELSE SET_UP_STRUCTURE,sds[ndf],inf
  ;Call the procedure to read the data
  IF inf EQ 0 THEN READ_DATA,sds,inf ELSE READ_DATA,sds[ndf],inf
  ;Call the Procedure to construct the HDF output filename based on input metadata.
  IF o3[0] EQ 'H5' THEN hdffilename='.h5' ELSE hdffilename='.hdf'
  FIND_HDF_FILENAME,hdffilename
  ;Check to see if file exists - if so delete (o/w will append to existing file)
  IF FILE_TEST(hdffilename) EQ 1 THEN FILE_DELETE,hdffilename

  ;Call the procedure to write the Global Attributes, Variable Attributes and Data
  ;to an HDF4 or HDF5 file
  AVDC_HDF_WRITE,hdffilename

  ;Free up memory by destroying the heap variables pointed at by its pointer arguments
  PTR_FREE,ds.data
  IF o3[3] EQ '' THEN $
    WIDGET_CONTROL,wtxt,set_value=hdffilename+' written successfully!',/Append,Set_text_top_line=lineno
  FOR i=dux[0],dux[1],dux[2] DO PRINTF,i,hdffilename+' written successfully!'
ENDFOR
FOR i=dux[0],dux[1],dux[2] DO BEGIN
  PRINTF,i,'' & PRINTF,i,'HDF file creation completed - Program Ended on '+SYSTIME(0)
ENDFOR
IF dux[1] GT -1 THEN FREE_LUN,dux[1]

IF o3[3] EQ '' THEN BEGIN
  WIDGET_CONTROL,b1,Sensitive=1,/Input_Focus
  WIDGET_CONTROL,wtxt,set_value='',/Append,Set_text_top_line=lineno+2L
  WIDGET_CONTROL,wtxt,set_value='HDF file creation completed - hit <Finish> to close program',/Append
  XMANAGER,'idlcr8hdf',base
ENDIF ELSE IF intype LT 0 THEN BEGIN ;Create Finish Dialog Box
  res=DIALOG_MESSAGE('HDF file creation completed successfully!',/Information,Title='AVDC IDLcr8HDF')
ENDIF

END ;Procedure IDLcr8HDF