diff --git a/idlcr8ascii.pro b/idlcr8ascii.pro index f4733c92e5ac501f7f68a40389abc792f9ef3174..b4e26c14a983e1c85d53ea23f967ed8f50cee33c 100644 --- a/idlcr8ascii.pro +++ b/idlcr8ascii.pro @@ -1,3051 +1,3058 @@ -;Main Program Version: idlcr8ascii.pro v4.0b26, 20240912 -; Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org -; -;Sub-versions: -; v3.01, 20081020 - If the HDF4 file is created using the HDF4.2r3 library, then extra information -; regarding the dimensions of the VAR_DEPEND values may be included in the file -; (which show up as extra datasets in the HDF_SD_FILEINFO call). A check for -; this is carried out and, if found, the information is excluded from the output. -; v3.02, 20090311 - Improve HDF4.2r3 library checks by comparing the number of datasets recorded -; under DATA_VARIABLES, with the number returned by the HDF_SD_FILEINFO call; -; Change 'WARNING' to 'INFORMATION; Add procedure to handle 'INFORMATION' text -; output; Add 'INFORMATION' text if anything irregular found with the datasets; -; Change 'Data dimensions exceed 8' and 'Data type not allowable' ERROR messages -; to 'INFORMATION' messages. Make log file output to the same directory as the -; first HDF file to be read. -; v3.03, 20091208 - Incorporate HDF_SD_ISCOORDVAR to differentiate between datasets and dimension -; variable names. Add check for allowable VAR_DATA_TYPE=STRING. Add all -; INFORMATION text messages to INFOTXT_OUTPUT_A procedure to avoid duplication -; of code that creates the messages; Add INFORMATION messages when reading HDF5 -; files; Rename AVDC Button to FORMAT and replace keyword option /AVDC with -; /FORMAT. Improve checks on parameter and keyword inputs; Improve checks for -; non-standard (i.e. non-groundbased) HDF files. -; v4.0b1, 20101122 - Adopt GEOMS metadata standard; Because FORMAT attribute(s) have been dropped -; can no longer use defined formatting values when outputing data to files; -; Default ASCII formatting adopted - scientific notation for REAL and DOUBLE, -; except for DATETIME and related values, which have formatting dependent on -; the data type; Numeric metadata values remain in their original data type -; when writing to session memory, meaning: variable attribute labels and values -; now written to separate structure variables sds.va_l and sds.va_v (previously -; sds.va), and structure has 2 dimensions (n_sds, n_atts) instead of 1 -; (previously n_sds); The dataset is written to the initial n_atts index (i.e. -; sds[n_sds,0].data); Rename saved SDS datatype according to GEOMS rules -; (affects INT, LONG, and FLOAT); Add JDF_2_DATETIME and JULIAN_DATE routines -; to the program; Include coordinate variables if they and the non-coordinate -; variables add up to the number of saved DATA_VARIABLES. Add return error code -; option so that program returns to the calling program if an error is -; generated. -; v4.0b2, 20110323 - If required reverse the order of VAR_SIZE and VAR_DEPEND values so that they -; match the order of the multi-dimensional dataset. Note that the HDF read programs -; automatically transpose the multi-dimensional datasets to conform to the IDL -; convention of fastest changing index being listed first (opposite for HDF). -; v4.0b3, 20111014 - Add netCDF support - writes variable attributes to GEOMS standard attributes -; where possible, otherwise writes to VAR_NOTES -; v4.0b4, 20111208 - Test for dimension ordering of the netCDF files, and reverse if required (same -; rules as for HDF). Create separate Dimension Ordering Routine -; v4.0b5, 20120328 - Add options to convert HDF/netCDF files to other HDF/netCDF formats e.g. H4 to -; H5 etc. Requires IDLcr8HDF in the search path to do this. -; v4.0b6, 20120426 - Bug fix when a netCDF file only contains datasets with single dimensions - -; VAR_SIZE information was not being extracted during the read process. -; v4.0b7 20130114 - Add check for IDL being run in DEMO mode by using LMGR command. Replace PRINTF,-1 -; statements (i.e. when dux[0] eq -1) with PRINT (o/w causes DEMO mode to stop). Also -; disable /LOG, /FORMAT and /DUMP keywords in DEMO mode. -; v4.0b8 20140325 - Fix bug that meant VAR_SIZE and VAR_DEPEND values were not being automatically -; reversed if the array sizes were the same and VAR_DEPEND did not include a DATETIME -; value. Now defaults to the 'reverse' option in the TEST_DIM_ORDER routine if no -; other conditions are satisfied; Fix bug that caused crash when VAR_SIZE values were -; not of type string. -; v4.0b9 20141110 - Fix bug that caused multi-dimensional array ordering to not be correctly identified -; if the first dataset to be checked in the file had the same number of elements in -; the array (e.g. was a set of Averaging Kernels). -; v4.0b10 20150127 - Allow for VAR_FILL_VALUE values for string datasets to be written and saved as an -; (empty) string of the correct length. Ensure string datasets are all written and -; saved to the correct length (that of the longest string in the dataset). -; v4.0b12 20151109 - Convert DATETIME (and related) max, min and fill values to LONG64 data type before -; determining the format for ASCII output to account for very large values. -; v4.0b15 20160725 - Fix bug causing program to crash when the number of DATA_VARIABLES values is greater -; than the number of datasets in the file. -; v4.0b19 20180218 - Fix bug that caused multi-dimensional array ordering to not be correctly identified -; if the datasets have the same VAR_SIZE i.e. rev_vd_vs stays as 2 through all the -; checks. Now rev_vd_vs changes to 1 (VAR_SIZE, VAR_DEPEND and dataset ordering is -; reversed) if rev_vd_vs=2 after all the checks. -; v4.0b20 20181116 - Fix bug that caused multi-dimensional array ordering to not be correctly written if -; not consistent through the file i.e. rev_vd_vs eq 3. Now rev_vd_vs changes to 1 -; (VAR_SIZE, VAR_DEPEND and dataset ordering is reversed), but information message -; advising of the issue is still reported. -; v4.0b21 20190514 - Fix bug that occurred when GEOMS variable attribute information is missing from an -; HDF4 file but written to the heap structure based on the contents of the file. Previously -; the variable attribute label was not written correctly. -; v4.0b22 20190806 - Provide support for reading netCDF4 files. Currently uses the HDF5 routines to do this -; so need to account for features unique to netCDF4 such as; the inclusion of dimension -; names; standard netCDF attribute names; empty attribute values that do not include a -; null termination character (which causes an error in the H5A_Read routine). New -; sub-routines include is_a_number_ascii, alpha_numeric_underscore, nc_dimension_chk, -; file_format_a. -; v4.0b23 20190821 - Fixed bugs associated with H5 TAG_NAMES checks introduced in v4.0b22. -; v4.0b24 20200830 - Add INFORMATION messages (20-22) that identify issues with the VAR_DEPEND and VAR_SIZE -; attributes, that were previously fixed 'quietly' by the program; Check input GEOMS file -; for zero filesize before doing checks. -; v4.0b25 20220805 - For NetCDF3 or 4, if SDS_NAME NE VAR_NAME then converts non-alphanumeric VAR_NAME -; characters to '_' and compares with SDS_NAME. If there is a match then program quietly -; converts SDS_NAME to VAR_NAME for testing, otherwise generates an error; If input file has -; an HDF5 file signature then checks for .nc .nc4 .netcdf and .netcdf4 extension and also -; calls NCDF_PARSE (if ge 8.5.2) to check for NC4 compatibility; Create DATA_TYPE_CHECKS -; procedure to do all the data type checks. Unable to check for unsigned 8-bit in netCDF3 -; files at this time; Generate an INFORMATION/ERROR message if VAR_UNITS is numeric -; (same as VAR_SIZE); INFORMATION/ERROR message added if a mis-match is found between -; the input file name and the FILE_NAME attribute value. -; v4.0b26 20240912 - For NetCDF3 files only output WARNING to check for signed BYTE entries when the dataset -; is of type BYTE (infotxt_output_a message 23). - -PRO idlcr8ascii_common -;Procedure to define the data COMMON block WIDGET_WIN_A, containing common variables -;associated with the Graphical User Interface -; ---------- -; Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org -; -; History: -; 20061004: Introduced to IDLCR8ASCII - Version 2.0 -; 20080302: 'cwidg' variable removed, and 'dux' array added to identify requested -; output options - Version 3.0 -; 20101122: Add rerr - Version 4.0b1 -; -; -; Input: Nil -; -; Output: Nil -; -; Called by: N/A -; -; Subroutines Called: None - -COMMON WIDGET_WIN_A,wtxt,lineno,base,o3,b3,dux,rerr - -END ; Procedure idlcr8ascii_common - - - -PRO intro_a_event, ev -;Procedure to define how Events from the Start-up Introduction Window are handled -; ---------- -;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org -; -; History: -; 20061004: Introduced to IDLCR8ASCII - Version 2.0 -; 20080302: Code added to define the Pop-up Window and Log Output events added to -; the Introduction Window - Version 3.0 -; -; Input: ev - Selected widget event structure -; -; Output: o3 - Common string array defining the various options for program output -; -; Called by: XMANAGER in INTRO_A -; -; Subroutines Called: None - -COMMON WIDGET_WIN_A - -;The uservalue is retrieved from a widget when an event occurs -WIDGET_CONTROL,ev.id,GET_UVALUE=uv -IF (uv EQ 'C') OR (uv EQ 'P') OR (uv EQ 'idlcr8ascii.log') OR $ - (uv EQ 'H4') OR (uv EQ 'H5') OR (uv EQ 'N3') THEN BEGIN - CASE 1 OF - uv EQ 'C': BEGIN - uvi=1 & eadd=12L - END - uv EQ 'P': uvi=3 - uv EQ 'H4': BEGIN - uvi=5 & eadd=6L - END - uv EQ 'H5': BEGIN - uvi=6 & eadd=5L - END - uv EQ 'N3': BEGIN - uvi=7 & eadd=4L - END - ELSE: uvi=4 - ENDCASE - IF o3[uvi] EQ uv THEN o3[uvi]='0' ELSE o3[uvi]=uv - IF (uv EQ 'C') OR (uv EQ 'H4') OR (uv EQ 'H5') OR (uv EQ 'N3') THEN BEGIN - IF (o3[1] EQ '0') AND (o3[5] EQ '0') AND (o3[6] EQ '0') AND (o3[7] EQ '0') THEN $ - WIDGET_CONTROL,ev.id+eadd,Sensitive=0 $ - ELSE BEGIN - WIDGET_CONTROL,ev.id+eadd,Sensitive=1 - IF uv EQ 'C' THEN BEGIN - WIDGET_CONTROL,ev.id+1,/Set_Button - o3[3]='P' - ENDIF - ENDELSE - ENDIF -ENDIF ELSE IF (uv EQ 'F') OR (uv EQ 'D') THEN o3[0]=uv $ ;Assign button event to a variable name -ELSE IF uv EQ 'Cont' THEN o3[0]='0' ;changes o3[0] from -1 to 0 -IF (uv NE 'C') AND (uv NE 'P') AND (uv NE 'idlcr8ascii.log') AND (uv NE 'H4') AND $ - (uv NE 'H5') AND (uv NE 'N3') THEN WIDGET_CONTROL,ev.top,/DESTROY - -END ;Intro_Event handler - - - -PRO intro_a, intype -;Procedure which creates an Introduction Window at start-up when IDLCR8ASCII is called without parameters, -;or invalid parameters, or is called by IDL Virtual Machine. The user has a choice of continuing with -;the program after selecting input options, or stopping the program. -; ---------- -;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org -; -; History: -; 20061004: Introduced to IDLCR8ASCII - Version 2.0 -; 20080302: Swapped the order of the option windows, so that the options indicated by the check -; boxes appear above the option boxes that close the Introduction Window and continue -; with the program; Added options to allow the user to send input/output log information -; to a Pop-Up Window and/or a Log File - Version 3.0 -; 20091208: Change AVDC button to FORMAT and keyword option from /AVDC to /FORMAT - Version 3.03 -; 20101122: Add vertxt array. Modify Introduction text to indicate that program accepts 'GEOMS -; Compliant' files, and also to show the new structure format - Version 4.0b1 -; -; Input: intype - Integer set to -2 or -3: -2 indicates normal state; -3 indicates that input -; parameters or keywords at the IDLCR8ASCII call are invalid. -; -; Output: Nil -; -; Called by: IDLCR8ASCII -; -; Subroutines Called: INTRO_A_EVENT (via XMANAGER) - -COMMON WIDGET_WIN_A - -;procedure which provides an introduction message before starting the program. -nhdr=46 & errtxt=STRARR(nhdr) -vertxt=['idlcr8ascii-v4.0_Readme.pdf','v4.0b26 September 2024'] -errtxt[1]='Welcome to IDLcr8ASCII. This program reads GEOMS compliant HDF4, HDF5 and netCDF files and' -errtxt[2]='saves contents to either session memory, an output window (summary only) or to ASCII or formatted' -errtxt[3]='files (also refer to '+vertxt[0]+').' -errtxt[5]='Inputs to the program:' -errtxt[6]=' HDF/netCDF FILE(s) - GEOMS compliant HDF4, HDF5 or netCDF files. Input can be by DIALOG_BOX' -errtxt[7]=' (IDL VM or full version of IDL), or by command line (full version of IDL only).' -errtxt[9]='Choice of output is made by selecting options in this DIALOG_BOX (IDL VM or full version' -errtxt[10]=' of IDL), or by ''Keywords'' at the command line input (full version only), as follows:' -errtxt[11]=' /F or /FORMAT - generates two output files (with .META and .DATA extensions). The resulting' -errtxt[12]=' files can be used as inputs to GEOMS compliant write programs (IDLcr8HDF etc), OR' -errtxt[13]=' /D or /DUMP - generates two output files (with .META and .DATA extensions). Data values' -errtxt[14]=' will be shown as indicated in the array format defined by VAR_SIZE, AND/OR' -errtxt[15]=' /C or /CATALOG - sends variable index, variable name, data format, and dimension size' -errtxt[16]=' information to an output window and/or log file, AND/OR' -errtxt[17]=' /P or /POPUP - sends log input/output information to a Pop-up Dialog Box, AND/OR' -errtxt[18]=' /L or /LOG - sends log input/output information to a file named idlcr8ascii.log' -errtxt[19]=' /H4 - outputs contents of the input file as an HDF4 file' -errtxt[20]=' /H5 - outputs contents of the input file as an HDF5 file' -errtxt[21]=' /NC3 - outputs contents of the input file as a netCDF3 file' -errtxt[23]='Example of command line input: idlcr8ascii,DFSpec[,/F][,/D][,/C][,/P][,/L][,/H4][,/H5][,/NC3]' -errtxt[24]=' where ''DFSpec'' can either be a string array containing filenames or a scalar string containing a' -errtxt[25]=' filespec or a single file name.' -errtxt[26]='If running the full version of IDL, and input is a single HDF file, output can also be saved' -errtxt[27]=' to session memory. This can be done by calling idlcr8ascii with the following command line:' -errtxt[28]=' idlcr8ascii,DFFile,GA,SDS[,/F][,/D][,/C][,/P][,/L][,/H4][,/H5][,/NC3].' -errtxt[29]='DFFile can either be the name of an input file or '''' (in which case a DIALOG_BOX will open' -errtxt[30]=' and prompt for the input filename). ''GA'' is a returned string array containing the Global' -errtxt[31]=' Attributes, and ''SDS'' is a returned structure using pointers containing the Variable' -errtxt[32]=' Attribute Labels (sds.va_l), Variable Attribute Values (sds.va_v), and the Data (sds.data). -errtxt[34]='Contacts -' -errtxt[35]=' Ian Boyd, Bryan Scientific Consulting LLC (iboyd@bryanscientific.org)' -errtxt[36]=' 9 Cambridge Terrace' -errtxt[37]=' Masterton 5810, New Zealand' -errtxt[39]=' Ann Mari Fjaeraa, EVDC Project Manager (amf@nilu.no)' -errtxt[40]=' Norwegian Institute for Air Research, Instituttveien 18' -errtxt[41]=' Postbox 100, N-2027 KJELLER, NORWAY' -errtxt[43]='EVDC Website: Tools and documentation available from http://evdc.esa.int/' -errtxt[45]='To continue, please choose from the options below.' -errtxt=' '+errtxt - -;Set-up text display widget -demomode=LMGR(/DEMO) & optsens=1 -IF intype EQ -3 THEN xtxt=' - Command Line Input Error' ELSE xtxt='' -base=WIDGET_BASE(Title='idlcr8ascii '+vertxt[1]+xtxt,Tlb_Frame_Attr=1,/Column) ;,Tab_Mode=1) -wtxt=WIDGET_TEXT(base,xsize=90,ysize=25,/Scroll) - -tip='Left Mouse Click or Tab to entry and hit <Spacebar>' -base2=WIDGET_BASE(base,/Nonexclusive) -cattxt='Catalog - Generate a summary of the contents of the input file(s) and send to an output window or file' -poptext='Open a Pop-Up Window to display log input/output (default if ''Catalog'' option is chosen)' -logtext='Append log input/output to the file ''idlcr8ascii.log'' ' -IF demomode THEN BEGIN - logtext=logtext+'(No log or netCDF file output permitted in IDL DEMO Mode)' & optsens=0 -ENDIF ELSE logtext=logtext+'(will create the file IF it doesn''t exist)' -h4text='Convert input file(s) to HDF4' -h5text='Convert input file(s) to HDF5' -nctext='Convert input file(s) to netCDF3' -fctext1='For the following 3 options, any existing files will be overwritten, IDLcr8HDF must be in the IDL Search Path,' -fctext2='and a valid Table Attribute Values (TAV) file must be in the same directory as the Input file(s).' -b1=WIDGET_BUTTON(base2,value=cattxt,uvalue='C',frame=3) -b2=WIDGET_BUTTON(base2,value=poptext,uvalue='P',frame=3) -b3=WIDGET_BUTTON(base2,value=logtext,uvalue='idlcr8ascii.log',sensitive=optsens,frame=3) - -base3=WIDGET_LABEL(base) -WIDGET_CONTROL,base3,set_value=fctext1 -base4=WIDGET_LABEL(base) -WIDGET_CONTROL,base4,set_value=fctext2 - -base5=WIDGET_BASE(base,/Nonexclusive,ROW=1) -b4=WIDGET_BUTTON(base5,value=h4text,uvalue='H4',frame=3) -b5=WIDGET_BUTTON(base5,value=h5text,uvalue='H5',frame=3) -b6=WIDGET_BUTTON(base5,value=nctext,uvalue='N3',sensitive=optsens,frame=3) - -base6=WIDGET_BASE(base,/Row) -b7=WIDGET_BUTTON(base6,value='ASCII Formatted',uvalue='F',sensitive=optsens,frame=3) ;,Tooltip=Tip) -b8=WIDGET_BUTTON(base6,value='ASCII Dump',uvalue='D',sensitive=optsens,frame=3) ;,Tooltip=Tip) -b9=WIDGET_BUTTON(base6,value='Continue',uvalue='Cont',frame=3,Sensitive=0) ;,Tooltip=Tip) -b10=WIDGET_BUTTON(base6,value='Stop',uvalue='S',frame=3) ;,ToolTip=Tip) -WIDGET_CONTROL,base,/Realize -WIDGET_CONTROL,b1,/Input_Focus -FOR i=0,N_ELEMENTS(errtxt)-1 do $ - WIDGET_CONTROL,wtxt,set_value=errtxt[i],/Append -XMANAGER,'intro_a',base - -END ;Intro_A - - - -PRO idlcr8ascii_event, ev -;Procedure to close the pop-up logging window after user selects 'Finish'. -; ---------- -;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org -; -; History: -; 20061004: Introduced to IDLCR8ASCII - Version 2.0 -; -; Input: Selected widget event structure -; -; Output: Nil -; -; Called by: XMANAGER in IDLCR8ASCII and STOP_WITH_ERROR_A -; -; Subroutines Called: None - -WIDGET_CONTROL,ev.top,/DESTROY -RETALL & HEAP_GC - -END ;Proc IDLcr8ASCII_Event - - - -PRO stop_with_error_a, txt1, txt2, lu -;Procedure called when an error in the program is detected. An error message is displayed -;and the program stopped and reset. If necessary, open files are closed. The error message -;is displayed in one or more of the following: a Pop-up dialog window; the Pop-up logging -;window; the Output Logging window in the IDLDE. -; ---------- -;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org -; -; History: -; 20050729: Original IDLCR8ASCII Routine - Version 1.0 -; 20061004: Set-up so that the error output is displayed in the output window dependent on the -; method that IDLCR8ASCII is called. If txt1 is preceeded by 'D_' or is null, the -; error output is to a Pop-up Dialog window. Otherwise output will be to a logging -; window. Renamed from STOP_WITH_ERROR to STOP_WITH_ERROR_A to avoid a name conflict -; with the equivalent procedure in IDLCR8HDF. Common variable definition WIDGET_WIN_A -; added - Version 2.0 -; 20080302: Added code which sends the error message to the IDLDE output window and/or an external -; file (as determined by the dux array values) - Version 3.0 -; 20101122: Allow routine to return to the calling program, rather than stop the application, if -; a 'reterr' argument is included in the call to idlcr8ascii - Version 4.0b1 - -; -; Inputs: txt1 - the initial text line of the error message. Generally contains the name of the routine -; where the error was detected. -; txt2 - the second text line which generally describes the error state. -; lu - Where applicable, the file unit that needs to be closed at the termination of the program, -; otherwise set to -1. -; -; Output: Nil -; -; Called by: The routine in which the error was detected. The following routines call STOP_WITH_ERROR: -; READ_HDF_SDS; IDLCR8ASCII -; -; Subroutines Called: IDLCR8ASCII_EVENT (via XMANAGER) - -COMMON WIDGET_WIN_A - -IF lu NE -1L THEN FREE_LUN,lu - -IF txt1 EQ '' THEN BEGIN ;<cancel> chosen on Intro box - res=DIALOG_MESSAGE('IDLcr8ASCII Stopped!',/Information,Title='EVDC/AVDC IDLcr8ASCII') -ENDIF ELSE BEGIN - IF STRMID(txt1,0,2) EQ 'D_' THEN txtx=STRMID(txt1,2) ELSE txtx=txt1 - FOR i=dux[0],dux[1],dux[2] DO BEGIN - IF i EQ -1 THEN BEGIN - PRINT,' ERROR in '+txtx & PRINT,' '+txt2 - PRINT,'' & PRINT,'IDLcr8ASCII stopped - Program Ended on '+SYSTIME(0) - ENDIF ELSE BEGIN - PRINTF,i,' ERROR in '+txtx & PRINTF,i,' '+txt2 - PRINTF,i,'' & PRINTF,i,'IDLcr8ASCII stopped - Program Ended on '+SYSTIME(0) - ENDELSE - ENDFOR - IF dux[1] GT -1 THEN FREE_LUN,dux[1] - IF (STRMID(txt1,0,2) EQ 'D_') AND (rerr EQ 'NA') THEN BEGIN - ;write error to DIALOG Box instead of Popup window - errtxt2=STRARR(4) - errtxt2[0]=STRMID(txt1,2) & errtxt2[1]=txt2 & errtxt2[3]='IDLcr8ASCII Stopped!' - res=DIALOG_MESSAGE(errtxt2,/Error,Title='EVDC/AVDC IDLcr8ASCII Error') - ENDIF ELSE IF rerr EQ 'NA' THEN BEGIN ;write error to Popup window - lineno=lineno+4L - WIDGET_CONTROL,wtxt,set_value=' ERROR in '+txt1,/Append - WIDGET_CONTROL,wtxt,set_value=' '+txt2,/Append - WIDGET_CONTROL,wtxt,set_value='',/Append,Set_text_top_line=lineno - WIDGET_CONTROL,wtxt,set_value='HDF file read stopped - hit <Finish> to close program',/Append - WIDGET_CONTROL,b3,Sensitive=1,/Input_Focus - XMANAGER,'stop_with_error_a',base,Event_Handler='idlcr8ascii_event' - ENDIF -ENDELSE -HEAP_GC -IF rerr EQ 'NA' THEN RETALL ELSE rerr='Unable to read HDF or NC file - '+txt2 - -END ;Procedure Stop_With_Error_A - - - -FUNCTION is_a_number_ascii, value - ON_IOERROR, ConversionError - IF STRTRIM(value,2) EQ '' THEN RETURN, 0B - n=DOUBLE(value) - RETURN, 1B - ConversionError: - RETURN, 0B -END - - - -FUNCTION alpha_numeric_underscore, str_value -;Function that returns the input string with non-alphanumeric characters replaced with '_' -; ---------- -;Written by Ian Boyd for the EVDC - iboyd@bryanscientific.org -; -; History: -; 20190806: Introduced - Version 4.0b22 -; -; Input: str_value: string -; -; Returns: string with non-alphanumeric values replaced with '_' -; -; Called by: Read_HDF_SDS -; -ON_IOERROR, ConversionError -numb=BINDGEN(10)+48B ;0-9 -ucb=BINDGEN(26)+65B ;A-Z -lcb=BINDGEN(26)+97B ;a-z -str_value=STRING(str_value) -new_str_value=str_value -n_char=STRLEN(str_value) -FOR i=0L,n_char-1L DO BEGIN - byt_value=BYTE(STRMID(new_str_value,i,1)) - testn=(byt_value GE numb[0]) AND (byt_value LE numb[9]) - testu=(byt_value GE ucb[0]) AND (byt_value LE ucb[25]) - testl=(byt_value GE lcb[0]) AND (byt_value LE lcb[25]) - IF (~testn) AND (~testu) AND (~testl) THEN $ - STRPUT,new_str_value,'_',i -ENDFOR -RETURN, new_str_value -ConversionError: -RETURN, 'io_error' - -END ;Function alpha_numeric_underscore - - - -PRO infotxt_output_a, in0, in1 -;Procedure called to report information relevant to the reading of the HDF file. -;This information can be reported to a Pop-up logging window, IDLDE output log window, and/or -;the log file. Code for this output was originally written directly in the affected procedures -; ---------- -;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org -; -; History: -; 20090311: Introduced to IDLCR8ASCII - Version 3.02 -; 20091208: Added all the INFORMATION text messages to the routine, to avoid duplication -; in the HDF4 and HDF5 read sections of READ_HDF_SDS - Version 3.03 -; 20160213: Update information message when in0[0] EQ 13 - Version 4.0b13 -; 20161130: Added in0[0] messages 18 and 19 to cover non-GEOMS metadata attributes issues -; - Version 4.0b16 -; 20220805: Modified message 2 (full message created in DATA_TYPE_CHECKS) and added messages -; 23 and 24 - Version 4.0b25 -; 20240912: Changed WARNING message in 23 so that it is only generated when the NC3 dataset -; is of type BYTE - Version 4.0b26 -; -; Inputs: in0 - Integer array containing input used to make correct text output -; in1 - String array containing input used to make correct text output -; -; Output: Nil -; -; Called by: The routine in which the request for INFORMATION text was made. -; The following routines call INFOTXT_OUTPUT_A: READ_HDF_SDS; OUTPUT_HDF_DATA; -; IDLCR8ASCII -; -; Subroutines Called: None - -COMMON WIDGET_WIN_A - -CASE 1 OF - in0[0] EQ 0: BEGIN - infotxt=STRARR(3) - IF LONG(in1[0]) EQ 0L THEN BEGIN - infotxt[0]=' INFORMATION: Global Attribute DATA_VARIABLES not found or has no values.' - infotxt[1]=' Dataset listing order matches the order that the dataset is read in the GEOMS file.' - ENDIF ELSE BEGIN - infotxt[0]=' INFORMATION: Number of Global Attribute DATA_VARIABLES values ('+in1[0]+')' - infotxt[1]=' does not match the number of datasets saved to the GEOMS file ('+in1[1]+')' - ENDELSE - infotxt[2]=' Number of datasets determined from '+in1[2]+' call' - END - in0[0] EQ 1: infotxt=' INFORMATION: '+in1[0]+' has '+in1[1]+' data dimensions' - in0[0] EQ 2: infotxt=' INFORMATION: '+in1[0] - in0[0] EQ 3: BEGIN - infotxt=STRARR(4) - IF (STRPOS(STRUPCASE(in1[1]),STRUPCASE(in1[0])) NE -1) AND (in0[1] EQ 4) AND (in0[2] EQ 0) THEN $ - infotxt[0]=' INFORMATION: Dataset Name is truncated (File created with HDF4.2r1 library or earlier):' $ - ELSE infotxt[0]=' INFORMATION: Dataset Name does not match VAR_NAME value:' - infotxt[1]=' SDS_NAME: '+in1[0] - infotxt[2]=' VAR_NAME: '+in1[1] - IF in0[2] EQ 0 THEN infotxt[3]=' Output uses VAR_NAME from Metadata' $ - ELSE infotxt[3]=' Output uses Dataset Name from the HDF file' - END - in0[0] EQ 4: BEGIN - infotxt=STRARR(2) - IF in0[1] EQ 0 THEN BEGIN - infotxt[0]=' INFORMATION: Metadata label VAR_NAME not found for dataset:' - infotxt[1]=' SDS_NAME: '+in1[0] - ENDIF ELSE IF (in0[2] EQ 0) AND (in0[3] NE 0L) THEN BEGIN - infotxt[0]=' INFORMATION: VAR_NAME value does not match any DATA_VARIABLES values:' - infotxt[1]=' VAR_NAME: '+in1[1] - ENDIF - END - in0[0] EQ 5: BEGIN - infotxt=STRARR(2) - infotxt[0]=' INFORMATION: No Variable Attributes returned after call to '+in1[1] - infotxt[1]=' SDS_NAME: '+in1[0] - END - in0[0] EQ 6: BEGIN - infotxt=' INFORMATION: Metadata Dataset listing order may not match the order of' - infotxt=infotxt+' the Global Attribute DATA_VARIABLES Values' - END - in0[0] EQ 7: BEGIN - infotxt=' INFORMATION: Can read one HDF or netCDF file into session memory.' - infotxt=infotxt+' Only first file in the array will be read' - END - in0[0] EQ 8: BEGIN - infotxt=' INFORMATION: '+in1[0]+' is a coordinate variable' - END - in0[0] EQ 9: BEGIN - infotxt=STRARR(3) - infotxt[0]=' INFORMATION: Calibrated data in dataset '+in1[0] - infotxt[1]=' has been converted back to its original form using the formula:' - infotxt[2]=' Orig_Data = Scale_Factor * (Cal_Data - Offset)' - END - in0[0] EQ 10: BEGIN - infotxt=STRARR(2) - infotxt[0]=' INFORMATION: VAR_DATA_TYPE='+in1[1]+' for Calibrated data in dataset '+in1[0] - infotxt[1]=' has been changed to the original datatype of '+in1[2] - END - in0[0] EQ 11: BEGIN - infotxt=STRARR(2) - CASE 1 OF - in0[1] EQ 20: BEGIN - infotxt[0]=' INFORMATION: Order of multi-dimensional datasets changed to match the programming' - infotxt[0]=infotxt[0]+' language convention: - infotxt[1]=' IDL/Fortran - fastest changing dimension first (datasets transposed when' - infotxt[1]=infotxt[1]+' reading/writing to the HDF file)' - END - in0[1] MOD 10 EQ 0: BEGIN - infotxt[0]=' INFORMATION: Listing order of multi-dimensional VAR_DEPEND and VAR_SIZE values' - infotxt[1]=' in the HDF file changed to match corresponding dataset dimension ordering' - END - ELSE: BEGIN - infotxt[0]=' INFORMATION: Unable to determine the correct listing order of multi-dimensional' - infotxt[1]=' VAR_SIZE and VAR_DEPEND values in the HDF file' - END - ENDCASE - END - in0[0] EQ 12: BEGIN - infotxt=STRARR(2) - infotxt[0]=' INFORMATION: Table Attribute Values file must be in the same directory as the input file' - infotxt[1]=' if wanting to convert from one Data Format to another' - END - in0[0] EQ 13: BEGIN - infotxt=STRARR(2) - infotxt[0]=' INFORMATION: IDLcr8HDF called but it is not in the IDL Search Path or it encountered an' - infotxt[1]=' unexpected error. If the latter please contact Ian Boyd at iboyd@bryanscientific.org' - END - in0[0] EQ 14: BEGIN - infotxt=STRARR(2) - infotxt[0]=' INFORMATION: /POPUP keyword cannot be used together with the ''reterr'' argument.' - infotxt[1]=' The request for a POPUP window has been ignored' - END - in0[0] EQ 15: BEGIN - infotxt=STRARR(2) - infotxt[0]=' INFORMATION: Argument ''reterr'' must be of type string.' ;a returnable variable of type string.' - infotxt[1]=' idlcr8ascii will stop normally if an error is encountered' - END - in0[0] EQ 16: BEGIN - infotxt=STRARR(2) - infotxt[0]=' INFORMATION: /LOG, /FORMAT, and /DUMP keywords cannot be used in IDL DEMO mode and will be ignored.' - infotxt[1]=' To generate ASCII files please use the free IDL Virtual Machine or a licenced version of IDL' - END - in0[0] EQ 17: BEGIN - infotxt=STRARR(2) - infotxt[0]=' INFORMATION: NetCDF file create feature is disabled in IDL DEMO mode.' - infotxt[1]=' To generate files please use the free IDL Virtual Machine or a licenced version of IDL' - END - in0[0] EQ 18: BEGIN - infotxt=' INFORMATION: Value for Attribute Label '+in1[0]+' is an invalid Data Type' - END - in0[0] EQ 19: BEGIN - infotxt=' INFORMATION: '+in1[0]+' is not a valid GEOMS Metadata Attribute' - END - in0[0] EQ 20: BEGIN - infotxt=' INFORMATION: '+in1[0]+' entry must be written to the file as a STRING for '+in1[1] - END - in0[0] EQ 21: BEGIN - infotxt=' INFORMATION: '+in1[0]+' sub-values must be separated by '';'' for '+in1[1] - END - in0[0] EQ 22: BEGIN - infotxt=' INFORMATION: Spaces not permitted in the '+in1[0]+' entry for '+in1[1] - END - in0[0] EQ 23: BEGIN - infotxt=STRARR(2) - itxt1=' WARNING: This program can''t check that BYTE datasets and attributes contain only' - itxt2=' 8-bit unsigned values (0-255) in netCDF3 files.' - infotxt[0]=itxt1+itxt2 - itxt1=' Please check that the '+in1[0]+' dataset and attributes' - itxt2=' with BYTE data type do not contain negative 8-bit signed values' - infotxt[1]=itxt1+itxt2 - END - in0[0] EQ 24: BEGIN - infotxt=STRARR(2) - infotxt[0]=' INFORMATION: Input file name does not match the Global Attribute entry' - infotxt[1]=' FILE_NAME='+in1[0] - END -ENDCASE - -dm=SIZE(infotxt,/N_Elements) -IF o3[3] EQ '' THEN lineno=lineno+dm - -FOR n=0,dm-1 DO BEGIN - IF o3[3] EQ '' THEN BEGIN - IF n EQ dm THEN WIDGET_CONTROL,wtxt,set_value='',/Append $ - ELSE WIDGET_CONTROL,wtxt,set_value=infotxt[n],/Append,Set_text_top_line=lineno - ENDIF - FOR m=dux[0],dux[1],dux[2] DO $ - IF m EQ -1 THEN PRINT,infotxt[n] ELSE PRINTF,m,infotxt[n] ;write out to log(s) -ENDFOR - -END ;Procedure InfoTxt_Output_A - - - -FUNCTION jdf_2_datetime, jdf, MJD2000=mjd2000, SHORTISO8601=iso, LONGISO8601=isoms -;Computes the UT date/time from JDF/MJD2000. -; ---------- -; Bojan R. Bojkov -; bojan.r.bojkov@nasa.gov -; 03/04/2004 -; 03/11/2004 bug fix -; 05/27/2005 add ShortISO8601 and LongISO8601 switches, -; and change seconds value to decimal seconds (Ian Boyd) -; -; References ---------- -; Explan. Supp. Astron. Almanac, p.604 (1992); through E. Celarier -; -; Caveats ---------- -; Valid for all YYYY >= -4712 (i.e. for all JD .ge. 0) -; The true Gregorian calendar was only adopted on 15 October 1582, -; so any dates before this are "virtual" Gregorian dates. -; -; Input ---------- -; jdf : double precision value -; mjd2000 : flag if input is MJD2000 -; iso : flag if output is in ISO8601 (YYYYMMDDThhmmssZ) -; isoms : flag if output is in ISO8601ms (YYYYMMDDThhmmss.sssZ) -; -; Output --------- -; floating point array [YYYY, MM, DD, hh, mn, ss.sss], -; or ISO8601 string format -; External subroutines --------- -; NONE - -jdf=DOUBLE(jdf) - -j0=2451544.5D -IF KEYWORD_SET(mjd2000) THEN jdhold=jdf+j0 ELSE jdhold=jdf -jdi=LONG(jdhold) -df=jdhold-jdi - -;Determine hh, mm, ss, ms -hh=(df+0.5)*24.D -q=WHERE(hh GE 24.D) -IF q[0] NE -1 THEN BEGIN - hh[q]=hh[q]-24.D - jdi[q]=jdi[q]+1 -ENDIF -t1=hh -hh=LONG(t1) -t2=(t1-hh)*60.D -mn=LONG(t2) -t3=(t2-mn)*60.D -ss=t3 -;ss=LONG(t3) -;ms=LONG((t3-ss)*1.d3) - -;Determine YYYY, MM, DD -t1=jdi+68569L -t2=(4*t1)/146097L -t1=t1-(146097L*t2+3L)/4L -t3=(4000L*(t1+1L))/1461001L -t1=t1-(1461L*t3)/4L + 31L -t4=(80L*t1)/2447L - -dd=t1-(2447L*t4)/80L -t1=t4/11L -mm=t4+2L-12L*t1 -yyyy=100L*(t2-49L)+t3+t1 - -dt=TRANSPOSE([[yyyy],[mm],[dd],[hh],[mn],[ss]]) -IF (KEYWORD_SET(iso)) OR (KEYWORD_SET(isoms)) THEN BEGIN - dts=STRARR(6) - IF (KEYWORD_SET(iso)) AND (ss-FIX(ss) GE 0.5) THEN BEGIN - ;recalculate to get correct seconds value - jdhold=jdf+0.000008D ;add ~0.7 secs - IF KEYWORD_SET(mjd2000) THEN jdhold=jdhold+j0 - CALDAT,jdhold,mm,dd,yyyy,hh,mn,ss - dt=TRANSPOSE([[yyyy],[mm],[dd],[hh],[mn],[ss]]) - ENDIF - dt=LONG(dt) - FOR i=1,5 DO $ - IF dt[i] LT 10L THEN dts[i]='0'+STRTRIM(dt[i],2) ELSE dts[i]=STRTRIM(dt[i],2) - IF KEYWORD_SET(isoms) THEN BEGIN - ssd=STRING(format='(f6.3)',ss) - IF FLOAT(ssd) LT 10.0 THEN dts[5]='0'+STRTRIM(ssd,2) ELSE dts[5]=STRTRIM(ssd,2) - ENDIF - RETURN,STRTRIM(dt[0],2)+dts[1]+dts[2]+'T'+dts[3]+dts[4]+dts[5]+'Z' -ENDIF ELSE RETURN, dt - -END ;Function jdf_2_datetime - - - -FUNCTION julian_date, date_ut, ISO8601=iso, MJD2000=mjd2000 -;Computes the Julian date (jd) or date in MJD2000 format in double precision. -; ---------- -; Bojan R. Bojkov -; bojan.r.bojkov@nasa.gov -; 06/03/2001 -; 09/24/2001: Code cleanup - Function Version 1.0 -; 10/16/2001: Mode corrections - Function Version 1.01 -; 03/07/2002: Comment cleanup and variable change - Function Version 2.0 -; 10/08/2002: Added jdf option - Function Version 2.1 -; 07/21/2003: Converted to a function - Function Version 3.0 -; 05/25/2005: Added ISO8601 and MJD2000 keywords. -; Input can either be a string or a numeric array -; (see below for input details). Returns -99999.d -; if the Input is invalid - Ian Boyd -; -; References ---------- -; Hughes, D.W., Yallop, B.D. and Hohenkerk, C.Y., "The Equation of Time", -; Mon. Not. R. astr. Soc., 238, pp 1529-1535. 1989. -; -; Results verified using: -; Meeus, J, "Astronomical Algorithms", William-Bell, Inc., Richmond VA, -; p62, 1991. -; -; Caveats ---------- -; NONE -; -; Input ---------- -; date_ut: Numeric array (UT: YYYY, MM, DD, [hh, mm, ss]), or if ISO8601 -; keyword set, a string containing datetime as YYYYMMDDThhmmssZ -; -; Output --------- -; jd: julian day, or if MJD2000 keyword set, -; a double precision day in MJD2000 format -; -; External subroutines --------- -; NONE - -ON_IOERROR,TypeConversionError -valid=0 ;Type conversion check -inputerror=0 ;Check that input is as expected -;Check input format -IF KEYWORD_SET(iso) THEN BEGIN ;input is YYYYMMDDThhmmssZ - IF STRLEN(date_ut) EQ 16 THEN BEGIN - ;test for numeric values (will return type conversion error value if not a number) - FOR i=0,7 DO check=FIX(STRMID(date_ut,i,1)) - FOR i=9,14 DO check=FIX(STRMID(date_ut,i,1)) - dt=INTARR(6) - dt[0]=FIX(STRMID(date_ut,0,4)) & dt[1]=FIX(STRMID(date_ut,4,2)) - dt[2]=FIX(STRMID(date_ut,6,2)) & dt[3]=FIX(STRMID(date_ut,9,2)) - dt[4]=FIX(STRMID(date_ut,11,2)) & dt[5]=FIX(STRMID(date_ut,13,2)) - date_ut=dt & achk=[0,6] ;to add hhmmss component - ENDIF ELSE inputerror=1 -ENDIF ELSE BEGIN - ;check that input contains at least YMD (and at most YMDhms) information and is either - ;an integer, long, float or double array - achk=SIZE(date_ut) - IF (achk[1] LT 3) OR (achk[1] GT 6) OR (achk[2] LT 2) OR (achk[2] GT 5) THEN inputerror=1 -ENDELSE - -IF inputerror EQ 0 THEN BEGIN ;can perform JD calculation - IF date_ut[1] GT 2 THEN BEGIN - y=DOUBLE(date_ut[0]) - m=DOUBLE(date_ut[1]-3) - d=DOUBLE(date_ut[2]) - ENDIF ELSE BEGIN - y=DOUBLE(date_ut[0]-1) - m=DOUBLE(date_ut[1]+9) - d=DOUBLE(date_ut[2]) - ENDELSE - - ;Compute Julian date: - j=LONG(365.25D0*(y+4712.D0))+LONG(30.6D0*m+0.5D0)+59.D0+d-0.5D0 - - ;Check for Julian or Gregorian calendar: - IF j LT 2299159.5D0 THEN jd=j $ ;If Julian calendar. - ELSE BEGIN ;If Gregorian calendar. - gn=38.D0-LONG(3.D0*LONG(49.D0+y/100.D0)/4.D0) - jd=j+gn - ENDELSE - - ;add hhmmss values if present - CASE 1 OF - achk[1] EQ 4: jd=jd+DOUBLE(date_ut[3])/24.D ;hh only - achk[1] EQ 5: jd=jd+(DOUBLE(date_ut[3])*3600.D + DOUBLE(date_ut[4])*60.D)/86400.D ;hhmm only - achk[1] EQ 6: jd=jd+(DOUBLE(date_ut[3])*3600.D + DOUBLE(date_ut[4])*60.D + $ - DOUBLE(date_ut[5]))/86400.D ;hhmmss - ELSE: - ENDCASE - - valid=1 ;Type conversion OK - - ;Set to MJD2000 if required - IF KEYWORD_SET(mjd2000) THEN jd=jd-2451544.5D -ENDIF - -TypeConversionError: -IF valid EQ 0 THEN RETURN,-99999.D ELSE RETURN, jd - -END ;Function Julian_Date - - - -PRO nc_dimension_chk, ftype, sd_id, di, n_sds, dgi, n_sdsg -;Procedure to check for netCDF dimension datasets. These will be quietly ignored during the -;read process -; ---------- -;Written by Ian Boyd for the EVDC - iboyd@bryanscientific.org -; -; History: -; 20190806: Introduced - Version 4.0b22 -; -; Inputs: ftype - format type, HDF5 or netCDF -; sd_id - GEOMS file identifier number -; di - index values for datasets -; n_sds - number of datasets determined by H5G_GET_NMEMBERS call -; -; Outputs: dgi - index values for GEOMS datasets (not generated by netCDF libraries) -; n_sdsg - number of GEOMS datasets -; -; Called by: Read_HDF_SDS -; -dimen_names=['constant','independent_','_strlen'] -n_dn=N_ELEMENTS(dimen_names) -ds_names=STRARR(n_sds) & gdi=INTARR(n_sds)-1 -FOR i=0,n_sds-1 DO BEGIN - IF ftype EQ 'H5' THEN sds_name=H5G_GET_MEMBER_NAME(sd_id,'/',di[i]) $ - ELSE BEGIN ;netCDF file - varstruct=NCDF_VARINQ(sd_id,i) - sds_name=varstruct.name - ENDELSE - ds_names[i]=STRTRIM(STRLOWCASE(sds_name),2) -ENDFOR - -FOR i=0,n_sds-1 DO BEGIN - dnicnt=0 & j=0 - WHILE (dnicnt EQ 0) AND (j LE n_dn-1) DO $ - IF STRPOS(ds_names[i],dimen_names[j]) NE -1 THEN dnicnt++ ELSE j++ - IF dnicnt NE 0 THEN BEGIN - ;Check that names are actually netCDF dimension names o/w will treat as a dataset name - CASE j OF - 0: IF ds_names[i] NE dimen_names[j] THEN dnicnt=0 ;i.e. not a netCDF dimension name - 1: BEGIN - ;check that first part of the name is INDEPENDENT_ and the second part is a number - IF STRMID(ds_names[i],0,STRLEN(dimen_names[j])) NE dimen_names[j] $ - THEN dnicnt=0 $ - ELSE BEGIN - res=STRSPLIT(ds_names[i],'_',/EXTRACT,COUNT=cres) - res=[res,''] ;to ensure at least 2 extracted values in ds_names[i] - if ~IS_A_NUMBER_ASCII(res[1]) THEN dnicnt=0 ; i.e. not a netCDF dimension name - ENDELSE - END - 2: BEGIN - ;check that first part of the name is a named dataset - res=STRSPLIT(ds_names[i],'_',/EXTRACT) - ni=WHERE(res[0] EQ ds_names,ncnt) - IF ncnt EQ 0 THEN dnicnt=0 ;i.e. not a netCDF dimension name - END - ENDCASE - ENDIF - IF dnicnt EQ 0 THEN gdi[i]=i ;i.e. this is a valid dataset name and not a netCDF dimension name -ENDFOR - -dgi=WHERE(gdi NE -1,n_sdsg) ;dgi and n_sdsg are returned by the procedure - -END ;Procedure nc_dimension_chk - - - -FUNCTION file_format_a, infile -;Function to identify the format of the input file - tests for HDF4, HDF5/netCDF4 and netCDF3. -;netCDF4 files will be identified as HDF5 and read in using the HDF5 library -; ---------- -;Written by Ian Boyd for the EVDC - iboyd@bryanscientific.org -; -; History: -; 20190806: Introduced - Version 4.0b22 -; 20220805: Additional checks for netCDF4 files if the file signature ia HDF5 - Version 4.0b25 -; -; Inputs: infile - Input GEOMS compliant file -; -; Outputs: string identifying the type of file (H4, H5, NC) or XX for unidentified file -; -; Called by: IDLCR8ASCII -; -;Markers -;HDF4: 14 3 19 (1 0 16 0 1 95 98 0 30) -;HDF5: 137 72 68 70 (13 10 26 10 0 0 0 0) -;nc3: 67 68 70 (1 0 0 0 0 0 0 0 10) -;nc4: 137 72 68 70 (13 10 26 10 (2 8 8 0)) - -h4id=[14B, 3B, 19B] -h5id=[137B, 72B, 68B, 70B] ;HDF -nc3id=[67B, 68B, 70B] ;CDF - -;First test using start of file byte markers -fid=BYTARR(4) -openr,fu,infile,/GET_LUN -readu,fu,fid -FREE_LUN,fu - -idfound='XX' -CASE 1 OF - ARRAY_EQUAL(fid[0:2],h4id): idfound='H4' - ARRAY_EQUAL(fid,h5id): idfound='H5' - ARRAY_EQUAL(fid[0:2],nc3id): idfound='N3' - ELSE: -ENDCASE - -;Second test, if required, using IDL commands -IF idfound EQ 'XX' THEN BEGIN - IF HDF_ISHDF(infile) EQ 1 THEN idfound='H4' $ - ELSE IF FLOAT(!Version.Release) GE 5.6 THEN BEGIN - IF H5F_IS_HDF5(infile) EQ 1 THEN idfound='H5' ;netCDF4 or HDF5 - ENDIF - IF idfound EQ 'XX' THEN BEGIN - ;final test for netCDF3 - validname=1 - CATCH, ncdferror ;To catch the error when attempting to open the file as a netCDF - ;Error Handler - IF ncdferror NE 0 THEN BEGIN ;not a netCDF file - validname=0 - CATCH, /CANCEL - ENDIF - IF validname EQ 1 THEN BEGIN - fileid=NCDF_OPEN(infile) - ;If get to here then file is netCDF - CATCH, /CANCEL - NCDF_CLOSE,fileid - idfound='N3' - ENDIF - ENDIF -ENDIF - -IF idfound EQ 'H5' THEN BEGIN - ;For H5, try to identify whether it is H5 or N4 - nc4ext=['nc','nc4','netcdf','netcdf4'] - fileext=STRMID(infile,STRPOS(infile,'.',/REVERSE_SEARCH)+1) - nci=WHERE(STRLOWCASE(fileext) EQ nc4ext,nccnt) - - vr=!Version.Release ;IDL version check (needs to be 8.5.2 or greater for NCDF_PARSE check - vrlast=STRMID(vr,STRPOS(vr,'.',/REVERSE_SEARCH)+1) - IF (FLOAT(vr) GT 8.5) OR ((STRMID(vr,0,3) EQ '8.5') AND (FIX(vrlast GE 2))) THEN parsechk=1B ELSE parsechk=0B - IF (nccnt NE 0) AND (parsechk) THEN BEGIN - ;Catch error from attempting to parse non-NetCDF-4 compliant HDF5 file - validname=1 - CATCH, ncdferror ;To catch the error when attempting to open the file as a netCDF - ;Error Handler - IF ncdferror NE 0 THEN BEGIN ;not a netCDF file - validname=0 - CATCH, /CANCEL - ENDIF - IF validname EQ 1 THEN BEGIN - fileid=NCDF_PARSE(infile) - ;If get to here then file can be opened using N4 libraries, so most likely a netcdf4 file - CATCH, /CANCEL - idfound='N4' - ENDIF - ENDIF ELSE IF nccnt NE 0 THEN idfound='N4' ;Unable to do NCDF_PARSE check but still likely nc4 -ENDIF - -RETURN, idfound - -END ;File_Format_A - - - -PRO test_dim_order, va_name, va_value, va_type, sds_dim, sds_name, rev_vd_vs -;Procedure to test the dimension ordering of any multi-dimensional datasets in the input DF file, and -;return correct ordering code (dimension ordering for idlcr8ascii uses IDL/Fortran convention) -; ---------- -;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org -; -; History: -; 20111208: Introduced - Version 4.0b4 -; 20140325: Fix bug that meant VAR_SIZE and VAR_DEPEND values were not being automatically -; reversed if the array sizes were the same and VAR_DEPEND did not include a DATETIME -; value. Now defaults to the 'reverse' option if no other conditions are satisfied; -; Fix bug that caused crash when VAR_SIZE values were not of type string - Version 4.0b8 -; 20141110: Fix bug that caused multi-dimensional array ordering to not be correctly identified -; if the first dataset to be checked in the file had the same number of elements in -; the array (e.g. was a set of Averaging Kernels) - Version 4.0b9 -; 20200930: Add INFORMATION messages (20-22) that identify issues with the VAR_DEPEND and VAR_SIZE -; attributes that were previously fixed 'quietly' by the program; Add sds_name to the -; variables required by the procedure - Version 4.0b24 -; -; Inputs: va_name - An abbreviated version of the Variable Name; either 'VD' (VAR_DEPEND) or 'VS' -; (VAR_SIZE) -; va_value - The corresponding Variable Value -; sds_dim - Dimension information for the DF Dataset being tested -; sds_name - Dataset name, required if an INFORMATION message is generated -; -; Outputs: rev_vd_vs - Scalar to indicate whether VAR_DEPEND and VAR_SIZE values (and data, in the case -; of netCDF measurements) need to be reversed -; -; Called by: Read_HDF_SDS - -IF va_type NE 'STRING' THEN BEGIN ;VAR_SIZE contains numeric values instead of in the form of a string - vavhold='' & n_vav=N_ELEMENTS(va_value) - FOR k=0,n_vav-1 DO BEGIN - IF k EQ n_vav-1 THEN vtxt='' ELSE vtxt=';' - vavhold=vavhold+STRTRIM(va_value[k],2)+vtxt - ENDFOR - IF va_name EQ 'VD' THEN vtxt='VAR_DEPEND' ELSE vtxt='VAR_SIZE' - INFOTXT_OUTPUT_A,[20],[vtxt,STRTRIM(sds_name,2)] -ENDIF ELSE vavhold=va_value -vs_v=STRCOMPRESS(STRSPLIT(vavhold,';, ',/EXTRACT,COUNT=rcnt),/REMOVE_ALL) - -IF (rcnt GT 1) AND ((STRPOS(vavhold,';') EQ -1) OR (STRPOS(vavhold,',') NE -1)) THEN BEGIN - IF va_name EQ 'VD' THEN itxt='VAR_DEPEND' ELSE itxt='VAR_SIZE' - INFOTXT_OUTPUT_A,[21],[itxt,STRTRIM(sds_name,2)] ;sub-values should be separated by ';' -ENDIF - -IF (rcnt EQ 1) OR ((rcnt GT 1) AND (STRPOS(vavhold,';') NE -1)) THEN BEGIN - IF STRCOMPRESS(vavhold,/REMOVE_ALL) NE vavhold THEN BEGIN - IF va_name EQ 'VD' THEN itxt='VAR_DEPEND' ELSE itxt='VAR_SIZE' - INFOTXT_OUTPUT_A,[22],[itxt,STRTRIM(sds_name,2)] ;no spaces permitted - ENDIF -ENDIF - -IF va_name EQ 'VD' THEN BEGIN ;VAR_DEPEND tests - dti=WHERE(STRUPCASE(vs_v) EQ 'DATETIME',dticnt) - ;Note: if DATETIME has VAR_SIZE=1 then the ordering is dependent on the order of the VAR_SIZE values only - IF (rcnt GT 1) AND (dticnt NE 0) THEN BEGIN ;multi-dimensions including DATETIME - test1=(dti[0] EQ rcnt-1) AND (sds_dim[0] GT 1) - ;DATETIME is the last value and present only once and has VAR_SIZE GT 1 - test2=(dti[0] EQ 0) AND (dticnt EQ 1) AND (sds_dim[0] GT 1) - ;DATETIME is the first value and present only once and has VAR_SIZE GT 1 - IF (test1) AND (rev_vd_vs EQ 2) THEN rev_vd_vs=10 $ - ELSE IF (test2) AND (rev_vd_vs EQ 2) THEN rev_vd_vs=11 $ - ELSE IF ((test1) AND (rev_vd_vs MOD 10 EQ 1)) OR ((test2) AND (rev_vd_vs EQ 10)) THEN $ - rev_vd_vs=3 $ ;inconsistent rules regarding dimension ordering in file - ELSE IF (test2) AND (rev_vd_vs EQ 0) THEN rev_vd_vs=20 - ;Dimension ordering is slowest changing first so change dataset ordering as well as - ;VAR_DEPEND and VAR_SIZE ordering - ENDIF -ENDIF ELSE BEGIN ;VAR_SIZE tests - IF rcnt GT 1 THEN BEGIN ;multi-dimensions - ;check all values are numeric - numchk=STRJOIN(vs_v,/SINGLE) & valok=1 - FOR k=0,STRLEN(numchk)-1 DO BEGIN - bchar=BYTE(STRMID(numchk,k,1)) - IF (bchar LT 48) OR (bchar GT 57) THEN valok=0 ;i.e. non-numeric character - ENDFOR - IF valok EQ 1 THEN BEGIN ;all values are numeric so do checks - vs_vl=LONG(vs_v) - test1=ARRAY_EQUAL(vs_vl,sds_dim) - test2=ARRAY_EQUAL(REVERSE(vs_vl),sds_dim) ;test in case values are the same e.g. 40;40 - ;If arrays are the same size then the default will be to reverse the VS and VD values - ;unless a different dimension ordering has already been identified - IF (test1 EQ 1) AND (test2 EQ 0) AND (rev_vd_vs EQ 2) THEN rev_vd_vs=0 $ - ELSE IF (test1 EQ 0) AND (test2 EQ 1) AND (rev_vd_vs EQ 2) THEN rev_vd_vs=1 $ - ELSE IF ((test1 EQ 1) AND (test2 EQ 0) AND (rev_vd_vs MOD 10 EQ 1)) OR $ - ((test1 EQ 0) AND (test2 EQ 1) AND (rev_vd_vs MOD 10 EQ 0)) THEN $ - rev_vd_vs=3 $ ;inconsistent VAR_SIZE and sds_dim agreement from dataset to dataset - ELSE IF (test1 EQ 1) AND (test2 EQ 1) AND (rev_vd_vs EQ 2) THEN $ - rev_vd_vs=2 $ ;first set of measurements tested have same dimensions so do not do anything - ELSE IF rev_vd_vs EQ 2 THEN rev_vd_vs=1 ;default if no other criteria are found as this - ;will then automatically reverse the VD and VS values - ENDIF - ENDIF -ENDELSE - -END ;Procedure Test_Dim_Order - - - -PRO data_type_checks, ftype, dt_chk_labels, hdftype, idltype, sds_name, var_name -;VAR_DATA_TYPE corresponds to the dataset data type and the VAR_VALID_MIN, VAR_VALID_MAX -;and VAR_FILL_VALUES also match the dataset data type. Note limitation that 8-bit -;unsigned integer (BYTE) values cannot be identified in netCDF3 files -; ---------- -;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org -; -; History: -; 20220805: Introduced to IDLCR8ASCII - Version 4.0b25 -; -; Inputs: ftype - a string identifying the file type (H4, H5, N3 or N4) -; dt_chk_labels - string array containing labels of attributes (plus 'Dataset') -; upon which the data type checks are done -; hdftype - string array containing the data type returned by the respective -; ftype specific calls -; idltype - string array containing the data types as IDL codes or names -; sds_name - string holding dataset name returned by the respective ftype specific -; cals -; var_name - string holding the VAR_NAME value (should be the same as sds_name) -; -; Outputs: N/A -; Called by: READ_HDF_SDS -; -; Subroutines Called: INFOTXT_OUTPUT_A (if program discovers an issue with the data type) -; Information Conditions: -; 1. VAR_DATA_TYPE is not an allowable data type -; 2. Dataset, Valid Min, Valid Max and/or Fill Value data types do not match the VAR_DATA_TYPE (if applicable) -; 3. Valid Min, Valid Max and/or Fill Value data types do not match the Dataset data type (if applicable) -; 3. Dataset, Valid Min, Valid Max and/or Fill value data types are not allowable - -;Allowable VAR_DATA_TYPEs -avdt=['','BYTE','SHORT','INTEGER','REAL','DOUBLE','','STRING','','','','','','',''] ;'LONG'] ;corresponding to IDL codes 1,2,3,4,5,7,14 (LONG/14 not used) -idl_name=['UNDEFINED','BYTE','SHORT','INTEGER','REAL','DOUBLE','COMPLEX','STRING','STRUCT','DCOMPLEX', $ - 'POINTER','OBJREF','UINT','ULONG','LONG64','ULONG64'] ;Note: Integer, Long and Float renamed Short, Integer and Real for sds_type -hdf_dt=['','DFNT_UINT8','DFNT_INT16','DFNT_INT32','DFNT_FLOAT32','DFNT_FLOAT64','','DFNT_CHAR8'] ;allowable HDF4 datatypes (note BYTE=8-bit unsigned) -;hdf_all=['DFNT_NONE','DFNT_UINT8','DFNT_INT16','DFNT_INT32','DFNT_FLOAT32','DFNT_FLOAT64','','DFNT_CHAR8', $ -; '','','','','DFNT_UINT16','DFNT_UINT32','DFNT_INT8'] -h5_dt=['H5T_INTEGER_0','H5T_INTEGER_1','H5T_FLOAT','H5T_STRING'] ;allowable HDF5/NC4 datatypes (0=unsigned, 1=signed) - -;dt_chk_labels=['Dataset','VAR_VALID_MIN','VAR_VALID_MAX','VAR_FILL_VALUE','VAR_DATA_TYPE'] ;dataset and attribute labels for data type checks -n_lab=N_ELEMENTS(dt_chk_labels) -dterr=STRARR(n_lab) ;non-zero indicates error - -IF var_name NE '' THEN usevn=var_name ELSE usevn=sds_name ;determine VAR_NAME value to use for error messages - -IF (ftype EQ 'H5') OR (ftype EQ 'N4') THEN BEGIN - ;need to convert hdftype and idltype values to equivalent idl_name and hdf_dt values - FOR i=0,n_lab-2 DO BEGIN - idltype[i]=idl_name[idltype[i]] ;convert IDL type code to type name - CASE 1 OF - hdftype[i] EQ 'H5T_FLOAT': BEGIN - IF idltype[i] EQ 'DOUBLE' THEN hdftype[i]='DFNT_FLOAT64' $ - ELSE hdftype[i]='DFNT_FLOAT32' - END - hdftype[i] EQ 'H5T_STRING': hdftype[i]='DFNT_CHAR8' - hdftype[i] EQ 'H5T_INTEGER_0': BEGIN - CASE idltype[i] OF - 'BYTE': hdftype[i]='DFNT_UINT8' - ('SHORT') OR ('UINT'): hdftype[i]='DFNT_UINT16' - ('INTEGER') OR ('ULONG'): hdftype[i]='DFNT_UINT32' - 'ULONG64': hdftype[i]='DFNT_UINT64' - ELSE: hdftype[i]='DFNT_UNDF' - ENDCASE - END - hdftype[i] EQ 'H5T_INTEGER_1': BEGIN - CASE idltype[i] OF - 'BYTE': hdftype[i]='DFNT_INT8' - 'SHORT': hdftype[i]='DFNT_INT16' - 'INTEGER': hdftype[i]='DFNT_INT32' - 'LONG64': hdftype[i]='DFNT_INT64' - ELSE: hdftype[i]='DFNT_UNDF' - ENDCASE - END - ELSE: - ENDCASE - ENDFOR -ENDIF ELSE IF ftype EQ 'N3' THEN BEGIN - ;need to convert hdftype and idltype values to equivalent idl_name and hdf_dt values - writeonce=0B - FOR i=0,n_lab-2 DO BEGIN - idltype[i]=idl_name[idltype[i]] ;convert IDL type code to type name - CASE 1 OF - hdftype[i] EQ 'BYTE_0': hdftype[i]='DFNT_UINT8' - hdftype[i] EQ 'BYTE_1': hdftype[i]='DFNT_INT8' - hdftype[i] EQ 'INT': hdftype[i]='DFNT_INT16' - hdftype[i] EQ 'LONG': hdftype[i]='DFNT_INT32' - hdftype[i] EQ 'FLOAT': hdftype[i]='DFNT_FLOAT32' - hdftype[i] EQ 'DOUBLE': hdftype[i]='DFNT_FLOAT64' - hdftype[i] EQ 'CHAR': hdftype[i]='DFNT_CHAR8' - ELSE: hdftype[i]='DFNT_UNDF' - ENDCASE - IF (idltype[i] EQ 'BYTE') AND (idltype[0] EQ 'BYTE') AND (~writeonce) THEN BEGIN - ;For VAR_VALID_MIN/MAX and VAR_FILL_VALUE only output if the dataset is of type BYTE as well - ;o/w probably means that the value was written as a character string instead of as numbers - INFOTXT_OUTPUT_A,[23],[usevn] - writeonce=1B - ENDIF - ENDFOR - -ENDIF - -;Check VAR_DATA_TYPE value -hdti=WHERE((idltype[n_lab-1] EQ avdt) AND (idltype[n_lab-1] NE ''),hdtcnt) -IF hdtcnt EQ 0 THEN BEGIN - IF idltype[n_lab-1] NE '' THEN $ - dterr[n_lab-1]=usevn+' '+dt_chk_labels[n_lab-1]+'='+idltype[n_lab-1]+' is not a valid GEOMS data type' -ENDIF - -;Check dataset, min, max and fill data types are valid -FOR i=0,n_lab-2 DO BEGIN - IF i EQ 0 THEN itxt=' value(s) ' ELSE itxt=' value ' - hdti=WHERE((hdftype[i] EQ hdf_dt) AND (hdftype[i] NE ''),hdtcnt) - IF hdtcnt EQ 0 THEN BEGIN ;identify the actual data type of the dataset - hdti=WHERE(idltype[i] EQ idl_name,hdtcnt) - IF hdtcnt EQ 0 THEN IF idltype[i] NE '' THEN hdti[0]=0 - CASE hdti[0] OF - -1: IF i EQ 0 THEN dterr[i]=usevn+' '+dt_chk_labels[i]+' data type is not identifiable' - 1: dterr[i]=usevn+' '+dt_chk_labels[i]+itxt+'must be 8-bit unsigned INTEGER (BYTE)' - 2: dterr[i]=usevn+' '+dt_chk_labels[i]+itxt+'must be 16-bit signed INTEGER' - 3: dterr[i]=usevn+' '+dt_chk_labels[i]+itxt+'must be 32-bit signed INTEGER' - ELSE: dterr[i]=usevn+' '+dt_chk_labels[i]+itxt+'not a valid GEOMS data type: '+idl_name[hdti[0]] - ENDCASE - ENDIF -ENDFOR - -;If valid VAR_DATA_TYPE is present check that the dataset and min, max and fill data types match -IF dterr[n_lab-1] EQ '' THEN BEGIN - ;Use error generated in SET_UP_STRUCTURE in idlcr8hdf instead - ;FOR i=0,n_lab-2 DO BEGIN - ; IF (idltype[n_lab-1] NE idltype[i]) AND (idltype[i] NE '') THEN BEGIN - ; dterr[i]=usevn+' '+dt_chk_labels[i]+' data type ('+hdftype[i]+') does not match VAR_DATA_TYPE='+idltype[n_lab-1] - ; ;note: overwrites any existing reported error - ; ENDIF - ;ENDFOR -ENDIF ELSE IF dterr[0] EQ '' THEN BEGIN - ;VAR_DATA_TYPE not valid so check min, max and fill data types against the dataset data type - FOR i=1,n_lab-2 DO BEGIN - IF (idltype[0] NE idltype[i]) AND (idltype[i] NE '') THEN BEGIN - dterr[i]=usevn+' '+dt_chk_labels[i]+' data type ('+idl_name[i]+') does not match Dataset data type ('+idl_name[0]+')' - ;note: overwrites any existing reported error - ENDIF - ENDFOR -ENDIF - -;Output information messages -FOR i=0,n_lab-1 DO $ - IF dterr[i] NE '' THEN INFOTXT_OUTPUT_A,[2],[dterr[i]] - -END ;Data_Type_Checks - - - -PRO read_hdf_sds, ifile, ga, sds, catinfo -;Procedure to read the contents of a GEOMS standard HDF or netCDF compatible file -;into session memory -; ---------- -;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org -; -; History: -; 20050729: Original IDLCR8ASCII Routine - Version 1.0 -; 20050912: Removed Common variable definition CATALOGINFO and made the variable a parameter passed -; to the procedure; Checks that the attribute variable names read by HDF_SD_GETINFO, match -; those recorded in DATA_VARIABLES in the Global Attributes. If not use the variable name -; listed in DATA_VARIABLES - Version 1.1 -; 20061004: Common variable definition WIDGET_WIN_A added for Error calls; Add option to read HDF5 -; files (needs IDL5.6 or newer); Add check that the HDF4 file has valid information on the -; number of global and variable attributes; Checks added in the event that the HDF4 or -; HDF5 file has been created by the NCSA utility programs H5toH4 or H4toH5 from an original -; AVDC/Envisat/NDACC HDF4 or HDF5 file; Ensures variable attributes and data are listed in -; the same order as that given under DATA_VARIABLES - Version 2.0 -; 20081020 - If the HDF4 file is created using the HDF4.2r3 library, then extra information -; regarding the dimensions of the VAR_DEPEND values may be included in the file (which -; show up as extra datasets in the HDF_SD_FILEINFO call). A check for this is carried out -; and, if found, the information is excluded from the output - Version 3.01 -; 20090311: Improve HDF4.2r3 library checks by comparing the number of datasets recorded under -; DATA_VARIABLES, with the number returned by the HDF_SD_FILEINFO call; Change 'WARNING' to -; 'INFORMATION and make calls to procedure INFOTXT_OUTPUT_A to avoid duplication. Add -; 'INFORMATION' text if anything irregular found with the datasets; Change 'Data dimensions -; exceed 8' and 'Data type not allowable' ERROR messages to 'INFORMATION' messages -; - Version 3.02 -; 20091208: Incorporate HDF_SD_ISCOORDVAR to differentiate between datasets and dimension variable -; names. Add check for allowable VAR_DATA_TYPE=STRING. Add all INFORMATION text messages to -; INFOTXT_OUTPUT_A procedure to avoid duplication of code that creates the messages; -; Add INFORMATION messages when reading HDF5 files; Improve checks for non-standard (i.e. -; non-groundbased) HDF input file - Version 3.03 -; 20101122: Numeric metadata values remain in their original data type when transferring to session -; memory, meaning: variable attribute labels and values now written to separate structure -; variables sds.va_l and sds.va_v (previously sds.va), and structure has 2 dimensions -; (n_sds, n_atts) instead of 1 (previously n_sds); The dataset is written to the initial -; n_atts index (i.e. sds[n_sds,0].data); Rename saved SDS datatype according to GEOMS rules -; (affects INT, LONG, and FLOAT); Compare HDF dimensions with VAR_SIZE values and transpose -; VAR_SIZE and VAR_DEPEND values if required - Version 4.0b1 -; 20111014: Add netCDF read capability - writes variable attributes to standard GEOMS variable -; attribute labels where possible, otherwise appends information to VAR_NOTES - Version 4.0b3 -; 20120426: Bug fix when a netCDF file only contains datasets with single dimensions, then VAR_SIZE -; information was not being extracted during the read process - Version 4.0b6 -; 20140325: Fix bug that caused crash when VAR_SIZE values were not of type string - Version 4.0b8 -; 20150127: Allow for VAR_FILL_VALUE values for string datasets to be written and saved as an -; (empty) string of the correct length. Ensure string datasets are all written and -; saved to the correct length (that of the longest string in the dataset) - Version 4.0b10 -; 20150217: Do not remove whitespace of any variable attribute values that are written as strings -; - Version 4.0b11 -; 20160614: Some HDF4 string datasets may not have 2 dimensions, so do not need to remove the first -; dimension (= maximum number of characters) - Version 4.0b14 -; 20160725: Fix bug causing program to crash when the number of DATA_VARIABLES values is greater -; than the number of datasets in the file - Version 4.0b15 -; 20161130: Assign SDS_name to catinfo for attributes when the VAR_NAME is not present; rewrite -; netCDF section to align it with HDF5 section and to account for dataset ordering that -; does not match that in DATA_VARIABLES - Version 4.0b16 -; 20161213: Fix bug when reading in netCDF files to stop all dataset variable attribute values to -; be converted to string format (now matches HDF4 and HDF5 checks). Fix bug that caused -; the program to crash when VAR_DEPEND=CONSTANT in netCDF files (sds_ndim=0) - Version 4.0b17 -; 20161218: Fix bug when reading netCDF attribute values that are of type byte rather than string - -; Version 4.0b18 -; 20180218 Fix bug that caused multi-dimensional array ordering to not be correctly identified -; if the datasets have the same VAR_SIZE. Now rev_vd_vs changes to 1 (VAR_SIZE and -; VAR_DEPEND ordering is reversed) if rev_vd_vs=2 after all the checks - Version 4.0b19 -; 20181116 Fix bug that caused multi-dimensional array ordering to not be correctly written if -; not consistent through the file i.e. rev_vd_vs eq 3. Now rev_vd_vs changes to 1 -; (VAR_SIZE, VAR_DEPEND and dataset ordering is reversed), but information message -; advising of the issue is still reported - Version 4.0b20 -; 20190514 Fix bug that occurred when GEOMS variable attribute information is missing from an -; HDF4 file but written to the heap structure based on the contents of the file. Previously -; the variable attribute label was not written correctly - Version 4.0b21 -; 20190806 Provide support for reading netCDF4 files. Currently uses the HDF5 routines to do this -; so need to account for features unique to netCDF4 such as; the inclusion of dimension -; names; standard netCDF attribute names; empty attribute values that do not include a -; null termination character (which causes an error in the H5A_Read routine) -; - Version 4.0b22 -; 20190821 Fixed bugs associated with H5 TAG_NAMES checks introduced in v4.0b22 - Version 4.0b23 -; 20200930 Add sds_name to TEST_DIM_ORDER procedure call - Version 4.0b24 -; 20220805 For NetCDF3 or 4, if SDS_NAME NE VAR_NAME then converts non-alphanumeric VAR_NAME -; characters to '_' and compares with SDS_NAME. If there is a match then program quietly -; converts SDS_NAME to VAR_NAME for testing, otherwise generates an error; Add code to -; collect information for the call to the DATA_TYPE_CHECKS procedure; Generate an -; INFORMATION/ERROR message if VAR_UNITS is numeric (same as VAR_SIZE); INFORMATION/ERROR -; message added if a mis-match is found between the input file name and the FILE_NAME attribute value. - -; -; Inputs: ifile - a string containing the name of the input file to be read in. -; catinfo - a string array identifying the type of input file ('H4','H5','N4','N3') -; -; Outputs: ga - a string array containing the global attribute labels and values extracted from the HDF -; file. -; sds - a structure using pointers, of size [n_sds,n_atts], containing the variable attribute -; labels and values (sds[n,m].va_l and sds[n,m].va_v) and the data (sds[n,0].data) -; extracted from the HDF file. -; catinfo - a string array of size [n_sds,4] (where n_sds is the number of datasets in the HDF -; file), containing information on the variable name, data type, data dimension, and -; number of attributes. -; -; Called by: IDLCR8ASCII -; -; Subroutines Called: STOP_WITH_ERROR_A (if error state detected) -; INFOTXT_OUTPUT_A (if program can make a change) -; DATA_TYPE_CHECKS (to perform data type checks on the datasets and some attributes) -; Possible Conditions for STOP_WITH_ERROR call (plus [line number] where called): -; 1. No global attributes or datasets present in the root group of an HDF5 file. -; 2. The number of global attributes or datasets is zero or not able to be determined -; in an HDF4 file. -; -; Information Conditions (when the program is able to make changes): -; 1. DATA_VARIABLES global attribute not found or has no values. -; 2. Number of datasets given under DATA_VARIABLES is not equal to the number saved to the -; file. -; 3. The number of dimensions in a dataset exceeds 8. -; 4. The data type of a dataset is not BYTE, SHORT, INTEGER, LONG, REAL, DOUBLE, or STRING. -; 5. Dataset name is truncated or does not match VAR_NAME value. -; 6. Metadata label VAR_NAME not found for the dataset. -; 7. VAR_NAME does not match any DATA_VARIABLES values. -; 8. No Variable Attributes returned after HDF call. -; 9. Metadata Dataset listing order may not match the order of the Global Attribute -; DATA_VARIABLES Values. -; 10. Variable is written to the HDF file as a coordinate variable. - -COMMON WIDGET_WIN_A - -;Note any calibrated (scaled) HDF4 dataset is corrected using the formula specified in -;UG_print42r3.pdf pg. 3-107: orig = cal * (cal_val - offset) -ncsa_cal=['scale_factor','scale_factor_err','add_offset','add_offset_err','calibrated_nt'] - -;Initialize scalar to indicate whether VAR_DEPEND and VAR_SIZE values need to be reversed -rev_vd_vs=2 ;1 = reverse attribute values; 0 = no reverse (and will generate message); -;2 = no action required; 3 = rev_vd_vs changed between 0 and 1 during checking, therefore error -;Note if rev_vd_vs is 2 or 3 after all the checks then it will be changed to 1 so that dimensions -;are swapped by default - -;List of Standard GEOMS Variable Attributes -attr_arr_data=['VAR_NAME','VAR_DESCRIPTION','VAR_NOTES','VAR_SIZE','VAR_DEPEND',$ - 'VAR_DATA_TYPE','VAR_UNITS','VAR_SI_CONVERSION','VAR_VALID_MIN',$ - 'VAR_VALID_MAX','VAR_FILL_VALUE'] -n_aad=N_ELEMENTS(attr_arr_data) - -;Possible error messages for this procedure -proname='Read_HDF_SDS procedure: ' -errtxt=STRARR(2) -errtxt[0]=' found in the Root Group of the HDF5 File.' -errtxt[1]=' is zero or not able to be determined (check for corrupted file).' -lu=-1 - -slabel=['VAR_UNITS','VAR_SI_CONVERSION','VAR_VALID_MIN','VAR_VALID_MAX','VAR_FILL_VALUE'] ;save values as is if dataset is of type STRING -dt_chk_labels=['Dataset','VAR_VALID_MIN','VAR_VALID_MAX','VAR_FILL_VALUE','VAR_DATA_TYPE'] ;dataset and attribute labels for data type checks -n_lab=N_ELEMENTS(dt_chk_labels) - -;If necessary, free up memory by destroying the heap variables pointed at by its pointer arguments -;from previous calls to this program -IF N_ELEMENTS(sds) NE 0 THEN PTR_FREE,sds.va_l,sds.va_v,sds.data - -;Define the HDF SDS storage structure -sds_set={va_l: PTR_NEW(), $ ;Variable Attribute Labels - va_v: PTR_NEW(), $ ;Variable Attribute Values - data: PTR_NEW()} ;SD data array - -ftype=catinfo[0,0] -ifilechk=STRLOWCASE(STRTRIM(FILE_BASENAME(ifile),2)) ;for FILE_NAME check -IF ftype EQ 'H4' THEN BEGIN - - ;The HDF_SD_START function opens an HDF file and initializes the SD interface. - sd_id=HDF_SD_START(ifile,/READ) - - ;Determine the number of SDS (n_sds) and global attributes (n_ga) found - ;in the current file. - HDF_SD_FILEINFO,sd_id,n_sds,n_ga - IF n_sds EQ 0L THEN ntxt='Number of SDS datasets' $ - ELSE IF n_ga EQ 0L THEN ntxt='Number of Global Attributes' $ - ELSE ntxt='' - IF ntxt NE '' THEN BEGIN - STOP_WITH_ERROR_A,o3[3]+proname,ntxt+errtxt[1],lu & RETURN - ENDIF - ga=STRARR(n_ga) ;set the Global Attribute dimensions - vn=[''] ;initialize vn array (to hold Dataset names) - n_sds_hold=n_sds & n_vn=0L & do_coordvar=0 - - ;Read the file's Global Attributes and determine number of DATA_VARIABLES - FOR i=0L,n_ga-1L DO BEGIN - ;The HDF_SD_ATTRINFO procedure reads or retrieves info about an SD attribute. - HDF_SD_ATTRINFO,sd_id,i,NAME=ga_name,DATA=ga_data,HDF_TYPE=ga_hdftype - ga_name=STRTRIM(ga_name,2) & ga_data=STRTRIM(ga_data,2) - ;Assign global attributes to ga - ga[i]=ga_name+'='+ga_data - ;read list of DATA_VARIABLES into array - IF STRUPCASE(ga_name) EQ 'DATA_VARIABLES' THEN vn=STRSPLIT(ga_data,' ;',/Extract,COUNT=n_vn) - ;String labels for GA not checked at this time as extra Global Attributes could be numeric - ;IF (ga_hdftype NE 'DFNT_NONE') AND (ga_hdftype NE 'DFNT_CHAR8') THEN $ - ; INFOTXT_OUTPUT_A,[20],['Global Attribute',STRTRIM(ga_name,2)] - IF STRUPCASE(ga_name) EQ 'FILE_NAME' THEN BEGIN - ;check that the actual file name matches the FILE_NAME entry - IF STRLOWCASE(STRTRIM(ga_data,2)) NE ifilechk THEN INFOTXT_OUTPUT_A,[24],[ga_data] - ENDIF - ENDFOR - - IF n_sds_hold NE n_vn THEN BEGIN - ;Do check for sds being a Dimension attribute - do_coordvar=1 - FOR i=0L,n_sds-1L DO BEGIN - ;The HDF_SD_SELECT function returns an SD dataset ID given the current - ;SD interface ID, and the zero-based SD dataset index. - sds_id=HDF_SD_SELECT(sd_id,i) - IF HDF_SD_ISCOORDVAR(sds_id) THEN n_sds_hold=n_sds_hold-1L - ;Closes the SDS interface. - HDF_SD_ENDACCESS,sds_id - ENDFOR - IF n_sds_hold EQ 0L THEN BEGIN - STOP_WITH_ERROR_A,o3[3]+proname,'Number of SDS datasets'+errtxt[1],lu - RETURN - ENDIF - - IF n_sds_hold NE n_vn THEN $ - INFOTXT_OUTPUT_A,[0],[STRTRIM(n_vn,2),STRTRIM(n_sds_hold,2),'HDF_SD_FILEINFO'] - ;DATA_VARIABLES values not read successfully, or number of datasets given under DATA_VARIABLES - ;is not equal to the number saved to the file - ENDIF - - ;Determine maximum number of attributes, and match the dataset order to DATA_VARIABLES list if possible - max_atts=0L & oi=LONARR(n_sds_hold) - dv_order=1 & c_sds=0L - catinfo=STRARR(n_sds_hold,4) ;output info for catalog output - FOR i=0L,n_sds-1L DO BEGIN - vnv='' & sds_name='' - hdftype=STRARR(n_lab-1) & idltype=STRARR(n_lab) ;to hold data types for dataset, min, max and fill values, and VAR_DATA_TYPE (idltype only) - ;The HDF_SD_SELECT function returns an SD dataset ID given the current - ;SD interface ID, and the zero-based SD dataset index. - sds_id=HDF_SD_SELECT(sd_id,i) - - IF (do_coordvar EQ 0) OR (NOT HDF_SD_ISCOORDVAR(sds_id)) THEN BEGIN - ;The HDF_SD_GETINFO procedure retrieves information about an SD dataset. - HDF_SD_GETINFO,sds_id,NATTS=sds_natts, $ ;no. attributes - HDF_TYPE=sds_hdftype, $ ;HDF data type - TYPE=sds_type, $ ;data type - DIMS=sds_dim, $ ;dimension information (automatically reversed by IDL) - NAME=sds_name ;dataset name - - ;Check for string attribute and, if so, remove the first dimension - IF (sds_type EQ 'STRING') AND (N_ELEMENTS(sds_dim) NE 1) THEN sds_dim=sds_dim[1:N_ELEMENTS(sds_dim)-1] - - ;Check for multi-dimensional dataset - n_sds_dim=N_ELEMENTS(sds_dim) - IF n_sds_dim GT 1 THEN multi_dim=1 - - ;Check number of dimensions - IF n_sds_dim gt 8 THEN INFOTXT_OUTPUT_A,[1],[sds_name,STRTRIM(n_sds_dim,2)] - - ;Check for coordinate variable - IF HDF_SD_ISCOORDVAR(sds_id) THEN INFOTXT_OUTPUT_A,[8],[sds_name] - - ;Check that a dataset name has been successfully read - sds_name=STRTRIM(sds_name,2) - IF sds_name EQ '' THEN sds_name='N/A' - - ;Rename data type to be compatible with the Metadata guidelines - sds_type=STRTRIM(STRUPCASE(sds_type),2) - IF sds_type EQ 'INT' THEN sds_type='SHORT' $ - ELSE IF sds_type EQ 'LONG' THEN sds_type='INTEGER' $ - ELSE IF sds_type EQ 'FLOAT' THEN sds_type='REAL' - - hdftype[0]=sds_hdftype & idltype[0]=sds_type - - IF sds_natts NE 0L THEN BEGIN - IF sds_natts GT max_atts THEN max_atts=sds_natts - vnf=0 & lcnt=0 & vcnt=sds_natts - ;Extract the variable attributes - FOR j=0L,sds_natts-1L DO BEGIN - ;The HDF_SD_ATTRINFO procedure reads or retrieves information about an SD attribute. - HDF_SD_ATTRINFO,sds_id,j,NAME=va_name,DATA=va_value,HDF_TYPE=va_hdftype,TYPE=va_type - va_name=STRTRIM(va_name,2) - schk=WHERE(STRUPCASE(va_name) EQ slabel,schkcnt) - - ;Rename data type to be compatible with the Metadata guidelines - va_type=STRTRIM(STRUPCASE(va_type),2) - IF va_type EQ 'INT' THEN va_type='SHORT' $ - ELSE IF va_type EQ 'LONG' THEN va_type='INTEGER' $ - ELSE IF va_type EQ 'FLOAT' THEN va_type='REAL' - - IF ((sds_type EQ 'STRING') AND (schkcnt EQ 0)) OR ((sds_type NE 'STRING') AND (va_type EQ 'STRING')) THEN $ - va_value=STRTRIM(va_value,2) - ;Check returned dataset attributes (sds_) with saved attributes (va_) - IF (STRUPCASE(va_name) EQ 'VAR_NAME') AND (vnf EQ 0) THEN BEGIN - ;check that the dataset name matches the VAR_NAME - vnf=1 - IF STRUPCASE(va_value) NE STRUPCASE(sds_name) THEN BEGIN - ;Try and determine which is wrong - Dataset name or VAR_NAME - default is Dataset name - li=WHERE((STRUPCASE(sds_name) EQ STRUPCASE(vn)) AND (vn[0] NE ''),lcnt) - ;If lcnt NE 0 then Dataset name matches DATA_VARIABLES value so assume that VAR_NAME is wrong - IF lcnt NE 0 THEN usesds=1 ELSE usesds=0 - INFOTXT_OUTPUT_A,[3,4,usesds],[sds_name,va_value] - IF usesds EQ 0 THEN sds_name=va_value ELSE va_value=sds_name - ENDIF - vnv=va_value - ;match the order of the dataset to the DATA_VARIABLES list - li=WHERE(STRUPCASE(va_value) EQ STRUPCASE(vn),lcnt) - ENDIF - IF STRUPCASE(va_name) EQ 'VAR_DATA_TYPE' THEN idltype[n_lab-1]=STRUPCASE(STRTRIM(va_value,2)) $ - ELSE IF STRUPCASE(va_name) EQ 'VAR_UNITS' THEN BEGIN - ;check that VAR_UNITS is a string value (in event that VAR_UNITS=1) - IF va_type NE 'STRING' THEN $ - INFOTXT_OUTPUT_A,[20],['VAR_UNITS',STRTRIM(sds_name,2)] - ENDIF ELSE IF STRUPCASE(va_name) EQ 'VAR_DEPEND' THEN $ ;do dimension ordering checks - TEST_DIM_ORDER,'VD',va_value,va_type,sds_dim,sds_name,rev_vd_vs $ - ELSE IF STRUPCASE(va_name) EQ 'VAR_SIZE' THEN $ ;do dimension ordering checks - TEST_DIM_ORDER,'VS',va_value,va_type,sds_dim,sds_name,rev_vd_vs $ - ELSE IF (STRUPCASE(va_name) EQ 'VAR_VALID_MIN') THEN BEGIN - hdftype[1]=va_hdftype & idltype[1]=va_type - ENDIF ELSE IF (STRUPCASE(va_name) EQ 'VAR_VALID_MAX') THEN BEGIN - hdftype[2]=va_hdftype & idltype[2]=va_type - ENDIF ELSE IF (STRUPCASE(va_name) EQ 'VAR_FILL_VALUE') THEN BEGIN - hdftype[3]=va_hdftype & idltype[3]=va_type - ENDIF - ENDFOR - ENDIF ELSE BEGIN ;No Variable Attributes found - vcnt=3 - ;Can the SDS_NAME be matched with a DATA_VARIABLES value? - li=WHERE(STRUPCASE(sds_name) EQ STRUPCASE(vn),lcnt) - IF lcnt NE 0 THEN vnf=1 ELSE vnf=0 - INFOTXT_OUTPUT_A,[5],[sds_name,'HDF_SD_GETINFO'] - ENDELSE - - ;Check for possible problems with determining array index - IF lcnt NE 0 THEN BEGIN - IF li[0] GE n_sds_hold THEN dv_order=0 $ ;can occur when number of DATA_VARIABLE values is greater than number of datasets - ELSE IF catinfo[li[0],0] NE '' THEN dv_order=0 ;This array has already been written to - ENDIF ELSE IF (lcnt EQ 0) OR (vnf EQ 0) THEN dv_order=0 - ;Write out information text if sds_name cannot be matched and determine array index - IF dv_order EQ 0 THEN BEGIN - li=WHERE(catinfo[*,0] EQ '') ;determine lowest available array index to write info to - in0=[4,vnf,lcnt,n_vn] - IF (vnf EQ 0) OR ((lcnt EQ 0) AND (n_vn NE 0L)) THEN INFOTXT_OUTPUT_A,in0,[sds_name,vnv] - ENDIF - - oi[c_sds]=li[0] ;Attribute order index - c_sds=c_sds+1L - - catinfo[li[0],0]=sds_name & catinfo[li[0],1]=sds_type - catinfo[li[0],2]=STRTRIM(sds_dim[0],2) & catinfo[li[0],3]=vcnt - IF N_ELEMENTS(sds_dim) GT 1 THEN $ - FOR j=1,N_ELEMENTS(sds_dim)-1 DO catinfo[li[0],2]=catinfo[li[0],2]+';'+STRTRIM(sds_dim[j],2) - ENDIF - - ;Do data type checks on the Dataset, VAR_DATA_TYPE value and data types of VAR_VALID_MIN, VAR_VALID_MAX and VAR_FILL_VALUES - DATA_TYPE_CHECKS,ftype,dt_chk_labels,hdftype,idltype,sds_name,vnv - ;Closes the SDS interface. - HDF_SD_ENDACCESS,sds_id - ENDFOR - - IF max_atts EQ 0L THEN max_atts=3 ;Use information from HDF_SD_GETINFO call only - - rev_vd_vsh=rev_vd_vs ;keep original value in hold variable (required if rev_vd_vs eq 3) - IF (rev_vd_vs EQ 2) OR (rev_vd_vs EQ 3) THEN rev_vd_vs=1 ;change default so that VAR_DEPEND and VAR_SIZE - ;are reversed if not o/w changed in TEST_DIM_ORDER - - ;Dimension the structure to the number of datasets x number of attributes - sds=REPLICATE(sds_set, n_sds_hold, max_atts) - - ;Write attributes to structure - c_sds=0L - !QUIET=1 ;suppress system error and information messages (used for CALDATA call in HDF_SD_GETINFO) - FOR i=0L,n_sds-1L DO BEGIN - notranspose=1 ;0/1 will change to 0 if the dataset needs to be transposed - ;The HDF_SD_SELECT function returns an SD dataset ID given the current - ;SD interface ID, and the zero-based SD dataset index. - sds_id=HDF_SD_SELECT(sd_id,i) - nocal=1 ;Boolean to identify dataset which has been calibrated - IF (do_coordvar EQ 0) OR (NOT HDF_SD_ISCOORDVAR(sds_id)) THEN BEGIN - ;The HDF_SD_GETINFO procedure retrieves information about an SD dataset. - ;Note - any saved pre-defined attributes will be called with HDF_SD_ATTRINFO call - HDF_SD_GETINFO,sds_id,NATTS=sds_natts, $ ;no. attributes - HDF_TYPE=sds_hdftype, $ ;HDF data type - TYPE=sds_type, $ ;data type - DIMS=sds_dim, $ ;dimension information - NAME=sds_name, $ ;dataset name - CALDATA=sds_cal ;pre-defined calibration info - - ;Check to see whether data has had scale factor and offset applied - IF (sds_cal.Cal NE 0.D) OR (sds_cal.Offset NE 0.D) THEN BEGIN - nocal=0 ;Dataset has been calibrated - ;identify datatype of the original dataset - CASE 1 OF - sds_cal.Num_Type EQ 21L: vdt_val='BYTE' - sds_cal.Num_Type EQ 22L: vdt_val='SHORT' - sds_cal.Num_Type EQ 24L: vdt_val='INTEGER' - sds_cal.Num_Type EQ 5L: vdt_val='REAL' - sds_cal.Num_Type EQ 6L: vdt_val='DOUBLE' - ELSE: vdt_val='' ;invalid or cannot determine original datatype - ENDCASE - ENDIF - - IF sds_natts NE 0L THEN BEGIN - ;Extract the variable attributes - jh=0 ;will only loop if attribute is not a pre-defined calibration attribute - FOR j=0L,sds_natts-1L DO BEGIN - ;The HDF_SD_ATTRINFO procedure reads or retrieves information about an SD attribute. - HDF_SD_ATTRINFO,sds_id,j,NAME=va_name,DATA=va_value,TYPE=va_type - va_name=STRTRIM(va_name,2) - - schk=WHERE(STRUPCASE(va_name) EQ slabel,schkcnt) - IF ((sds_type EQ 'STRING') AND (schkcnt EQ 0)) OR ((sds_type NE 'STRING') AND (va_type EQ 'STRING')) THEN $ - va_value=STRTRIM(va_value,2) - IF (nocal EQ 0) AND (STRUPCASE(va_name) EQ 'VAR_DATA_TYPE') THEN BEGIN - ;If required prepare INFORMATION text (written after DATA has been corrected) - IF (vdt_val NE STRUPCASE(va_value)) AND (vdt_val NE '') THEN BEGIN - io_arr=[STRTRIM(sds_name,2),va_value,vdt_val] - va_value=vdt_val - ENDIF ELSE BEGIN - io_arr=[''] & vdt_val=va_value - ENDELSE - ENDIF - - IF ((STRUPCASE(va_name) EQ 'VAR_DEPEND') OR (STRUPCASE(va_name) EQ 'VAR_SIZE')) AND $ - ((rev_vd_vs MOD 10 EQ 1) OR (rev_vd_vs EQ 20)) THEN BEGIN ;need to reverse the va_values - - IF va_type NE 'STRING' THEN BEGIN ;VAR_SIZE contains numeric values instead of in the form of a string - vavhold='' & n_vav=N_ELEMENTS(va_value) - FOR k=0,n_vav-1 DO BEGIN - IF k EQ n_vav-1 THEN vtxt='' ELSE vtxt=';' - vavhold=vavhold+STRTRIM(va_value[k],2)+vtxt - ENDFOR - ENDIF ELSE vavhold=va_value - - vs_v=STRCOMPRESS(STRSPLIT(vavhold,';, ',/EXTRACT,COUNT=rcnt),/REMOVE_ALL) - IF rcnt GT 1 THEN BEGIN ;multi-dimensions - IF (rev_vd_vs EQ 20) THEN notranspose=0 ;need to transpose dataset as well - vs_v=REVERSE(vs_v) - FOR k=0,rcnt-1 DO $ - IF k EQ 0 THEN va_value=vs_v[k] ELSE va_value=va_value+';'+vs_v[k] - ENDIF - ENDIF - - ;Test for pre-defined calibration attribute and do not save as the data have - ;been coverted back to original values - pi=WHERE(STRLOWCASE(va_name) EQ ncsa_cal,pcnt) - IF pcnt EQ 0 THEN BEGIN - sds[oi[c_sds],jh].va_l=PTR_NEW(va_name) - sds[oi[c_sds],jh].va_v=PTR_NEW(va_value) - jh=jh+1 - ENDIF - ENDFOR - ENDIF ELSE BEGIN ;No Variable Attributes found - va_lab=['VAR_NAME','VAR_SIZE','VAR_DATA_TYPE'] - FOR j=0,2 DO sds[oi[c_sds],j].va_l=PTR_NEW(va_lab[j]) - sds[oi[c_sds],0].va_v=PTR_NEW(sds_name) - sds[oi[c_sds],1].va_v=PTR_NEW(sds_dim) - ;Rename format to be compatible with the Metadata guidelines - IF sds_type EQ 'INT' THEN sds_type='SHORT' $ - ELSE IF sds_type EQ 'LONG' THEN sds_type='INTEGER' $ - ELSE IF sds_type EQ 'FLOAT' THEN sds_type='REAL' - sds[oi[c_sds],2].va_v=PTR_NEW(sds_type) - ENDELSE - ;Extract the data - HDF_SD_GETDATA,sds_id,datasize - ;Test to see if dataset dimension ordering needs to be changed - IF notranspose EQ 0 THEN datasize=TRANSPOSE(datasize) - - IF nocal EQ 0 THEN BEGIN - ;apply scale factor and offset corrections to data and write text to log file - CASE 1 OF - vdt_val EQ 'BYTE': datasize=BYTE(sds_cal.Cal*(datasize-sds_cal.Offset)) - vdt_val EQ 'SHORT': datasize=FIX(sds_cal.Cal*(datasize-sds_cal.Offset)) - vdt_val EQ 'INTEGER': datasize=LONG(sds_cal.Cal*(datasize-sds_cal.Offset)) - vdt_val EQ 'REAL': datasize=FLOAT(sds_cal.Cal*(datasize-sds_cal.Offset)) - ELSE: datasize=DOUBLE(sds_cal.Cal*(datasize-sds_cal.Offset)) - ENDCASE - INFOTXT_OUTPUT_A,[9],[STRTRIM(sds_name,2)] - IF io_arr[0] NE '' THEN INFOTXT_OUTPUT_A,[10],io_arr - ENDIF - sds[oi[c_sds],0].data=PTR_NEW(datasize) - c_sds=c_sds+1L - ENDIF - - ;Closes the SDS interface. - HDF_SD_ENDACCESS,sds_id - ENDFOR - !QUIET=0 ;Allow system error and information messages - ;The HDF_SD_END function closes the SD interface to an HDF file. - HDF_SD_END,sd_id -ENDIF ELSE IF (ftype EQ 'H5') OR (ftype EQ 'N4') THEN BEGIN ;HDF5 or netCDF4 format file - stdfm=1 ;Boolean indicating standard AVDC/EVDC/NDACC H5 format or not - dv_order=1 ;Boolean indicating whether Metadata Variable listing order matches DATA_VARIABLES values - n_vn=0L ;Initialize the number of Variable Names in the standard format h5 file - - ;labels to quietly ignore if the file is netCDF4 - nc4_ignore=['reference_list','dimension_list','class','name','_netcdf4dimid'] - - ;Do bulk read so errors generated by netCDF4 inconsistencies can be identified without - ;crashing the program e.g. null string attribute values - h5p=H5_PARSE(ifile) - - hdf_file_id=H5F_OPEN(ifile) - sd_id=H5G_OPEN(hdf_file_id,'/') - - ;Get number of attributes within the root group (these should be the Global Attributes) - n_ga=H5A_GET_NUM_ATTRS(sd_id) - ;Get the number of objects within the root group (including datasets, groups etc) - n_obj=H5G_GET_NMEMBERS(sd_id,'/') - - IF n_obj NE 0L THEN BEGIN - d_obj=INTARR(n_obj) & g_obj=INTARR(n_obj) - ;Determine number of datasets. Note: if H5 file created using H4toH5 then an extra - ;group will have been created which will not be used. Also datasets may be listed - ;alphabetically. This section also determines whether the H5 file is a standard - ;AVDC/EVDC/NDACC groundbased file or otherwise. - FOR i=0L,n_obj-1L DO BEGIN - sds_name=H5G_GET_MEMBER_NAME(sd_id,'/',i) - h5fstat=H5G_GET_OBJINFO(sd_id,sds_name) - IF h5fstat.type EQ 'DATASET' THEN d_obj[i]=1 $ - ELSE IF h5fstat.type EQ 'GROUP' THEN g_obj[i]=1 - ENDFOR - di=WHERE(d_obj EQ 1,n_sds) - gi=WHERE(g_obj EQ 1,n_grp) - IF (n_ga EQ 0) AND (n_sds EQ 0) AND (n_grp NE 0) THEN stdfm=0 - ENDIF ELSE n_sds=0L - - IF stdfm EQ 0 THEN catinfo[0,0]='HE5' ;non-standard dataset so need to call READ_NONSTD_H5 - - IF n_ga NE 0 THEN BEGIN - ga=STRARR(n_ga) ;set the Global Attribute dimensions - vn=[''] ;initialize vn array (to hold Dataset names) - ;Read in the Global Attribute labels and values - FOR i=0L,n_ga-1L DO BEGIN - ga_id=H5A_OPEN_IDX(sd_id,i) - ga_name=H5A_GET_NAME(ga_id) & ga_name=STRTRIM(ga_name,2) - - ;test for valid attribute value - can be invalid if it is netCDF4 and equal to '' - gni=WHERE(TAG_NAMES(h5p) EQ STRUPCASE(ga_name)) - attest=h5p.(gni[0]) - IF STRTRIM(attest._data[0],2) EQ '<read error>' THEN ga_data='' $ - ELSE BEGIN - dt_id=H5A_GET_TYPE(ga_id) - ga_data=H5A_READ(ga_id) & ga_data=STRTRIM(ga_data,2) - ga_datatype=H5T_GET_CLASS(dt_id) - H5T_CLOSE,dt_id - ;String labels for GA not checked at this time as extra Global Attributes could be numeric - ;IF ga_datatype NE 'H5T_STRING' THEN $ - ; INFOTXT_OUTPUT_A,[20],['Global Attribute',STRTRIM(ga_name,2)] - ENDELSE - ;Check for _GLOSDS suffix and strip (added for compatibility with H4toH5 utility) - IF STRMID(STRUPCASE(ga_name),STRLEN(ga_name)-7) EQ '_GLOSDS' THEN $ - ga_name=STRMID(ga_name,0,STRPOS(ga_name,'_',/Reverse_Search)) - WHILE STRMID(ga_name,STRLEN(ga_name)-1) EQ '_' DO ga_name=STRMID(ga_name,0,STRLEN(ga_name)-1) - ga[i]=ga_name+'='+ga_data - H5A_CLOSE,ga_id - ;Extract the list of the DATA_VARIABLES - IF STRUPCASE(ga_name) EQ 'DATA_VARIABLES' THEN vn=STRSPLIT(ga_data,' ;',/Extract,COUNT=n_vn) - IF STRUPCASE(ga_name) EQ 'FILE_NAME' THEN BEGIN - ;check that the actual file name matches the FILE_NAME entry - IF STRLOWCASE(STRTRIM(ga_data,2)) NE ifilechk THEN INFOTXT_OUTPUT_A,[24],[ga_data] - ENDIF - ENDFOR - ENDIF ELSE IF stdfm EQ 1 THEN BEGIN - STOP_WITH_ERROR_A,o3[3]+proname,'No Global Attributes'+errtxt[0],lu - RETURN - ENDIF - - IF n_sds NE 0L THEN BEGIN - NC_DIMENSION_CHK,'H5',sd_id,di,n_sds,dgi,n_sdsg ;check for netCDF dimension names (will quietly ignore) - di=dgi & n_sds=n_sdsg - IF n_vn NE n_sds THEN $ - INFOTXT_OUTPUT_A,[0],[STRTRIM(n_vn,2),STRTRIM(n_sds,2),'H5G_GET_NMEMBERS'] - ;DATA_VARIABLES values not read successfully, or number of datasets given under DATA_VARIABLES - ;is not equal to the number saved to the file - - ;Determine maximum number of Attributes, correct dataset order, and dimension order - max_atts=0L & oi=LONARR(n_sds) - dv_order=1 - catinfo=STRARR(n_sds,4) ;output info for catalog output - FOR i=0,n_sds-1 DO BEGIN - vnv='' & sds_name='' - hdftype=STRARR(n_lab-1) & idltype=STRARR(n_lab) ;to hold data types for dataset, min, max and fill values, and VAR_DATA_TYPE (idltype only) - - sds_name=H5G_GET_MEMBER_NAME(sd_id,'/',di[i]) - ds_id=H5D_OPEN(sd_id,sds_name) - ;need to swap non-alphanumeric characters to '_' to match names from h5_parse for attribute values test - h5p_name=ALPHA_NUMERIC_UNDERSCORE(STRUPCASE(sds_name)) - - IF sds_name EQ '' THEN sds_name='N/A' - - ;determine dataset datatype - dt_id=H5D_GET_TYPE(ds_id) - dt_datatype=H5T_GET_CLASS(dt_id) - IF dt_datatype EQ 'H5T_INTEGER' THEN BEGIN - dt_sign=H5T_GET_SIGN(dt_id) ;0=unsigned; 1=signed - dt_datatype=dt_datatype+'_'+STRTRIM(dt_sign,2) - ;Byte needs to be unsigned, Short and Integer are signed - ENDIF - H5T_CLOSE,dt_id - - datasize=H5D_READ(ds_id) - sds_dim=SIZE(datasize,/DIMENSIONS) & n_sds_dim=N_ELEMENTS(sds_dim) - FOR sdl=0,n_sds_dim-1 DO IF sds_dim[sdl] EQ 0 THEN sds_dim[sdl]=1 ;constant scalar h5 - IF (ftype EQ 'N4') AND (dt_datatype EQ 'H5T_STRING') THEN BEGIN - ;need to do char_dim checks for STRING datasets and remove the string length dimension - char_dim=sds_dim[0] - IF n_sds_dim EQ 1 THEN sds_dim[0]=1 $ - ELSE BEGIN - sds_dim=sds_dim[1:n_sds_dim-1] & n_sds_dim=n_sds_dim-1 - ENDELSE - dschk=STRARR(sds_dim) - FOR sdl=0L,char_dim-1L DO dschk=dschk+datasize[sdl,*] - datasize=dschk - ENDIF - sds_type=SIZE(datasize,/TYPE) - - hdftype[0]=dt_datatype & idltype[0]=STRTRIM(sds_type,2) - - ;Determine the number of attributes associated with the dataset - sds_natts=H5A_GET_NUM_ATTRS(ds_id) - IF sds_natts GT max_atts THEN max_atts=sds_natts - - IF sds_natts NE 0L THEN BEGIN - vnf=0 & lcnt=0 - ci=STRARR(4) ;array containing catalog attributes - ;Read in Dataset Attributes - jj=0 ;use this index in the event of invalid attribute names (e.g. dimension_list from netCDF4 file) - FOR j=0L,sds_natts-1L DO BEGIN - da_id=H5A_OPEN_IDX(ds_id,j) - da_name=H5A_GET_NAME(da_id) & da_name=STRTRIM(da_name,2) - - ;determine attribute datatype - at_id=H5A_GET_TYPE(da_id) - at_datatype=H5T_GET_CLASS(at_id) - IF at_datatype EQ 'H5T_INTEGER' THEN BEGIN - at_sign=H5T_GET_SIGN(at_id) ;0=unsigned; 1=signed - at_datatype=at_datatype+'_'+STRTRIM(at_sign,2) - ;Byte needs to be unsigned, Short and Integer are signed - ENDIF - H5T_CLOSE,at_id - - nc4i=WHERE(STRLOWCASE(da_name) EQ nc4_ignore,nc4icnt) - IF nc4icnt EQ 0 THEN BEGIN - ;test for valid attribute value - can be invalid if it is netCDF4 and equal to '' - gni=WHERE(TAG_NAMES(h5p) EQ STRUPCASE(h5p_name)) ;Tag index from h5_parse read - attest=h5p.(gni[0]) - gni=WHERE(TAG_NAMES(attest) EQ STRUPCASE(da_name)) - attestx=attest.(gni[0]) - IF STRTRIM(attestx._data[0],2) EQ '<read error>' THEN va_value='' $ - ELSE va_value=H5A_READ(da_id) - IF SIZE(va_value,/TYPE) EQ 7 THEN va_type='STRING' ELSE va_type='NON_STRING' - idltypehold=STRTRIM(SIZE(va_value,/TYPE),2) - schk=WHERE(STRUPCASE(da_name) EQ slabel,schkcnt) - IF ((sds_type EQ 7) AND (schkcnt EQ 0)) OR ((sds_type NE 7) AND (va_type EQ 'STRING')) THEN $ - va_value=STRTRIM(va_value,2) - CASE 1 OF - (STRUPCASE(da_name) EQ 'VAR_NAME') AND (vnf EQ 0):BEGIN - vnf=1 - ;check that the dataset name matches the VAR_NAME - IF STRUPCASE(va_value) NE STRUPCASE(sds_name) THEN BEGIN - test1=ftype EQ 'N4' - test2=STRUPCASE(ALPHA_NUMERIC_UNDERSCORE(va_value)) EQ STRUPCASE(sds_name) - ;For nc4 files check if '.'s have been replaced by '_'s in the SDS_NAME. If so then it is OK - ;and quietly change SDS_NAME to the VAR_NAME value - IF (test1) AND (test2) THEN sds_name=va_value $ - ELSE BEGIN - ;Try and determine which is wrong - Dataset name or VAR_NAME - default is Dataset name - li=WHERE((STRUPCASE(sds_name) EQ STRUPCASE(vn)) AND (vn[0] NE ''),lcnt) - ;If lcnt NE 0 then Dataset name matches DATA_VARIABLES value so assume that VAR_NAME is wrong - IF lcnt NE 0 THEN usesds=1B ELSE usesds=0B - INFOTXT_OUTPUT_A,[3,5,usesds],[sds_name,va_value] - IF usesds THEN va_value=sds_name else sds_name=va_value - ENDELSE - ENDIF - ;Determine actual list order from DATA_VARIABLE listing (H5 lists datasets alphabetically) - li=WHERE(STRUPCASE(va_value[0]) EQ STRUPCASE(vn),lcnt) - ci[0]=va_value & vnv=va_value - END - STRUPCASE(da_name) EQ 'VAR_DATA_TYPE': BEGIN - ci[1]=va_value & idltype[n_lab-1]=STRUPCASE(STRTRIM(va_value,2)) - END - STRUPCASE(da_name) EQ 'VAR_UNITS': BEGIN - ;check that VAR_UNITS is a string value (in event that VAR_UNITS=1) - IF va_type NE 'STRING' THEN $ - INFOTXT_OUTPUT_A,[20],['VAR_UNITS',STRTRIM(sds_name,2)] - END - STRUPCASE(da_name) EQ 'VAR_DEPEND': BEGIN - TEST_DIM_ORDER,'VD',va_value,va_type,sds_dim,sds_name,rev_vd_vs - END - STRUPCASE(da_name) EQ 'VAR_SIZE': BEGIN - TEST_DIM_ORDER,'VS',va_value,va_type,sds_dim,sds_name,rev_vd_vs - END - STRUPCASE(da_name) EQ 'VAR_VALID_MIN': BEGIN - hdftype[1]=at_datatype & idltype[1]=idltypehold - END - STRUPCASE(da_name) EQ 'VAR_VALID_MAX': BEGIN - hdftype[2]=at_datatype & idltype[2]=idltypehold - END - STRUPCASE(da_name) EQ 'VAR_FILL_VALUE': BEGIN - hdftype[3]=at_datatype & idltype[3]=idltypehold - END - ELSE: - ENDCASE - jj++ - ENDIF ;ELSE IF nc4i[0] EQ 4 THEN catinfo[0,0]='H5N' ;netCDF4 file - not implemented yet - H5A_CLOSE,da_id - ENDFOR - ci[3]=STRTRIM(jj,2) - IF ci[0] EQ '' THEN ci[0]=sds_name - ENDIF ELSE BEGIN ;No Attributes, so can only write dataset name to Metadata - ci=[STRUPCASE(sds_name),'','','1'] & vnv=sds_name - ;Can the SDS_NAME be matched with a DATA_VARIABLES value? - li=WHERE(STRUPCASE(sds_name) EQ STRUPCASE(vn),lcnt) - IF lcnt NE 0 THEN vnf=1 ELSE vnf=0 - INFOTXT_OUTPUT_A,[5],[sds_name,'H5A_GET_NUM_ATTRS'] - ENDELSE - FOR k=0,N_ELEMENTS(sds_dim)-1 DO BEGIN - IF k EQ 0 THEN ci[2]=STRTRIM(sds_dim[k],2) ELSE ci[2]=ci[2]+';'+STRTRIM(sds_dim[k],2) - ENDFOR - - ;Check for possible problems with determining array index - IF lcnt NE 0 THEN BEGIN - IF li[0] GE n_sds THEN dv_order=0 $ ;can occur when number of DATA_VARIABLE values is greater than number of datasets - ELSE IF catinfo[li[0],0] NE '' THEN dv_order=0 ;This array has already been written to - ENDIF ELSE IF (lcnt EQ 0) OR (vnf EQ 0) THEN dv_order=0 - ;Write out information text if sds_name cannot be matched and determine array index - IF dv_order EQ 0 THEN BEGIN - li=WHERE(catinfo[*,0] EQ '') ;determine lowest available array index to write info to - in0=[4,vnf,lcnt,n_vn] - IF (vnf EQ 0) OR ((lcnt EQ 0) AND (n_vn NE 0L)) THEN INFOTXT_OUTPUT_A,in0,[sds_name,vnv] - ENDIF - - H5D_CLOSE,ds_id - oi[i]=li[0] ;to put datasets in the correct order - catinfo[li[0],*]=ci[*] - - ;Do data type checks on the Dataset, VAR_DATA_TYPE value and data types of VAR_VALID_MIN, VAR_VALID_MAX and VAR_FILL_VALUES - DATA_TYPE_CHECKS,ftype,dt_chk_labels,hdftype,idltype,sds_name,vnv - - ENDFOR - ENDIF ELSE IF stdfm EQ 1 THEN BEGIN - STOP_WITH_ERROR_A,o3[3]+proname,'No Datasets'+errtxt[0],lu & RETURN - ENDIF - - rev_vd_vsh=rev_vd_vs ;keep original value in hold variable (required if rev_vd_vs eq 3) - IF (rev_vd_vs EQ 2) OR (rev_vd_vs EQ 3) THEN rev_vd_vs=1 ;change default so that VAR_DEPEND and VAR_SIZE - ;are reversed if not o/w changed in TEST_DIM_ORDER - - ;Dimension the structure to the size of the SDS datasets (with dimension n_sds) - sds=REPLICATE(sds_set, n_sds, max_atts) - ;Put datasets and attributes into sds structure - FOR i=0,n_sds-1 DO BEGIN - notranspose=1 ;0/1 will change to 0 if the dataset needs to be transposed - sds_name=H5G_GET_MEMBER_NAME(sd_id,'/',di[i]) - ds_id=H5D_OPEN(sd_id,sds_name) - ;need to swap non-alphanumeric characters to '_' to match names from h5_parse for attribute values test - h5p_name=ALPHA_NUMERIC_UNDERSCORE(STRUPCASE(sds_name)) - - IF sds_name EQ '' THEN sds_name='N/A' - datasize=H5D_READ(ds_id) - sds_type=SIZE(datasize,/TYPE) - sds_dim=SIZE(datasize,/DIMENSIONS) & n_sds_dim=N_ELEMENTS(sds_dim) - FOR sdl=0,n_sds_dim-1 DO IF sds_dim[sdl] EQ 0 THEN sds_dim[sdl]=1 ;constant scalar h5 - IF (ftype EQ 'N4') AND (sds_type EQ 7) THEN BEGIN - ;need to do char_dim checks for STRING datasets and remove the string length dimension - char_dim=sds_dim[0] - IF n_sds_dim EQ 1 THEN sds_dim[0]=1 $ - ELSE BEGIN - sds_dim=sds_dim[1:n_sds_dim-1] & n_sds_dim=n_sds_dim-1 - ENDELSE - dschk=STRARR(sds_dim) - FOR sdl=0L,char_dim-1L DO dschk=dschk+datasize[sdl,*] - datasize=dschk - ENDIF - - ;Determine the number of attributes associated with the dataset - sds_natts=H5A_GET_NUM_ATTRS(ds_id) - - IF sds_natts NE 0L THEN BEGIN - ;Read in Dataset Attributes - jj=0 ;use this index in the event of invalid attribute names (e.g. dimension_list fro netCDF4 file) - FOR j=0L,sds_natts-1L DO BEGIN - da_id=H5A_OPEN_IDX(ds_id,j) - da_name=H5A_GET_NAME(da_id) & da_name=STRTRIM(da_name,2) - nc4i=WHERE(STRLOWCASE(da_name) EQ nc4_ignore,nc4icnt) - IF nc4icnt EQ 0 THEN BEGIN - ;test for valid attribute value - can be invalid if it is netCDF4 and equal to '' - gni=WHERE(TAG_NAMES(h5p) EQ STRUPCASE(h5p_name)) ;Tag index from h5_parse read - attest=h5p.(gni[0]) - gni=WHERE(TAG_NAMES(attest) EQ STRUPCASE(da_name)) - attestx=attest.(gni[0]) - IF STRTRIM(attestx._data[0],2) EQ '<read error>' THEN va_value='' $ - ELSE va_value=H5A_READ(da_id) - - IF SIZE(va_value,/TYPE) EQ 7 THEN va_type='STRING' ELSE va_type='NON_STRING' - schk=WHERE(STRUPCASE(da_name) EQ slabel,schkcnt) - IF ((sds_type EQ 7) AND (schkcnt EQ 0)) OR ((sds_type NE 7) AND (va_type EQ 'STRING')) THEN $ - va_value=STRTRIM(va_value,2) - H5A_CLOSE,da_id - - IF ((STRUPCASE(da_name) EQ 'VAR_DEPEND') OR (STRUPCASE(da_name) EQ 'VAR_SIZE')) AND $ - ((rev_vd_vs MOD 10 EQ 1) OR (rev_vd_vs EQ 20)) THEN BEGIN ;need to reverse the va_values - - IF va_type NE 'STRING' THEN BEGIN ;VAR_SIZE contains numeric values instead of in the form of a string - vavhold='' & n_vav=N_ELEMENTS(va_value) - FOR k=0,n_vav-1 DO BEGIN - IF k EQ n_vav-1 THEN vtxt='' ELSE vtxt=';' - vavhold=vavhold+STRTRIM(va_value[k],2)+vtxt - ENDFOR - ENDIF ELSE vavhold=va_value - - vs_v=STRCOMPRESS(STRSPLIT(vavhold,';',/EXTRACT,COUNT=rcnt),/REMOVE_ALL) - IF rcnt GT 1 THEN BEGIN ;multi-dimensions - IF (rev_vd_vs EQ 20) THEN $ ;AND (STRPOS(STRUPCASE(va_value),'DATETIME') NE -1) THEN $ - notranspose=0 ;need to transpose dataset as well - vs_v=REVERSE(vs_v) - FOR k=0,rcnt-1 DO $ - IF k EQ 0 THEN va_value=vs_v[k] ELSE va_value=va_value+';'+vs_v[k] - ENDIF - ENDIF - sds[oi[i],jj].va_l=PTR_NEW(da_name) - sds[oi[i],jj].va_v=PTR_NEW(va_value) - jj++ - ENDIF - ENDFOR - ENDIF ELSE BEGIN ;No Attributes, so can only write dataset name to structure - val='VAR_NAME' - sds[oi[i],0].va_l=PTR_NEW(val) - sds[oi[i],0].va_v=PTR_NEW(sds_name) - ENDELSE - - H5D_CLOSE,ds_id - ;Test to see if dataset dimension ordering needs to be changed - IF notranspose EQ 0 THEN datasize=TRANSPOSE(datasize) - sds[oi[i],0].data=PTR_NEW(datasize) - ENDFOR - - H5G_CLOSE,sd_id - H5F_CLOSE,hdf_file_id -ENDIF ELSE BEGIN ;netCDF3 file (N3) - ncdf_desc=['long_name'] - ncdf_std=['units','valid_range','_fillvalue'] - ncdf_dimen=['constant','independent'] - - dv_order=1 ;Boolean indicating whether Metadata Variable listing order matches DATA_VARIABLES values - n_vn=0L ;Initialize the number of Variable Names in the standard format netCDF file - - fileid=NCDF_OPEN(ifile) - ;Get number of global atts and variables - filestruct=NCDF_INQUIRE(fileid) - n_dim=filestruct.ndims - n_ga=filestruct.ngatts - n_sds=filestruct.nvars - - IF n_sds EQ 0L THEN ntxt='Number of SDS datasets' $ - ELSE IF n_ga EQ 0L THEN ntxt='Number of Global Attributes' $ - ELSE ntxt='' - IF ntxt NE '' THEN BEGIN - STOP_WITH_ERROR_A,o3[3]+proname,ntxt+errtxt[1],lu & RETURN - ENDIF - - ;read global attributes - ga=STRARR(n_ga) & vn=[''] - FOR j=0,n_ga-1 DO BEGIN - ga_name=NCDF_ATTNAME(fileid,j,/GLOBAL) - NCDF_ATTGET,fileid,ga_name,ga_data,/GLOBAL - ga_data=STRTRIM(ga_data,2) - ga[j]=STRTRIM(STRUPCASE(ga_name),2)+'='+ga_data - ;read list of DATA_VARIABLES into array - IF STRUPCASE(ga_name) EQ 'DATA_VARIABLES' THEN vn=STRSPLIT(ga_data,' ;',/Extract,COUNT=n_vn) - IF STRUPCASE(ga_name) EQ 'FILE_NAME' THEN BEGIN - ;check that the actual file name matches the FILE_NAME entry - IF STRLOWCASE(STRTRIM(ga_data,2)) NE ifilechk THEN INFOTXT_OUTPUT_A,[24],[ga_data] - ENDIF - ENDFOR - - ;read dimension information - IF n_dim NE 0 THEN BEGIN - mv_dim=STRARR(n_dim,2) - FOR j=0,n_dim-1 DO BEGIN - NCDF_DIMINQ,fileid,j,dimname,dimsize - mv_dim[j,0]=STRTRIM(dimname,2) - mv_dim[j,1]=STRTRIM(dimsize,2) - ENDFOR - ENDIF ELSE mv_dim=[''] - - di=INDGEN(n_sds) - NC_DIMENSION_CHK,'N3',fileid,di,n_sds,dgi,n_sdsg - di=dgi & n_sds=n_sdsg ;di contains index values of valid sds datasets - - IF n_vn NE n_sds THEN $ - INFOTXT_OUTPUT_A,[0],[STRTRIM(n_vn,2),STRTRIM(n_sds,2),'NCDF_INQUIRE'] - ;DATA_VARIABLES values not read successfully, or number of datasets given under DATA_VARIABLES - ;is not equal to the number saved to the file - - ;Determine maximum number of Attributes, correct dataset order, and dimension order - max_atts=0L & oi=LONARR(n_sds) - catinfo=STRARR(n_sds,4) ;output info for catalog output - - FOR j=0,n_sds-1 DO BEGIN - vnv='' & sds_name='' - hdftype=STRARR(n_lab-1) & idltype=STRARR(n_lab) ;to hold data types for dataset, min, max and fill values, and VAR_DATA_TYPE (idltype only) - - ;Extract data from a netCDF dataset and get size information - NCDF_VARGET,fileid,di[j],sds_data - sds_type=SIZE(sds_data,/TYPE) - - varstruct=NCDF_VARINQ(fileid,di[j]) - sds_natts=varstruct.natts - IF sds_natts GT max_atts THEN max_atts=sds_natts - sds_name=varstruct.name - sds_datatype=varstruct.datatype ;'BYTE', 'CHAR', 'INT', 'LONG', 'FLOAT', or 'DOUBLE' - sds_dimid=varstruct.dim - sds_ndim=varstruct.ndims - - ;determine the actual dimension values from the dim IDs given in varstruct.dim - ;IF sds_ndim NE 0 THEN BEGIN - ; sds_dim=LONARR(sds_ndim) - ; FOR k=0,sds_ndim-1 DO sds_dim[k]=mv_dim[sds_dimid[k],1] - ;ENDIF ELSE sds_dim=[1L] - - IF (sds_datatype EQ 'CHAR') AND (sds_type EQ 1) THEN sds_type=7 - sds_dim=SIZE(sds_data,/DIMENSIONS) & n_sds_dim=N_ELEMENTS(sds_dim) - FOR sdl=0,n_sds_dim-1 DO IF sds_dim[sdl] EQ 0 THEN sds_dim[sdl]=1 ;constant scalar h5 - IF sds_type EQ 7 THEN BEGIN - ;need to do char_dim checks for STRING datasets and remove the string length dimension - char_dim=sds_dim[0] - IF n_sds_dim EQ 1 THEN sds_dim[0]=1 $ - ELSE BEGIN - sds_dim=sds_dim[1:n_sds_dim-1] & n_sds_dim=n_sds_dim-1 - ENDELSE - ENDIF - - IF sds_datatype EQ 'BYTE' THEN $ - IF MIN(sds_data) LT 0 THEN sds_datatype='BYTE_1' ELSE sds_datatype='BYTE_0' - ;This doesn't work as NCDF_VARGET converts BYTE values to 8-bit unsigned if they are negative signed values - - hdftype[0]=sds_datatype & idltype[0]=STRTRIM(sds_type,2) - - ;Read Variable Attributes - IF sds_natts NE 0L THEN BEGIN - vnf=0 & lcnt=0 & vnv='' - ci=STRARR(4) ;array containing catalog attributes - std_fnd=INTARR(n_aad) - ;Read in Dataset Attributes - FOR k=0,sds_natts-1 DO BEGIN - varattsname=NCDF_ATTNAME(fileid,di[j],k) - NCDF_ATTGET,fileid,di[j],varattsname,va_value - attstruct=NCDF_ATTINQ(fileid,di[j],varattsname) - va_name=STRUPCASE(varattsname) - vavtype=SIZE(va_value,/TYPE) - - IF (vavtype EQ 7) OR (vavtype EQ 1) THEN va_type='STRING' ELSE va_type='NON-STRING' - hdftypehold=attstruct.datatype - IF hdftypehold EQ 'BYTE' THEN $ - IF va_value LT 0 THEN hdftypehold='BYTE_1' ELSE hdftypehold='BYTE_0' - ;This doesn't work as NCDF_VARGET converts BYTE values to 8-bit unsigned if they are negative signed values - idltypehold=vavtype - - schk=WHERE(va_name EQ slabel,schkcnt) - test1=(sds_datatype EQ 'CHAR') AND (schkcnt EQ 0) ;string dataset but not one of the slabel values - test2=(sds_datatype NE 'CHAR') AND (va_type EQ 'STRING') ;not a string dataset but string attribute value - test3=(sds_datatype EQ 'CHAR') AND (schkcnt NE 0) AND (vavtype EQ 1) ;string dataset, slabel and attribute value is of type byte - IF (test1) OR (test2) THEN va_value=STRTRIM(va_value,2) $ - ELSE IF test3 THEN BEGIN - va_value=STRING(va_value) & idltypehold=7 - ENDIF - - CASE 1 OF - (STRUPCASE(va_name) EQ 'VAR_NAME') AND (vnf EQ 0):BEGIN - vnf=1 - ;check that the dataset name matches the VAR_NAME - IF STRUPCASE(va_value) NE STRUPCASE(sds_name) THEN BEGIN - test1=ftype EQ 'N3' - test2=STRUPCASE(ALPHA_NUMERIC_UNDERSCORE(va_value)) EQ STRUPCASE(sds_name) - ;For nc3 files check if '.'s have been replaced by '_'s in the SDS_NAME. If so then it is OK - ;and quietly change SDS_NAME to the VAR_NAME value - IF (test1) AND (test2) THEN sds_name=va_value $ - ELSE BEGIN - ;Try and determine which is wrong - Dataset name or VAR_NAME - default is Dataset name - li=WHERE((STRUPCASE(sds_name) EQ STRUPCASE(vn)) AND (vn[0] NE ''),lcnt) - ;If lcnt NE 0 then Dataset name matches DATA_VARIABLES value so assume that VAR_NAME is wrong - IF lcnt NE 0 THEN usesds=1 ELSE usesds=0 - INFOTXT_OUTPUT_A,[3,5,usesds],[sds_name,va_value] - IF usesds EQ 0 THEN sds_name=va_value ELSE va_value=sds_name - ENDELSE - ENDIF - ;Determine actual list order from DATA_VARIABLE listing - li=WHERE(STRUPCASE(va_value[0]) EQ STRUPCASE(vn),lcnt) - ci[0]=va_value & vnv=va_value - END - STRUPCASE(va_name) EQ 'VAR_DATA_TYPE': BEGIN - ci[1]=va_value & idltype[n_lab-1]=STRUPCASE(STRTRIM(va_value,2)) - END - STRUPCASE(va_name) EQ 'VAR_UNITS': BEGIN - ;check that VAR_UNITS is a string value (in event that VAR_UNITS=1) - IF va_type NE 'STRING' THEN $ - INFOTXT_OUTPUT_A,[20],['VAR_UNITS',STRTRIM(sds_name,2)] - END - STRUPCASE(va_name) EQ 'VAR_DEPEND': BEGIN - TEST_DIM_ORDER,'VD',va_value,va_type,sds_dim,sds_name,rev_vd_vs - END - STRUPCASE(va_name) EQ 'VAR_SIZE': BEGIN - TEST_DIM_ORDER,'VS',va_value,va_type,sds_dim,sds_name,rev_vd_vs - END - STRUPCASE(va_name) EQ 'VAR_VALID_MIN': BEGIN - hdftype[1]=hdftypehold & idltype[1]=idltypehold - END - STRUPCASE(va_name) EQ 'VAR_VALID_MAX': BEGIN - hdftype[2]=hdftypehold & idltype[2]=idltypehold - END - STRUPCASE(va_name) EQ 'VAR_FILL_VALUE': BEGIN - hdftype[3]=hdftypehold & idltype[3]=idltypehold - END - ELSE: - ENDCASE - ENDFOR - ci[3]=STRTRIM(sds_natts,2) - IF ci[0] EQ '' THEN ci[0]=sds_name - ENDIF ELSE BEGIN ;No Attributes, so can only write dataset name to Metadata - ci=[STRUPCASE(sds_name),'','','1'] & vnv=sds_name - ;Can the SDS_NAME be matched with a DATA_VARIABLES value? - li=WHERE(STRUPCASE(sds_name) EQ STRUPCASE(vn),lcnt) - IF lcnt NE 0 THEN vnf=1 ELSE vnf=0 - INFOTXT_OUTPUT_A,[5],[sds_name,'NCDF_VARINQ'] - ENDELSE - FOR k=0,N_ELEMENTS(sds_dim)-1 DO BEGIN - IF k EQ 0 THEN ci[2]=STRTRIM(sds_dim[k],2) ELSE ci[2]=ci[2]+';'+STRTRIM(sds_dim[k],2) - ENDFOR - - ;Check for possible problems with determining array index - IF lcnt NE 0 THEN BEGIN - IF li[0] GE n_sds THEN dv_order=0 $ ;can occur when number of DATA_VARIABLE values is greater than number of datasets - ELSE IF catinfo[li[0],0] NE '' THEN dv_order=0 ;This array has already been written to - ENDIF ELSE IF (lcnt EQ 0) OR (vnf EQ 0) THEN dv_order=0 - ;Write out information text if sds_name cannot be matched and determine array index - IF dv_order EQ 0 THEN BEGIN - li=WHERE(catinfo[*,0] EQ '') ;determine lowest available array index to write info to - in0=[4,vnf,lcnt,n_vn] - IF (vnf EQ 0) OR ((lcnt EQ 0) AND (n_vn NE 0L)) THEN INFOTXT_OUTPUT_A,in0,[sds_name,vnv] - ENDIF - - oi[j]=li[0] ;to put datasets in the correct order - catinfo[li[0],*]=ci[*] - - ;Do data type checks on the Dataset, VAR_DATA_TYPE value and data types of VAR_VALID_MIN, VAR_VALID_MAX and VAR_FILL_VALUES - DATA_TYPE_CHECKS,ftype,dt_chk_labels,hdftype,idltype,sds_name,vnv - - ENDFOR - - rev_vd_vsh=rev_vd_vs ;keep original value in hold variable (required if rev_vd_vs eq 3) - IF (rev_vd_vs EQ 2) OR (rev_vd_vs EQ 3) THEN rev_vd_vs=1 ;change default so that VAR_DEPEND and VAR_SIZE - ;are reversed if not o/w changed in TEST_DIM_ORDER - - ;Dimension the structure to the size of the SDS datasets (with dimension n_sds) - sds=REPLICATE(sds_set, n_sds, max_atts) - ;Put datasets and attributes into sds structure - FOR j=0,n_sds-1 DO BEGIN - notranspose=1 ;0/1 will change to 0 if the dataset needs to be transposed - varstruct=NCDF_VARINQ(fileid,di[j]) - sds_name=varstruct.name - IF sds_name EQ '' THEN sds_name='N/A' - sds_natts=varstruct.natts - sds_datatype=varstruct.datatype - sds_ndim=varstruct.ndims - - ;Extract data from a netCDF dataset and get size information - NCDF_VARGET,fileid,di[j],sds_data - - IF sds_natts NE 0L THEN BEGIN - ;Read in Dataset Attributes - FOR k=0L,sds_natts-1L DO BEGIN - varattsname=NCDF_ATTNAME(fileid,di[j],k) - NCDF_ATTGET,fileid,di[j],varattsname,va_value - va_name=STRUPCASE(varattsname) - vavtype=SIZE(va_value,/TYPE) - vavhold=va_value - IF (vavtype EQ 7) OR (vavtype EQ 1) THEN va_type='STRING' ELSE va_type='NON-STRING' - - schk=WHERE(va_name EQ slabel,schkcnt) - test1=(sds_datatype EQ 'CHAR') AND (schkcnt EQ 0) ;string dataset but not one of the slabel values - test2=(sds_datatype NE 'CHAR') AND (va_type EQ 'STRING') ;not a string dataset but string attribute value - test3=(sds_datatype EQ 'CHAR') AND (schkcnt NE 0) AND (vavtype EQ 1) ;string dataset, slabel and attribute value is of type byte - IF (test1) OR (test2) THEN va_value=STRTRIM(va_value,2) $ - ELSE IF test3 THEN va_value=STRING(va_value) - - IF ((STRUPCASE(va_name) EQ 'VAR_DEPEND') OR (STRUPCASE(va_name) EQ 'VAR_SIZE')) AND $ - ((rev_vd_vs MOD 10 EQ 1) OR (rev_vd_vs EQ 20)) THEN BEGIN ;need to reverse the va_values - - IF va_type NE 'STRING' THEN BEGIN ;VAR_SIZE contains numeric values instead of in the form of a string - vavhold='' & n_vav=N_ELEMENTS(va_value) - FOR i=0,n_vav-1 DO BEGIN - IF i EQ n_vav-1 THEN vtxt='' ELSE vtxt=';' - vavhold=vavhold+STRTRIM(va_value[i],2)+vtxt - ENDFOR - ENDIF ELSE vavhold=va_value - - vs_v=STRCOMPRESS(STRSPLIT(vavhold,';',/EXTRACT,COUNT=rcnt),/REMOVE_ALL) - IF rcnt GT 1 THEN BEGIN ;multi-dimensions - IF (rev_vd_vs EQ 20) THEN $ ;AND (STRPOS(STRUPCASE(va_value),'DATETIME') NE -1) THEN $ - notranspose=0 ;need to transpose dataset as well - vs_v=REVERSE(vs_v) - FOR i=0,rcnt-1 DO $ - IF i EQ 0 THEN va_value=vs_v[i] ELSE va_value=va_value+';'+vs_v[i] - ENDIF - ENDIF - IF ((STRUPCASE(va_name) EQ 'VAR_VALID_MIN') OR (STRUPCASE(va_name) EQ 'VAR_VALID_MAX') OR $ - (STRUPCASE(va_name) EQ 'VAR_FILL_VALUE')) AND (sds_datatype EQ 'BYTE') AND $ - (vavtype EQ 1) THEN va_value=vavhold - sds[oi[j],k].va_l=PTR_NEW(va_name) - sds[oi[j],k].va_v=PTR_NEW(va_value) - ENDFOR - ;This section commented out for now - Version 4.0b16 - ;Check attributes again in case there are missing GEOMS Attributes that can be filled in - ;vnt='' - ;FOR k=0,sds_natts-1 DO BEGIN - ; varattsname=NCDF_ATTNAME(fileid,j,k) - ; NCDF_ATTGET,fileid,j,varattsname,va_value - ; va_name=STRUPCASE(varattsname) - ; va_type=SIZE(va_value,/TYPE) - - ; schk=WHERE(va_name EQ slabel,schkcnt) - ; IF ((sds_datatype EQ 'CHAR') AND (schkcnt EQ 0)) OR ((sds_datatype NE 'CHAR') AND $ - ; ((va_type EQ 1) OR (va_type EQ 7))) THEN va_value=STRTRIM(va_value,2) $ - ; ELSE IF (va_type EQ 1) OR (va_type EQ 7) THEN va_value=STRING(va_value) - - ; ;Check for VAR_NOTES entry - ; lcname=STRLOWCASE(varattsname) - ; ni=WHERE(lcname EQ ncdf_desc,ncnt) - ; IF ncnt EQ 0 THEN ni=WHERE(lcname EQ ncdf_std,ncnt) - ; IF (ncnt EQ 0) AND (lcname NE 'units') THEN BEGIN ;append label and value to VAR_NOTES - ; mi=WHERE(attr_arr_data EQ 'VAR_NOTES') - ; IF vnt NE '' THEN BEGIN - ; IF STRMID(vnt,STRLEN(vnt)-1,1) EQ '.' THEN itxt=' ' ELSE itxt='. ' - ; ENDIF ELSE itxt='' - ; vnt=vnt+itxt+STRTRIM(varattsname,2)+': '+STRTRIM(va_value,2) - ; ENDIF - ; mcnt=0 - ; ;Check for VAR_DESCRIPTION entry (long_name) - ; IF lcname EQ 'long_name' THEN mi=WHERE(attr_arr_data EQ 'VAR_DESCRIPTION',mcnt) - ; ;Check for VAR_UNITS entry - ; IF lcname EQ 'units' THEN mi=WHERE(attr_arr_data EQ 'VAR_UNITS',mcnt) - ; IF (mcnt NE 0) AND (std_fnd[mi[0]] EQ 0) THEN sds[oi[j],mi[0]].va_v=PTR_NEW(STRTRIM(va_value,2)) - ; ;Check for valid_range and _FillValue attributes - ; ni=WHERE(lcname EQ ncdf_std,ncnt) - ; IF ncnt NE 0 THEN BEGIN - ; vdt=SIZE(varattsval,/TYPE) - ; IF lcname EQ 'valid_range' THEN BEGIN - ; mi=WHERE(attr_arr_data EQ 'VAR_VALID_MIN') - ; IF std_fnd[mi[0]] EQ 0 THEN sds[oi[j],mi[0]].va_v=PTR_NEW(varattsval[0]) - ; mi=WHERE(attr_arr_data EQ 'VAR_VALID_MAX') - ; IF std_fnd[mi[0]] EQ 0 THEN sds[oi[j],mi[0]].va_v=PTR_NEW(varattsval[1]) - ; ENDIF ELSE BEGIN - ; mi=WHERE(attr_arr_data EQ 'VAR_FILL_VALUE') - ; IF std_fnd[mi[0]] EQ 0 THEN sds[oi[j],mi[0]].va_v=PTR_NEW(varattsval) - ; ENDELSE - ; ENDIF - ;ENDFOR - ;IF vnt NE '' THEN BEGIN - ; mi=WHERE(attr_arr_data EQ 'VAR_NOTES') - ; IF std_fnd[mi[0]] EQ 0 THEN sds[oi[j],mi[0]].va_v=PTR_NEW(vnt) - ;ENDIF - ; ;Write VAR_NAME to metadata - ;mi=WHERE(attr_arr_data EQ 'VAR_NAME') - ;IF std_fnd[mi[0]] EQ 0 THEN sds[oi[j],mi[0]].va_v=PTR_NEW(sds_name) - ;catinfo[oi[j],0]=sds_name - ;;Write VAR_DATA_TYPE to metadata - ;mi=WHERE(attr_arr_data EQ 'VAR_DATA_TYPE') - ;CASE 1 OF - ; sds_datatype EQ 'BYTE':catinfo[oi[j],1]='BYTE' - ; sds_datatype EQ 'CHAR':catinfo[oi[j],1]='STRING' - ; (sds_datatype EQ 'INT') OR (sds_datatype EQ 'SHORT'):catinfo[oi[j],1]='SHORT' - ; sds_datatype EQ 'LONG':catinfo[oi[j],1]='INTEGER' - ; sds_datatype EQ 'FLOAT':catinfo[oi[j],1]='REAL' - ; sds_datatype EQ 'DOUBLE':catinfo[oi[j],1]='DOUBLE' - ;ENDCASE - ;sds[oi[j],mi[0]].va_v=PTR_NEW(catinfo[oi[j],1]) - ;;Write VAR_DEPEND to metadata - ;mi=WHERE(attr_arr_data EQ 'VAR_DEPEND') - ;IF std_fnd[mi[0]] EQ 0 THEN BEGIN - ; vd='' - ; FOR k=0,sds_ndim-1 DO BEGIN - ; IF k EQ 0 THEN vd=vd+STRTRIM(mv_dim[sds_dim[k],0],2) $ - ; ELSE vd=vd+';'+STRTRIM(mv_dim[sds_dim[k],0],2) - ; ENDFOR - ; sds[oi[j],mi[0]].va_v=PTR_NEW(vd) - ;ENDIF - ;;Determine VAR_SIZE from dataset attributes - ;mi=WHERE(attr_arr_data EQ 'VAR_SIZE') - ;IF std_fnd[mi[0]] EQ 0 THEN BEGIN - ; sds_dim=SIZE(sds_data,/DIMENSIONS) - ; IF sds_dim[0] EQ 0 THEN sds_dim[0]=1 - ; vs='' - ; FOR k=0,N_ELEMENTS(sds_dim)-1 DO $ - ; IF k EQ 0 THEN vs=vs+STRTRIM(sds_dim[k],2) ELSE vs=vs+';'+STRTRIM(sds_dim[k],2) - ; sds[oi[j],mi[0]].va_v=PTR_NEW(vs) - ;ENDIF - - ENDIF ELSE BEGIN ;No Attributes, so can only write dataset name to structure - val='VAR_NAME' - sds[oi[j],0].va_l=PTR_NEW(val) - sds[oi[j],0].va_v=PTR_NEW(sds_name) - ENDELSE - - ;Test to see if dataset dimension ordering needs to be changed - IF notranspose EQ 0 THEN sds_data=TRANSPOSE(sds_data) - IF sds_datatype EQ 'CHAR' THEN sds_data=STRING(sds_data) - sds[oi[j],0].data=PTR_NEW(sds_data) - ENDFOR - - NCDF_CLOSE,fileid - ;Add DATA_VARIABLES to global attributes if not already present - gi=WHERE(STRMID(ga,0,14) EQ 'DATA_VARIABLES',gcnt) - IF gcnt EQ 0 THEN BEGIN - FOR j=0,n_sds-1 DO BEGIN - IF j EQ 0 THEN dv=catinfo[j,0] ELSE dv=dv+';'+catinfo[j,0] - ENDFOR - ga=[ga,'DATA_VARIABLES='+dv] - ENDIF - dv_order=-1 & n_vn=n_sds -ENDELSE - -;Array listing order may not be correct -IF (dv_order EQ 0) AND (n_vn NE 0L) THEN INFOTXT_OUTPUT_A,[6] - -;If necessary write comment regarding VAR_SIZE and VAR_DEPEND dimension ordering -rev_vd_vs=rev_vd_vsh ;used for when rev_vd_vs eq 3 -IF (rev_vd_vs MOD 10 EQ 0) OR (rev_vd_vs EQ 3) THEN INFOTXT_OUTPUT_A,[11,rev_vd_vs] - -END ;Procedure Read_HDF_SDS - - - -PRO output_hdf_data, ifile, ga, sds, catinfo -;Procedure to output the contents of an HDF file in ASCII form, either in a format compatible for -;conversion back to HDF, or in Row and Column format, or as a summary list -; ---------- -;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org -; -; History: -; 20050729: Original IDLCR8ASCII Routine - Version 1.0 -; 20050912: Removed Common variable definition CATALOGINFO and made the variable a parameter passed -; to the procedure; Make output filenames lower case; If data type is double and the -; accompanying VIS_FORMAT value starts with an 'F' (e.g. F9.4), then, change the 'F' to a -; 'D' when specifying the format for output - Version 1.1 -; 20061004: Common variable definition WIDGET_WIN_A added for Error calls; Display input/output -; information in a pop-up display window if the program is opened using IDL VM - Version 2.0 -; 20080302: Added code which sends the Catalog and log output to the IDLDE output window and/or an -; external file (as determined by the dux array values); Ensure all data values are -; separated by a space when the Dump output option is chosen - Version 3.0 -; 20101122: Adopt GEOMS metadata standard; Because FORMAT attribute(s) have been dropped can no longer -; use defined formatting values when outputing data to files; Default ASCII formatting -; adopted - scientific notation for REAL and DOUBLE, except for DATETIME and related values, -; which have formatting dependent on the data type - Version 4.0b1 -; 20150127: Ensure all string datasets are written and saved as left justified strings (previously -; defaulted to right justified) - Version 4.0b10 -; 20151109: Convert DATETIME (and related) max, min and fill values to LONG64 data type before -; determining the format for ASCII output to account for very large values - Version 4.0b12 -; 20161130: Add checks for Metadata values written in an invalid data type (e.g. structure) and for -; invalid attributes - Version 4.0b16 -; -; Inputs: ifile - a string containing the name of the HDF file to be read in. -; ga - a string array containing the global attribute labels and values extracted from the HDF -; file. -; sds - a structure using pointers, of size [n_sds,n_atts], containing the variable attribute -; labels and values (sds[n,m].va_l and sds[n,m].va_v) and the data (sds[n,0].data) -; extracted from the HDF file. -; catinfo - a string array of size [n_sds,4] (where n_sds is the number of datasets in the HDF -; file), containing information on the variable name, data type, data dimension, and -; number of attributes -; -; Outputs: Nil -; -; Called by: IDLCR8ASCII -; -; Subroutines Called: None - -COMMON WIDGET_WIN_A - -;determine number of datasets and attributes (from sds.va) -as=SIZE(catinfo) -n_sds=as[1] -indir=FILE_DIRNAME(ifile,/MARK_DIRECTORY) -infile=FILE_BASENAME(ifile) - -vdtype=[1,2,3,4,5,7,12,13,14,15] ;valid IDL data types -badlabel=[''] ;list of attribute labels where the values are not valid (to avoid repeated log messages) - -IF o3[1] EQ 'C' THEN BEGIN - IF o3[3] EQ '' THEN BEGIN - lineno=lineno+n_sds+1 - wintxt='Listing of Var_Name Index; Var_Name; Var_Data_Type; Var_Size' - WIDGET_CONTROL,wtxt,set_value=wintxt,/Append,Set_text_top_line=lineno - ENDIF - FOR i=dux[0],dux[1],dux[2] DO BEGIN - IF i EQ -1 THEN PRINT,'Listing of Var_Name Index; Var_Name; Var_Data_Type; Var_Size' $ - ELSE PRINTF,i,'Listing of Var_Name Index; Var_Name; Var_Data_Type; Var_Size' - ENDFOR - FOR j=0,n_sds-1 DO BEGIN - IF o3[3] EQ '' THEN BEGIN - wintxt=STRTRIM(j,2)+'; '+catinfo[j,0]+'; '+catinfo[j,1]+'; '+catinfo[j,2] - WIDGET_CONTROL,wtxt,set_value=wintxt,/Append - ENDIF - FOR i=dux[0],dux[1],dux[2] DO $ - IF i EQ -1 THEN PRINT,STRTRIM(j,2),'; ',catinfo[j,0],'; ',catinfo[j,1],'; ',catinfo[j,2] $ - ELSE PRINTF,i,STRTRIM(j,2),'; ',catinfo[j,0],'; ',catinfo[j,1],'; ',catinfo[j,2] - ENDFOR -ENDIF -IF o3[0] NE '0' THEN BEGIN - ;create output filenames - outf1=STRMID(infile,0,STRPOS(infile,'.',/Reverse_Search))+'.meta' - outf2=STRMID(infile,0,STRPOS(infile,'.',/Reverse_Search))+'.data' - OPENW,lu1,indir+outf1,/GET_LUN - OPENW,lu2,indir+outf2,/GET_LUN - - ;HDF4 and NETCDF pre-defined attributes - ncsa=['long_name','units','format','coordsys','valid_range','_FillValue','scale_factor', $ - 'scale_factor_err','add_offset','add_offset_err','calibrated_nt'] - - PRINTF,lu1,'! Output from IDLcr8ASCII application v4.0' - PRINTF,lu1,'! '+ifile & PRINTF,lu1,'!' - PRINTF,lu1,'! Global Attributes' - FOR i=0,N_ELEMENTS(ga)-1 DO PRINTF,lu1,ga[i] - - FOR j=0,n_sds-1 DO BEGIN - n_atts=FIX(catinfo[j,3]) - IF n_atts NE 0 THEN BEGIN - PRINTF,lu1,'!' & PRINTF,lu1,'! Variable Attributes' - PRINTF,lu2,catinfo[j,0] - FOR i=0,n_atts DO BEGIN ;number of attributes. Last loop is for Data - IF i EQ n_atts THEN vavt=SIZE(*sds[j,0].data,/TYPE) $ - ELSE vavt=SIZE(*sds[j,i].va_v,/TYPE) - gti=WHERE(vavt EQ vdtype,g_vavt) ;test to see if the data type is valid - IF g_vavt EQ 1 THEN BEGIN ;it is OK to transfer the attribute value to a variable - IF i EQ n_atts THEN vav=*sds[j,0].data ELSE vav=*sds[j,i].va_v - ENDIF - IF (STRPOS(STRUPCASE(catinfo[j,0]),'DATETIME') NE -1) AND ((vavt EQ 4) OR (vavt EQ 5)) THEN BEGIN - ns=MAX(STRLEN(STRTRIM(LONG64(vav),2))) - IF vavt EQ 4 THEN dp=6 ELSE dp=9 ;indicates the number of decimal places to use in the output - ;If the number is very large then output as an exponent - IF ns LT 8 THEN fmt='(D'+STRTRIM(ns+dp+1,2)+'.'+STRTRIM(dp,2)+')' ELSE fmt='(E0)' - ENDIF ELSE IF (vavt EQ 4) OR (vavt EQ 5) THEN fmt='(E0)' $ - ELSE IF vavt EQ 7 THEN fmt='(A-)' $ - ELSE fmt='(I0)' - IF i NE n_atts THEN BEGIN ;write out metadata in form LABEL=VALUE - nvav=N_ELEMENTS(vav) - vat=*sds[j,i].va_l - ni=WHERE(STRLOWCASE(vat) EQ STRLOWCASE(ncsa),ncnt) ;check for pre-defined attributes and exclude - IF (ncnt EQ 0) AND (g_vavt EQ 1) THEN BEGIN - IF nvav EQ 1 THEN vat=vat+'='+STRING(FORMAT=fmt,vav[0]) $ ;'(A-'+STRTRIM(STRLEN(vav[0]),2)+')',vav[0]) $ - ELSE BEGIN - FOR k=0,N_ELEMENTS(vav)-1 DO BEGIN - IF k EQ 0 THEN vat=vat+'='+STRTRIM(STRING(FORMAT=fmt,vav[k]),2) $ - ELSE vat=vat+';'+STRTRIM(STRING(FORMAT=fmt,vav[k]),2) - ENDFOR - ENDELSE - PRINTF,lu1,vat - ENDIF ELSE IF g_vavt NE 1 THEN BEGIN - bli=WHERE(vat EQ badlabel,blcnt) - IF blcnt EQ 0 THEN BEGIN - badlabel=[badlabel,vat] - INFOTXT_OUTPUT_A,[18],[vat] - ENDIF - ENDIF - ENDIF - ENDFOR - - ;Write out data - IF o3[0] EQ 'F' THEN BEGIN - PRINTF,lu2,format=fmt,vav - ENDIF ELSE IF o3[0] EQ 'D' THEN BEGIN - ;Ensure there is a single character between all values - sdssize=SIZE(vav,/Dimension) - sdshold=STRTRIM(STRING(FORMAT=fmt,vav),2) - maxstr=MAX(STRLEN(sdshold)) - ;use default IDL formatting rules - sdshold=STRING(format='(A-'+STRTRIM(maxstr,2)+')',sdshold) - ;Uncomment line below if format described by VIS_FORMAT is required - ;sdshold=STRING(format=p_f,*sds[j].data) - IF sdssize[0] NE 0 THEN sdshold=REFORM(sdshold,sdssize) - PRINTF,lu2,sdshold - ENDIF - ENDIF ELSE BEGIN ;Variable attributes do not have an associated dataset - INFOTXT_OUTPUT_A,[19],[catinfo[j,0]] - ENDELSE - ENDFOR - PRINTF,lu1,'!' & PRINTF,lu1,'! End of output file created by IDLcr8ASCII' - FREE_LUN,lu1 & FREE_LUN,lu2 - IF o3[3] EQ '' THEN BEGIN - lineno=lineno+2L - WIDGET_CONTROL,wtxt,set_value=outf1+' created!',/Append,Set_text_top_line=lineno - WIDGET_CONTROL,wtxt,set_value=outf2+' created!',/Append,Set_text_top_line=lineno - ENDIF - FOR i=dux[0],dux[1],dux[2] DO BEGIN - PRINTF,i,' '+outf1+' created!' & PRINTF,i,' '+outf2+' created!' - ENDFOR -ENDIF -END ;Procedure Output_HDF_Data - - - -PRO idlcr8ascii, ifile, ga, sds, reterr, $ - FORMAT=o1, DUMP=o2, CATALOG=o4, POPUP=o5, LOG=o6, H4=o7, H5=o8, NC=o9 -;Main IDL procedure to convert GEOMS compliant netCDF, HDF4 and HDF5 files to ASCII, Session Memory -;or another recognized Data format. -; -;Program documentation, idlcr8ascii-v4.0_Readme.pdf, available on http://avdc.gsfc.nasa.gov. -; -;Program sub-version 4.0b26 (20240912) -; ---------- -;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org -; -; History: -; 20050729: Original Release - Version 1.0 -; 20050912: Removed Common variable definition CATALOGINFO; If the format of the -; DATA_START_DATE and FILE_GENERATION_DATE values is MJD2000, then change to ISO8601, -; unless the output option is /Dump - Version 1.1 -; 20061004: Common variable definition WIDGET_WIN_A added for Error calls; Make the code suitable -; for running on a licensed version of IDL (using the .pro and .sav versions of the code) -; and on IDL Virtual Machine (using the .sav version of the code); Change the command -; line keyword options. Remove the /Help option (window now opened if there are no command -; line parameters), and /Catalog can now be called together with /AVDC or /Dump; The -; program can now handle multiple HDF files as input (either as a string array or as a -; file spec); Display input/output information as well as errors and warnings in a pop-up -; display window if the program is called using IDL VM; Help window becomes an -; Introduction window, and the user has the option of continuing to run the program with -; file inputs; Include option to read HDF5 versions of the ground-based files -; - Version 2.0 -; 20080302: Remove HELP,/TRACEBACK call, which identified how the program was called. This was used -; to stop output to the IDLDE output window in the event that IDL VM was used, but it is -; not required as IDL VM ignores these print calls; Add options to send Catalog and logged -; input/output information to a Pop-up Box (/Popup) and/or to an external file (/Log). Add -; DIALOG_BOX to show completion of the program if program inputs are via the INTRO box, and -; logging window option isn't requested - Version 3.0 -; 20091208: Add INFORMATION text message to INFOTXT_OUTPUT_A procedure; Replace /AVDC keyword with -; /FORMAT (note /AVDC also retained for backward compatibility purposes); Improve checks -; on parameter and keyword inputs - Version 3.03 -; 20101122: Changes made to account for new GEOMS conventions, including changing the format of the -; structure so that numeric metadata values can be saved in their actual datatype, rather -; than string; Add return error code option so that program returns to the calling program -; if an error is generated - Version 4.0b1 -; 20120328: Add options to convert HDF/netCDF files to other HDF/netCDF formats e.g. H4 to -; H5 etc. Requires IDLcr8HDF in the search path to do this - Version 4.0b5 -; 20130114: Add check for IDL being run in DEMO mode by using LMGR command. Replace PRINTF,-1 -; statements (i.e. when dux[0] eq -1) with PRINT (o/w causes DEMO mode to stop). Also disable -; /LOG, /FORMAT and /DUMP keywords and stop ASCII file output in DEMO mode - Version 4.0b7 -; 20150127: Add extra condition for the file extension when checking whether an input file is netCDF -; due to the program crashing when incorrectly identifying an HDF5 file as netCDF when -; using IDL8.3 (OK for IDL6.4) - Version 4.0b10 -; 20160213: Add CATCH, /CANCEL after idlcr8hdf call - Version 4.0b13 -; 20190806: Call routine FILE_FORMAT_A to determine the correct format of the input file(s) -; - Version 4.0b22 -; 20200930: Check that GEOMS file does not have filesize of zero before calling FILE_FORMAT_A -; - Version 4.0b24 -; -; Inputs: ifile - a string array or filespec containing the name of the HDF file(s) to be read in -; -; OPTIONS -; FORMAT - Program generates two output files per input file (with .meta and .data -; extensions). The resulting files are compatible with programs that can write HDF -; files using these two files as input. -; DUMP - Program generates two output files per input file (with .meta and .data extensions). -; Data values will be shown as indicated by the array format defined by VAR_SIZE. -; CATALOG - Program sends the variable index, variable name, format, and dimension(s) -; information to a pop-up window or the IDL DE output log window. -; POPUP - to append input/output information as well as warnings and errors to a pop-up -; display window -; LOG - to append input/output/catalog information as well as errors to a log file named -; idlcr8ascii.log -; H4 - outputs contents of the input file as an HDF4 file -; H5 - outputs contents of the input file as an HDF4 file -; NC - outputs contents of the input file as a netCDF3 file -; -; Outputs: ga - If included as a parameter in the command line, a string array containing the global -; attribute labels and values extracted from a single HDF file. -; sds - If included as a parameter in the command line, a structure using pointers, of size -; [n_sds,n_atts], containing the variable attribute labels and values (sds[n,m].va_l -; and sds[n.m].va_v) and the data (sds[n,0].data) extracted from a single HDF file. -; The data is saved in the form defined by the variable attributes -; reterr - optional string input that, if included, returns an error message to the calling -; program rather than stop the program. Note that the Pop-up option will be -; deselected if present together with this argument. -; Two ASCII files per input file containing the attributes and datasets extracted from the HDF -; file(s), if the FORMAT or DUMP options are chosen. Summary metadata information if the -; CATALOG option is chosen. Other Hierachical data format files if H4, H5, or netCDF are chosen. -; -; Called by: Main Routine -; -; Subroutines Called: INTRO_A (if program called without command line parameters) -; FILE_FORMAT_A (to determine the format of the file(s)) -; READ_HDF_SDS -; JDF_2_DATETIME -; OUTPUT_HDF_DATA (if output is not just to session memory only) -; STOP_WITH_ERROR_A (if error state detected) -; INFOTXT_OUTPUT_A (if program can make a change) -; Possible Conditions for STOP_WITH_ERROR call (plus [line number] where called): -; 1. No valid HDF4, HDF5 or netCDF file selected. -; -; Information Conditions (when the program is able to make changes): -; 1. If Session Memory option is chosen can only read in one HDF file. -; 2. /POPUP keyword cannot be used together with the 'reterr' argument. -; 3. Argument 'reterr' must be a scalar variable of type string. -; 4. /LOG, /FORMAT, and /DUMP keywords cannot be used in IDL DEMO mode -; 5. NetCDF file create feature is disabled in IDL DEMO mode - -COMMON WIDGET_WIN_A - -;Possible error messages for this procedure -proname='IDLCR8ASCII procedure: ' -IF FLOAT(!Version.Release) GE 5.6 THEN errtxt='No valid HDF or netCDF file selected.' $ -ELSE BEGIN - errtxt='No valid HDF or netCDF file selected ' - errtxt=errtxt+'(Note: Input file can only be HDF4 or netCDF3 for IDL'+!Version.Release+').' -ENDELSE -lu=-1 - -ON_IOERROR,TypeConversionError ;used when checking Datetime format - -demomode=LMGR(/DEMO) ;Boolean to check for IDL being run in demo mode (disable /LOG option) -IF N_PARAMS() EQ 1 THEN BEGIN ;File input only - ;check to see what output keyword options are set - if none then intype=-3 (i.e. invalid) - IF (~ KEYWORD_SET(o1)) AND (~ KEYWORD_SET(o2)) AND (~ KEYWORD_SET(o4)) AND $ - (~ KEYWORD_SET(o7)) AND (~ KEYWORD_SET(o8)) AND (~ KEYWORD_SET(o9)) THEN $ - intype=-3 ELSE intype=SIZE(ifile,/Type) -ENDIF ELSE IF N_PARAMS() GE 1 THEN intype=SIZE(ifile,/Type) $ ;Check that infile is a string -ELSE IF (KEYWORD_SET(o1)) OR (KEYWORD_SET(o2)) OR (KEYWORD_SET(o4)) OR $ - (KEYWORD_SET(o7)) OR (KEYWORD_SET(o8)) OR (KEYWORD_SET(o9)) THEN $ - intype=-1 $ ;need to open dialog box for input file -ELSE IF (KEYWORD_SET(o5)) OR (KEYWORD_SET(o6)) THEN intype=-3 $ ;cannot have these keyword options only -ELSE intype=-2 ;no parameter or keyword inputs in idlcr8ascii call -IF (intype NE 7) AND (intype GE 0) THEN intype=-3 ;first command line parameter not a string type -dux=[-1,-1,1] ;initialize flags for output log to IDLDE Output Window and/or to file -;dux[0] default allows output to the IDLDE output window (ignored by IDL VM) -;dux[1] is the program assigned file unit for sending log output to file -;dux[2] is the step between dux[0] and dux[1] -rerr='NA' ;initialize return error string - -o3=['-1','0','0','0','0','0','0','0'] -IF intype LT -1 THEN BEGIN ;either no or invalid input parameters and/or keywords - INTRO_A,intype ;Open Intro Box and determine output options (FORMAT/DUMP, Catalog, etc) - IF o3[0] EQ '-1' THEN BEGIN - STOP_WITH_ERROR_A,'','',lu & RETURN - ENDIF - IF o3[3] EQ 'P' THEN o3[3]='' ELSE o3[3]='D_' -ENDIF ELSE BEGIN ;Set options (FORMAT/DUMP/CONVERT, Catalog, Session Memory, Popup Window, Log Output) - IF (KEYWORD_SET(o1)) THEN o3=['F','0','0','D_','0','0','0','0'] $ - ELSE IF KEYWORD_SET(o2) THEN o3=['D','0','0','D_','0','0','0','0'] $ - ELSE o3=['0','0','0','D_','0','0','0','0'] - IF N_PARAMS() GE 3 THEN o3[2]='M' - IF KEYWORD_SET(o4) THEN o3[1]='C' - IF KEYWORD_SET(o5) THEN o3[3]='' - IF KEYWORD_SET(o6) THEN o3[4]='idlcr8ascii.log' - IF KEYWORD_SET(o7) THEN o3[5]='H4' - IF KEYWORD_SET(o8) THEN o3[6]='H5' - IF KEYWORD_SET(o9) THEN o3[7]='N3' -ENDELSE - -;Check for reterr parameter -infoval=INTARR(4) -IF (N_PARAMS() EQ 2) OR (N_PARAMS() GE 4) THEN BEGIN - ;If reterr included allows error output to return to a calling program - IF N_PARAMS() EQ 2 THEN reterr=ga ;session memory option not wanted - ;IF (ARG_PRESENT(reterr)) AND (SIZE(reterr,/Type) EQ 7) THEN BEGIN - IF SIZE(reterr,/Type) EQ 7 THEN BEGIN - rerr='' - IF o3[3] EQ '' THEN BEGIN - ;Deselect POPUP option and write INFORMATION message - o3[3]='D_' & infoval[0]=14 - ENDIF - ENDIF ELSE infoval[1]=15 - ;reterr input included but is of the incorrect type, or parameter cannot be returned to the calling program -ENDIF - -;Do check for /LOG, /FORMAT, /DUMP, and/or /NC keywords in IDL DEMO Mode and disable if necessary -IF (demomode) AND ((o3[4] EQ 'idlcr8ascii.log') OR (o3[0] NE '0') OR (o3[7] EQ 'N3')) THEN BEGIN - IF (o3[4] EQ 'idlcr8ascii.log') OR (o3[0] NE '0') THEN infoval[2]=16 - IF o3[7] EQ 'N3' THEN infoval[3]=17 - o3[0]='0' & o3[4]='0' & o3[7]='0' -ENDIF - -IF N_PARAMS() GE 1 THEN BEGIN - ;Check that input HDF/netCDF files exist. If not then prompt the user for the missing files - arorfs=SIZE(ifile,/Dimensions) ;Is file input Filespec or String Array? - IF (arorfs[0] EQ 0) THEN BEGIN ;input is filespec - IF ifile NE '' THEN ifile=FILE_SEARCH(ifile) - isize=SIZE(ifile,/Dimensions) - IF isize[0] EQ 0 THEN ifile=[''] ;no files found matching filespec - ENDIF - IF N_ELEMENTS(arorfs) GT 1 THEN ifile=REFORM(ifile,N_ELEMENTS(ifile),/Overwrite) ;Convert to 1-D array - file_exist=FILE_TEST(ifile,/Read) - gi=WHERE(file_exist EQ 1,gcnt) - IF gcnt NE 0 THEN ifile=ifile[gi] ;contains all valid filenames -ENDIF ELSE ifile=[''] -gi=WHERE(ifile NE '',nfile) -IF nfile EQ 0L THEN BEGIN - IF o3[2] EQ 'M' THEN $ ;pick one file only - ifile=DIALOG_PICKFILE(Filter=['*.hdf','*.h5','*.nc;*nc4'],/Must_Exist, $ - Title='Select HDF4, HDF5, or netCDF GEOMS Compatible Format File') $ - ELSE $ ;multiple selection permissable - ifile=DIALOG_PICKFILE(Filter=['*.hdf','*.h5','*.nc;*.nc4'],/Must_Exist, $ - /Multiple_Files,Title='Select HDF4, HDF5, or netCDF GEOMS Compatible Format File(s)') -ENDIF -;now check to see that files were actually selected, if not STOP! -gi=WHERE(ifile NE '',nfile) -IF nfile EQ 0L THEN BEGIN - STOP_WITH_ERROR_A,'D_'+proname,errtxt,lu - IF STRLEN(rerr) GT 2 THEN BEGIN - IF N_PARAMS() EQ 2 THEN ga=rerr ELSE reterr=rerr - ENDIF - RETURN -ENDIF ELSE BEGIN - ifile=ifile[gi] & dsort=SORT(ifile) & ifile=ifile[dsort] - ifile=FILE_SEARCH(ifile,/FULLY_QUALIFY_PATH) -ENDELSE - -;Set dux values according to output options -IF o3[4] NE '0' THEN BEGIN ;open idlcr8ascii.log - o3[4]=FILE_DIRNAME(ifile[0],/Mark_Directory)+o3[4] - res=FILE_TEST(o3[4],/Write) - IF res EQ 0 THEN openw,du,o3[4],/GET_LUN $ - ELSE BEGIN - OPENW,du,o3[4],/Append,/GET_LUN & FOR i=0,1 DO PRINTF,du,'' - ENDELSE - dux[1]=du & dux[2]=dux[1]-dux[0] -ENDIF -FOR i=dux[0],dux[1],dux[2] DO $ - IF i EQ -1 THEN PRINT,'HDF/netCDF File Input/Output Log - Program Started on '+SYSTIME(0) $ - ELSE PRINTF,i,'HDF/netCDF File Input/Output Log - Program Started on '+SYSTIME(0) - -IF o3[3] EQ '' THEN BEGIN - ;Set-up output window display widget - base=WIDGET_BASE(Title='HDF/netCDF File Input/Output Log',Units=2,yoffset=3,xoffset=3,Tlb_Frame_Attr=1,/Column) ;,Tab_Mode=1) - wtxt=WIDGET_TEXT(base,/Scroll,xsize=100,ysize=12) - base2=WIDGET_BASE(base) - b3=WIDGET_BUTTON(base2,value='Finish',uvalue='DONE',frame=3,Sensitive=0) ;,Accelerator='Return') - WIDGET_CONTROL,wtxt,/Realize - WIDGET_CONTROL,base,/Realize - lineno=0L -ENDIF - -IF (o3[2] EQ 'M') AND (N_ELEMENTS(ifile) GT 1) THEN BEGIN - ifile=[ifile[0]] & nfile=1L - INFOTXT_OUTPUT_A,[7] -ENDIF - -FOR i=0,N_ELEMENTS(infoval)-1 DO IF infoval[i] NE 0 THEN INFOTXT_OUTPUT_A,[infoval[i]] - -FOR ndf=0L,nfile-1L DO BEGIN - catinfo=STRARR(1,4) ;initialize catinfo array - ifile[ndf]=FILE_SEARCH(ifile[ndf],/Fully_Qualify_Path) - IF o3[3] EQ '' THEN BEGIN - lineno=lineno+2L - IF ndf NE 0 THEN WIDGET_CONTROL,wtxt,set_value='',/Append - WIDGET_CONTROL,wtxt,set_value='Reading '+ifile[ndf],/Append,Set_text_top_line=lineno - ENDIF - FOR i=dux[0],dux[1],dux[2] DO BEGIN - IF i EQ -1 THEN BEGIN - IF ndf NE 0 THEN PRINT,'' - PRINT,'Reading '+ifile[ndf] & PRINT,'' - ENDIF ELSE BEGIN - IF ndf NE 0 THEN PRINTF,i,'' - PRINTF,i,'Reading '+ifile[ndf] & PRINTF,i,'' - ENDELSE - ENDFOR - - ftype='XX' - IF (FILE_TEST(ifile[ndf])) AND (~FILE_TEST(ifile[ndf],/ZERO_LENGTH)) THEN BEGIN - ;Identify the file format - ftype=FILE_FORMAT_A(ifile[ndf]) - IF ftype NE 'XX' THEN catinfo[0,0]=ftype - ENDIF - IF ftype EQ 'XX' THEN BEGIN - ;Either file doesn't exist or is not a valid file format - STOP_WITH_ERROR_A,o3[3]+proname,errtxt,lu - IF STRLEN(rerr) GT 2 THEN BEGIN - IF N_PARAMS() EQ 2 THEN ga=rerr ELSE reterr=rerr - ENDIF - RETURN - ENDIF - - ;Open and read the file - READ_HDF_SDS,ifile[ndf],ga,sds,catinfo - IF STRLEN(rerr) GT 2 THEN BEGIN - reterr=rerr & RETURN - ENDIF - - IF catinfo[0,0] EQ 'HE5' THEN BEGIN - STOP_WITH_ERROR_A,o3[3]+proname,errtxt,lu - IF STRLEN(rerr) GT 2 THEN BEGIN - IF N_PARAMS() EQ 2 THEN ga=rerr ELSE reterr=rerr - ENDIF - RETURN - ENDIF ELSE BEGIN ;Do checks and output standard format file - ;Convert format of DATA_START/STOP_DATE and FILE_GENERATION_DATE if required - ;Note: for /Dump option output will still be original format - isodates=['DATA_START_DATE','DATA_STOP_DATE','FILE_GENERATION_DATE'] - niso=N_ELEMENTS(isodates) - gaiso=STRARR(niso) & gih=INTARR(niso) - FOR i=0,niso-1 DO BEGIN - gi=WHERE(STRPOS(STRUPCASE(ga),isodates[i]) NE -1,gcnt) - IF gcnt EQ 1 THEN BEGIN - res=STRSPLIT(ga[gi[0]],' =',/EXTRACT) - IF N_ELEMENTS(res) EQ 2 THEN BEGIN - IF STRPOS(STRUPCASE(res[1]),'Z') EQ -1 THEN BEGIN ;i.e. not ISO8601 - valid=0 ;used for Type Conversion Check - mjd=DOUBLE(res[1]) ;will jump to TypeConversionError if Res[1] is not a number - valid=1 ;got to here so valid conversion (presumably it is MJD2000) - TypeConversionError: - IF valid EQ 1 THEN BEGIN - iso='' & iso=JDF_2_DATETIME(mjd,/MJD2000,/S) - gaiso[i]=res[0]+'='+iso - IF o3[0] EQ 'F' THEN ga[gi[0]]=gaiso[i] - gih[i]=gi[0] - ENDIF - ENDIF - ENDIF - ENDIF - ENDFOR - - ;Create ASCII Output - IF (o3[0] NE '0') OR (o3[1] NE '0') THEN OUTPUT_HDF_DATA,ifile[ndf],ga,sds,catinfo - IF (o3[5] NE '0') OR (o3[6] NE '0') OR (o3[7] NE '0') THEN BEGIN - ;Check for existence of TAV file in the input file directory or working directory - indir=FILE_DIRNAME(ifile[ndf],/MARK_DIRECTORY) - tav=FILE_SEARCH(indir+'tableattrvalue*.dat',/FULLY_QUALIFY_PATH) - IF tav[0] EQ '' THEN INFOTXT_OUTPUT_A,[12] $ ;No valid TAV file so format conversion cannot be done - ELSE BEGIN - ;Convert data to a different format - validpath=1 & ftest=5 & dferr='' - IF demomode THEN ftestmax=6 ELSE ftestmax=7 - WHILE (validpath EQ 1) AND (ftest LE ftestmax) DO BEGIN - IF o3[ftest] NE '0' THEN BEGIN - CATCH, path_error ;Return program generated error if IDLcr8HDF is not in the search path - ;Error Handler - IF path_error NE 0 THEN BEGIN ;IDLcr8HDF is not in the IDL Search Path - validpath=0 - INFOTXT_OUTPUT_A,[13] - ;PRINT, 'Error index: ', path_error - ;PRINT, 'Error message: ', !ERROR_STATE.MSG - CATCH, /CANCEL - ENDIF - IF validpath EQ 1 THEN BEGIN - ;Try to call IDLcr8HDF - IF demomode THEN BEGIN - CASE 1 OF - ftest EQ 5: IDLCR8HDF,ga,sds,tav[0],indir,dferr - ftest EQ 6: IDLCR8HDF,ga,sds,tav[0],indir,dferr,/H5 - ENDCASE - ENDIF ELSE BEGIN - CASE 1 OF - ftest EQ 5: IDLCR8HDF,ga,sds,tav[0],indir,dferr,/L - ftest EQ 6: IDLCR8HDF,ga,sds,tav[0],indir,dferr,/H5,/L - ftest EQ 7: IDLCR8HDF,ga,sds,tav[0],indir,dferr,/NC,/L - ENDCASE - ENDELSE - CATCH, /CANCEL - IF o3[4] NE '0' THEN BEGIN - ;transfer contents of idlcr8hdf.log to idlcr8ascii.log - OPENR,fu,indir+'idlcr8hdf.log',/DELETE,/GET_LUN - dum='' - READF,fu,dum & READF,fu,dum - WHILE NOT EOF(fu) DO BEGIN - READF,fu,dum & PRINTF,du,dum - ENDWHILE - FREE_LUN,fu - ENDIF ELSE FILE_DELETE,indir+'idlcr8hdf.log',/QUIET - ENDIF - ENDIF - ftest++ - ENDWHILE - ENDELSE - ENDIF - FOR i=0,niso-1 DO IF gaiso[i] NE '' THEN ga[gih[i]]=gaiso[i] - IF o3[2] EQ 'M' THEN BEGIN - IF o3[3] EQ '' THEN BEGIN - lineno=lineno+1L - WIDGET_CONTROL,wtxt,set_value=ifile+' array and heap structure created!',/Append,Set_text_top_line=lineno - ENDIF - FOR i=dux[0],dux[1],dux[2] DO $ - IF i EQ -1 THEN PRINT,' '+ifile[ndf]+' array and heap structure created!' $ - ELSE PRINTF,i,' '+ifile[ndf]+' array and heap structure created!' - ENDIF - ENDELSE -ENDFOR - -IF rerr EQ '' THEN $ - IF nfile EQ 1 THEN reterr=ifile[0]+' successfully read' $ - ELSE reterr='Files successfully read' -IF N_PARAMS() EQ 2 THEN ga=reterr - -FOR i=dux[0],dux[1],dux[2] DO BEGIN - IF i EQ -1 THEN BEGIN - PRINT,'' & PRINT,'File read completed - Program Ended on '+SYSTIME(0) - ENDIF ELSE BEGIN - PRINTF,i,'' & PRINTF,i,'File read completed - Program Ended on '+SYSTIME(0) - ENDELSE -ENDFOR -IF dux[1] GT -1 THEN FREE_LUN,dux[1] -IF o3[3] EQ '' THEN BEGIN - WIDGET_CONTROL,b3,Sensitive=1,/Input_Focus - WIDGET_CONTROL,wtxt,set_value='',/Append,Set_text_top_line=lineno+2L - WIDGET_CONTROL,wtxt,set_value='HDF file read completed - hit <Finish> to close program',/Append - XMANAGER,'idlcr8ascii',base -ENDIF ELSE IF intype LT 0 THEN BEGIN ;Create Finish Dialog Box - res=DIALOG_MESSAGE('HDF file read completed!',/Information,Title='EVDC/AVDC IDLcr8ASCII') -ENDIF - -END ;procedure IDLcr8ASCII +;Main Program Version: idlcr8ascii.pro v4.0b27, 20240927 +; Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org +; +;Sub-versions: +; v3.01, 20081020 - If the HDF4 file is created using the HDF4.2r3 library, then extra information +; regarding the dimensions of the VAR_DEPEND values may be included in the file +; (which show up as extra datasets in the HDF_SD_FILEINFO call). A check for +; this is carried out and, if found, the information is excluded from the output. +; v3.02, 20090311 - Improve HDF4.2r3 library checks by comparing the number of datasets recorded +; under DATA_VARIABLES, with the number returned by the HDF_SD_FILEINFO call; +; Change 'WARNING' to 'INFORMATION; Add procedure to handle 'INFORMATION' text +; output; Add 'INFORMATION' text if anything irregular found with the datasets; +; Change 'Data dimensions exceed 8' and 'Data type not allowable' ERROR messages +; to 'INFORMATION' messages. Make log file output to the same directory as the +; first HDF file to be read. +; v3.03, 20091208 - Incorporate HDF_SD_ISCOORDVAR to differentiate between datasets and dimension +; variable names. Add check for allowable VAR_DATA_TYPE=STRING. Add all +; INFORMATION text messages to INFOTXT_OUTPUT_A procedure to avoid duplication +; of code that creates the messages; Add INFORMATION messages when reading HDF5 +; files; Rename AVDC Button to FORMAT and replace keyword option /AVDC with +; /FORMAT. Improve checks on parameter and keyword inputs; Improve checks for +; non-standard (i.e. non-groundbased) HDF files. +; v4.0b1, 20101122 - Adopt GEOMS metadata standard; Because FORMAT attribute(s) have been dropped +; can no longer use defined formatting values when outputing data to files; +; Default ASCII formatting adopted - scientific notation for REAL and DOUBLE, +; except for DATETIME and related values, which have formatting dependent on +; the data type; Numeric metadata values remain in their original data type +; when writing to session memory, meaning: variable attribute labels and values +; now written to separate structure variables sds.va_l and sds.va_v (previously +; sds.va), and structure has 2 dimensions (n_sds, n_atts) instead of 1 +; (previously n_sds); The dataset is written to the initial n_atts index (i.e. +; sds[n_sds,0].data); Rename saved SDS datatype according to GEOMS rules +; (affects INT, LONG, and FLOAT); Add JDF_2_DATETIME and JULIAN_DATE routines +; to the program; Include coordinate variables if they and the non-coordinate +; variables add up to the number of saved DATA_VARIABLES. Add return error code +; option so that program returns to the calling program if an error is +; generated. +; v4.0b2, 20110323 - If required reverse the order of VAR_SIZE and VAR_DEPEND values so that they +; match the order of the multi-dimensional dataset. Note that the HDF read programs +; automatically transpose the multi-dimensional datasets to conform to the IDL +; convention of fastest changing index being listed first (opposite for HDF). +; v4.0b3, 20111014 - Add netCDF support - writes variable attributes to GEOMS standard attributes +; where possible, otherwise writes to VAR_NOTES +; v4.0b4, 20111208 - Test for dimension ordering of the netCDF files, and reverse if required (same +; rules as for HDF). Create separate Dimension Ordering Routine +; v4.0b5, 20120328 - Add options to convert HDF/netCDF files to other HDF/netCDF formats e.g. H4 to +; H5 etc. Requires IDLcr8HDF in the search path to do this. +; v4.0b6, 20120426 - Bug fix when a netCDF file only contains datasets with single dimensions - +; VAR_SIZE information was not being extracted during the read process. +; v4.0b7 20130114 - Add check for IDL being run in DEMO mode by using LMGR command. Replace PRINTF,-1 +; statements (i.e. when dux[0] eq -1) with PRINT (o/w causes DEMO mode to stop). Also +; disable /LOG, /FORMAT and /DUMP keywords in DEMO mode. +; v4.0b8 20140325 - Fix bug that meant VAR_SIZE and VAR_DEPEND values were not being automatically +; reversed if the array sizes were the same and VAR_DEPEND did not include a DATETIME +; value. Now defaults to the 'reverse' option in the TEST_DIM_ORDER routine if no +; other conditions are satisfied; Fix bug that caused crash when VAR_SIZE values were +; not of type string. +; v4.0b9 20141110 - Fix bug that caused multi-dimensional array ordering to not be correctly identified +; if the first dataset to be checked in the file had the same number of elements in +; the array (e.g. was a set of Averaging Kernels). +; v4.0b10 20150127 - Allow for VAR_FILL_VALUE values for string datasets to be written and saved as an +; (empty) string of the correct length. Ensure string datasets are all written and +; saved to the correct length (that of the longest string in the dataset). +; v4.0b12 20151109 - Convert DATETIME (and related) max, min and fill values to LONG64 data type before +; determining the format for ASCII output to account for very large values. +; v4.0b15 20160725 - Fix bug causing program to crash when the number of DATA_VARIABLES values is greater +; than the number of datasets in the file. +; v4.0b19 20180218 - Fix bug that caused multi-dimensional array ordering to not be correctly identified +; if the datasets have the same VAR_SIZE i.e. rev_vd_vs stays as 2 through all the +; checks. Now rev_vd_vs changes to 1 (VAR_SIZE, VAR_DEPEND and dataset ordering is +; reversed) if rev_vd_vs=2 after all the checks. +; v4.0b20 20181116 - Fix bug that caused multi-dimensional array ordering to not be correctly written if +; not consistent through the file i.e. rev_vd_vs eq 3. Now rev_vd_vs changes to 1 +; (VAR_SIZE, VAR_DEPEND and dataset ordering is reversed), but information message +; advising of the issue is still reported. +; v4.0b21 20190514 - Fix bug that occurred when GEOMS variable attribute information is missing from an +; HDF4 file but written to the heap structure based on the contents of the file. Previously +; the variable attribute label was not written correctly. +; v4.0b22 20190806 - Provide support for reading netCDF4 files. Currently uses the HDF5 routines to do this +; so need to account for features unique to netCDF4 such as; the inclusion of dimension +; names; standard netCDF attribute names; empty attribute values that do not include a +; null termination character (which causes an error in the H5A_Read routine). New +; sub-routines include is_a_number_ascii, alpha_numeric_underscore, nc_dimension_chk, +; file_format_a. +; v4.0b23 20190821 - Fixed bugs associated with H5 TAG_NAMES checks introduced in v4.0b22. +; v4.0b24 20200830 - Add INFORMATION messages (20-22) that identify issues with the VAR_DEPEND and VAR_SIZE +; attributes, that were previously fixed 'quietly' by the program; Check input GEOMS file +; for zero filesize before doing checks. +; v4.0b25 20220805 - For NetCDF3 or 4, if SDS_NAME NE VAR_NAME then converts non-alphanumeric VAR_NAME +; characters to '_' and compares with SDS_NAME. If there is a match then program quietly +; converts SDS_NAME to VAR_NAME for testing, otherwise generates an error; If input file has +; an HDF5 file signature then checks for .nc .nc4 .netcdf and .netcdf4 extension and also +; calls NCDF_PARSE (if ge 8.5.2) to check for NC4 compatibility; Create DATA_TYPE_CHECKS +; procedure to do all the data type checks. Unable to check for unsigned 8-bit in netCDF3 +; files at this time; Generate an INFORMATION/ERROR message if VAR_UNITS is numeric +; (same as VAR_SIZE); INFORMATION/ERROR message added if a mis-match is found between +; the input file name and the FILE_NAME attribute value. +; v4.0b26 20240912 - For NetCDF3 files only output WARNING to check for signed BYTE entries when the dataset +; is of type BYTE (infotxt_output_a message 23). +; v4.0b27 20240927 - For netCDF4 files allow for additional standard netCDF attribute names. + +PRO idlcr8ascii_common +;Procedure to define the data COMMON block WIDGET_WIN_A, containing common variables +;associated with the Graphical User Interface +; ---------- +; Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org +; +; History: +; 20061004: Introduced to IDLCR8ASCII - Version 2.0 +; 20080302: 'cwidg' variable removed, and 'dux' array added to identify requested +; output options - Version 3.0 +; 20101122: Add rerr - Version 4.0b1 +; +; +; Input: Nil +; +; Output: Nil +; +; Called by: N/A +; +; Subroutines Called: None + +COMMON WIDGET_WIN_A,wtxt,lineno,base,o3,b3,dux,rerr + +END ; Procedure idlcr8ascii_common + + + +PRO intro_a_event, ev +;Procedure to define how Events from the Start-up Introduction Window are handled +; ---------- +;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org +; +; History: +; 20061004: Introduced to IDLCR8ASCII - Version 2.0 +; 20080302: Code added to define the Pop-up Window and Log Output events added to +; the Introduction Window - Version 3.0 +; +; Input: ev - Selected widget event structure +; +; Output: o3 - Common string array defining the various options for program output +; +; Called by: XMANAGER in INTRO_A +; +; Subroutines Called: None + +COMMON WIDGET_WIN_A + +;The uservalue is retrieved from a widget when an event occurs +WIDGET_CONTROL,ev.id,GET_UVALUE=uv +IF (uv EQ 'C') OR (uv EQ 'P') OR (uv EQ 'idlcr8ascii.log') OR $ + (uv EQ 'H4') OR (uv EQ 'H5') OR (uv EQ 'N3') THEN BEGIN + CASE 1 OF + uv EQ 'C': BEGIN + uvi=1 & eadd=12L + END + uv EQ 'P': uvi=3 + uv EQ 'H4': BEGIN + uvi=5 & eadd=6L + END + uv EQ 'H5': BEGIN + uvi=6 & eadd=5L + END + uv EQ 'N3': BEGIN + uvi=7 & eadd=4L + END + ELSE: uvi=4 + ENDCASE + IF o3[uvi] EQ uv THEN o3[uvi]='0' ELSE o3[uvi]=uv + IF (uv EQ 'C') OR (uv EQ 'H4') OR (uv EQ 'H5') OR (uv EQ 'N3') THEN BEGIN + IF (o3[1] EQ '0') AND (o3[5] EQ '0') AND (o3[6] EQ '0') AND (o3[7] EQ '0') THEN $ + WIDGET_CONTROL,ev.id+eadd,Sensitive=0 $ + ELSE BEGIN + WIDGET_CONTROL,ev.id+eadd,Sensitive=1 + IF uv EQ 'C' THEN BEGIN + WIDGET_CONTROL,ev.id+1,/Set_Button + o3[3]='P' + ENDIF + ENDELSE + ENDIF +ENDIF ELSE IF (uv EQ 'F') OR (uv EQ 'D') THEN o3[0]=uv $ ;Assign button event to a variable name +ELSE IF uv EQ 'Cont' THEN o3[0]='0' ;changes o3[0] from -1 to 0 +IF (uv NE 'C') AND (uv NE 'P') AND (uv NE 'idlcr8ascii.log') AND (uv NE 'H4') AND $ + (uv NE 'H5') AND (uv NE 'N3') THEN WIDGET_CONTROL,ev.top,/DESTROY + +END ;Intro_Event handler + + + +PRO intro_a, intype +;Procedure which creates an Introduction Window at start-up when IDLCR8ASCII is called without parameters, +;or invalid parameters, or is called by IDL Virtual Machine. The user has a choice of continuing with +;the program after selecting input options, or stopping the program. +; ---------- +;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org +; +; History: +; 20061004: Introduced to IDLCR8ASCII - Version 2.0 +; 20080302: Swapped the order of the option windows, so that the options indicated by the check +; boxes appear above the option boxes that close the Introduction Window and continue +; with the program; Added options to allow the user to send input/output log information +; to a Pop-Up Window and/or a Log File - Version 3.0 +; 20091208: Change AVDC button to FORMAT and keyword option from /AVDC to /FORMAT - Version 3.03 +; 20101122: Add vertxt array. Modify Introduction text to indicate that program accepts 'GEOMS +; Compliant' files, and also to show the new structure format - Version 4.0b1 +; +; Input: intype - Integer set to -2 or -3: -2 indicates normal state; -3 indicates that input +; parameters or keywords at the IDLCR8ASCII call are invalid. +; +; Output: Nil +; +; Called by: IDLCR8ASCII +; +; Subroutines Called: INTRO_A_EVENT (via XMANAGER) + +COMMON WIDGET_WIN_A + +;procedure which provides an introduction message before starting the program. +nhdr=46 & errtxt=STRARR(nhdr) +vertxt=['idlcr8ascii-v4.0_Readme.pdf','v4.0b27 September 2024'] +errtxt[1]='Welcome to IDLcr8ASCII. This program reads GEOMS compliant HDF4, HDF5 and netCDF files and' +errtxt[2]='saves contents to either session memory, an output window (summary only) or to ASCII or formatted' +errtxt[3]='files (also refer to '+vertxt[0]+').' +errtxt[5]='Inputs to the program:' +errtxt[6]=' HDF/netCDF FILE(s) - GEOMS compliant HDF4, HDF5 or netCDF files. Input can be by DIALOG_BOX' +errtxt[7]=' (IDL VM or full version of IDL), or by command line (full version of IDL only).' +errtxt[9]='Choice of output is made by selecting options in this DIALOG_BOX (IDL VM or full version' +errtxt[10]=' of IDL), or by ''Keywords'' at the command line input (full version only), as follows:' +errtxt[11]=' /F or /FORMAT - generates two output files (with .META and .DATA extensions). The resulting' +errtxt[12]=' files can be used as inputs to GEOMS compliant write programs (IDLcr8HDF etc), OR' +errtxt[13]=' /D or /DUMP - generates two output files (with .META and .DATA extensions). Data values' +errtxt[14]=' will be shown as indicated in the array format defined by VAR_SIZE, AND/OR' +errtxt[15]=' /C or /CATALOG - sends variable index, variable name, data format, and dimension size' +errtxt[16]=' information to an output window and/or log file, AND/OR' +errtxt[17]=' /P or /POPUP - sends log input/output information to a Pop-up Dialog Box, AND/OR' +errtxt[18]=' /L or /LOG - sends log input/output information to a file named idlcr8ascii.log' +errtxt[19]=' /H4 - outputs contents of the input file as an HDF4 file' +errtxt[20]=' /H5 - outputs contents of the input file as an HDF5 file' +errtxt[21]=' /NC3 - outputs contents of the input file as a netCDF3 file' +errtxt[23]='Example of command line input: idlcr8ascii,DFSpec[,/F][,/D][,/C][,/P][,/L][,/H4][,/H5][,/NC3]' +errtxt[24]=' where ''DFSpec'' can either be a string array containing filenames or a scalar string containing a' +errtxt[25]=' filespec or a single file name.' +errtxt[26]='If running the full version of IDL, and input is a single HDF file, output can also be saved' +errtxt[27]=' to session memory. This can be done by calling idlcr8ascii with the following command line:' +errtxt[28]=' idlcr8ascii,DFFile,GA,SDS[,/F][,/D][,/C][,/P][,/L][,/H4][,/H5][,/NC3].' +errtxt[29]='DFFile can either be the name of an input file or '''' (in which case a DIALOG_BOX will open' +errtxt[30]=' and prompt for the input filename). ''GA'' is a returned string array containing the Global' +errtxt[31]=' Attributes, and ''SDS'' is a returned structure using pointers containing the Variable' +errtxt[32]=' Attribute Labels (sds.va_l), Variable Attribute Values (sds.va_v), and the Data (sds.data). +errtxt[34]='Contacts -' +errtxt[35]=' Ian Boyd, Bryan Scientific Consulting LLC (iboyd@bryanscientific.org)' +errtxt[36]=' 9 Cambridge Terrace' +errtxt[37]=' Masterton 5810, New Zealand' +errtxt[39]=' Ann Mari Fjaeraa, EVDC Project Manager (amf@nilu.no)' +errtxt[40]=' Norwegian Institute for Air Research, Instituttveien 18' +errtxt[41]=' Postbox 100, N-2027 KJELLER, NORWAY' +errtxt[43]='EVDC Website: Tools and documentation available from http://evdc.esa.int/' +errtxt[45]='To continue, please choose from the options below.' +errtxt=' '+errtxt + +;Set-up text display widget +demomode=LMGR(/DEMO) & optsens=1 +IF intype EQ -3 THEN xtxt=' - Command Line Input Error' ELSE xtxt='' +base=WIDGET_BASE(Title='idlcr8ascii '+vertxt[1]+xtxt,Tlb_Frame_Attr=1,/Column) ;,Tab_Mode=1) +wtxt=WIDGET_TEXT(base,xsize=90,ysize=25,/Scroll) + +tip='Left Mouse Click or Tab to entry and hit <Spacebar>' +base2=WIDGET_BASE(base,/Nonexclusive) +cattxt='Catalog - Generate a summary of the contents of the input file(s) and send to an output window or file' +poptext='Open a Pop-Up Window to display log input/output (default if ''Catalog'' option is chosen)' +logtext='Append log input/output to the file ''idlcr8ascii.log'' ' +IF demomode THEN BEGIN + logtext=logtext+'(No log or netCDF file output permitted in IDL DEMO Mode)' & optsens=0 +ENDIF ELSE logtext=logtext+'(will create the file IF it doesn''t exist)' +h4text='Convert input file(s) to HDF4' +h5text='Convert input file(s) to HDF5' +nctext='Convert input file(s) to netCDF3' +fctext1='For the following 3 options, any existing files will be overwritten, IDLcr8HDF must be in the IDL Search Path,' +fctext2='and a valid Table Attribute Values (TAV) file must be in the same directory as the Input file(s).' +b1=WIDGET_BUTTON(base2,value=cattxt,uvalue='C',frame=3) +b2=WIDGET_BUTTON(base2,value=poptext,uvalue='P',frame=3) +b3=WIDGET_BUTTON(base2,value=logtext,uvalue='idlcr8ascii.log',sensitive=optsens,frame=3) + +base3=WIDGET_LABEL(base) +WIDGET_CONTROL,base3,set_value=fctext1 +base4=WIDGET_LABEL(base) +WIDGET_CONTROL,base4,set_value=fctext2 + +base5=WIDGET_BASE(base,/Nonexclusive,ROW=1) +b4=WIDGET_BUTTON(base5,value=h4text,uvalue='H4',frame=3) +b5=WIDGET_BUTTON(base5,value=h5text,uvalue='H5',frame=3) +b6=WIDGET_BUTTON(base5,value=nctext,uvalue='N3',sensitive=optsens,frame=3) + +base6=WIDGET_BASE(base,/Row) +b7=WIDGET_BUTTON(base6,value='ASCII Formatted',uvalue='F',sensitive=optsens,frame=3) ;,Tooltip=Tip) +b8=WIDGET_BUTTON(base6,value='ASCII Dump',uvalue='D',sensitive=optsens,frame=3) ;,Tooltip=Tip) +b9=WIDGET_BUTTON(base6,value='Continue',uvalue='Cont',frame=3,Sensitive=0) ;,Tooltip=Tip) +b10=WIDGET_BUTTON(base6,value='Stop',uvalue='S',frame=3) ;,ToolTip=Tip) +WIDGET_CONTROL,base,/Realize +WIDGET_CONTROL,b1,/Input_Focus +FOR i=0,N_ELEMENTS(errtxt)-1 do $ + WIDGET_CONTROL,wtxt,set_value=errtxt[i],/Append +XMANAGER,'intro_a',base + +END ;Intro_A + + + +PRO idlcr8ascii_event, ev +;Procedure to close the pop-up logging window after user selects 'Finish'. +; ---------- +;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org +; +; History: +; 20061004: Introduced to IDLCR8ASCII - Version 2.0 +; +; Input: Selected widget event structure +; +; Output: Nil +; +; Called by: XMANAGER in IDLCR8ASCII and STOP_WITH_ERROR_A +; +; Subroutines Called: None + +WIDGET_CONTROL,ev.top,/DESTROY +RETALL & HEAP_GC + +END ;Proc IDLcr8ASCII_Event + + + +PRO stop_with_error_a, txt1, txt2, lu +;Procedure called when an error in the program is detected. An error message is displayed +;and the program stopped and reset. If necessary, open files are closed. The error message +;is displayed in one or more of the following: a Pop-up dialog window; the Pop-up logging +;window; the Output Logging window in the IDLDE. +; ---------- +;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org +; +; History: +; 20050729: Original IDLCR8ASCII Routine - Version 1.0 +; 20061004: Set-up so that the error output is displayed in the output window dependent on the +; method that IDLCR8ASCII is called. If txt1 is preceeded by 'D_' or is null, the +; error output is to a Pop-up Dialog window. Otherwise output will be to a logging +; window. Renamed from STOP_WITH_ERROR to STOP_WITH_ERROR_A to avoid a name conflict +; with the equivalent procedure in IDLCR8HDF. Common variable definition WIDGET_WIN_A +; added - Version 2.0 +; 20080302: Added code which sends the error message to the IDLDE output window and/or an external +; file (as determined by the dux array values) - Version 3.0 +; 20101122: Allow routine to return to the calling program, rather than stop the application, if +; a 'reterr' argument is included in the call to idlcr8ascii - Version 4.0b1 + +; +; Inputs: txt1 - the initial text line of the error message. Generally contains the name of the routine +; where the error was detected. +; txt2 - the second text line which generally describes the error state. +; lu - Where applicable, the file unit that needs to be closed at the termination of the program, +; otherwise set to -1. +; +; Output: Nil +; +; Called by: The routine in which the error was detected. The following routines call STOP_WITH_ERROR: +; READ_HDF_SDS; IDLCR8ASCII +; +; Subroutines Called: IDLCR8ASCII_EVENT (via XMANAGER) + +COMMON WIDGET_WIN_A + +IF lu NE -1L THEN FREE_LUN,lu + +IF txt1 EQ '' THEN BEGIN ;<cancel> chosen on Intro box + res=DIALOG_MESSAGE('IDLcr8ASCII Stopped!',/Information,Title='EVDC/AVDC IDLcr8ASCII') +ENDIF ELSE BEGIN + IF STRMID(txt1,0,2) EQ 'D_' THEN txtx=STRMID(txt1,2) ELSE txtx=txt1 + FOR i=dux[0],dux[1],dux[2] DO BEGIN + IF i EQ -1 THEN BEGIN + PRINT,' ERROR in '+txtx & PRINT,' '+txt2 + PRINT,'' & PRINT,'IDLcr8ASCII stopped - Program Ended on '+SYSTIME(0) + ENDIF ELSE BEGIN + PRINTF,i,' ERROR in '+txtx & PRINTF,i,' '+txt2 + PRINTF,i,'' & PRINTF,i,'IDLcr8ASCII stopped - Program Ended on '+SYSTIME(0) + ENDELSE + ENDFOR + IF dux[1] GT -1 THEN FREE_LUN,dux[1] + IF (STRMID(txt1,0,2) EQ 'D_') AND (rerr EQ 'NA') THEN BEGIN + ;write error to DIALOG Box instead of Popup window + errtxt2=STRARR(4) + errtxt2[0]=STRMID(txt1,2) & errtxt2[1]=txt2 & errtxt2[3]='IDLcr8ASCII Stopped!' + res=DIALOG_MESSAGE(errtxt2,/Error,Title='EVDC/AVDC IDLcr8ASCII Error') + ENDIF ELSE IF rerr EQ 'NA' THEN BEGIN ;write error to Popup window + lineno=lineno+4L + WIDGET_CONTROL,wtxt,set_value=' ERROR in '+txt1,/Append + WIDGET_CONTROL,wtxt,set_value=' '+txt2,/Append + WIDGET_CONTROL,wtxt,set_value='',/Append,Set_text_top_line=lineno + WIDGET_CONTROL,wtxt,set_value='HDF file read stopped - hit <Finish> to close program',/Append + WIDGET_CONTROL,b3,Sensitive=1,/Input_Focus + XMANAGER,'stop_with_error_a',base,Event_Handler='idlcr8ascii_event' + ENDIF +ENDELSE +HEAP_GC +IF rerr EQ 'NA' THEN RETALL ELSE rerr='Unable to read HDF or NC file - '+txt2 + +END ;Procedure Stop_With_Error_A + + + +FUNCTION is_a_number_ascii, value + ON_IOERROR, ConversionError + IF STRTRIM(value,2) EQ '' THEN RETURN, 0B + n=DOUBLE(value) + RETURN, 1B + ConversionError: + RETURN, 0B +END + + + +FUNCTION alpha_numeric_underscore, str_value +;Function that returns the input string with non-alphanumeric characters replaced with '_' +; ---------- +;Written by Ian Boyd for the EVDC - iboyd@bryanscientific.org +; +; History: +; 20190806: Introduced - Version 4.0b22 +; +; Input: str_value: string +; +; Returns: string with non-alphanumeric values replaced with '_' +; +; Called by: Read_HDF_SDS +; +ON_IOERROR, ConversionError +numb=BINDGEN(10)+48B ;0-9 +ucb=BINDGEN(26)+65B ;A-Z +lcb=BINDGEN(26)+97B ;a-z +str_value=STRING(str_value) +new_str_value=str_value +n_char=STRLEN(str_value) +FOR i=0L,n_char-1L DO BEGIN + byt_value=BYTE(STRMID(new_str_value,i,1)) + testn=(byt_value GE numb[0]) AND (byt_value LE numb[9]) + testu=(byt_value GE ucb[0]) AND (byt_value LE ucb[25]) + testl=(byt_value GE lcb[0]) AND (byt_value LE lcb[25]) + IF (~testn) AND (~testu) AND (~testl) THEN $ + STRPUT,new_str_value,'_',i +ENDFOR +RETURN, new_str_value +ConversionError: +RETURN, 'io_error' + +END ;Function alpha_numeric_underscore + + + +PRO infotxt_output_a, in0, in1 +;Procedure called to report information relevant to the reading of the HDF file. +;This information can be reported to a Pop-up logging window, IDLDE output log window, and/or +;the log file. Code for this output was originally written directly in the affected procedures +; ---------- +;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org +; +; History: +; 20090311: Introduced to IDLCR8ASCII - Version 3.02 +; 20091208: Added all the INFORMATION text messages to the routine, to avoid duplication +; in the HDF4 and HDF5 read sections of READ_HDF_SDS - Version 3.03 +; 20160213: Update information message when in0[0] EQ 13 - Version 4.0b13 +; 20161130: Added in0[0] messages 18 and 19 to cover non-GEOMS metadata attributes issues +; - Version 4.0b16 +; 20220805: Modified message 2 (full message created in DATA_TYPE_CHECKS) and added messages +; 23 and 24 - Version 4.0b25 +; 20240912: Changed WARNING message in 23 so that it is only generated when the NC3 dataset +; is of type BYTE - Version 4.0b26 +; +; Inputs: in0 - Integer array containing input used to make correct text output +; in1 - String array containing input used to make correct text output +; +; Output: Nil +; +; Called by: The routine in which the request for INFORMATION text was made. +; The following routines call INFOTXT_OUTPUT_A: READ_HDF_SDS; OUTPUT_HDF_DATA; +; IDLCR8ASCII +; +; Subroutines Called: None + +COMMON WIDGET_WIN_A + +CASE 1 OF + in0[0] EQ 0: BEGIN + infotxt=STRARR(3) + IF LONG(in1[0]) EQ 0L THEN BEGIN + infotxt[0]=' INFORMATION: Global Attribute DATA_VARIABLES not found or has no values.' + infotxt[1]=' Dataset listing order matches the order that the dataset is read in the GEOMS file.' + ENDIF ELSE BEGIN + infotxt[0]=' INFORMATION: Number of Global Attribute DATA_VARIABLES values ('+in1[0]+')' + infotxt[1]=' does not match the number of datasets saved to the GEOMS file ('+in1[1]+')' + ENDELSE + infotxt[2]=' Number of datasets determined from '+in1[2]+' call' + END + in0[0] EQ 1: infotxt=' INFORMATION: '+in1[0]+' has '+in1[1]+' data dimensions' + in0[0] EQ 2: infotxt=' INFORMATION: '+in1[0] + in0[0] EQ 3: BEGIN + infotxt=STRARR(4) + IF (STRPOS(STRUPCASE(in1[1]),STRUPCASE(in1[0])) NE -1) AND (in0[1] EQ 4) AND (in0[2] EQ 0) THEN $ + infotxt[0]=' INFORMATION: Dataset Name is truncated (File created with HDF4.2r1 library or earlier):' $ + ELSE infotxt[0]=' INFORMATION: Dataset Name does not match VAR_NAME value:' + infotxt[1]=' SDS_NAME: '+in1[0] + infotxt[2]=' VAR_NAME: '+in1[1] + IF in0[2] EQ 0 THEN infotxt[3]=' Output uses VAR_NAME from Metadata' $ + ELSE infotxt[3]=' Output uses Dataset Name from the HDF file' + END + in0[0] EQ 4: BEGIN + infotxt=STRARR(2) + IF in0[1] EQ 0 THEN BEGIN + infotxt[0]=' INFORMATION: Metadata label VAR_NAME not found for dataset:' + infotxt[1]=' SDS_NAME: '+in1[0] + ENDIF ELSE IF (in0[2] EQ 0) AND (in0[3] NE 0L) THEN BEGIN + infotxt[0]=' INFORMATION: VAR_NAME value does not match any DATA_VARIABLES values:' + infotxt[1]=' VAR_NAME: '+in1[1] + ENDIF + END + in0[0] EQ 5: BEGIN + infotxt=STRARR(2) + infotxt[0]=' INFORMATION: No Variable Attributes returned after call to '+in1[1] + infotxt[1]=' SDS_NAME: '+in1[0] + END + in0[0] EQ 6: BEGIN + infotxt=' INFORMATION: Metadata Dataset listing order may not match the order of' + infotxt=infotxt+' the Global Attribute DATA_VARIABLES Values' + END + in0[0] EQ 7: BEGIN + infotxt=' INFORMATION: Can read one HDF or netCDF file into session memory.' + infotxt=infotxt+' Only first file in the array will be read' + END + in0[0] EQ 8: BEGIN + infotxt=' INFORMATION: '+in1[0]+' is a coordinate variable' + END + in0[0] EQ 9: BEGIN + infotxt=STRARR(3) + infotxt[0]=' INFORMATION: Calibrated data in dataset '+in1[0] + infotxt[1]=' has been converted back to its original form using the formula:' + infotxt[2]=' Orig_Data = Scale_Factor * (Cal_Data - Offset)' + END + in0[0] EQ 10: BEGIN + infotxt=STRARR(2) + infotxt[0]=' INFORMATION: VAR_DATA_TYPE='+in1[1]+' for Calibrated data in dataset '+in1[0] + infotxt[1]=' has been changed to the original datatype of '+in1[2] + END + in0[0] EQ 11: BEGIN + infotxt=STRARR(2) + CASE 1 OF + in0[1] EQ 20: BEGIN + infotxt[0]=' INFORMATION: Order of multi-dimensional datasets changed to match the programming' + infotxt[0]=infotxt[0]+' language convention: + infotxt[1]=' IDL/Fortran - fastest changing dimension first (datasets transposed when' + infotxt[1]=infotxt[1]+' reading/writing to the HDF file)' + END + in0[1] MOD 10 EQ 0: BEGIN + infotxt[0]=' INFORMATION: Listing order of multi-dimensional VAR_DEPEND and VAR_SIZE values' + infotxt[1]=' in the HDF file changed to match corresponding dataset dimension ordering' + END + ELSE: BEGIN + infotxt[0]=' INFORMATION: Unable to determine the correct listing order of multi-dimensional' + infotxt[1]=' VAR_SIZE and VAR_DEPEND values in the HDF file' + END + ENDCASE + END + in0[0] EQ 12: BEGIN + infotxt=STRARR(2) + infotxt[0]=' INFORMATION: Table Attribute Values file must be in the same directory as the input file' + infotxt[1]=' if wanting to convert from one Data Format to another' + END + in0[0] EQ 13: BEGIN + infotxt=STRARR(2) + infotxt[0]=' INFORMATION: IDLcr8HDF called but it is not in the IDL Search Path or it encountered an' + infotxt[1]=' unexpected error. If the latter please contact Ian Boyd at iboyd@bryanscientific.org' + END + in0[0] EQ 14: BEGIN + infotxt=STRARR(2) + infotxt[0]=' INFORMATION: /POPUP keyword cannot be used together with the ''reterr'' argument.' + infotxt[1]=' The request for a POPUP window has been ignored' + END + in0[0] EQ 15: BEGIN + infotxt=STRARR(2) + infotxt[0]=' INFORMATION: Argument ''reterr'' must be of type string.' ;a returnable variable of type string.' + infotxt[1]=' idlcr8ascii will stop normally if an error is encountered' + END + in0[0] EQ 16: BEGIN + infotxt=STRARR(2) + infotxt[0]=' INFORMATION: /LOG, /FORMAT, and /DUMP keywords cannot be used in IDL DEMO mode and will be ignored.' + infotxt[1]=' To generate ASCII files please use the free IDL Virtual Machine or a licenced version of IDL' + END + in0[0] EQ 17: BEGIN + infotxt=STRARR(2) + infotxt[0]=' INFORMATION: NetCDF file create feature is disabled in IDL DEMO mode.' + infotxt[1]=' To generate files please use the free IDL Virtual Machine or a licenced version of IDL' + END + in0[0] EQ 18: BEGIN + infotxt=' INFORMATION: Value for Attribute Label '+in1[0]+' is an invalid Data Type' + END + in0[0] EQ 19: BEGIN + infotxt=' INFORMATION: '+in1[0]+' is not a valid GEOMS Metadata Attribute' + END + in0[0] EQ 20: BEGIN + infotxt=' INFORMATION: '+in1[0]+' entry must be written to the file as a STRING for '+in1[1] + END + in0[0] EQ 21: BEGIN + infotxt=' INFORMATION: '+in1[0]+' sub-values must be separated by '';'' for '+in1[1] + END + in0[0] EQ 22: BEGIN + infotxt=' INFORMATION: Spaces not permitted in the '+in1[0]+' entry for '+in1[1] + END + in0[0] EQ 23: BEGIN + infotxt=STRARR(2) + itxt1=' WARNING: This program can''t check that BYTE datasets and attributes contain only' + itxt2=' 8-bit unsigned values (0-255) in netCDF3 files.' + infotxt[0]=itxt1+itxt2 + itxt1=' Please check that the '+in1[0]+' dataset and attributes' + itxt2=' with BYTE data type do not contain negative 8-bit signed values' + infotxt[1]=itxt1+itxt2 + END + in0[0] EQ 24: BEGIN + infotxt=STRARR(2) + infotxt[0]=' INFORMATION: Input file name does not match the Global Attribute entry' + infotxt[1]=' FILE_NAME='+in1[0] + END +ENDCASE + +dm=SIZE(infotxt,/N_Elements) +IF o3[3] EQ '' THEN lineno=lineno+dm + +FOR n=0,dm-1 DO BEGIN + IF o3[3] EQ '' THEN BEGIN + IF n EQ dm THEN WIDGET_CONTROL,wtxt,set_value='',/Append $ + ELSE WIDGET_CONTROL,wtxt,set_value=infotxt[n],/Append,Set_text_top_line=lineno + ENDIF + FOR m=dux[0],dux[1],dux[2] DO $ + IF m EQ -1 THEN PRINT,infotxt[n] ELSE PRINTF,m,infotxt[n] ;write out to log(s) +ENDFOR + +END ;Procedure InfoTxt_Output_A + + + +FUNCTION jdf_2_datetime, jdf, MJD2000=mjd2000, SHORTISO8601=iso, LONGISO8601=isoms +;Computes the UT date/time from JDF/MJD2000. +; ---------- +; Bojan R. Bojkov +; bojan.r.bojkov@nasa.gov +; 03/04/2004 +; 03/11/2004 bug fix +; 05/27/2005 add ShortISO8601 and LongISO8601 switches, +; and change seconds value to decimal seconds (Ian Boyd) +; +; References ---------- +; Explan. Supp. Astron. Almanac, p.604 (1992); through E. Celarier +; +; Caveats ---------- +; Valid for all YYYY >= -4712 (i.e. for all JD .ge. 0) +; The true Gregorian calendar was only adopted on 15 October 1582, +; so any dates before this are "virtual" Gregorian dates. +; +; Input ---------- +; jdf : double precision value +; mjd2000 : flag if input is MJD2000 +; iso : flag if output is in ISO8601 (YYYYMMDDThhmmssZ) +; isoms : flag if output is in ISO8601ms (YYYYMMDDThhmmss.sssZ) +; +; Output --------- +; floating point array [YYYY, MM, DD, hh, mn, ss.sss], +; or ISO8601 string format +; External subroutines --------- +; NONE + +jdf=DOUBLE(jdf) + +j0=2451544.5D +IF KEYWORD_SET(mjd2000) THEN jdhold=jdf+j0 ELSE jdhold=jdf +jdi=LONG(jdhold) +df=jdhold-jdi + +;Determine hh, mm, ss, ms +hh=(df+0.5)*24.D +q=WHERE(hh GE 24.D) +IF q[0] NE -1 THEN BEGIN + hh[q]=hh[q]-24.D + jdi[q]=jdi[q]+1 +ENDIF +t1=hh +hh=LONG(t1) +t2=(t1-hh)*60.D +mn=LONG(t2) +t3=(t2-mn)*60.D +ss=t3 +;ss=LONG(t3) +;ms=LONG((t3-ss)*1.d3) + +;Determine YYYY, MM, DD +t1=jdi+68569L +t2=(4*t1)/146097L +t1=t1-(146097L*t2+3L)/4L +t3=(4000L*(t1+1L))/1461001L +t1=t1-(1461L*t3)/4L + 31L +t4=(80L*t1)/2447L + +dd=t1-(2447L*t4)/80L +t1=t4/11L +mm=t4+2L-12L*t1 +yyyy=100L*(t2-49L)+t3+t1 + +dt=TRANSPOSE([[yyyy],[mm],[dd],[hh],[mn],[ss]]) +IF (KEYWORD_SET(iso)) OR (KEYWORD_SET(isoms)) THEN BEGIN + dts=STRARR(6) + IF (KEYWORD_SET(iso)) AND (ss-FIX(ss) GE 0.5) THEN BEGIN + ;recalculate to get correct seconds value + jdhold=jdf+0.000008D ;add ~0.7 secs + IF KEYWORD_SET(mjd2000) THEN jdhold=jdhold+j0 + CALDAT,jdhold,mm,dd,yyyy,hh,mn,ss + dt=TRANSPOSE([[yyyy],[mm],[dd],[hh],[mn],[ss]]) + ENDIF + dt=LONG(dt) + FOR i=1,5 DO $ + IF dt[i] LT 10L THEN dts[i]='0'+STRTRIM(dt[i],2) ELSE dts[i]=STRTRIM(dt[i],2) + IF KEYWORD_SET(isoms) THEN BEGIN + ssd=STRING(format='(f6.3)',ss) + IF FLOAT(ssd) LT 10.0 THEN dts[5]='0'+STRTRIM(ssd,2) ELSE dts[5]=STRTRIM(ssd,2) + ENDIF + RETURN,STRTRIM(dt[0],2)+dts[1]+dts[2]+'T'+dts[3]+dts[4]+dts[5]+'Z' +ENDIF ELSE RETURN, dt + +END ;Function jdf_2_datetime + + + +FUNCTION julian_date, date_ut, ISO8601=iso, MJD2000=mjd2000 +;Computes the Julian date (jd) or date in MJD2000 format in double precision. +; ---------- +; Bojan R. Bojkov +; bojan.r.bojkov@nasa.gov +; 06/03/2001 +; 09/24/2001: Code cleanup - Function Version 1.0 +; 10/16/2001: Mode corrections - Function Version 1.01 +; 03/07/2002: Comment cleanup and variable change - Function Version 2.0 +; 10/08/2002: Added jdf option - Function Version 2.1 +; 07/21/2003: Converted to a function - Function Version 3.0 +; 05/25/2005: Added ISO8601 and MJD2000 keywords. +; Input can either be a string or a numeric array +; (see below for input details). Returns -99999.d +; if the Input is invalid - Ian Boyd +; +; References ---------- +; Hughes, D.W., Yallop, B.D. and Hohenkerk, C.Y., "The Equation of Time", +; Mon. Not. R. astr. Soc., 238, pp 1529-1535. 1989. +; +; Results verified using: +; Meeus, J, "Astronomical Algorithms", William-Bell, Inc., Richmond VA, +; p62, 1991. +; +; Caveats ---------- +; NONE +; +; Input ---------- +; date_ut: Numeric array (UT: YYYY, MM, DD, [hh, mm, ss]), or if ISO8601 +; keyword set, a string containing datetime as YYYYMMDDThhmmssZ +; +; Output --------- +; jd: julian day, or if MJD2000 keyword set, +; a double precision day in MJD2000 format +; +; External subroutines --------- +; NONE + +ON_IOERROR,TypeConversionError +valid=0 ;Type conversion check +inputerror=0 ;Check that input is as expected +;Check input format +IF KEYWORD_SET(iso) THEN BEGIN ;input is YYYYMMDDThhmmssZ + IF STRLEN(date_ut) EQ 16 THEN BEGIN + ;test for numeric values (will return type conversion error value if not a number) + FOR i=0,7 DO check=FIX(STRMID(date_ut,i,1)) + FOR i=9,14 DO check=FIX(STRMID(date_ut,i,1)) + dt=INTARR(6) + dt[0]=FIX(STRMID(date_ut,0,4)) & dt[1]=FIX(STRMID(date_ut,4,2)) + dt[2]=FIX(STRMID(date_ut,6,2)) & dt[3]=FIX(STRMID(date_ut,9,2)) + dt[4]=FIX(STRMID(date_ut,11,2)) & dt[5]=FIX(STRMID(date_ut,13,2)) + date_ut=dt & achk=[0,6] ;to add hhmmss component + ENDIF ELSE inputerror=1 +ENDIF ELSE BEGIN + ;check that input contains at least YMD (and at most YMDhms) information and is either + ;an integer, long, float or double array + achk=SIZE(date_ut) + IF (achk[1] LT 3) OR (achk[1] GT 6) OR (achk[2] LT 2) OR (achk[2] GT 5) THEN inputerror=1 +ENDELSE + +IF inputerror EQ 0 THEN BEGIN ;can perform JD calculation + IF date_ut[1] GT 2 THEN BEGIN + y=DOUBLE(date_ut[0]) + m=DOUBLE(date_ut[1]-3) + d=DOUBLE(date_ut[2]) + ENDIF ELSE BEGIN + y=DOUBLE(date_ut[0]-1) + m=DOUBLE(date_ut[1]+9) + d=DOUBLE(date_ut[2]) + ENDELSE + + ;Compute Julian date: + j=LONG(365.25D0*(y+4712.D0))+LONG(30.6D0*m+0.5D0)+59.D0+d-0.5D0 + + ;Check for Julian or Gregorian calendar: + IF j LT 2299159.5D0 THEN jd=j $ ;If Julian calendar. + ELSE BEGIN ;If Gregorian calendar. + gn=38.D0-LONG(3.D0*LONG(49.D0+y/100.D0)/4.D0) + jd=j+gn + ENDELSE + + ;add hhmmss values if present + CASE 1 OF + achk[1] EQ 4: jd=jd+DOUBLE(date_ut[3])/24.D ;hh only + achk[1] EQ 5: jd=jd+(DOUBLE(date_ut[3])*3600.D + DOUBLE(date_ut[4])*60.D)/86400.D ;hhmm only + achk[1] EQ 6: jd=jd+(DOUBLE(date_ut[3])*3600.D + DOUBLE(date_ut[4])*60.D + $ + DOUBLE(date_ut[5]))/86400.D ;hhmmss + ELSE: + ENDCASE + + valid=1 ;Type conversion OK + + ;Set to MJD2000 if required + IF KEYWORD_SET(mjd2000) THEN jd=jd-2451544.5D +ENDIF + +TypeConversionError: +IF valid EQ 0 THEN RETURN,-99999.D ELSE RETURN, jd + +END ;Function Julian_Date + + + +PRO nc_dimension_chk, ftype, sd_id, di, n_sds, dgi, n_sdsg +;Procedure to check for netCDF dimension datasets. These will be quietly ignored during the +;read process +; ---------- +;Written by Ian Boyd for the EVDC - iboyd@bryanscientific.org +; +; History: +; 20190806: Introduced - Version 4.0b22 +; +; Inputs: ftype - format type, HDF5 or netCDF +; sd_id - GEOMS file identifier number +; di - index values for datasets +; n_sds - number of datasets determined by H5G_GET_NMEMBERS call +; +; Outputs: dgi - index values for GEOMS datasets (not generated by netCDF libraries) +; n_sdsg - number of GEOMS datasets +; +; Called by: Read_HDF_SDS +; +dimen_names=['constant','independent_','_strlen'] +n_dn=N_ELEMENTS(dimen_names) +ds_names=STRARR(n_sds) & gdi=INTARR(n_sds)-1 +FOR i=0,n_sds-1 DO BEGIN + IF ftype EQ 'H5' THEN sds_name=H5G_GET_MEMBER_NAME(sd_id,'/',di[i]) $ + ELSE BEGIN ;netCDF file + varstruct=NCDF_VARINQ(sd_id,i) + sds_name=varstruct.name + ENDELSE + ds_names[i]=STRTRIM(STRLOWCASE(sds_name),2) +ENDFOR + +FOR i=0,n_sds-1 DO BEGIN + dnicnt=0 & j=0 + WHILE (dnicnt EQ 0) AND (j LE n_dn-1) DO $ + IF STRPOS(ds_names[i],dimen_names[j]) NE -1 THEN dnicnt++ ELSE j++ + IF dnicnt NE 0 THEN BEGIN + ;Check that names are actually netCDF dimension names o/w will treat as a dataset name + CASE j OF + 0: IF ds_names[i] NE dimen_names[j] THEN dnicnt=0 ;i.e. not a netCDF dimension name + 1: BEGIN + ;check that first part of the name is INDEPENDENT_ and the second part is a number + IF STRMID(ds_names[i],0,STRLEN(dimen_names[j])) NE dimen_names[j] $ + THEN dnicnt=0 $ + ELSE BEGIN + res=STRSPLIT(ds_names[i],'_',/EXTRACT,COUNT=cres) + res=[res,''] ;to ensure at least 2 extracted values in ds_names[i] + if ~IS_A_NUMBER_ASCII(res[1]) THEN dnicnt=0 ; i.e. not a netCDF dimension name + ENDELSE + END + 2: BEGIN + ;check that first part of the name is a named dataset + res=STRSPLIT(ds_names[i],'_',/EXTRACT) + ni=WHERE(res[0] EQ ds_names,ncnt) + IF ncnt EQ 0 THEN dnicnt=0 ;i.e. not a netCDF dimension name + END + ENDCASE + ENDIF + IF dnicnt EQ 0 THEN gdi[i]=i ;i.e. this is a valid dataset name and not a netCDF dimension name +ENDFOR + +dgi=WHERE(gdi NE -1,n_sdsg) ;dgi and n_sdsg are returned by the procedure + +END ;Procedure nc_dimension_chk + + + +FUNCTION file_format_a, infile +;Function to identify the format of the input file - tests for HDF4, HDF5/netCDF4 and netCDF3. +;netCDF4 files will be identified as HDF5 and read in using the HDF5 library +; ---------- +;Written by Ian Boyd for the EVDC - iboyd@bryanscientific.org +; +; History: +; 20190806: Introduced - Version 4.0b22 +; 20220805: Additional checks for netCDF4 files if the file signature ia HDF5 - Version 4.0b25 +; +; Inputs: infile - Input GEOMS compliant file +; +; Outputs: string identifying the type of file (H4, H5, NC) or XX for unidentified file +; +; Called by: IDLCR8ASCII +; +;Markers +;HDF4: 14 3 19 (1 0 16 0 1 95 98 0 30) +;HDF5: 137 72 68 70 (13 10 26 10 0 0 0 0) +;nc3: 67 68 70 (1 0 0 0 0 0 0 0 10) +;nc4: 137 72 68 70 (13 10 26 10 (2 8 8 0)) + +h4id=[14B, 3B, 19B] +h5id=[137B, 72B, 68B, 70B] ;HDF +nc3id=[67B, 68B, 70B] ;CDF + +;First test using start of file byte markers +fid=BYTARR(4) +openr,fu,infile,/GET_LUN +readu,fu,fid +FREE_LUN,fu + +idfound='XX' +CASE 1 OF + ARRAY_EQUAL(fid[0:2],h4id): idfound='H4' + ARRAY_EQUAL(fid,h5id): idfound='H5' + ARRAY_EQUAL(fid[0:2],nc3id): idfound='N3' + ELSE: +ENDCASE + +;Second test, if required, using IDL commands +IF idfound EQ 'XX' THEN BEGIN + IF HDF_ISHDF(infile) EQ 1 THEN idfound='H4' $ + ELSE IF FLOAT(!Version.Release) GE 5.6 THEN BEGIN + IF H5F_IS_HDF5(infile) EQ 1 THEN idfound='H5' ;netCDF4 or HDF5 + ENDIF + IF idfound EQ 'XX' THEN BEGIN + ;final test for netCDF3 + validname=1 + CATCH, ncdferror ;To catch the error when attempting to open the file as a netCDF + ;Error Handler + IF ncdferror NE 0 THEN BEGIN ;not a netCDF file + validname=0 + CATCH, /CANCEL + ENDIF + IF validname EQ 1 THEN BEGIN + fileid=NCDF_OPEN(infile) + ;If get to here then file is netCDF + CATCH, /CANCEL + NCDF_CLOSE,fileid + idfound='N3' + ENDIF + ENDIF +ENDIF + +IF idfound EQ 'H5' THEN BEGIN + ;For H5, try to identify whether it is H5 or N4 + nc4ext=['nc','nc4','netcdf','netcdf4'] + fileext=STRMID(infile,STRPOS(infile,'.',/REVERSE_SEARCH)+1) + nci=WHERE(STRLOWCASE(fileext) EQ nc4ext,nccnt) + + vr=!Version.Release ;IDL version check (needs to be 8.5.2 or greater for NCDF_PARSE check + vrlast=STRMID(vr,STRPOS(vr,'.',/REVERSE_SEARCH)+1) + IF (FLOAT(vr) GT 8.5) OR ((STRMID(vr,0,3) EQ '8.5') AND (FIX(vrlast GE 2))) THEN parsechk=1B ELSE parsechk=0B + IF (nccnt NE 0) AND (parsechk) THEN BEGIN + ;Catch error from attempting to parse non-NetCDF-4 compliant HDF5 file + validname=1 + CATCH, ncdferror ;To catch the error when attempting to open the file as a netCDF + ;Error Handler + IF ncdferror NE 0 THEN BEGIN ;not a netCDF file + validname=0 + CATCH, /CANCEL + ENDIF + IF validname EQ 1 THEN BEGIN + fileid=NCDF_PARSE(infile) + ;If get to here then file can be opened using N4 libraries, so most likely a netcdf4 file + CATCH, /CANCEL + idfound='N4' + ENDIF + ENDIF ELSE IF nccnt NE 0 THEN idfound='N4' ;Unable to do NCDF_PARSE check but still likely nc4 +ENDIF + +RETURN, idfound + +END ;File_Format_A + + + +PRO test_dim_order, va_name, va_value, va_type, sds_dim, sds_name, rev_vd_vs +;Procedure to test the dimension ordering of any multi-dimensional datasets in the input DF file, and +;return correct ordering code (dimension ordering for idlcr8ascii uses IDL/Fortran convention) +; ---------- +;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org +; +; History: +; 20111208: Introduced - Version 4.0b4 +; 20140325: Fix bug that meant VAR_SIZE and VAR_DEPEND values were not being automatically +; reversed if the array sizes were the same and VAR_DEPEND did not include a DATETIME +; value. Now defaults to the 'reverse' option if no other conditions are satisfied; +; Fix bug that caused crash when VAR_SIZE values were not of type string - Version 4.0b8 +; 20141110: Fix bug that caused multi-dimensional array ordering to not be correctly identified +; if the first dataset to be checked in the file had the same number of elements in +; the array (e.g. was a set of Averaging Kernels) - Version 4.0b9 +; 20200930: Add INFORMATION messages (20-22) that identify issues with the VAR_DEPEND and VAR_SIZE +; attributes that were previously fixed 'quietly' by the program; Add sds_name to the +; variables required by the procedure - Version 4.0b24 +; +; Inputs: va_name - An abbreviated version of the Variable Name; either 'VD' (VAR_DEPEND) or 'VS' +; (VAR_SIZE) +; va_value - The corresponding Variable Value +; sds_dim - Dimension information for the DF Dataset being tested +; sds_name - Dataset name, required if an INFORMATION message is generated +; +; Outputs: rev_vd_vs - Scalar to indicate whether VAR_DEPEND and VAR_SIZE values (and data, in the case +; of netCDF measurements) need to be reversed +; +; Called by: Read_HDF_SDS + +IF va_type NE 'STRING' THEN BEGIN ;VAR_SIZE contains numeric values instead of in the form of a string + vavhold='' & n_vav=N_ELEMENTS(va_value) + FOR k=0,n_vav-1 DO BEGIN + IF k EQ n_vav-1 THEN vtxt='' ELSE vtxt=';' + vavhold=vavhold+STRTRIM(va_value[k],2)+vtxt + ENDFOR + IF va_name EQ 'VD' THEN vtxt='VAR_DEPEND' ELSE vtxt='VAR_SIZE' + INFOTXT_OUTPUT_A,[20],[vtxt,STRTRIM(sds_name,2)] +ENDIF ELSE vavhold=va_value +vs_v=STRCOMPRESS(STRSPLIT(vavhold,';, ',/EXTRACT,COUNT=rcnt),/REMOVE_ALL) + +IF (rcnt GT 1) AND ((STRPOS(vavhold,';') EQ -1) OR (STRPOS(vavhold,',') NE -1)) THEN BEGIN + IF va_name EQ 'VD' THEN itxt='VAR_DEPEND' ELSE itxt='VAR_SIZE' + INFOTXT_OUTPUT_A,[21],[itxt,STRTRIM(sds_name,2)] ;sub-values should be separated by ';' +ENDIF + +IF (rcnt EQ 1) OR ((rcnt GT 1) AND (STRPOS(vavhold,';') NE -1)) THEN BEGIN + IF STRCOMPRESS(vavhold,/REMOVE_ALL) NE vavhold THEN BEGIN + IF va_name EQ 'VD' THEN itxt='VAR_DEPEND' ELSE itxt='VAR_SIZE' + INFOTXT_OUTPUT_A,[22],[itxt,STRTRIM(sds_name,2)] ;no spaces permitted + ENDIF +ENDIF + +IF va_name EQ 'VD' THEN BEGIN ;VAR_DEPEND tests + dti=WHERE(STRUPCASE(vs_v) EQ 'DATETIME',dticnt) + ;Note: if DATETIME has VAR_SIZE=1 then the ordering is dependent on the order of the VAR_SIZE values only + IF (rcnt GT 1) AND (dticnt NE 0) THEN BEGIN ;multi-dimensions including DATETIME + test1=(dti[0] EQ rcnt-1) AND (sds_dim[0] GT 1) + ;DATETIME is the last value and present only once and has VAR_SIZE GT 1 + test2=(dti[0] EQ 0) AND (dticnt EQ 1) AND (sds_dim[0] GT 1) + ;DATETIME is the first value and present only once and has VAR_SIZE GT 1 + IF (test1) AND (rev_vd_vs EQ 2) THEN rev_vd_vs=10 $ + ELSE IF (test2) AND (rev_vd_vs EQ 2) THEN rev_vd_vs=11 $ + ELSE IF ((test1) AND (rev_vd_vs MOD 10 EQ 1)) OR ((test2) AND (rev_vd_vs EQ 10)) THEN $ + rev_vd_vs=3 $ ;inconsistent rules regarding dimension ordering in file + ELSE IF (test2) AND (rev_vd_vs EQ 0) THEN rev_vd_vs=20 + ;Dimension ordering is slowest changing first so change dataset ordering as well as + ;VAR_DEPEND and VAR_SIZE ordering + ENDIF +ENDIF ELSE BEGIN ;VAR_SIZE tests + IF rcnt GT 1 THEN BEGIN ;multi-dimensions + ;check all values are numeric + numchk=STRJOIN(vs_v,/SINGLE) & valok=1 + FOR k=0,STRLEN(numchk)-1 DO BEGIN + bchar=BYTE(STRMID(numchk,k,1)) + IF (bchar LT 48) OR (bchar GT 57) THEN valok=0 ;i.e. non-numeric character + ENDFOR + IF valok EQ 1 THEN BEGIN ;all values are numeric so do checks + vs_vl=LONG(vs_v) + test1=ARRAY_EQUAL(vs_vl,sds_dim) + test2=ARRAY_EQUAL(REVERSE(vs_vl),sds_dim) ;test in case values are the same e.g. 40;40 + ;If arrays are the same size then the default will be to reverse the VS and VD values + ;unless a different dimension ordering has already been identified + IF (test1 EQ 1) AND (test2 EQ 0) AND (rev_vd_vs EQ 2) THEN rev_vd_vs=0 $ + ELSE IF (test1 EQ 0) AND (test2 EQ 1) AND (rev_vd_vs EQ 2) THEN rev_vd_vs=1 $ + ELSE IF ((test1 EQ 1) AND (test2 EQ 0) AND (rev_vd_vs MOD 10 EQ 1)) OR $ + ((test1 EQ 0) AND (test2 EQ 1) AND (rev_vd_vs MOD 10 EQ 0)) THEN $ + rev_vd_vs=3 $ ;inconsistent VAR_SIZE and sds_dim agreement from dataset to dataset + ELSE IF (test1 EQ 1) AND (test2 EQ 1) AND (rev_vd_vs EQ 2) THEN $ + rev_vd_vs=2 $ ;first set of measurements tested have same dimensions so do not do anything + ELSE IF rev_vd_vs EQ 2 THEN rev_vd_vs=1 ;default if no other criteria are found as this + ;will then automatically reverse the VD and VS values + ENDIF + ENDIF +ENDELSE + +END ;Procedure Test_Dim_Order + + + +PRO data_type_checks, ftype, dt_chk_labels, hdftype, idltype, sds_name, var_name +;VAR_DATA_TYPE corresponds to the dataset data type and the VAR_VALID_MIN, VAR_VALID_MAX +;and VAR_FILL_VALUES also match the dataset data type. Note limitation that 8-bit +;unsigned integer (BYTE) values cannot be identified in netCDF3 files +; ---------- +;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org +; +; History: +; 20220805: Introduced to IDLCR8ASCII - Version 4.0b25 +; +; Inputs: ftype - a string identifying the file type (H4, H5, N3 or N4) +; dt_chk_labels - string array containing labels of attributes (plus 'Dataset') +; upon which the data type checks are done +; hdftype - string array containing the data type returned by the respective +; ftype specific calls +; idltype - string array containing the data types as IDL codes or names +; sds_name - string holding dataset name returned by the respective ftype specific +; cals +; var_name - string holding the VAR_NAME value (should be the same as sds_name) +; +; Outputs: N/A +; Called by: READ_HDF_SDS +; +; Subroutines Called: INFOTXT_OUTPUT_A (if program discovers an issue with the data type) +; Information Conditions: +; 1. VAR_DATA_TYPE is not an allowable data type +; 2. Dataset, Valid Min, Valid Max and/or Fill Value data types do not match the VAR_DATA_TYPE (if applicable) +; 3. Valid Min, Valid Max and/or Fill Value data types do not match the Dataset data type (if applicable) +; 3. Dataset, Valid Min, Valid Max and/or Fill value data types are not allowable + +;Allowable VAR_DATA_TYPEs +avdt=['','BYTE','SHORT','INTEGER','REAL','DOUBLE','','STRING','','','','','','',''] ;'LONG'] ;corresponding to IDL codes 1,2,3,4,5,7,14 (LONG/14 not used) +idl_name=['UNDEFINED','BYTE','SHORT','INTEGER','REAL','DOUBLE','COMPLEX','STRING','STRUCT','DCOMPLEX', $ + 'POINTER','OBJREF','UINT','ULONG','LONG64','ULONG64'] ;Note: Integer, Long and Float renamed Short, Integer and Real for sds_type +hdf_dt=['','DFNT_UINT8','DFNT_INT16','DFNT_INT32','DFNT_FLOAT32','DFNT_FLOAT64','','DFNT_CHAR8'] ;allowable HDF4 datatypes (note BYTE=8-bit unsigned) +;hdf_all=['DFNT_NONE','DFNT_UINT8','DFNT_INT16','DFNT_INT32','DFNT_FLOAT32','DFNT_FLOAT64','','DFNT_CHAR8', $ +; '','','','','DFNT_UINT16','DFNT_UINT32','DFNT_INT8'] +h5_dt=['H5T_INTEGER_0','H5T_INTEGER_1','H5T_FLOAT','H5T_STRING'] ;allowable HDF5/NC4 datatypes (0=unsigned, 1=signed) + +;dt_chk_labels=['Dataset','VAR_VALID_MIN','VAR_VALID_MAX','VAR_FILL_VALUE','VAR_DATA_TYPE'] ;dataset and attribute labels for data type checks +n_lab=N_ELEMENTS(dt_chk_labels) +dterr=STRARR(n_lab) ;non-zero indicates error + +IF var_name NE '' THEN usevn=var_name ELSE usevn=sds_name ;determine VAR_NAME value to use for error messages + +IF (ftype EQ 'H5') OR (ftype EQ 'N4') THEN BEGIN + ;need to convert hdftype and idltype values to equivalent idl_name and hdf_dt values + FOR i=0,n_lab-2 DO BEGIN + idltype[i]=idl_name[idltype[i]] ;convert IDL type code to type name + CASE 1 OF + hdftype[i] EQ 'H5T_FLOAT': BEGIN + IF idltype[i] EQ 'DOUBLE' THEN hdftype[i]='DFNT_FLOAT64' $ + ELSE hdftype[i]='DFNT_FLOAT32' + END + hdftype[i] EQ 'H5T_STRING': hdftype[i]='DFNT_CHAR8' + hdftype[i] EQ 'H5T_INTEGER_0': BEGIN + CASE idltype[i] OF + 'BYTE': hdftype[i]='DFNT_UINT8' + ('SHORT') OR ('UINT'): hdftype[i]='DFNT_UINT16' + ('INTEGER') OR ('ULONG'): hdftype[i]='DFNT_UINT32' + 'ULONG64': hdftype[i]='DFNT_UINT64' + ELSE: hdftype[i]='DFNT_UNDF' + ENDCASE + END + hdftype[i] EQ 'H5T_INTEGER_1': BEGIN + CASE idltype[i] OF + 'BYTE': hdftype[i]='DFNT_INT8' + 'SHORT': hdftype[i]='DFNT_INT16' + 'INTEGER': hdftype[i]='DFNT_INT32' + 'LONG64': hdftype[i]='DFNT_INT64' + ELSE: hdftype[i]='DFNT_UNDF' + ENDCASE + END + ELSE: + ENDCASE + ENDFOR +ENDIF ELSE IF ftype EQ 'N3' THEN BEGIN + ;need to convert hdftype and idltype values to equivalent idl_name and hdf_dt values + writeonce=0B + FOR i=0,n_lab-2 DO BEGIN + idltype[i]=idl_name[idltype[i]] ;convert IDL type code to type name + CASE 1 OF + hdftype[i] EQ 'BYTE_0': hdftype[i]='DFNT_UINT8' + hdftype[i] EQ 'BYTE_1': hdftype[i]='DFNT_INT8' + hdftype[i] EQ 'INT': hdftype[i]='DFNT_INT16' + hdftype[i] EQ 'LONG': hdftype[i]='DFNT_INT32' + hdftype[i] EQ 'FLOAT': hdftype[i]='DFNT_FLOAT32' + hdftype[i] EQ 'DOUBLE': hdftype[i]='DFNT_FLOAT64' + hdftype[i] EQ 'CHAR': hdftype[i]='DFNT_CHAR8' + ELSE: hdftype[i]='DFNT_UNDF' + ENDCASE + IF (idltype[i] EQ 'BYTE') AND (idltype[0] EQ 'BYTE') AND (~writeonce) THEN BEGIN + ;For VAR_VALID_MIN/MAX and VAR_FILL_VALUE only output if the dataset is of type BYTE as well + ;o/w probably means that the value was written as a character string instead of as numbers + INFOTXT_OUTPUT_A,[23],[usevn] + writeonce=1B + ENDIF + ENDFOR + +ENDIF + +;Check VAR_DATA_TYPE value +hdti=WHERE((idltype[n_lab-1] EQ avdt) AND (idltype[n_lab-1] NE ''),hdtcnt) +IF hdtcnt EQ 0 THEN BEGIN + IF idltype[n_lab-1] NE '' THEN $ + dterr[n_lab-1]=usevn+' '+dt_chk_labels[n_lab-1]+'='+idltype[n_lab-1]+' is not a valid GEOMS data type' +ENDIF + +;Check dataset, min, max and fill data types are valid +FOR i=0,n_lab-2 DO BEGIN + IF i EQ 0 THEN itxt=' value(s) ' ELSE itxt=' value ' + hdti=WHERE((hdftype[i] EQ hdf_dt) AND (hdftype[i] NE ''),hdtcnt) + IF hdtcnt EQ 0 THEN BEGIN ;identify the actual data type of the dataset + hdti=WHERE(idltype[i] EQ idl_name,hdtcnt) + IF hdtcnt EQ 0 THEN IF idltype[i] NE '' THEN hdti[0]=0 + CASE hdti[0] OF + -1: IF i EQ 0 THEN dterr[i]=usevn+' '+dt_chk_labels[i]+' data type is not identifiable' + 1: dterr[i]=usevn+' '+dt_chk_labels[i]+itxt+'must be 8-bit unsigned INTEGER (BYTE)' + 2: dterr[i]=usevn+' '+dt_chk_labels[i]+itxt+'must be 16-bit signed INTEGER' + 3: dterr[i]=usevn+' '+dt_chk_labels[i]+itxt+'must be 32-bit signed INTEGER' + ELSE: dterr[i]=usevn+' '+dt_chk_labels[i]+itxt+'not a valid GEOMS data type: '+idl_name[hdti[0]] + ENDCASE + ENDIF +ENDFOR + +;If valid VAR_DATA_TYPE is present check that the dataset and min, max and fill data types match +IF dterr[n_lab-1] EQ '' THEN BEGIN + ;Use error generated in SET_UP_STRUCTURE in idlcr8hdf instead + ;FOR i=0,n_lab-2 DO BEGIN + ; IF (idltype[n_lab-1] NE idltype[i]) AND (idltype[i] NE '') THEN BEGIN + ; dterr[i]=usevn+' '+dt_chk_labels[i]+' data type ('+hdftype[i]+') does not match VAR_DATA_TYPE='+idltype[n_lab-1] + ; ;note: overwrites any existing reported error + ; ENDIF + ;ENDFOR +ENDIF ELSE IF dterr[0] EQ '' THEN BEGIN + ;VAR_DATA_TYPE not valid so check min, max and fill data types against the dataset data type + FOR i=1,n_lab-2 DO BEGIN + IF (idltype[0] NE idltype[i]) AND (idltype[i] NE '') THEN BEGIN + dterr[i]=usevn+' '+dt_chk_labels[i]+' data type ('+idl_name[i]+') does not match Dataset data type ('+idl_name[0]+')' + ;note: overwrites any existing reported error + ENDIF + ENDFOR +ENDIF + +;Output information messages +FOR i=0,n_lab-1 DO $ + IF dterr[i] NE '' THEN INFOTXT_OUTPUT_A,[2],[dterr[i]] + +END ;Data_Type_Checks + + + +PRO read_hdf_sds, ifile, ga, sds, catinfo +;Procedure to read the contents of a GEOMS standard HDF or netCDF compatible file +;into session memory +; ---------- +;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org +; +; History: +; 20050729: Original IDLCR8ASCII Routine - Version 1.0 +; 20050912: Removed Common variable definition CATALOGINFO and made the variable a parameter passed +; to the procedure; Checks that the attribute variable names read by HDF_SD_GETINFO, match +; those recorded in DATA_VARIABLES in the Global Attributes. If not use the variable name +; listed in DATA_VARIABLES - Version 1.1 +; 20061004: Common variable definition WIDGET_WIN_A added for Error calls; Add option to read HDF5 +; files (needs IDL5.6 or newer); Add check that the HDF4 file has valid information on the +; number of global and variable attributes; Checks added in the event that the HDF4 or +; HDF5 file has been created by the NCSA utility programs H5toH4 or H4toH5 from an original +; AVDC/Envisat/NDACC HDF4 or HDF5 file; Ensures variable attributes and data are listed in +; the same order as that given under DATA_VARIABLES - Version 2.0 +; 20081020 - If the HDF4 file is created using the HDF4.2r3 library, then extra information +; regarding the dimensions of the VAR_DEPEND values may be included in the file (which +; show up as extra datasets in the HDF_SD_FILEINFO call). A check for this is carried out +; and, if found, the information is excluded from the output - Version 3.01 +; 20090311: Improve HDF4.2r3 library checks by comparing the number of datasets recorded under +; DATA_VARIABLES, with the number returned by the HDF_SD_FILEINFO call; Change 'WARNING' to +; 'INFORMATION and make calls to procedure INFOTXT_OUTPUT_A to avoid duplication. Add +; 'INFORMATION' text if anything irregular found with the datasets; Change 'Data dimensions +; exceed 8' and 'Data type not allowable' ERROR messages to 'INFORMATION' messages +; - Version 3.02 +; 20091208: Incorporate HDF_SD_ISCOORDVAR to differentiate between datasets and dimension variable +; names. Add check for allowable VAR_DATA_TYPE=STRING. Add all INFORMATION text messages to +; INFOTXT_OUTPUT_A procedure to avoid duplication of code that creates the messages; +; Add INFORMATION messages when reading HDF5 files; Improve checks for non-standard (i.e. +; non-groundbased) HDF input file - Version 3.03 +; 20101122: Numeric metadata values remain in their original data type when transferring to session +; memory, meaning: variable attribute labels and values now written to separate structure +; variables sds.va_l and sds.va_v (previously sds.va), and structure has 2 dimensions +; (n_sds, n_atts) instead of 1 (previously n_sds); The dataset is written to the initial +; n_atts index (i.e. sds[n_sds,0].data); Rename saved SDS datatype according to GEOMS rules +; (affects INT, LONG, and FLOAT); Compare HDF dimensions with VAR_SIZE values and transpose +; VAR_SIZE and VAR_DEPEND values if required - Version 4.0b1 +; 20111014: Add netCDF read capability - writes variable attributes to standard GEOMS variable +; attribute labels where possible, otherwise appends information to VAR_NOTES - Version 4.0b3 +; 20120426: Bug fix when a netCDF file only contains datasets with single dimensions, then VAR_SIZE +; information was not being extracted during the read process - Version 4.0b6 +; 20140325: Fix bug that caused crash when VAR_SIZE values were not of type string - Version 4.0b8 +; 20150127: Allow for VAR_FILL_VALUE values for string datasets to be written and saved as an +; (empty) string of the correct length. Ensure string datasets are all written and +; saved to the correct length (that of the longest string in the dataset) - Version 4.0b10 +; 20150217: Do not remove whitespace of any variable attribute values that are written as strings +; - Version 4.0b11 +; 20160614: Some HDF4 string datasets may not have 2 dimensions, so do not need to remove the first +; dimension (= maximum number of characters) - Version 4.0b14 +; 20160725: Fix bug causing program to crash when the number of DATA_VARIABLES values is greater +; than the number of datasets in the file - Version 4.0b15 +; 20161130: Assign SDS_name to catinfo for attributes when the VAR_NAME is not present; rewrite +; netCDF section to align it with HDF5 section and to account for dataset ordering that +; does not match that in DATA_VARIABLES - Version 4.0b16 +; 20161213: Fix bug when reading in netCDF files to stop all dataset variable attribute values to +; be converted to string format (now matches HDF4 and HDF5 checks). Fix bug that caused +; the program to crash when VAR_DEPEND=CONSTANT in netCDF files (sds_ndim=0) - Version 4.0b17 +; 20161218: Fix bug when reading netCDF attribute values that are of type byte rather than string - +; Version 4.0b18 +; 20180218 Fix bug that caused multi-dimensional array ordering to not be correctly identified +; if the datasets have the same VAR_SIZE. Now rev_vd_vs changes to 1 (VAR_SIZE and +; VAR_DEPEND ordering is reversed) if rev_vd_vs=2 after all the checks - Version 4.0b19 +; 20181116 Fix bug that caused multi-dimensional array ordering to not be correctly written if +; not consistent through the file i.e. rev_vd_vs eq 3. Now rev_vd_vs changes to 1 +; (VAR_SIZE, VAR_DEPEND and dataset ordering is reversed), but information message +; advising of the issue is still reported - Version 4.0b20 +; 20190514 Fix bug that occurred when GEOMS variable attribute information is missing from an +; HDF4 file but written to the heap structure based on the contents of the file. Previously +; the variable attribute label was not written correctly - Version 4.0b21 +; 20190806 Provide support for reading netCDF4 files. Currently uses the HDF5 routines to do this +; so need to account for features unique to netCDF4 such as; the inclusion of dimension +; names; standard netCDF attribute names; empty attribute values that do not include a +; null termination character (which causes an error in the H5A_Read routine) +; - Version 4.0b22 +; 20190821 Fixed bugs associated with H5 TAG_NAMES checks introduced in v4.0b22 - Version 4.0b23 +; 20200930 Add sds_name to TEST_DIM_ORDER procedure call - Version 4.0b24 +; 20220805 For NetCDF3 or 4, if SDS_NAME NE VAR_NAME then converts non-alphanumeric VAR_NAME +; characters to '_' and compares with SDS_NAME. If there is a match then program quietly +; converts SDS_NAME to VAR_NAME for testing, otherwise generates an error; Add code to +; collect information for the call to the DATA_TYPE_CHECKS procedure; Generate an +; INFORMATION/ERROR message if VAR_UNITS is numeric (same as VAR_SIZE); INFORMATION/ERROR +; message added if a mis-match is found between the input file name and the FILE_NAME attribute value. + +; +; Inputs: ifile - a string containing the name of the input file to be read in. +; catinfo - a string array identifying the type of input file ('H4','H5','N4','N3') +; +; Outputs: ga - a string array containing the global attribute labels and values extracted from the HDF +; file. +; sds - a structure using pointers, of size [n_sds,n_atts], containing the variable attribute +; labels and values (sds[n,m].va_l and sds[n,m].va_v) and the data (sds[n,0].data) +; extracted from the HDF file. +; catinfo - a string array of size [n_sds,4] (where n_sds is the number of datasets in the HDF +; file), containing information on the variable name, data type, data dimension, and +; number of attributes. +; +; Called by: IDLCR8ASCII +; +; Subroutines Called: STOP_WITH_ERROR_A (if error state detected) +; INFOTXT_OUTPUT_A (if program can make a change) +; DATA_TYPE_CHECKS (to perform data type checks on the datasets and some attributes) +; Possible Conditions for STOP_WITH_ERROR call (plus [line number] where called): +; 1. No global attributes or datasets present in the root group of an HDF5 file. +; 2. The number of global attributes or datasets is zero or not able to be determined +; in an HDF4 file. +; +; Information Conditions (when the program is able to make changes): +; 1. DATA_VARIABLES global attribute not found or has no values. +; 2. Number of datasets given under DATA_VARIABLES is not equal to the number saved to the +; file. +; 3. The number of dimensions in a dataset exceeds 8. +; 4. The data type of a dataset is not BYTE, SHORT, INTEGER, LONG, REAL, DOUBLE, or STRING. +; 5. Dataset name is truncated or does not match VAR_NAME value. +; 6. Metadata label VAR_NAME not found for the dataset. +; 7. VAR_NAME does not match any DATA_VARIABLES values. +; 8. No Variable Attributes returned after HDF call. +; 9. Metadata Dataset listing order may not match the order of the Global Attribute +; DATA_VARIABLES Values. +; 10. Variable is written to the HDF file as a coordinate variable. + +COMMON WIDGET_WIN_A + +;Note any calibrated (scaled) HDF4 dataset is corrected using the formula specified in +;UG_print42r3.pdf pg. 3-107: orig = cal * (cal_val - offset) +ncsa_cal=['scale_factor','scale_factor_err','add_offset','add_offset_err','calibrated_nt'] + +;Initialize scalar to indicate whether VAR_DEPEND and VAR_SIZE values need to be reversed +rev_vd_vs=2 ;1 = reverse attribute values; 0 = no reverse (and will generate message); +;2 = no action required; 3 = rev_vd_vs changed between 0 and 1 during checking, therefore error +;Note if rev_vd_vs is 2 or 3 after all the checks then it will be changed to 1 so that dimensions +;are swapped by default + +;List of Standard GEOMS Variable Attributes +attr_arr_data=['VAR_NAME','VAR_DESCRIPTION','VAR_NOTES','VAR_SIZE','VAR_DEPEND',$ + 'VAR_DATA_TYPE','VAR_UNITS','VAR_SI_CONVERSION','VAR_VALID_MIN',$ + 'VAR_VALID_MAX','VAR_FILL_VALUE'] +n_aad=N_ELEMENTS(attr_arr_data) + +;Possible error messages for this procedure +proname='Read_HDF_SDS procedure: ' +errtxt=STRARR(2) +errtxt[0]=' found in the Root Group of the HDF5 File.' +errtxt[1]=' is zero or not able to be determined (check for corrupted file).' +lu=-1 + +slabel=['VAR_UNITS','VAR_SI_CONVERSION','VAR_VALID_MIN','VAR_VALID_MAX','VAR_FILL_VALUE'] ;save values as is if dataset is of type STRING +dt_chk_labels=['Dataset','VAR_VALID_MIN','VAR_VALID_MAX','VAR_FILL_VALUE','VAR_DATA_TYPE'] ;dataset and attribute labels for data type checks +n_lab=N_ELEMENTS(dt_chk_labels) + +;If necessary, free up memory by destroying the heap variables pointed at by its pointer arguments +;from previous calls to this program +IF N_ELEMENTS(sds) NE 0 THEN PTR_FREE,sds.va_l,sds.va_v,sds.data + +;Define the HDF SDS storage structure +sds_set={va_l: PTR_NEW(), $ ;Variable Attribute Labels + va_v: PTR_NEW(), $ ;Variable Attribute Values + data: PTR_NEW()} ;SD data array + +ftype=catinfo[0,0] +ifilechk=STRLOWCASE(STRTRIM(FILE_BASENAME(ifile),2)) ;for FILE_NAME check +IF ftype EQ 'H4' THEN BEGIN + + ;The HDF_SD_START function opens an HDF file and initializes the SD interface. + sd_id=HDF_SD_START(ifile,/READ) + + ;Determine the number of SDS (n_sds) and global attributes (n_ga) found + ;in the current file. + HDF_SD_FILEINFO,sd_id,n_sds,n_ga + IF n_sds EQ 0L THEN ntxt='Number of SDS datasets' $ + ELSE IF n_ga EQ 0L THEN ntxt='Number of Global Attributes' $ + ELSE ntxt='' + IF ntxt NE '' THEN BEGIN + STOP_WITH_ERROR_A,o3[3]+proname,ntxt+errtxt[1],lu & RETURN + ENDIF + ga=STRARR(n_ga) ;set the Global Attribute dimensions + vn=[''] ;initialize vn array (to hold Dataset names) + n_sds_hold=n_sds & n_vn=0L & do_coordvar=0 + + ;Read the file's Global Attributes and determine number of DATA_VARIABLES + FOR i=0L,n_ga-1L DO BEGIN + ;The HDF_SD_ATTRINFO procedure reads or retrieves info about an SD attribute. + HDF_SD_ATTRINFO,sd_id,i,NAME=ga_name,DATA=ga_data,HDF_TYPE=ga_hdftype + ga_name=STRTRIM(ga_name,2) & ga_data=STRTRIM(ga_data,2) + ;Assign global attributes to ga + ga[i]=ga_name+'='+ga_data + ;read list of DATA_VARIABLES into array + IF STRUPCASE(ga_name) EQ 'DATA_VARIABLES' THEN vn=STRSPLIT(ga_data,' ;',/Extract,COUNT=n_vn) + ;String labels for GA not checked at this time as extra Global Attributes could be numeric + ;IF (ga_hdftype NE 'DFNT_NONE') AND (ga_hdftype NE 'DFNT_CHAR8') THEN $ + ; INFOTXT_OUTPUT_A,[20],['Global Attribute',STRTRIM(ga_name,2)] + IF STRUPCASE(ga_name) EQ 'FILE_NAME' THEN BEGIN + ;check that the actual file name matches the FILE_NAME entry + IF STRLOWCASE(STRTRIM(ga_data,2)) NE ifilechk THEN INFOTXT_OUTPUT_A,[24],[ga_data] + ENDIF + ENDFOR + + IF n_sds_hold NE n_vn THEN BEGIN + ;Do check for sds being a Dimension attribute + do_coordvar=1 + FOR i=0L,n_sds-1L DO BEGIN + ;The HDF_SD_SELECT function returns an SD dataset ID given the current + ;SD interface ID, and the zero-based SD dataset index. + sds_id=HDF_SD_SELECT(sd_id,i) + IF HDF_SD_ISCOORDVAR(sds_id) THEN n_sds_hold=n_sds_hold-1L + ;Closes the SDS interface. + HDF_SD_ENDACCESS,sds_id + ENDFOR + IF n_sds_hold EQ 0L THEN BEGIN + STOP_WITH_ERROR_A,o3[3]+proname,'Number of SDS datasets'+errtxt[1],lu + RETURN + ENDIF + + IF n_sds_hold NE n_vn THEN $ + INFOTXT_OUTPUT_A,[0],[STRTRIM(n_vn,2),STRTRIM(n_sds_hold,2),'HDF_SD_FILEINFO'] + ;DATA_VARIABLES values not read successfully, or number of datasets given under DATA_VARIABLES + ;is not equal to the number saved to the file + ENDIF + + ;Determine maximum number of attributes, and match the dataset order to DATA_VARIABLES list if possible + max_atts=0L & oi=LONARR(n_sds_hold) + dv_order=1 & c_sds=0L + catinfo=STRARR(n_sds_hold,4) ;output info for catalog output + FOR i=0L,n_sds-1L DO BEGIN + vnv='' & sds_name='' + hdftype=STRARR(n_lab-1) & idltype=STRARR(n_lab) ;to hold data types for dataset, min, max and fill values, and VAR_DATA_TYPE (idltype only) + ;The HDF_SD_SELECT function returns an SD dataset ID given the current + ;SD interface ID, and the zero-based SD dataset index. + sds_id=HDF_SD_SELECT(sd_id,i) + + IF (do_coordvar EQ 0) OR (NOT HDF_SD_ISCOORDVAR(sds_id)) THEN BEGIN + ;The HDF_SD_GETINFO procedure retrieves information about an SD dataset. + HDF_SD_GETINFO,sds_id,NATTS=sds_natts, $ ;no. attributes + HDF_TYPE=sds_hdftype, $ ;HDF data type + TYPE=sds_type, $ ;data type + DIMS=sds_dim, $ ;dimension information (automatically reversed by IDL) + NAME=sds_name ;dataset name + + ;Check for string attribute and, if so, remove the first dimension + IF (sds_type EQ 'STRING') AND (N_ELEMENTS(sds_dim) NE 1) THEN sds_dim=sds_dim[1:N_ELEMENTS(sds_dim)-1] + + ;Check for multi-dimensional dataset + n_sds_dim=N_ELEMENTS(sds_dim) + IF n_sds_dim GT 1 THEN multi_dim=1 + + ;Check number of dimensions + IF n_sds_dim gt 8 THEN INFOTXT_OUTPUT_A,[1],[sds_name,STRTRIM(n_sds_dim,2)] + + ;Check for coordinate variable + IF HDF_SD_ISCOORDVAR(sds_id) THEN INFOTXT_OUTPUT_A,[8],[sds_name] + + ;Check that a dataset name has been successfully read + sds_name=STRTRIM(sds_name,2) + IF sds_name EQ '' THEN sds_name='N/A' + + ;Rename data type to be compatible with the Metadata guidelines + sds_type=STRTRIM(STRUPCASE(sds_type),2) + IF sds_type EQ 'INT' THEN sds_type='SHORT' $ + ELSE IF sds_type EQ 'LONG' THEN sds_type='INTEGER' $ + ELSE IF sds_type EQ 'FLOAT' THEN sds_type='REAL' + + hdftype[0]=sds_hdftype & idltype[0]=sds_type + + IF sds_natts NE 0L THEN BEGIN + IF sds_natts GT max_atts THEN max_atts=sds_natts + vnf=0 & lcnt=0 & vcnt=sds_natts + ;Extract the variable attributes + FOR j=0L,sds_natts-1L DO BEGIN + ;The HDF_SD_ATTRINFO procedure reads or retrieves information about an SD attribute. + HDF_SD_ATTRINFO,sds_id,j,NAME=va_name,DATA=va_value,HDF_TYPE=va_hdftype,TYPE=va_type + va_name=STRTRIM(va_name,2) + schk=WHERE(STRUPCASE(va_name) EQ slabel,schkcnt) + + ;Rename data type to be compatible with the Metadata guidelines + va_type=STRTRIM(STRUPCASE(va_type),2) + IF va_type EQ 'INT' THEN va_type='SHORT' $ + ELSE IF va_type EQ 'LONG' THEN va_type='INTEGER' $ + ELSE IF va_type EQ 'FLOAT' THEN va_type='REAL' + + IF ((sds_type EQ 'STRING') AND (schkcnt EQ 0)) OR ((sds_type NE 'STRING') AND (va_type EQ 'STRING')) THEN $ + va_value=STRTRIM(va_value,2) + ;Check returned dataset attributes (sds_) with saved attributes (va_) + IF (STRUPCASE(va_name) EQ 'VAR_NAME') AND (vnf EQ 0) THEN BEGIN + ;check that the dataset name matches the VAR_NAME + vnf=1 + IF STRUPCASE(va_value) NE STRUPCASE(sds_name) THEN BEGIN + ;Try and determine which is wrong - Dataset name or VAR_NAME - default is Dataset name + li=WHERE((STRUPCASE(sds_name) EQ STRUPCASE(vn)) AND (vn[0] NE ''),lcnt) + ;If lcnt NE 0 then Dataset name matches DATA_VARIABLES value so assume that VAR_NAME is wrong + IF lcnt NE 0 THEN usesds=1 ELSE usesds=0 + INFOTXT_OUTPUT_A,[3,4,usesds],[sds_name,va_value] + IF usesds EQ 0 THEN sds_name=va_value ELSE va_value=sds_name + ENDIF + vnv=va_value + ;match the order of the dataset to the DATA_VARIABLES list + li=WHERE(STRUPCASE(va_value) EQ STRUPCASE(vn),lcnt) + ENDIF + IF STRUPCASE(va_name) EQ 'VAR_DATA_TYPE' THEN idltype[n_lab-1]=STRUPCASE(STRTRIM(va_value,2)) $ + ELSE IF STRUPCASE(va_name) EQ 'VAR_UNITS' THEN BEGIN + ;check that VAR_UNITS is a string value (in event that VAR_UNITS=1) + IF va_type NE 'STRING' THEN $ + INFOTXT_OUTPUT_A,[20],['VAR_UNITS',STRTRIM(sds_name,2)] + ENDIF ELSE IF STRUPCASE(va_name) EQ 'VAR_DEPEND' THEN $ ;do dimension ordering checks + TEST_DIM_ORDER,'VD',va_value,va_type,sds_dim,sds_name,rev_vd_vs $ + ELSE IF STRUPCASE(va_name) EQ 'VAR_SIZE' THEN $ ;do dimension ordering checks + TEST_DIM_ORDER,'VS',va_value,va_type,sds_dim,sds_name,rev_vd_vs $ + ELSE IF (STRUPCASE(va_name) EQ 'VAR_VALID_MIN') THEN BEGIN + hdftype[1]=va_hdftype & idltype[1]=va_type + ENDIF ELSE IF (STRUPCASE(va_name) EQ 'VAR_VALID_MAX') THEN BEGIN + hdftype[2]=va_hdftype & idltype[2]=va_type + ENDIF ELSE IF (STRUPCASE(va_name) EQ 'VAR_FILL_VALUE') THEN BEGIN + hdftype[3]=va_hdftype & idltype[3]=va_type + ENDIF + ENDFOR + ENDIF ELSE BEGIN ;No Variable Attributes found + vcnt=3 + ;Can the SDS_NAME be matched with a DATA_VARIABLES value? + li=WHERE(STRUPCASE(sds_name) EQ STRUPCASE(vn),lcnt) + IF lcnt NE 0 THEN vnf=1 ELSE vnf=0 + INFOTXT_OUTPUT_A,[5],[sds_name,'HDF_SD_GETINFO'] + ENDELSE + + ;Check for possible problems with determining array index + IF lcnt NE 0 THEN BEGIN + IF li[0] GE n_sds_hold THEN dv_order=0 $ ;can occur when number of DATA_VARIABLE values is greater than number of datasets + ELSE IF catinfo[li[0],0] NE '' THEN dv_order=0 ;This array has already been written to + ENDIF ELSE IF (lcnt EQ 0) OR (vnf EQ 0) THEN dv_order=0 + ;Write out information text if sds_name cannot be matched and determine array index + IF dv_order EQ 0 THEN BEGIN + li=WHERE(catinfo[*,0] EQ '') ;determine lowest available array index to write info to + in0=[4,vnf,lcnt,n_vn] + IF (vnf EQ 0) OR ((lcnt EQ 0) AND (n_vn NE 0L)) THEN INFOTXT_OUTPUT_A,in0,[sds_name,vnv] + ENDIF + + oi[c_sds]=li[0] ;Attribute order index + c_sds=c_sds+1L + + catinfo[li[0],0]=sds_name & catinfo[li[0],1]=sds_type + catinfo[li[0],2]=STRTRIM(sds_dim[0],2) & catinfo[li[0],3]=vcnt + IF N_ELEMENTS(sds_dim) GT 1 THEN $ + FOR j=1,N_ELEMENTS(sds_dim)-1 DO catinfo[li[0],2]=catinfo[li[0],2]+';'+STRTRIM(sds_dim[j],2) + ENDIF + + ;Do data type checks on the Dataset, VAR_DATA_TYPE value and data types of VAR_VALID_MIN, VAR_VALID_MAX and VAR_FILL_VALUES + DATA_TYPE_CHECKS,ftype,dt_chk_labels,hdftype,idltype,sds_name,vnv + ;Closes the SDS interface. + HDF_SD_ENDACCESS,sds_id + ENDFOR + + IF max_atts EQ 0L THEN max_atts=3 ;Use information from HDF_SD_GETINFO call only + + rev_vd_vsh=rev_vd_vs ;keep original value in hold variable (required if rev_vd_vs eq 3) + IF (rev_vd_vs EQ 2) OR (rev_vd_vs EQ 3) THEN rev_vd_vs=1 ;change default so that VAR_DEPEND and VAR_SIZE + ;are reversed if not o/w changed in TEST_DIM_ORDER + + ;Dimension the structure to the number of datasets x number of attributes + sds=REPLICATE(sds_set, n_sds_hold, max_atts) + + ;Write attributes to structure + c_sds=0L + !QUIET=1 ;suppress system error and information messages (used for CALDATA call in HDF_SD_GETINFO) + FOR i=0L,n_sds-1L DO BEGIN + notranspose=1 ;0/1 will change to 0 if the dataset needs to be transposed + ;The HDF_SD_SELECT function returns an SD dataset ID given the current + ;SD interface ID, and the zero-based SD dataset index. + sds_id=HDF_SD_SELECT(sd_id,i) + nocal=1 ;Boolean to identify dataset which has been calibrated + IF (do_coordvar EQ 0) OR (NOT HDF_SD_ISCOORDVAR(sds_id)) THEN BEGIN + ;The HDF_SD_GETINFO procedure retrieves information about an SD dataset. + ;Note - any saved pre-defined attributes will be called with HDF_SD_ATTRINFO call + HDF_SD_GETINFO,sds_id,NATTS=sds_natts, $ ;no. attributes + HDF_TYPE=sds_hdftype, $ ;HDF data type + TYPE=sds_type, $ ;data type + DIMS=sds_dim, $ ;dimension information + NAME=sds_name, $ ;dataset name + CALDATA=sds_cal ;pre-defined calibration info + + ;Check to see whether data has had scale factor and offset applied + IF (sds_cal.Cal NE 0.D) OR (sds_cal.Offset NE 0.D) THEN BEGIN + nocal=0 ;Dataset has been calibrated + ;identify datatype of the original dataset + CASE 1 OF + sds_cal.Num_Type EQ 21L: vdt_val='BYTE' + sds_cal.Num_Type EQ 22L: vdt_val='SHORT' + sds_cal.Num_Type EQ 24L: vdt_val='INTEGER' + sds_cal.Num_Type EQ 5L: vdt_val='REAL' + sds_cal.Num_Type EQ 6L: vdt_val='DOUBLE' + ELSE: vdt_val='' ;invalid or cannot determine original datatype + ENDCASE + ENDIF + + IF sds_natts NE 0L THEN BEGIN + ;Extract the variable attributes + jh=0 ;will only loop if attribute is not a pre-defined calibration attribute + FOR j=0L,sds_natts-1L DO BEGIN + ;The HDF_SD_ATTRINFO procedure reads or retrieves information about an SD attribute. + HDF_SD_ATTRINFO,sds_id,j,NAME=va_name,DATA=va_value,TYPE=va_type + va_name=STRTRIM(va_name,2) + + schk=WHERE(STRUPCASE(va_name) EQ slabel,schkcnt) + IF ((sds_type EQ 'STRING') AND (schkcnt EQ 0)) OR ((sds_type NE 'STRING') AND (va_type EQ 'STRING')) THEN $ + va_value=STRTRIM(va_value,2) + IF (nocal EQ 0) AND (STRUPCASE(va_name) EQ 'VAR_DATA_TYPE') THEN BEGIN + ;If required prepare INFORMATION text (written after DATA has been corrected) + IF (vdt_val NE STRUPCASE(va_value)) AND (vdt_val NE '') THEN BEGIN + io_arr=[STRTRIM(sds_name,2),va_value,vdt_val] + va_value=vdt_val + ENDIF ELSE BEGIN + io_arr=[''] & vdt_val=va_value + ENDELSE + ENDIF + + IF ((STRUPCASE(va_name) EQ 'VAR_DEPEND') OR (STRUPCASE(va_name) EQ 'VAR_SIZE')) AND $ + ((rev_vd_vs MOD 10 EQ 1) OR (rev_vd_vs EQ 20)) THEN BEGIN ;need to reverse the va_values + + IF va_type NE 'STRING' THEN BEGIN ;VAR_SIZE contains numeric values instead of in the form of a string + vavhold='' & n_vav=N_ELEMENTS(va_value) + FOR k=0,n_vav-1 DO BEGIN + IF k EQ n_vav-1 THEN vtxt='' ELSE vtxt=';' + vavhold=vavhold+STRTRIM(va_value[k],2)+vtxt + ENDFOR + ENDIF ELSE vavhold=va_value + + vs_v=STRCOMPRESS(STRSPLIT(vavhold,';, ',/EXTRACT,COUNT=rcnt),/REMOVE_ALL) + IF rcnt GT 1 THEN BEGIN ;multi-dimensions + IF (rev_vd_vs EQ 20) THEN notranspose=0 ;need to transpose dataset as well + vs_v=REVERSE(vs_v) + FOR k=0,rcnt-1 DO $ + IF k EQ 0 THEN va_value=vs_v[k] ELSE va_value=va_value+';'+vs_v[k] + ENDIF + ENDIF + + ;Test for pre-defined calibration attribute and do not save as the data have + ;been coverted back to original values + pi=WHERE(STRLOWCASE(va_name) EQ ncsa_cal,pcnt) + IF pcnt EQ 0 THEN BEGIN + sds[oi[c_sds],jh].va_l=PTR_NEW(va_name) + sds[oi[c_sds],jh].va_v=PTR_NEW(va_value) + jh=jh+1 + ENDIF + ENDFOR + ENDIF ELSE BEGIN ;No Variable Attributes found + va_lab=['VAR_NAME','VAR_SIZE','VAR_DATA_TYPE'] + FOR j=0,2 DO sds[oi[c_sds],j].va_l=PTR_NEW(va_lab[j]) + sds[oi[c_sds],0].va_v=PTR_NEW(sds_name) + sds[oi[c_sds],1].va_v=PTR_NEW(sds_dim) + ;Rename format to be compatible with the Metadata guidelines + IF sds_type EQ 'INT' THEN sds_type='SHORT' $ + ELSE IF sds_type EQ 'LONG' THEN sds_type='INTEGER' $ + ELSE IF sds_type EQ 'FLOAT' THEN sds_type='REAL' + sds[oi[c_sds],2].va_v=PTR_NEW(sds_type) + ENDELSE + ;Extract the data + HDF_SD_GETDATA,sds_id,datasize + ;Test to see if dataset dimension ordering needs to be changed + IF notranspose EQ 0 THEN datasize=TRANSPOSE(datasize) + + IF nocal EQ 0 THEN BEGIN + ;apply scale factor and offset corrections to data and write text to log file + CASE 1 OF + vdt_val EQ 'BYTE': datasize=BYTE(sds_cal.Cal*(datasize-sds_cal.Offset)) + vdt_val EQ 'SHORT': datasize=FIX(sds_cal.Cal*(datasize-sds_cal.Offset)) + vdt_val EQ 'INTEGER': datasize=LONG(sds_cal.Cal*(datasize-sds_cal.Offset)) + vdt_val EQ 'REAL': datasize=FLOAT(sds_cal.Cal*(datasize-sds_cal.Offset)) + ELSE: datasize=DOUBLE(sds_cal.Cal*(datasize-sds_cal.Offset)) + ENDCASE + INFOTXT_OUTPUT_A,[9],[STRTRIM(sds_name,2)] + IF io_arr[0] NE '' THEN INFOTXT_OUTPUT_A,[10],io_arr + ENDIF + sds[oi[c_sds],0].data=PTR_NEW(datasize) + c_sds=c_sds+1L + ENDIF + + ;Closes the SDS interface. + HDF_SD_ENDACCESS,sds_id + ENDFOR + !QUIET=0 ;Allow system error and information messages + ;The HDF_SD_END function closes the SD interface to an HDF file. + HDF_SD_END,sd_id +ENDIF ELSE IF (ftype EQ 'H5') OR (ftype EQ 'N4') THEN BEGIN ;HDF5 or netCDF4 format file + stdfm=1 ;Boolean indicating standard AVDC/EVDC/NDACC H5 format or not + dv_order=1 ;Boolean indicating whether Metadata Variable listing order matches DATA_VARIABLES values + n_vn=0L ;Initialize the number of Variable Names in the standard format h5 file + + ;labels to quietly ignore if the file is netCDF4 + nc4_ignore=['reference_list','dimension_list','class','name','_netcdf4dimid','_netcdf4coordinates','_ncproperties'] + + ;Do bulk read so errors generated by netCDF4 inconsistencies can be identified without + ;crashing the program e.g. null string attribute values + h5p=H5_PARSE(ifile) + + hdf_file_id=H5F_OPEN(ifile) + sd_id=H5G_OPEN(hdf_file_id,'/') + + ;Get number of attributes within the root group (these should be the Global Attributes) + n_ga=H5A_GET_NUM_ATTRS(sd_id) + ;Get the number of objects within the root group (including datasets, groups etc) + n_obj=H5G_GET_NMEMBERS(sd_id,'/') + + IF n_obj NE 0L THEN BEGIN + d_obj=INTARR(n_obj) & g_obj=INTARR(n_obj) + ;Determine number of datasets. Note: if H5 file created using H4toH5 then an extra + ;group will have been created which will not be used. Also datasets may be listed + ;alphabetically. This section also determines whether the H5 file is a standard + ;AVDC/EVDC/NDACC groundbased file or otherwise. + FOR i=0L,n_obj-1L DO BEGIN + sds_name=H5G_GET_MEMBER_NAME(sd_id,'/',i) + h5fstat=H5G_GET_OBJINFO(sd_id,sds_name) + IF h5fstat.type EQ 'DATASET' THEN d_obj[i]=1 $ + ELSE IF h5fstat.type EQ 'GROUP' THEN g_obj[i]=1 + ENDFOR + di=WHERE(d_obj EQ 1,n_sds) + gi=WHERE(g_obj EQ 1,n_grp) + IF (n_ga EQ 0) AND (n_sds EQ 0) AND (n_grp NE 0) THEN stdfm=0 + ENDIF ELSE n_sds=0L + + IF stdfm EQ 0 THEN catinfo[0,0]='HE5' ;non-standard dataset so need to call READ_NONSTD_H5 + + IF n_ga NE 0 THEN BEGIN + ga=STRARR(n_ga) ;set the Global Attribute dimensions + vn=[''] ;initialize vn array (to hold Dataset names) + ;Read in the Global Attribute labels and values + FOR i=0L,n_ga-1L DO BEGIN + ga_id=H5A_OPEN_IDX(sd_id,i) + ga_name=H5A_GET_NAME(ga_id) & ga_name=STRTRIM(ga_name,2) + ;Check for netCDF4 specific attributes and ignore + nc4i=WHERE(STRLOWCASE(ga_name) EQ nc4_ignore,nc4icnt) + IF nc4icnt EQ 0 THEN BEGIN + ;test for valid attribute value - can be invalid if it is netCDF4 and equal to '' + gni=WHERE(TAG_NAMES(h5p) EQ STRUPCASE(ga_name)) + attest=h5p.(gni[0]) + IF STRTRIM(attest._data[0],2) EQ '<read error>' THEN ga_data='' $ + ELSE BEGIN + dt_id=H5A_GET_TYPE(ga_id) + ga_data=H5A_READ(ga_id) & ga_data=STRTRIM(ga_data,2) + ga_datatype=H5T_GET_CLASS(dt_id) + H5T_CLOSE,dt_id + ;String labels for GA not checked at this time as extra Global Attributes could be numeric + ;IF ga_datatype NE 'H5T_STRING' THEN $ + ; INFOTXT_OUTPUT_A,[20],['Global Attribute',STRTRIM(ga_name,2)] + ENDELSE + ;Check for _GLOSDS suffix and strip (added for compatibility with H4toH5 utility) + IF STRMID(STRUPCASE(ga_name),STRLEN(ga_name)-7) EQ '_GLOSDS' THEN $ + ga_name=STRMID(ga_name,0,STRPOS(ga_name,'_',/Reverse_Search)) + WHILE STRMID(ga_name,STRLEN(ga_name)-1) EQ '_' DO ga_name=STRMID(ga_name,0,STRLEN(ga_name)-1) + ga[i]=ga_name+'='+ga_data + ENDIF + H5A_CLOSE,ga_id + ;Extract the list of the DATA_VARIABLES + IF STRUPCASE(ga_name) EQ 'DATA_VARIABLES' THEN vn=STRSPLIT(ga_data,' ;',/Extract,COUNT=n_vn) + IF STRUPCASE(ga_name) EQ 'FILE_NAME' THEN BEGIN + ;check that the actual file name matches the FILE_NAME entry + IF STRLOWCASE(STRTRIM(ga_data,2)) NE ifilechk THEN INFOTXT_OUTPUT_A,[24],[ga_data] + ENDIF + ENDFOR + ;recalculate n_ga and ga in case of netCDF4 name which is ignored + gai=WHERE(ga NE '',n_ga) + IF n_ga NE 0 THEN ga=ga[gai] + ENDIF ELSE IF stdfm EQ 1 THEN BEGIN + STOP_WITH_ERROR_A,o3[3]+proname,'No Global Attributes'+errtxt[0],lu + RETURN + ENDIF + + IF n_sds NE 0L THEN BEGIN + NC_DIMENSION_CHK,'H5',sd_id,di,n_sds,dgi,n_sdsg ;check for netCDF dimension names (will quietly ignore) + di=dgi & n_sds=n_sdsg + IF n_vn NE n_sds THEN $ + INFOTXT_OUTPUT_A,[0],[STRTRIM(n_vn,2),STRTRIM(n_sds,2),'H5G_GET_NMEMBERS'] + ;DATA_VARIABLES values not read successfully, or number of datasets given under DATA_VARIABLES + ;is not equal to the number saved to the file + + ;Determine maximum number of Attributes, correct dataset order, and dimension order + max_atts=0L & oi=LONARR(n_sds) + dv_order=1 + catinfo=STRARR(n_sds,4) ;output info for catalog output + FOR i=0,n_sds-1 DO BEGIN + vnv='' & sds_name='' + hdftype=STRARR(n_lab-1) & idltype=STRARR(n_lab) ;to hold data types for dataset, min, max and fill values, and VAR_DATA_TYPE (idltype only) + + sds_name=H5G_GET_MEMBER_NAME(sd_id,'/',di[i]) + ds_id=H5D_OPEN(sd_id,sds_name) + ;need to swap non-alphanumeric characters to '_' to match names from h5_parse for attribute values test + h5p_name=ALPHA_NUMERIC_UNDERSCORE(STRUPCASE(sds_name)) + + IF sds_name EQ '' THEN sds_name='N/A' + + ;determine dataset datatype + dt_id=H5D_GET_TYPE(ds_id) + dt_datatype=H5T_GET_CLASS(dt_id) + IF dt_datatype EQ 'H5T_INTEGER' THEN BEGIN + dt_sign=H5T_GET_SIGN(dt_id) ;0=unsigned; 1=signed + dt_datatype=dt_datatype+'_'+STRTRIM(dt_sign,2) + ;Byte needs to be unsigned, Short and Integer are signed + ENDIF + H5T_CLOSE,dt_id + + datasize=H5D_READ(ds_id) + sds_dim=SIZE(datasize,/DIMENSIONS) & n_sds_dim=N_ELEMENTS(sds_dim) + FOR sdl=0,n_sds_dim-1 DO IF sds_dim[sdl] EQ 0 THEN sds_dim[sdl]=1 ;constant scalar h5 + IF (ftype EQ 'N4') AND (dt_datatype EQ 'H5T_STRING') THEN BEGIN + ;need to do char_dim checks for STRING datasets and remove the string length dimension + char_dim=sds_dim[0] + IF n_sds_dim EQ 1 THEN sds_dim[0]=1 $ + ELSE BEGIN + sds_dim=sds_dim[1:n_sds_dim-1] & n_sds_dim=n_sds_dim-1 + ENDELSE + dschk=STRARR(sds_dim) + FOR sdl=0L,char_dim-1L DO dschk=dschk+datasize[sdl,*] + datasize=dschk + ENDIF + sds_type=SIZE(datasize,/TYPE) + + hdftype[0]=dt_datatype & idltype[0]=STRTRIM(sds_type,2) + + ;Determine the number of attributes associated with the dataset + sds_natts=H5A_GET_NUM_ATTRS(ds_id) + IF sds_natts GT max_atts THEN max_atts=sds_natts + + IF sds_natts NE 0L THEN BEGIN + vnf=0 & lcnt=0 + ci=STRARR(4) ;array containing catalog attributes + ;Read in Dataset Attributes + jj=0 ;use this index in the event of invalid attribute names (e.g. dimension_list from netCDF4 file) + FOR j=0L,sds_natts-1L DO BEGIN + da_id=H5A_OPEN_IDX(ds_id,j) + da_name=H5A_GET_NAME(da_id) & da_name=STRTRIM(da_name,2) + + ;determine attribute datatype + at_id=H5A_GET_TYPE(da_id) + at_datatype=H5T_GET_CLASS(at_id) + IF at_datatype EQ 'H5T_INTEGER' THEN BEGIN + at_sign=H5T_GET_SIGN(at_id) ;0=unsigned; 1=signed + at_datatype=at_datatype+'_'+STRTRIM(at_sign,2) + ;Byte needs to be unsigned, Short and Integer are signed + ENDIF + H5T_CLOSE,at_id + + nc4i=WHERE(STRLOWCASE(da_name) EQ nc4_ignore,nc4icnt) + IF nc4icnt EQ 0 THEN BEGIN + ;test for valid attribute value - can be invalid if it is netCDF4 and equal to '' + gni=WHERE(TAG_NAMES(h5p) EQ STRUPCASE(h5p_name)) ;Tag index from h5_parse read + attest=h5p.(gni[0]) + gni=WHERE(TAG_NAMES(attest) EQ STRUPCASE(da_name)) + attestx=attest.(gni[0]) + IF STRTRIM(attestx._data[0],2) EQ '<read error>' THEN va_value='' $ + ELSE va_value=H5A_READ(da_id) + IF SIZE(va_value,/TYPE) EQ 7 THEN va_type='STRING' ELSE va_type='NON_STRING' + idltypehold=STRTRIM(SIZE(va_value,/TYPE),2) + schk=WHERE(STRUPCASE(da_name) EQ slabel,schkcnt) + IF ((sds_type EQ 7) AND (schkcnt EQ 0)) OR ((sds_type NE 7) AND (va_type EQ 'STRING')) THEN $ + va_value=STRTRIM(va_value,2) + CASE 1 OF + (STRUPCASE(da_name) EQ 'VAR_NAME') AND (vnf EQ 0):BEGIN + vnf=1 + ;check that the dataset name matches the VAR_NAME + IF STRUPCASE(va_value) NE STRUPCASE(sds_name) THEN BEGIN + test1=ftype EQ 'N4' + test2=STRUPCASE(ALPHA_NUMERIC_UNDERSCORE(va_value)) EQ STRUPCASE(sds_name) + ;For nc4 files check if '.'s have been replaced by '_'s in the SDS_NAME. If so then it is OK + ;and quietly change SDS_NAME to the VAR_NAME value + IF (test1) AND (test2) THEN sds_name=va_value $ + ELSE BEGIN + ;Try and determine which is wrong - Dataset name or VAR_NAME - default is Dataset name + li=WHERE((STRUPCASE(sds_name) EQ STRUPCASE(vn)) AND (vn[0] NE ''),lcnt) + ;If lcnt NE 0 then Dataset name matches DATA_VARIABLES value so assume that VAR_NAME is wrong + IF lcnt NE 0 THEN usesds=1B ELSE usesds=0B + INFOTXT_OUTPUT_A,[3,5,usesds],[sds_name,va_value] + IF usesds THEN va_value=sds_name else sds_name=va_value + ENDELSE + ENDIF + ;Determine actual list order from DATA_VARIABLE listing (H5 lists datasets alphabetically) + li=WHERE(STRUPCASE(va_value[0]) EQ STRUPCASE(vn),lcnt) + ci[0]=va_value & vnv=va_value + END + STRUPCASE(da_name) EQ 'VAR_DATA_TYPE': BEGIN + ci[1]=va_value & idltype[n_lab-1]=STRUPCASE(STRTRIM(va_value,2)) + END + STRUPCASE(da_name) EQ 'VAR_UNITS': BEGIN + ;check that VAR_UNITS is a string value (in event that VAR_UNITS=1) + IF va_type NE 'STRING' THEN $ + INFOTXT_OUTPUT_A,[20],['VAR_UNITS',STRTRIM(sds_name,2)] + END + STRUPCASE(da_name) EQ 'VAR_DEPEND': BEGIN + TEST_DIM_ORDER,'VD',va_value,va_type,sds_dim,sds_name,rev_vd_vs + END + STRUPCASE(da_name) EQ 'VAR_SIZE': BEGIN + TEST_DIM_ORDER,'VS',va_value,va_type,sds_dim,sds_name,rev_vd_vs + END + STRUPCASE(da_name) EQ 'VAR_VALID_MIN': BEGIN + hdftype[1]=at_datatype & idltype[1]=idltypehold + END + STRUPCASE(da_name) EQ 'VAR_VALID_MAX': BEGIN + hdftype[2]=at_datatype & idltype[2]=idltypehold + END + STRUPCASE(da_name) EQ 'VAR_FILL_VALUE': BEGIN + hdftype[3]=at_datatype & idltype[3]=idltypehold + END + ELSE: + ENDCASE + jj++ + ENDIF ;ELSE IF nc4i[0] EQ 4 THEN catinfo[0,0]='H5N' ;netCDF4 file - not implemented yet + H5A_CLOSE,da_id + ENDFOR + ci[3]=STRTRIM(jj,2) + IF ci[0] EQ '' THEN ci[0]=sds_name + ENDIF ELSE BEGIN ;No Attributes, so can only write dataset name to Metadata + ci=[STRUPCASE(sds_name),'','','1'] & vnv=sds_name + ;Can the SDS_NAME be matched with a DATA_VARIABLES value? + li=WHERE(STRUPCASE(sds_name) EQ STRUPCASE(vn),lcnt) + IF lcnt NE 0 THEN vnf=1 ELSE vnf=0 + INFOTXT_OUTPUT_A,[5],[sds_name,'H5A_GET_NUM_ATTRS'] + ENDELSE + FOR k=0,N_ELEMENTS(sds_dim)-1 DO BEGIN + IF k EQ 0 THEN ci[2]=STRTRIM(sds_dim[k],2) ELSE ci[2]=ci[2]+';'+STRTRIM(sds_dim[k],2) + ENDFOR + + ;Check for possible problems with determining array index + IF lcnt NE 0 THEN BEGIN + IF li[0] GE n_sds THEN dv_order=0 $ ;can occur when number of DATA_VARIABLE values is greater than number of datasets + ELSE IF catinfo[li[0],0] NE '' THEN dv_order=0 ;This array has already been written to + ENDIF ELSE IF (lcnt EQ 0) OR (vnf EQ 0) THEN dv_order=0 + ;Write out information text if sds_name cannot be matched and determine array index + IF dv_order EQ 0 THEN BEGIN + li=WHERE(catinfo[*,0] EQ '') ;determine lowest available array index to write info to + in0=[4,vnf,lcnt,n_vn] + IF (vnf EQ 0) OR ((lcnt EQ 0) AND (n_vn NE 0L)) THEN INFOTXT_OUTPUT_A,in0,[sds_name,vnv] + ENDIF + + H5D_CLOSE,ds_id + oi[i]=li[0] ;to put datasets in the correct order + catinfo[li[0],*]=ci[*] + + ;Do data type checks on the Dataset, VAR_DATA_TYPE value and data types of VAR_VALID_MIN, VAR_VALID_MAX and VAR_FILL_VALUES + DATA_TYPE_CHECKS,ftype,dt_chk_labels,hdftype,idltype,sds_name,vnv + + ENDFOR + ENDIF ELSE IF stdfm EQ 1 THEN BEGIN + STOP_WITH_ERROR_A,o3[3]+proname,'No Datasets'+errtxt[0],lu & RETURN + ENDIF + + rev_vd_vsh=rev_vd_vs ;keep original value in hold variable (required if rev_vd_vs eq 3) + IF (rev_vd_vs EQ 2) OR (rev_vd_vs EQ 3) THEN rev_vd_vs=1 ;change default so that VAR_DEPEND and VAR_SIZE + ;are reversed if not o/w changed in TEST_DIM_ORDER + + ;Dimension the structure to the size of the SDS datasets (with dimension n_sds) + sds=REPLICATE(sds_set, n_sds, max_atts) + ;Put datasets and attributes into sds structure + FOR i=0,n_sds-1 DO BEGIN + notranspose=1 ;0/1 will change to 0 if the dataset needs to be transposed + sds_name=H5G_GET_MEMBER_NAME(sd_id,'/',di[i]) + ds_id=H5D_OPEN(sd_id,sds_name) + ;need to swap non-alphanumeric characters to '_' to match names from h5_parse for attribute values test + h5p_name=ALPHA_NUMERIC_UNDERSCORE(STRUPCASE(sds_name)) + + IF sds_name EQ '' THEN sds_name='N/A' + datasize=H5D_READ(ds_id) + sds_type=SIZE(datasize,/TYPE) + sds_dim=SIZE(datasize,/DIMENSIONS) & n_sds_dim=N_ELEMENTS(sds_dim) + FOR sdl=0,n_sds_dim-1 DO IF sds_dim[sdl] EQ 0 THEN sds_dim[sdl]=1 ;constant scalar h5 + IF (ftype EQ 'N4') AND (sds_type EQ 7) THEN BEGIN + ;need to do char_dim checks for STRING datasets and remove the string length dimension + char_dim=sds_dim[0] + IF n_sds_dim EQ 1 THEN sds_dim[0]=1 $ + ELSE BEGIN + sds_dim=sds_dim[1:n_sds_dim-1] & n_sds_dim=n_sds_dim-1 + ENDELSE + dschk=STRARR(sds_dim) + FOR sdl=0L,char_dim-1L DO dschk=dschk+datasize[sdl,*] + datasize=dschk + ENDIF + + ;Determine the number of attributes associated with the dataset + sds_natts=H5A_GET_NUM_ATTRS(ds_id) + + IF sds_natts NE 0L THEN BEGIN + ;Read in Dataset Attributes + jj=0 ;use this index in the event of invalid attribute names (e.g. dimension_list fro netCDF4 file) + FOR j=0L,sds_natts-1L DO BEGIN + da_id=H5A_OPEN_IDX(ds_id,j) + da_name=H5A_GET_NAME(da_id) & da_name=STRTRIM(da_name,2) + nc4i=WHERE(STRLOWCASE(da_name) EQ nc4_ignore,nc4icnt) + IF nc4icnt EQ 0 THEN BEGIN + ;test for valid attribute value - can be invalid if it is netCDF4 and equal to '' + gni=WHERE(TAG_NAMES(h5p) EQ STRUPCASE(h5p_name)) ;Tag index from h5_parse read + attest=h5p.(gni[0]) + gni=WHERE(TAG_NAMES(attest) EQ STRUPCASE(da_name)) + attestx=attest.(gni[0]) + IF STRTRIM(attestx._data[0],2) EQ '<read error>' THEN va_value='' $ + ELSE va_value=H5A_READ(da_id) + + IF SIZE(va_value,/TYPE) EQ 7 THEN va_type='STRING' ELSE va_type='NON_STRING' + schk=WHERE(STRUPCASE(da_name) EQ slabel,schkcnt) + IF ((sds_type EQ 7) AND (schkcnt EQ 0)) OR ((sds_type NE 7) AND (va_type EQ 'STRING')) THEN $ + va_value=STRTRIM(va_value,2) + H5A_CLOSE,da_id + + IF ((STRUPCASE(da_name) EQ 'VAR_DEPEND') OR (STRUPCASE(da_name) EQ 'VAR_SIZE')) AND $ + ((rev_vd_vs MOD 10 EQ 1) OR (rev_vd_vs EQ 20)) THEN BEGIN ;need to reverse the va_values + + IF va_type NE 'STRING' THEN BEGIN ;VAR_SIZE contains numeric values instead of in the form of a string + vavhold='' & n_vav=N_ELEMENTS(va_value) + FOR k=0,n_vav-1 DO BEGIN + IF k EQ n_vav-1 THEN vtxt='' ELSE vtxt=';' + vavhold=vavhold+STRTRIM(va_value[k],2)+vtxt + ENDFOR + ENDIF ELSE vavhold=va_value + + vs_v=STRCOMPRESS(STRSPLIT(vavhold,';',/EXTRACT,COUNT=rcnt),/REMOVE_ALL) + IF rcnt GT 1 THEN BEGIN ;multi-dimensions + IF (rev_vd_vs EQ 20) THEN $ ;AND (STRPOS(STRUPCASE(va_value),'DATETIME') NE -1) THEN $ + notranspose=0 ;need to transpose dataset as well + vs_v=REVERSE(vs_v) + FOR k=0,rcnt-1 DO $ + IF k EQ 0 THEN va_value=vs_v[k] ELSE va_value=va_value+';'+vs_v[k] + ENDIF + ENDIF + sds[oi[i],jj].va_l=PTR_NEW(da_name) + sds[oi[i],jj].va_v=PTR_NEW(va_value) + jj++ + ENDIF + ENDFOR + ENDIF ELSE BEGIN ;No Attributes, so can only write dataset name to structure + val='VAR_NAME' + sds[oi[i],0].va_l=PTR_NEW(val) + sds[oi[i],0].va_v=PTR_NEW(sds_name) + ENDELSE + + H5D_CLOSE,ds_id + ;Test to see if dataset dimension ordering needs to be changed + IF notranspose EQ 0 THEN datasize=TRANSPOSE(datasize) + sds[oi[i],0].data=PTR_NEW(datasize) + ENDFOR + + H5G_CLOSE,sd_id + H5F_CLOSE,hdf_file_id +ENDIF ELSE BEGIN ;netCDF3 file (N3) + ncdf_desc=['long_name'] + ncdf_std=['units','valid_range','_fillvalue'] + ncdf_dimen=['constant','independent'] + + dv_order=1 ;Boolean indicating whether Metadata Variable listing order matches DATA_VARIABLES values + n_vn=0L ;Initialize the number of Variable Names in the standard format netCDF file + + fileid=NCDF_OPEN(ifile) + ;Get number of global atts and variables + filestruct=NCDF_INQUIRE(fileid) + n_dim=filestruct.ndims + n_ga=filestruct.ngatts + n_sds=filestruct.nvars + + IF n_sds EQ 0L THEN ntxt='Number of SDS datasets' $ + ELSE IF n_ga EQ 0L THEN ntxt='Number of Global Attributes' $ + ELSE ntxt='' + IF ntxt NE '' THEN BEGIN + STOP_WITH_ERROR_A,o3[3]+proname,ntxt+errtxt[1],lu & RETURN + ENDIF + + ;read global attributes + ga=STRARR(n_ga) & vn=[''] + FOR j=0,n_ga-1 DO BEGIN + ga_name=NCDF_ATTNAME(fileid,j,/GLOBAL) + NCDF_ATTGET,fileid,ga_name,ga_data,/GLOBAL + ga_data=STRTRIM(ga_data,2) + ga[j]=STRTRIM(STRUPCASE(ga_name),2)+'='+ga_data + ;read list of DATA_VARIABLES into array + IF STRUPCASE(ga_name) EQ 'DATA_VARIABLES' THEN vn=STRSPLIT(ga_data,' ;',/Extract,COUNT=n_vn) + IF STRUPCASE(ga_name) EQ 'FILE_NAME' THEN BEGIN + ;check that the actual file name matches the FILE_NAME entry + IF STRLOWCASE(STRTRIM(ga_data,2)) NE ifilechk THEN INFOTXT_OUTPUT_A,[24],[ga_data] + ENDIF + ENDFOR + + ;read dimension information + IF n_dim NE 0 THEN BEGIN + mv_dim=STRARR(n_dim,2) + FOR j=0,n_dim-1 DO BEGIN + NCDF_DIMINQ,fileid,j,dimname,dimsize + mv_dim[j,0]=STRTRIM(dimname,2) + mv_dim[j,1]=STRTRIM(dimsize,2) + ENDFOR + ENDIF ELSE mv_dim=[''] + + di=INDGEN(n_sds) + NC_DIMENSION_CHK,'N3',fileid,di,n_sds,dgi,n_sdsg + di=dgi & n_sds=n_sdsg ;di contains index values of valid sds datasets + + IF n_vn NE n_sds THEN $ + INFOTXT_OUTPUT_A,[0],[STRTRIM(n_vn,2),STRTRIM(n_sds,2),'NCDF_INQUIRE'] + ;DATA_VARIABLES values not read successfully, or number of datasets given under DATA_VARIABLES + ;is not equal to the number saved to the file + + ;Determine maximum number of Attributes, correct dataset order, and dimension order + max_atts=0L & oi=LONARR(n_sds) + catinfo=STRARR(n_sds,4) ;output info for catalog output + + FOR j=0,n_sds-1 DO BEGIN + vnv='' & sds_name='' + hdftype=STRARR(n_lab-1) & idltype=STRARR(n_lab) ;to hold data types for dataset, min, max and fill values, and VAR_DATA_TYPE (idltype only) + + ;Extract data from a netCDF dataset and get size information + NCDF_VARGET,fileid,di[j],sds_data + sds_type=SIZE(sds_data,/TYPE) + + varstruct=NCDF_VARINQ(fileid,di[j]) + sds_natts=varstruct.natts + IF sds_natts GT max_atts THEN max_atts=sds_natts + sds_name=varstruct.name + sds_datatype=varstruct.datatype ;'BYTE', 'CHAR', 'INT', 'LONG', 'FLOAT', or 'DOUBLE' + sds_dimid=varstruct.dim + sds_ndim=varstruct.ndims + + ;determine the actual dimension values from the dim IDs given in varstruct.dim + ;IF sds_ndim NE 0 THEN BEGIN + ; sds_dim=LONARR(sds_ndim) + ; FOR k=0,sds_ndim-1 DO sds_dim[k]=mv_dim[sds_dimid[k],1] + ;ENDIF ELSE sds_dim=[1L] + + IF (sds_datatype EQ 'CHAR') AND (sds_type EQ 1) THEN sds_type=7 + sds_dim=SIZE(sds_data,/DIMENSIONS) & n_sds_dim=N_ELEMENTS(sds_dim) + FOR sdl=0,n_sds_dim-1 DO IF sds_dim[sdl] EQ 0 THEN sds_dim[sdl]=1 ;constant scalar h5 + IF sds_type EQ 7 THEN BEGIN + ;need to do char_dim checks for STRING datasets and remove the string length dimension + char_dim=sds_dim[0] + IF n_sds_dim EQ 1 THEN sds_dim[0]=1 $ + ELSE BEGIN + sds_dim=sds_dim[1:n_sds_dim-1] & n_sds_dim=n_sds_dim-1 + ENDELSE + ENDIF + + IF sds_datatype EQ 'BYTE' THEN $ + IF MIN(sds_data) LT 0 THEN sds_datatype='BYTE_1' ELSE sds_datatype='BYTE_0' + ;This doesn't work as NCDF_VARGET converts BYTE values to 8-bit unsigned if they are negative signed values + + hdftype[0]=sds_datatype & idltype[0]=STRTRIM(sds_type,2) + + ;Read Variable Attributes + IF sds_natts NE 0L THEN BEGIN + vnf=0 & lcnt=0 & vnv='' + ci=STRARR(4) ;array containing catalog attributes + std_fnd=INTARR(n_aad) + ;Read in Dataset Attributes + FOR k=0,sds_natts-1 DO BEGIN + varattsname=NCDF_ATTNAME(fileid,di[j],k) + NCDF_ATTGET,fileid,di[j],varattsname,va_value + attstruct=NCDF_ATTINQ(fileid,di[j],varattsname) + va_name=STRUPCASE(varattsname) + vavtype=SIZE(va_value,/TYPE) + + IF (vavtype EQ 7) OR (vavtype EQ 1) THEN va_type='STRING' ELSE va_type='NON-STRING' + hdftypehold=attstruct.datatype + IF hdftypehold EQ 'BYTE' THEN $ + IF va_value LT 0 THEN hdftypehold='BYTE_1' ELSE hdftypehold='BYTE_0' + ;This doesn't work as NCDF_VARGET converts BYTE values to 8-bit unsigned if they are negative signed values + idltypehold=vavtype + + schk=WHERE(va_name EQ slabel,schkcnt) + test1=(sds_datatype EQ 'CHAR') AND (schkcnt EQ 0) ;string dataset but not one of the slabel values + test2=(sds_datatype NE 'CHAR') AND (va_type EQ 'STRING') ;not a string dataset but string attribute value + test3=(sds_datatype EQ 'CHAR') AND (schkcnt NE 0) AND (vavtype EQ 1) ;string dataset, slabel and attribute value is of type byte + IF (test1) OR (test2) THEN va_value=STRTRIM(va_value,2) $ + ELSE IF test3 THEN BEGIN + va_value=STRING(va_value) & idltypehold=7 + ENDIF + + CASE 1 OF + (STRUPCASE(va_name) EQ 'VAR_NAME') AND (vnf EQ 0):BEGIN + vnf=1 + ;check that the dataset name matches the VAR_NAME + IF STRUPCASE(va_value) NE STRUPCASE(sds_name) THEN BEGIN + test1=ftype EQ 'N3' + test2=STRUPCASE(ALPHA_NUMERIC_UNDERSCORE(va_value)) EQ STRUPCASE(sds_name) + ;For nc3 files check if '.'s have been replaced by '_'s in the SDS_NAME. If so then it is OK + ;and quietly change SDS_NAME to the VAR_NAME value + IF (test1) AND (test2) THEN sds_name=va_value $ + ELSE BEGIN + ;Try and determine which is wrong - Dataset name or VAR_NAME - default is Dataset name + li=WHERE((STRUPCASE(sds_name) EQ STRUPCASE(vn)) AND (vn[0] NE ''),lcnt) + ;If lcnt NE 0 then Dataset name matches DATA_VARIABLES value so assume that VAR_NAME is wrong + IF lcnt NE 0 THEN usesds=1 ELSE usesds=0 + INFOTXT_OUTPUT_A,[3,5,usesds],[sds_name,va_value] + IF usesds EQ 0 THEN sds_name=va_value ELSE va_value=sds_name + ENDELSE + ENDIF + ;Determine actual list order from DATA_VARIABLE listing + li=WHERE(STRUPCASE(va_value[0]) EQ STRUPCASE(vn),lcnt) + ci[0]=va_value & vnv=va_value + END + STRUPCASE(va_name) EQ 'VAR_DATA_TYPE': BEGIN + ci[1]=va_value & idltype[n_lab-1]=STRUPCASE(STRTRIM(va_value,2)) + END + STRUPCASE(va_name) EQ 'VAR_UNITS': BEGIN + ;check that VAR_UNITS is a string value (in event that VAR_UNITS=1) + IF va_type NE 'STRING' THEN $ + INFOTXT_OUTPUT_A,[20],['VAR_UNITS',STRTRIM(sds_name,2)] + END + STRUPCASE(va_name) EQ 'VAR_DEPEND': BEGIN + TEST_DIM_ORDER,'VD',va_value,va_type,sds_dim,sds_name,rev_vd_vs + END + STRUPCASE(va_name) EQ 'VAR_SIZE': BEGIN + TEST_DIM_ORDER,'VS',va_value,va_type,sds_dim,sds_name,rev_vd_vs + END + STRUPCASE(va_name) EQ 'VAR_VALID_MIN': BEGIN + hdftype[1]=hdftypehold & idltype[1]=idltypehold + END + STRUPCASE(va_name) EQ 'VAR_VALID_MAX': BEGIN + hdftype[2]=hdftypehold & idltype[2]=idltypehold + END + STRUPCASE(va_name) EQ 'VAR_FILL_VALUE': BEGIN + hdftype[3]=hdftypehold & idltype[3]=idltypehold + END + ELSE: + ENDCASE + ENDFOR + ci[3]=STRTRIM(sds_natts,2) + IF ci[0] EQ '' THEN ci[0]=sds_name + ENDIF ELSE BEGIN ;No Attributes, so can only write dataset name to Metadata + ci=[STRUPCASE(sds_name),'','','1'] & vnv=sds_name + ;Can the SDS_NAME be matched with a DATA_VARIABLES value? + li=WHERE(STRUPCASE(sds_name) EQ STRUPCASE(vn),lcnt) + IF lcnt NE 0 THEN vnf=1 ELSE vnf=0 + INFOTXT_OUTPUT_A,[5],[sds_name,'NCDF_VARINQ'] + ENDELSE + FOR k=0,N_ELEMENTS(sds_dim)-1 DO BEGIN + IF k EQ 0 THEN ci[2]=STRTRIM(sds_dim[k],2) ELSE ci[2]=ci[2]+';'+STRTRIM(sds_dim[k],2) + ENDFOR + + ;Check for possible problems with determining array index + IF lcnt NE 0 THEN BEGIN + IF li[0] GE n_sds THEN dv_order=0 $ ;can occur when number of DATA_VARIABLE values is greater than number of datasets + ELSE IF catinfo[li[0],0] NE '' THEN dv_order=0 ;This array has already been written to + ENDIF ELSE IF (lcnt EQ 0) OR (vnf EQ 0) THEN dv_order=0 + ;Write out information text if sds_name cannot be matched and determine array index + IF dv_order EQ 0 THEN BEGIN + li=WHERE(catinfo[*,0] EQ '') ;determine lowest available array index to write info to + in0=[4,vnf,lcnt,n_vn] + IF (vnf EQ 0) OR ((lcnt EQ 0) AND (n_vn NE 0L)) THEN INFOTXT_OUTPUT_A,in0,[sds_name,vnv] + ENDIF + + oi[j]=li[0] ;to put datasets in the correct order + catinfo[li[0],*]=ci[*] + + ;Do data type checks on the Dataset, VAR_DATA_TYPE value and data types of VAR_VALID_MIN, VAR_VALID_MAX and VAR_FILL_VALUES + DATA_TYPE_CHECKS,ftype,dt_chk_labels,hdftype,idltype,sds_name,vnv + + ENDFOR + + rev_vd_vsh=rev_vd_vs ;keep original value in hold variable (required if rev_vd_vs eq 3) + IF (rev_vd_vs EQ 2) OR (rev_vd_vs EQ 3) THEN rev_vd_vs=1 ;change default so that VAR_DEPEND and VAR_SIZE + ;are reversed if not o/w changed in TEST_DIM_ORDER + + ;Dimension the structure to the size of the SDS datasets (with dimension n_sds) + sds=REPLICATE(sds_set, n_sds, max_atts) + ;Put datasets and attributes into sds structure + FOR j=0,n_sds-1 DO BEGIN + notranspose=1 ;0/1 will change to 0 if the dataset needs to be transposed + varstruct=NCDF_VARINQ(fileid,di[j]) + sds_name=varstruct.name + IF sds_name EQ '' THEN sds_name='N/A' + sds_natts=varstruct.natts + sds_datatype=varstruct.datatype + sds_ndim=varstruct.ndims + + ;Extract data from a netCDF dataset and get size information + NCDF_VARGET,fileid,di[j],sds_data + + IF sds_natts NE 0L THEN BEGIN + ;Read in Dataset Attributes + FOR k=0L,sds_natts-1L DO BEGIN + varattsname=NCDF_ATTNAME(fileid,di[j],k) + NCDF_ATTGET,fileid,di[j],varattsname,va_value + va_name=STRUPCASE(varattsname) + vavtype=SIZE(va_value,/TYPE) + vavhold=va_value + IF (vavtype EQ 7) OR (vavtype EQ 1) THEN va_type='STRING' ELSE va_type='NON-STRING' + + schk=WHERE(va_name EQ slabel,schkcnt) + test1=(sds_datatype EQ 'CHAR') AND (schkcnt EQ 0) ;string dataset but not one of the slabel values + test2=(sds_datatype NE 'CHAR') AND (va_type EQ 'STRING') ;not a string dataset but string attribute value + test3=(sds_datatype EQ 'CHAR') AND (schkcnt NE 0) AND (vavtype EQ 1) ;string dataset, slabel and attribute value is of type byte + IF (test1) OR (test2) THEN va_value=STRTRIM(va_value,2) $ + ELSE IF test3 THEN va_value=STRING(va_value) + + IF ((STRUPCASE(va_name) EQ 'VAR_DEPEND') OR (STRUPCASE(va_name) EQ 'VAR_SIZE')) AND $ + ((rev_vd_vs MOD 10 EQ 1) OR (rev_vd_vs EQ 20)) THEN BEGIN ;need to reverse the va_values + + IF va_type NE 'STRING' THEN BEGIN ;VAR_SIZE contains numeric values instead of in the form of a string + vavhold='' & n_vav=N_ELEMENTS(va_value) + FOR i=0,n_vav-1 DO BEGIN + IF i EQ n_vav-1 THEN vtxt='' ELSE vtxt=';' + vavhold=vavhold+STRTRIM(va_value[i],2)+vtxt + ENDFOR + ENDIF ELSE vavhold=va_value + + vs_v=STRCOMPRESS(STRSPLIT(vavhold,';',/EXTRACT,COUNT=rcnt),/REMOVE_ALL) + IF rcnt GT 1 THEN BEGIN ;multi-dimensions + IF (rev_vd_vs EQ 20) THEN $ ;AND (STRPOS(STRUPCASE(va_value),'DATETIME') NE -1) THEN $ + notranspose=0 ;need to transpose dataset as well + vs_v=REVERSE(vs_v) + FOR i=0,rcnt-1 DO $ + IF i EQ 0 THEN va_value=vs_v[i] ELSE va_value=va_value+';'+vs_v[i] + ENDIF + ENDIF + IF ((STRUPCASE(va_name) EQ 'VAR_VALID_MIN') OR (STRUPCASE(va_name) EQ 'VAR_VALID_MAX') OR $ + (STRUPCASE(va_name) EQ 'VAR_FILL_VALUE')) AND (sds_datatype EQ 'BYTE') AND $ + (vavtype EQ 1) THEN va_value=vavhold + sds[oi[j],k].va_l=PTR_NEW(va_name) + sds[oi[j],k].va_v=PTR_NEW(va_value) + ENDFOR + ;This section commented out for now - Version 4.0b16 + ;Check attributes again in case there are missing GEOMS Attributes that can be filled in + ;vnt='' + ;FOR k=0,sds_natts-1 DO BEGIN + ; varattsname=NCDF_ATTNAME(fileid,j,k) + ; NCDF_ATTGET,fileid,j,varattsname,va_value + ; va_name=STRUPCASE(varattsname) + ; va_type=SIZE(va_value,/TYPE) + + ; schk=WHERE(va_name EQ slabel,schkcnt) + ; IF ((sds_datatype EQ 'CHAR') AND (schkcnt EQ 0)) OR ((sds_datatype NE 'CHAR') AND $ + ; ((va_type EQ 1) OR (va_type EQ 7))) THEN va_value=STRTRIM(va_value,2) $ + ; ELSE IF (va_type EQ 1) OR (va_type EQ 7) THEN va_value=STRING(va_value) + + ; ;Check for VAR_NOTES entry + ; lcname=STRLOWCASE(varattsname) + ; ni=WHERE(lcname EQ ncdf_desc,ncnt) + ; IF ncnt EQ 0 THEN ni=WHERE(lcname EQ ncdf_std,ncnt) + ; IF (ncnt EQ 0) AND (lcname NE 'units') THEN BEGIN ;append label and value to VAR_NOTES + ; mi=WHERE(attr_arr_data EQ 'VAR_NOTES') + ; IF vnt NE '' THEN BEGIN + ; IF STRMID(vnt,STRLEN(vnt)-1,1) EQ '.' THEN itxt=' ' ELSE itxt='. ' + ; ENDIF ELSE itxt='' + ; vnt=vnt+itxt+STRTRIM(varattsname,2)+': '+STRTRIM(va_value,2) + ; ENDIF + ; mcnt=0 + ; ;Check for VAR_DESCRIPTION entry (long_name) + ; IF lcname EQ 'long_name' THEN mi=WHERE(attr_arr_data EQ 'VAR_DESCRIPTION',mcnt) + ; ;Check for VAR_UNITS entry + ; IF lcname EQ 'units' THEN mi=WHERE(attr_arr_data EQ 'VAR_UNITS',mcnt) + ; IF (mcnt NE 0) AND (std_fnd[mi[0]] EQ 0) THEN sds[oi[j],mi[0]].va_v=PTR_NEW(STRTRIM(va_value,2)) + ; ;Check for valid_range and _FillValue attributes + ; ni=WHERE(lcname EQ ncdf_std,ncnt) + ; IF ncnt NE 0 THEN BEGIN + ; vdt=SIZE(varattsval,/TYPE) + ; IF lcname EQ 'valid_range' THEN BEGIN + ; mi=WHERE(attr_arr_data EQ 'VAR_VALID_MIN') + ; IF std_fnd[mi[0]] EQ 0 THEN sds[oi[j],mi[0]].va_v=PTR_NEW(varattsval[0]) + ; mi=WHERE(attr_arr_data EQ 'VAR_VALID_MAX') + ; IF std_fnd[mi[0]] EQ 0 THEN sds[oi[j],mi[0]].va_v=PTR_NEW(varattsval[1]) + ; ENDIF ELSE BEGIN + ; mi=WHERE(attr_arr_data EQ 'VAR_FILL_VALUE') + ; IF std_fnd[mi[0]] EQ 0 THEN sds[oi[j],mi[0]].va_v=PTR_NEW(varattsval) + ; ENDELSE + ; ENDIF + ;ENDFOR + ;IF vnt NE '' THEN BEGIN + ; mi=WHERE(attr_arr_data EQ 'VAR_NOTES') + ; IF std_fnd[mi[0]] EQ 0 THEN sds[oi[j],mi[0]].va_v=PTR_NEW(vnt) + ;ENDIF + ; ;Write VAR_NAME to metadata + ;mi=WHERE(attr_arr_data EQ 'VAR_NAME') + ;IF std_fnd[mi[0]] EQ 0 THEN sds[oi[j],mi[0]].va_v=PTR_NEW(sds_name) + ;catinfo[oi[j],0]=sds_name + ;;Write VAR_DATA_TYPE to metadata + ;mi=WHERE(attr_arr_data EQ 'VAR_DATA_TYPE') + ;CASE 1 OF + ; sds_datatype EQ 'BYTE':catinfo[oi[j],1]='BYTE' + ; sds_datatype EQ 'CHAR':catinfo[oi[j],1]='STRING' + ; (sds_datatype EQ 'INT') OR (sds_datatype EQ 'SHORT'):catinfo[oi[j],1]='SHORT' + ; sds_datatype EQ 'LONG':catinfo[oi[j],1]='INTEGER' + ; sds_datatype EQ 'FLOAT':catinfo[oi[j],1]='REAL' + ; sds_datatype EQ 'DOUBLE':catinfo[oi[j],1]='DOUBLE' + ;ENDCASE + ;sds[oi[j],mi[0]].va_v=PTR_NEW(catinfo[oi[j],1]) + ;;Write VAR_DEPEND to metadata + ;mi=WHERE(attr_arr_data EQ 'VAR_DEPEND') + ;IF std_fnd[mi[0]] EQ 0 THEN BEGIN + ; vd='' + ; FOR k=0,sds_ndim-1 DO BEGIN + ; IF k EQ 0 THEN vd=vd+STRTRIM(mv_dim[sds_dim[k],0],2) $ + ; ELSE vd=vd+';'+STRTRIM(mv_dim[sds_dim[k],0],2) + ; ENDFOR + ; sds[oi[j],mi[0]].va_v=PTR_NEW(vd) + ;ENDIF + ;;Determine VAR_SIZE from dataset attributes + ;mi=WHERE(attr_arr_data EQ 'VAR_SIZE') + ;IF std_fnd[mi[0]] EQ 0 THEN BEGIN + ; sds_dim=SIZE(sds_data,/DIMENSIONS) + ; IF sds_dim[0] EQ 0 THEN sds_dim[0]=1 + ; vs='' + ; FOR k=0,N_ELEMENTS(sds_dim)-1 DO $ + ; IF k EQ 0 THEN vs=vs+STRTRIM(sds_dim[k],2) ELSE vs=vs+';'+STRTRIM(sds_dim[k],2) + ; sds[oi[j],mi[0]].va_v=PTR_NEW(vs) + ;ENDIF + + ENDIF ELSE BEGIN ;No Attributes, so can only write dataset name to structure + val='VAR_NAME' + sds[oi[j],0].va_l=PTR_NEW(val) + sds[oi[j],0].va_v=PTR_NEW(sds_name) + ENDELSE + + ;Test to see if dataset dimension ordering needs to be changed + IF notranspose EQ 0 THEN sds_data=TRANSPOSE(sds_data) + IF sds_datatype EQ 'CHAR' THEN sds_data=STRING(sds_data) + sds[oi[j],0].data=PTR_NEW(sds_data) + ENDFOR + + NCDF_CLOSE,fileid + ;Add DATA_VARIABLES to global attributes if not already present + gi=WHERE(STRMID(ga,0,14) EQ 'DATA_VARIABLES',gcnt) + IF gcnt EQ 0 THEN BEGIN + FOR j=0,n_sds-1 DO BEGIN + IF j EQ 0 THEN dv=catinfo[j,0] ELSE dv=dv+';'+catinfo[j,0] + ENDFOR + ga=[ga,'DATA_VARIABLES='+dv] + ENDIF + dv_order=-1 & n_vn=n_sds +ENDELSE + +;Array listing order may not be correct +IF (dv_order EQ 0) AND (n_vn NE 0L) THEN INFOTXT_OUTPUT_A,[6] + +;If necessary write comment regarding VAR_SIZE and VAR_DEPEND dimension ordering +rev_vd_vs=rev_vd_vsh ;used for when rev_vd_vs eq 3 +IF (rev_vd_vs MOD 10 EQ 0) OR (rev_vd_vs EQ 3) THEN INFOTXT_OUTPUT_A,[11,rev_vd_vs] + +END ;Procedure Read_HDF_SDS + + + +PRO output_hdf_data, ifile, ga, sds, catinfo +;Procedure to output the contents of an HDF file in ASCII form, either in a format compatible for +;conversion back to HDF, or in Row and Column format, or as a summary list +; ---------- +;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org +; +; History: +; 20050729: Original IDLCR8ASCII Routine - Version 1.0 +; 20050912: Removed Common variable definition CATALOGINFO and made the variable a parameter passed +; to the procedure; Make output filenames lower case; If data type is double and the +; accompanying VIS_FORMAT value starts with an 'F' (e.g. F9.4), then, change the 'F' to a +; 'D' when specifying the format for output - Version 1.1 +; 20061004: Common variable definition WIDGET_WIN_A added for Error calls; Display input/output +; information in a pop-up display window if the program is opened using IDL VM - Version 2.0 +; 20080302: Added code which sends the Catalog and log output to the IDLDE output window and/or an +; external file (as determined by the dux array values); Ensure all data values are +; separated by a space when the Dump output option is chosen - Version 3.0 +; 20101122: Adopt GEOMS metadata standard; Because FORMAT attribute(s) have been dropped can no longer +; use defined formatting values when outputing data to files; Default ASCII formatting +; adopted - scientific notation for REAL and DOUBLE, except for DATETIME and related values, +; which have formatting dependent on the data type - Version 4.0b1 +; 20150127: Ensure all string datasets are written and saved as left justified strings (previously +; defaulted to right justified) - Version 4.0b10 +; 20151109: Convert DATETIME (and related) max, min and fill values to LONG64 data type before +; determining the format for ASCII output to account for very large values - Version 4.0b12 +; 20161130: Add checks for Metadata values written in an invalid data type (e.g. structure) and for +; invalid attributes - Version 4.0b16 +; +; Inputs: ifile - a string containing the name of the HDF file to be read in. +; ga - a string array containing the global attribute labels and values extracted from the HDF +; file. +; sds - a structure using pointers, of size [n_sds,n_atts], containing the variable attribute +; labels and values (sds[n,m].va_l and sds[n,m].va_v) and the data (sds[n,0].data) +; extracted from the HDF file. +; catinfo - a string array of size [n_sds,4] (where n_sds is the number of datasets in the HDF +; file), containing information on the variable name, data type, data dimension, and +; number of attributes +; +; Outputs: Nil +; +; Called by: IDLCR8ASCII +; +; Subroutines Called: None + +COMMON WIDGET_WIN_A + +;determine number of datasets and attributes (from sds.va) +as=SIZE(catinfo) +n_sds=as[1] +indir=FILE_DIRNAME(ifile,/MARK_DIRECTORY) +infile=FILE_BASENAME(ifile) + +vdtype=[1,2,3,4,5,7,12,13,14,15] ;valid IDL data types +badlabel=[''] ;list of attribute labels where the values are not valid (to avoid repeated log messages) + +IF o3[1] EQ 'C' THEN BEGIN + IF o3[3] EQ '' THEN BEGIN + lineno=lineno+n_sds+1 + wintxt='Listing of Var_Name Index; Var_Name; Var_Data_Type; Var_Size' + WIDGET_CONTROL,wtxt,set_value=wintxt,/Append,Set_text_top_line=lineno + ENDIF + FOR i=dux[0],dux[1],dux[2] DO BEGIN + IF i EQ -1 THEN PRINT,'Listing of Var_Name Index; Var_Name; Var_Data_Type; Var_Size' $ + ELSE PRINTF,i,'Listing of Var_Name Index; Var_Name; Var_Data_Type; Var_Size' + ENDFOR + FOR j=0,n_sds-1 DO BEGIN + IF o3[3] EQ '' THEN BEGIN + wintxt=STRTRIM(j,2)+'; '+catinfo[j,0]+'; '+catinfo[j,1]+'; '+catinfo[j,2] + WIDGET_CONTROL,wtxt,set_value=wintxt,/Append + ENDIF + FOR i=dux[0],dux[1],dux[2] DO $ + IF i EQ -1 THEN PRINT,STRTRIM(j,2),'; ',catinfo[j,0],'; ',catinfo[j,1],'; ',catinfo[j,2] $ + ELSE PRINTF,i,STRTRIM(j,2),'; ',catinfo[j,0],'; ',catinfo[j,1],'; ',catinfo[j,2] + ENDFOR +ENDIF +IF o3[0] NE '0' THEN BEGIN + ;create output filenames + outf1=STRMID(infile,0,STRPOS(infile,'.',/Reverse_Search))+'.meta' + outf2=STRMID(infile,0,STRPOS(infile,'.',/Reverse_Search))+'.data' + OPENW,lu1,indir+outf1,/GET_LUN + OPENW,lu2,indir+outf2,/GET_LUN + + ;HDF4 and NETCDF pre-defined attributes + ncsa=['long_name','units','format','coordsys','valid_range','_FillValue','scale_factor', $ + 'scale_factor_err','add_offset','add_offset_err','calibrated_nt'] + + PRINTF,lu1,'! Output from IDLcr8ASCII application v4.0' + PRINTF,lu1,'! '+ifile & PRINTF,lu1,'!' + PRINTF,lu1,'! Global Attributes' + FOR i=0,N_ELEMENTS(ga)-1 DO PRINTF,lu1,ga[i] + + FOR j=0,n_sds-1 DO BEGIN + n_atts=FIX(catinfo[j,3]) + IF n_atts NE 0 THEN BEGIN + PRINTF,lu1,'!' & PRINTF,lu1,'! Variable Attributes' + PRINTF,lu2,catinfo[j,0] + FOR i=0,n_atts DO BEGIN ;number of attributes. Last loop is for Data + IF i EQ n_atts THEN vavt=SIZE(*sds[j,0].data,/TYPE) $ + ELSE vavt=SIZE(*sds[j,i].va_v,/TYPE) + gti=WHERE(vavt EQ vdtype,g_vavt) ;test to see if the data type is valid + IF g_vavt EQ 1 THEN BEGIN ;it is OK to transfer the attribute value to a variable + IF i EQ n_atts THEN vav=*sds[j,0].data ELSE vav=*sds[j,i].va_v + ENDIF + IF (STRPOS(STRUPCASE(catinfo[j,0]),'DATETIME') NE -1) AND ((vavt EQ 4) OR (vavt EQ 5)) THEN BEGIN + ns=MAX(STRLEN(STRTRIM(LONG64(vav),2))) + IF vavt EQ 4 THEN dp=6 ELSE dp=9 ;indicates the number of decimal places to use in the output + ;If the number is very large then output as an exponent + IF ns LT 8 THEN fmt='(D'+STRTRIM(ns+dp+1,2)+'.'+STRTRIM(dp,2)+')' ELSE fmt='(E0)' + ENDIF ELSE IF (vavt EQ 4) OR (vavt EQ 5) THEN fmt='(E0)' $ + ELSE IF vavt EQ 7 THEN fmt='(A-)' $ + ELSE fmt='(I0)' + IF i NE n_atts THEN BEGIN ;write out metadata in form LABEL=VALUE + nvav=N_ELEMENTS(vav) + vat=*sds[j,i].va_l + ni=WHERE(STRLOWCASE(vat) EQ STRLOWCASE(ncsa),ncnt) ;check for pre-defined attributes and exclude + IF (ncnt EQ 0) AND (g_vavt EQ 1) THEN BEGIN + IF nvav EQ 1 THEN vat=vat+'='+STRING(FORMAT=fmt,vav[0]) $ ;'(A-'+STRTRIM(STRLEN(vav[0]),2)+')',vav[0]) $ + ELSE BEGIN + FOR k=0,N_ELEMENTS(vav)-1 DO BEGIN + IF k EQ 0 THEN vat=vat+'='+STRTRIM(STRING(FORMAT=fmt,vav[k]),2) $ + ELSE vat=vat+';'+STRTRIM(STRING(FORMAT=fmt,vav[k]),2) + ENDFOR + ENDELSE + PRINTF,lu1,vat + ENDIF ELSE IF g_vavt NE 1 THEN BEGIN + bli=WHERE(vat EQ badlabel,blcnt) + IF blcnt EQ 0 THEN BEGIN + badlabel=[badlabel,vat] + INFOTXT_OUTPUT_A,[18],[vat] + ENDIF + ENDIF + ENDIF + ENDFOR + + ;Write out data + IF o3[0] EQ 'F' THEN BEGIN + PRINTF,lu2,format=fmt,vav + ENDIF ELSE IF o3[0] EQ 'D' THEN BEGIN + ;Ensure there is a single character between all values + sdssize=SIZE(vav,/Dimension) + sdshold=STRTRIM(STRING(FORMAT=fmt,vav),2) + maxstr=MAX(STRLEN(sdshold)) + ;use default IDL formatting rules + sdshold=STRING(format='(A-'+STRTRIM(maxstr,2)+')',sdshold) + ;Uncomment line below if format described by VIS_FORMAT is required + ;sdshold=STRING(format=p_f,*sds[j].data) + IF sdssize[0] NE 0 THEN sdshold=REFORM(sdshold,sdssize) + PRINTF,lu2,sdshold + ENDIF + ENDIF ELSE BEGIN ;Variable attributes do not have an associated dataset + INFOTXT_OUTPUT_A,[19],[catinfo[j,0]] + ENDELSE + ENDFOR + PRINTF,lu1,'!' & PRINTF,lu1,'! End of output file created by IDLcr8ASCII' + FREE_LUN,lu1 & FREE_LUN,lu2 + IF o3[3] EQ '' THEN BEGIN + lineno=lineno+2L + WIDGET_CONTROL,wtxt,set_value=outf1+' created!',/Append,Set_text_top_line=lineno + WIDGET_CONTROL,wtxt,set_value=outf2+' created!',/Append,Set_text_top_line=lineno + ENDIF + FOR i=dux[0],dux[1],dux[2] DO BEGIN + PRINTF,i,' '+outf1+' created!' & PRINTF,i,' '+outf2+' created!' + ENDFOR +ENDIF +END ;Procedure Output_HDF_Data + + + +PRO idlcr8ascii, ifile, ga, sds, reterr, $ + FORMAT=o1, DUMP=o2, CATALOG=o4, POPUP=o5, LOG=o6, H4=o7, H5=o8, NC=o9 +;Main IDL procedure to convert GEOMS compliant netCDF, HDF4 and HDF5 files to ASCII, Session Memory +;or another recognized Data format. +; +;Program documentation, idlcr8ascii-v4.0_Readme.pdf, available on http://avdc.gsfc.nasa.gov. +; +;Program sub-version 4.0b27 (20240927) +; ---------- +;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org +; +; History: +; 20050729: Original Release - Version 1.0 +; 20050912: Removed Common variable definition CATALOGINFO; If the format of the +; DATA_START_DATE and FILE_GENERATION_DATE values is MJD2000, then change to ISO8601, +; unless the output option is /Dump - Version 1.1 +; 20061004: Common variable definition WIDGET_WIN_A added for Error calls; Make the code suitable +; for running on a licensed version of IDL (using the .pro and .sav versions of the code) +; and on IDL Virtual Machine (using the .sav version of the code); Change the command +; line keyword options. Remove the /Help option (window now opened if there are no command +; line parameters), and /Catalog can now be called together with /AVDC or /Dump; The +; program can now handle multiple HDF files as input (either as a string array or as a +; file spec); Display input/output information as well as errors and warnings in a pop-up +; display window if the program is called using IDL VM; Help window becomes an +; Introduction window, and the user has the option of continuing to run the program with +; file inputs; Include option to read HDF5 versions of the ground-based files +; - Version 2.0 +; 20080302: Remove HELP,/TRACEBACK call, which identified how the program was called. This was used +; to stop output to the IDLDE output window in the event that IDL VM was used, but it is +; not required as IDL VM ignores these print calls; Add options to send Catalog and logged +; input/output information to a Pop-up Box (/Popup) and/or to an external file (/Log). Add +; DIALOG_BOX to show completion of the program if program inputs are via the INTRO box, and +; logging window option isn't requested - Version 3.0 +; 20091208: Add INFORMATION text message to INFOTXT_OUTPUT_A procedure; Replace /AVDC keyword with +; /FORMAT (note /AVDC also retained for backward compatibility purposes); Improve checks +; on parameter and keyword inputs - Version 3.03 +; 20101122: Changes made to account for new GEOMS conventions, including changing the format of the +; structure so that numeric metadata values can be saved in their actual datatype, rather +; than string; Add return error code option so that program returns to the calling program +; if an error is generated - Version 4.0b1 +; 20120328: Add options to convert HDF/netCDF files to other HDF/netCDF formats e.g. H4 to +; H5 etc. Requires IDLcr8HDF in the search path to do this - Version 4.0b5 +; 20130114: Add check for IDL being run in DEMO mode by using LMGR command. Replace PRINTF,-1 +; statements (i.e. when dux[0] eq -1) with PRINT (o/w causes DEMO mode to stop). Also disable +; /LOG, /FORMAT and /DUMP keywords and stop ASCII file output in DEMO mode - Version 4.0b7 +; 20150127: Add extra condition for the file extension when checking whether an input file is netCDF +; due to the program crashing when incorrectly identifying an HDF5 file as netCDF when +; using IDL8.3 (OK for IDL6.4) - Version 4.0b10 +; 20160213: Add CATCH, /CANCEL after idlcr8hdf call - Version 4.0b13 +; 20190806: Call routine FILE_FORMAT_A to determine the correct format of the input file(s) +; - Version 4.0b22 +; 20200930: Check that GEOMS file does not have filesize of zero before calling FILE_FORMAT_A +; - Version 4.0b24 +; +; Inputs: ifile - a string array or filespec containing the name of the HDF file(s) to be read in +; +; OPTIONS +; FORMAT - Program generates two output files per input file (with .meta and .data +; extensions). The resulting files are compatible with programs that can write HDF +; files using these two files as input. +; DUMP - Program generates two output files per input file (with .meta and .data extensions). +; Data values will be shown as indicated by the array format defined by VAR_SIZE. +; CATALOG - Program sends the variable index, variable name, format, and dimension(s) +; information to a pop-up window or the IDL DE output log window. +; POPUP - to append input/output information as well as warnings and errors to a pop-up +; display window +; LOG - to append input/output/catalog information as well as errors to a log file named +; idlcr8ascii.log +; H4 - outputs contents of the input file as an HDF4 file +; H5 - outputs contents of the input file as an HDF4 file +; NC - outputs contents of the input file as a netCDF3 file +; +; Outputs: ga - If included as a parameter in the command line, a string array containing the global +; attribute labels and values extracted from a single HDF file. +; sds - If included as a parameter in the command line, a structure using pointers, of size +; [n_sds,n_atts], containing the variable attribute labels and values (sds[n,m].va_l +; and sds[n.m].va_v) and the data (sds[n,0].data) extracted from a single HDF file. +; The data is saved in the form defined by the variable attributes +; reterr - optional string input that, if included, returns an error message to the calling +; program rather than stop the program. Note that the Pop-up option will be +; deselected if present together with this argument. +; Two ASCII files per input file containing the attributes and datasets extracted from the HDF +; file(s), if the FORMAT or DUMP options are chosen. Summary metadata information if the +; CATALOG option is chosen. Other Hierachical data format files if H4, H5, or netCDF are chosen. +; +; Called by: Main Routine +; +; Subroutines Called: INTRO_A (if program called without command line parameters) +; FILE_FORMAT_A (to determine the format of the file(s)) +; READ_HDF_SDS +; JDF_2_DATETIME +; OUTPUT_HDF_DATA (if output is not just to session memory only) +; STOP_WITH_ERROR_A (if error state detected) +; INFOTXT_OUTPUT_A (if program can make a change) +; Possible Conditions for STOP_WITH_ERROR call (plus [line number] where called): +; 1. No valid HDF4, HDF5 or netCDF file selected. +; +; Information Conditions (when the program is able to make changes): +; 1. If Session Memory option is chosen can only read in one HDF file. +; 2. /POPUP keyword cannot be used together with the 'reterr' argument. +; 3. Argument 'reterr' must be a scalar variable of type string. +; 4. /LOG, /FORMAT, and /DUMP keywords cannot be used in IDL DEMO mode +; 5. NetCDF file create feature is disabled in IDL DEMO mode + +COMMON WIDGET_WIN_A + +;Possible error messages for this procedure +proname='IDLCR8ASCII procedure: ' +IF FLOAT(!Version.Release) GE 5.6 THEN errtxt='No valid HDF or netCDF file selected.' $ +ELSE BEGIN + errtxt='No valid HDF or netCDF file selected ' + errtxt=errtxt+'(Note: Input file can only be HDF4 or netCDF3 for IDL'+!Version.Release+').' +ENDELSE +lu=-1 + +ON_IOERROR,TypeConversionError ;used when checking Datetime format + +demomode=LMGR(/DEMO) ;Boolean to check for IDL being run in demo mode (disable /LOG option) +IF N_PARAMS() EQ 1 THEN BEGIN ;File input only + ;check to see what output keyword options are set - if none then intype=-3 (i.e. invalid) + IF (~ KEYWORD_SET(o1)) AND (~ KEYWORD_SET(o2)) AND (~ KEYWORD_SET(o4)) AND $ + (~ KEYWORD_SET(o7)) AND (~ KEYWORD_SET(o8)) AND (~ KEYWORD_SET(o9)) THEN $ + intype=-3 ELSE intype=SIZE(ifile,/Type) +ENDIF ELSE IF N_PARAMS() GE 1 THEN intype=SIZE(ifile,/Type) $ ;Check that infile is a string +ELSE IF (KEYWORD_SET(o1)) OR (KEYWORD_SET(o2)) OR (KEYWORD_SET(o4)) OR $ + (KEYWORD_SET(o7)) OR (KEYWORD_SET(o8)) OR (KEYWORD_SET(o9)) THEN $ + intype=-1 $ ;need to open dialog box for input file +ELSE IF (KEYWORD_SET(o5)) OR (KEYWORD_SET(o6)) THEN intype=-3 $ ;cannot have these keyword options only +ELSE intype=-2 ;no parameter or keyword inputs in idlcr8ascii call +IF (intype NE 7) AND (intype GE 0) THEN intype=-3 ;first command line parameter not a string type +dux=[-1,-1,1] ;initialize flags for output log to IDLDE Output Window and/or to file +;dux[0] default allows output to the IDLDE output window (ignored by IDL VM) +;dux[1] is the program assigned file unit for sending log output to file +;dux[2] is the step between dux[0] and dux[1] +rerr='NA' ;initialize return error string + +o3=['-1','0','0','0','0','0','0','0'] +IF intype LT -1 THEN BEGIN ;either no or invalid input parameters and/or keywords + INTRO_A,intype ;Open Intro Box and determine output options (FORMAT/DUMP, Catalog, etc) + IF o3[0] EQ '-1' THEN BEGIN + STOP_WITH_ERROR_A,'','',lu & RETURN + ENDIF + IF o3[3] EQ 'P' THEN o3[3]='' ELSE o3[3]='D_' +ENDIF ELSE BEGIN ;Set options (FORMAT/DUMP/CONVERT, Catalog, Session Memory, Popup Window, Log Output) + IF (KEYWORD_SET(o1)) THEN o3=['F','0','0','D_','0','0','0','0'] $ + ELSE IF KEYWORD_SET(o2) THEN o3=['D','0','0','D_','0','0','0','0'] $ + ELSE o3=['0','0','0','D_','0','0','0','0'] + IF N_PARAMS() GE 3 THEN o3[2]='M' + IF KEYWORD_SET(o4) THEN o3[1]='C' + IF KEYWORD_SET(o5) THEN o3[3]='' + IF KEYWORD_SET(o6) THEN o3[4]='idlcr8ascii.log' + IF KEYWORD_SET(o7) THEN o3[5]='H4' + IF KEYWORD_SET(o8) THEN o3[6]='H5' + IF KEYWORD_SET(o9) THEN o3[7]='N3' +ENDELSE + +;Check for reterr parameter +infoval=INTARR(4) +IF (N_PARAMS() EQ 2) OR (N_PARAMS() GE 4) THEN BEGIN + ;If reterr included allows error output to return to a calling program + IF N_PARAMS() EQ 2 THEN reterr=ga ;session memory option not wanted + ;IF (ARG_PRESENT(reterr)) AND (SIZE(reterr,/Type) EQ 7) THEN BEGIN + IF SIZE(reterr,/Type) EQ 7 THEN BEGIN + rerr='' + IF o3[3] EQ '' THEN BEGIN + ;Deselect POPUP option and write INFORMATION message + o3[3]='D_' & infoval[0]=14 + ENDIF + ENDIF ELSE infoval[1]=15 + ;reterr input included but is of the incorrect type, or parameter cannot be returned to the calling program +ENDIF + +;Do check for /LOG, /FORMAT, /DUMP, and/or /NC keywords in IDL DEMO Mode and disable if necessary +IF (demomode) AND ((o3[4] EQ 'idlcr8ascii.log') OR (o3[0] NE '0') OR (o3[7] EQ 'N3')) THEN BEGIN + IF (o3[4] EQ 'idlcr8ascii.log') OR (o3[0] NE '0') THEN infoval[2]=16 + IF o3[7] EQ 'N3' THEN infoval[3]=17 + o3[0]='0' & o3[4]='0' & o3[7]='0' +ENDIF + +IF N_PARAMS() GE 1 THEN BEGIN + ;Check that input HDF/netCDF files exist. If not then prompt the user for the missing files + arorfs=SIZE(ifile,/Dimensions) ;Is file input Filespec or String Array? + IF (arorfs[0] EQ 0) THEN BEGIN ;input is filespec + IF ifile NE '' THEN ifile=FILE_SEARCH(ifile) + isize=SIZE(ifile,/Dimensions) + IF isize[0] EQ 0 THEN ifile=[''] ;no files found matching filespec + ENDIF + IF N_ELEMENTS(arorfs) GT 1 THEN ifile=REFORM(ifile,N_ELEMENTS(ifile),/Overwrite) ;Convert to 1-D array + file_exist=FILE_TEST(ifile,/Read) + gi=WHERE(file_exist EQ 1,gcnt) + IF gcnt NE 0 THEN ifile=ifile[gi] ;contains all valid filenames +ENDIF ELSE ifile=[''] +gi=WHERE(ifile NE '',nfile) +IF nfile EQ 0L THEN BEGIN + IF o3[2] EQ 'M' THEN $ ;pick one file only + ifile=DIALOG_PICKFILE(Filter=['*.hdf','*.h5','*.nc;*nc4'],/Must_Exist, $ + Title='Select HDF4, HDF5, or netCDF GEOMS Compatible Format File') $ + ELSE $ ;multiple selection permissable + ifile=DIALOG_PICKFILE(Filter=['*.hdf','*.h5','*.nc;*.nc4'],/Must_Exist, $ + /Multiple_Files,Title='Select HDF4, HDF5, or netCDF GEOMS Compatible Format File(s)') +ENDIF +;now check to see that files were actually selected, if not STOP! +gi=WHERE(ifile NE '',nfile) +IF nfile EQ 0L THEN BEGIN + STOP_WITH_ERROR_A,'D_'+proname,errtxt,lu + IF STRLEN(rerr) GT 2 THEN BEGIN + IF N_PARAMS() EQ 2 THEN ga=rerr ELSE reterr=rerr + ENDIF + RETURN +ENDIF ELSE BEGIN + ifile=ifile[gi] & dsort=SORT(ifile) & ifile=ifile[dsort] + ifile=FILE_SEARCH(ifile,/FULLY_QUALIFY_PATH) +ENDELSE + +;Set dux values according to output options +IF o3[4] NE '0' THEN BEGIN ;open idlcr8ascii.log + o3[4]=FILE_DIRNAME(ifile[0],/Mark_Directory)+o3[4] + res=FILE_TEST(o3[4],/Write) + IF res EQ 0 THEN openw,du,o3[4],/GET_LUN $ + ELSE BEGIN + OPENW,du,o3[4],/Append,/GET_LUN & FOR i=0,1 DO PRINTF,du,'' + ENDELSE + dux[1]=du & dux[2]=dux[1]-dux[0] +ENDIF +FOR i=dux[0],dux[1],dux[2] DO $ + IF i EQ -1 THEN PRINT,'HDF/netCDF File Input/Output Log - Program Started on '+SYSTIME(0) $ + ELSE PRINTF,i,'HDF/netCDF File Input/Output Log - Program Started on '+SYSTIME(0) + +IF o3[3] EQ '' THEN BEGIN + ;Set-up output window display widget + base=WIDGET_BASE(Title='HDF/netCDF File Input/Output Log',Units=2,yoffset=3,xoffset=3,Tlb_Frame_Attr=1,/Column) ;,Tab_Mode=1) + wtxt=WIDGET_TEXT(base,/Scroll,xsize=100,ysize=12) + base2=WIDGET_BASE(base) + b3=WIDGET_BUTTON(base2,value='Finish',uvalue='DONE',frame=3,Sensitive=0) ;,Accelerator='Return') + WIDGET_CONTROL,wtxt,/Realize + WIDGET_CONTROL,base,/Realize + lineno=0L +ENDIF + +IF (o3[2] EQ 'M') AND (N_ELEMENTS(ifile) GT 1) THEN BEGIN + ifile=[ifile[0]] & nfile=1L + INFOTXT_OUTPUT_A,[7] +ENDIF + +FOR i=0,N_ELEMENTS(infoval)-1 DO IF infoval[i] NE 0 THEN INFOTXT_OUTPUT_A,[infoval[i]] + +FOR ndf=0L,nfile-1L DO BEGIN + catinfo=STRARR(1,4) ;initialize catinfo array + ifile[ndf]=FILE_SEARCH(ifile[ndf],/Fully_Qualify_Path) + IF o3[3] EQ '' THEN BEGIN + lineno=lineno+2L + IF ndf NE 0 THEN WIDGET_CONTROL,wtxt,set_value='',/Append + WIDGET_CONTROL,wtxt,set_value='Reading '+ifile[ndf],/Append,Set_text_top_line=lineno + ENDIF + FOR i=dux[0],dux[1],dux[2] DO BEGIN + IF i EQ -1 THEN BEGIN + IF ndf NE 0 THEN PRINT,'' + PRINT,'Reading '+ifile[ndf] & PRINT,'' + ENDIF ELSE BEGIN + IF ndf NE 0 THEN PRINTF,i,'' + PRINTF,i,'Reading '+ifile[ndf] & PRINTF,i,'' + ENDELSE + ENDFOR + + ftype='XX' + IF (FILE_TEST(ifile[ndf])) AND (~FILE_TEST(ifile[ndf],/ZERO_LENGTH)) THEN BEGIN + ;Identify the file format + ftype=FILE_FORMAT_A(ifile[ndf]) + IF ftype NE 'XX' THEN catinfo[0,0]=ftype + ENDIF + IF ftype EQ 'XX' THEN BEGIN + ;Either file doesn't exist or is not a valid file format + STOP_WITH_ERROR_A,o3[3]+proname,errtxt,lu + IF STRLEN(rerr) GT 2 THEN BEGIN + IF N_PARAMS() EQ 2 THEN ga=rerr ELSE reterr=rerr + ENDIF + RETURN + ENDIF + + ;Open and read the file + READ_HDF_SDS,ifile[ndf],ga,sds,catinfo + IF STRLEN(rerr) GT 2 THEN BEGIN + reterr=rerr & RETURN + ENDIF + + IF catinfo[0,0] EQ 'HE5' THEN BEGIN + STOP_WITH_ERROR_A,o3[3]+proname,errtxt,lu + IF STRLEN(rerr) GT 2 THEN BEGIN + IF N_PARAMS() EQ 2 THEN ga=rerr ELSE reterr=rerr + ENDIF + RETURN + ENDIF ELSE BEGIN ;Do checks and output standard format file + ;Convert format of DATA_START/STOP_DATE and FILE_GENERATION_DATE if required + ;Note: for /Dump option output will still be original format + isodates=['DATA_START_DATE','DATA_STOP_DATE','FILE_GENERATION_DATE'] + niso=N_ELEMENTS(isodates) + gaiso=STRARR(niso) & gih=INTARR(niso) + FOR i=0,niso-1 DO BEGIN + gi=WHERE(STRPOS(STRUPCASE(ga),isodates[i]) NE -1,gcnt) + IF gcnt EQ 1 THEN BEGIN + res=STRSPLIT(ga[gi[0]],' =',/EXTRACT) + IF N_ELEMENTS(res) EQ 2 THEN BEGIN + IF STRPOS(STRUPCASE(res[1]),'Z') EQ -1 THEN BEGIN ;i.e. not ISO8601 + valid=0 ;used for Type Conversion Check + mjd=DOUBLE(res[1]) ;will jump to TypeConversionError if Res[1] is not a number + valid=1 ;got to here so valid conversion (presumably it is MJD2000) + TypeConversionError: + IF valid EQ 1 THEN BEGIN + iso='' & iso=JDF_2_DATETIME(mjd,/MJD2000,/S) + gaiso[i]=res[0]+'='+iso + IF o3[0] EQ 'F' THEN ga[gi[0]]=gaiso[i] + gih[i]=gi[0] + ENDIF + ENDIF + ENDIF + ENDIF + ENDFOR + + ;Create ASCII Output + IF (o3[0] NE '0') OR (o3[1] NE '0') THEN OUTPUT_HDF_DATA,ifile[ndf],ga,sds,catinfo + IF (o3[5] NE '0') OR (o3[6] NE '0') OR (o3[7] NE '0') THEN BEGIN + ;Check for existence of TAV file in the input file directory or working directory + indir=FILE_DIRNAME(ifile[ndf],/MARK_DIRECTORY) + tav=FILE_SEARCH(indir+'tableattrvalue*.dat',/FULLY_QUALIFY_PATH) + IF tav[0] EQ '' THEN INFOTXT_OUTPUT_A,[12] $ ;No valid TAV file so format conversion cannot be done + ELSE BEGIN + ;Convert data to a different format + validpath=1 & ftest=5 & dferr='' + IF demomode THEN ftestmax=6 ELSE ftestmax=7 + WHILE (validpath EQ 1) AND (ftest LE ftestmax) DO BEGIN + IF o3[ftest] NE '0' THEN BEGIN + CATCH, path_error ;Return program generated error if IDLcr8HDF is not in the search path + ;Error Handler + IF path_error NE 0 THEN BEGIN ;IDLcr8HDF is not in the IDL Search Path + validpath=0 + INFOTXT_OUTPUT_A,[13] + ;PRINT, 'Error index: ', path_error + ;PRINT, 'Error message: ', !ERROR_STATE.MSG + CATCH, /CANCEL + ENDIF + IF validpath EQ 1 THEN BEGIN + ;Try to call IDLcr8HDF + IF demomode THEN BEGIN + CASE 1 OF + ftest EQ 5: IDLCR8HDF,ga,sds,tav[0],indir,dferr + ftest EQ 6: IDLCR8HDF,ga,sds,tav[0],indir,dferr,/H5 + ENDCASE + ENDIF ELSE BEGIN + CASE 1 OF + ftest EQ 5: IDLCR8HDF,ga,sds,tav[0],indir,dferr,/L + ftest EQ 6: IDLCR8HDF,ga,sds,tav[0],indir,dferr,/H5,/L + ftest EQ 7: IDLCR8HDF,ga,sds,tav[0],indir,dferr,/NC,/L + ENDCASE + ENDELSE + CATCH, /CANCEL + IF o3[4] NE '0' THEN BEGIN + ;transfer contents of idlcr8hdf.log to idlcr8ascii.log + OPENR,fu,indir+'idlcr8hdf.log',/DELETE,/GET_LUN + dum='' + READF,fu,dum & READF,fu,dum + WHILE NOT EOF(fu) DO BEGIN + READF,fu,dum & PRINTF,du,dum + ENDWHILE + FREE_LUN,fu + ENDIF ELSE FILE_DELETE,indir+'idlcr8hdf.log',/QUIET + ENDIF + ENDIF + ftest++ + ENDWHILE + ENDELSE + ENDIF + FOR i=0,niso-1 DO IF gaiso[i] NE '' THEN ga[gih[i]]=gaiso[i] + IF o3[2] EQ 'M' THEN BEGIN + IF o3[3] EQ '' THEN BEGIN + lineno=lineno+1L + WIDGET_CONTROL,wtxt,set_value=ifile+' array and heap structure created!',/Append,Set_text_top_line=lineno + ENDIF + FOR i=dux[0],dux[1],dux[2] DO $ + IF i EQ -1 THEN PRINT,' '+ifile[ndf]+' array and heap structure created!' $ + ELSE PRINTF,i,' '+ifile[ndf]+' array and heap structure created!' + ENDIF + ENDELSE +ENDFOR + +IF rerr EQ '' THEN $ + IF nfile EQ 1 THEN reterr=ifile[0]+' successfully read' $ + ELSE reterr='Files successfully read' +IF N_PARAMS() EQ 2 THEN ga=reterr + +FOR i=dux[0],dux[1],dux[2] DO BEGIN + IF i EQ -1 THEN BEGIN + PRINT,'' & PRINT,'File read completed - Program Ended on '+SYSTIME(0) + ENDIF ELSE BEGIN + PRINTF,i,'' & PRINTF,i,'File read completed - Program Ended on '+SYSTIME(0) + ENDELSE +ENDFOR +IF dux[1] GT -1 THEN FREE_LUN,dux[1] +IF o3[3] EQ '' THEN BEGIN + WIDGET_CONTROL,b3,Sensitive=1,/Input_Focus + WIDGET_CONTROL,wtxt,set_value='',/Append,Set_text_top_line=lineno+2L + WIDGET_CONTROL,wtxt,set_value='HDF file read completed - hit <Finish> to close program',/Append + XMANAGER,'idlcr8ascii',base +ENDIF ELSE IF intype LT 0 THEN BEGIN ;Create Finish Dialog Box + res=DIALOG_MESSAGE('HDF file read completed!',/Information,Title='EVDC/AVDC IDLcr8ASCII') +ENDIF + +END ;procedure IDLcr8ASCII