diff --git a/idlcr8hdf.pro b/idlcr8hdf.pro index 01c018444f2085acb03384f4bedf0d500a03b711..f596ef3d4977b4dff63c5d84e7ffec6d60a5fe50 100644 --- a/idlcr8hdf.pro +++ b/idlcr8hdf.pro @@ -1,6790 +1,6794 @@ -;Main Program Version: idlcr8hdf.pro v4.0b64, 20240912 -; Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org -; -;Sub-versions (refer to idlcr8hdf-v4.0_Readme.pdf for full history) - -PRO idlcr8hdf_common -;Procedure to define the COMMON blocks for this program -; ---------- -;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org -; -;History: -; 20050909: Introduced to IDLCR8HDF - Version 1.1 -; 20061012: Variable 'ncsa' added to METADATA; 'iarr', 'larr', 'rarr', 'darr' holding Arrays -; removed from DATA and replaced with Structure 'ds'; Variable 'vfv' added to DATA; -; WIDGET_WIN added for common variables associated with the Graphical User Interface -; - Version 2.0 -; 20080302: tab_type integer added to TABLEDATA - Version 3.0 -; 20100205: rerr string added to WIDGET_WIN - Version 3.09 -; 20110401: vnchange added to DATA; mv_lng and mv_dbl added to METADATA; vserror added to DATA; -; vfv removed from DATA; ncsa removed from METADATA - Version 4.0b1 -; 20150127: mv_str added to METADATA to hold maximum string length of string dataset entries - -; Version 4.0b25 -; 20151012: Rename free_attr to attr_free and add qa_yes - Version 4.0b31 -; -;Input: Nil -; -;Output: Nil -; -;Called by: N/A -; -;Subroutines Called: None - -COMMON TABLEDATA, tab_arr,tab_ver,tab_type -COMMON METADATA, meta_arr,attr_arr_glob,attr_arr_data,attr_free,mv_lng,mv_dbl,mv_str -COMMON DATA, ds,ndm,nvn,vn,vu,vnchange,vserror,qa_yes -COMMON WIDGET_WIN, wtxt,b1,lineno,base,o3,dux,rerr,lvals - -END ;Procedure idlcr8hdf_common - - - -PRO intro_event, ev -;Procedure to define how Events from the Start-up Introduction Window are handled -; ---------- -;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org -; -;History: -; 20061012: Introduced to IDLCR8HDF - Version 2.0 -; -;Input: ev - Selected widget event structure -; -;Output: o3 - Common string array defining the various options for program output -; -;Called by: XMANAGER in INTRO -; -;Subroutines Called: None - -COMMON WIDGET_WIN - -;The uservalue is retrieved from a widget when an event occurs -WIDGET_CONTROL,ev.id,GET_UVALUE=uv -;Assign/Remove AVK button event to/from a variable name -IF uv EQ 'AVK' THEN IF o3[1] EQ uv THEN o3[1]='0' ELSE o3[1]=uv $ -;Assign/Remove Log Output button event to/from a variable name -ELSE IF uv EQ 'idlcr8hdf.log' THEN IF o3[2] EQ uv THEN o3[2]='0' ELSE o3[2]=uv $ -;Assign/Remove Pop-up window for Log Output button event to/from a variable name -ELSE IF uv EQ 'Pop' THEN IF o3[3] EQ uv THEN o3[3]='0' ELSE o3[3]=uv $ -;Assign button event to a variable name -ELSE IF (uv EQ 'H4') OR (uv EQ 'H5') OR (uv EQ 'NC') THEN BEGIN - o3[0]=uv - IF uv EQ 'H5' THEN BEGIN - WIDGET_CONTROL,ev.id+1,Sensitive=1 - WIDGET_CONTROL,ev.id+2,Sensitive=1,Set_Combobox_Select=0 - ENDIF -ENDIF ELSE IF uv EQ '0' THEN IF ev.str EQ 'None' THEN o3[0]='H5' ELSE o3[0]='H5_'+STRTRIM(ev.str,2) $ -ELSE o3[0]='0' ;Cancel button chosen -IF (uv NE 'AVK') AND (uv NE 'idlcr8hdf.log') AND (uv NE 'Pop') AND (uv NE 'H5') THEN WIDGET_CONTROL,ev.top,/DESTROY - -END ;Intro_Event - - - -PRO intro, intype -;Procedure which creates an Introduction Window at start-up when IDLCR8HDF is called without parameters, -;or invalid parameters, or is called by IDL Virtual Machine. The user has a choice of continuing with -;the program after selecting input options, or stopping the program. -; ---------- -;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org -; -;History: -; 20061012: Introduced to IDLCR8HDF - Version 2.0 -; 20080302: Swapped the order of the option windows, so that the options indicated by the check -; boxes appear above the option boxes that close the Introduction Window and continue -; with the program - Version 3.0 -; 20100205: Include text regarding the RETERR argument which can be accepted as an input -; parameter when using the full version of idlcr8hdf - Version 3.09 -; 20110401: Add vertxt string array to hold text which changes between versions of the code; -; Change text to reference GEOMS compliant files instead of AVDC/EVDC/NDACC; Change -; text regarding the format of the structure required for input - Version 4.0b1 -; 20111220: Change text and options to include netCDF; change contact details - Version 4.0b7 -; 20130114: Make insensitive /LOG and /NC options if the program is called in IDL DEMO mode -; -Version 4.0b14 -; -;Input: intype - Integer set to -1 or -2: -1 indicates normal state; -2 indicates that input -; parameters at the IDLCR8HDF call were invalid. -; -;Output: Nil -; -;Called by: IDLCR8HDF -; -;Subroutines Called: INTRO_EVENT (via XMANAGER) - -COMMON WIDGET_WIN - -nhdr=44 & errtxt=STRARR(nhdr) -vertxt=['idlcr8hdf-v4.0_Readme.pdf','v4.0b64 September 2024'] -errtxt[1]='Welcome to IDLcr8HDF. This program creates GEOMS compliant HDF4, HDF5 and netCDF files' -errtxt[2]='(also refer to '+vertxt[0]+').' -errtxt[4]='Inputs to the program (IDL Virtual Machine (VM) and IDL Licensed (LIC) Versions):' -errtxt[5]=' METADATA TEMPLATE FILE - Containing the Global and Variable Attribute information for the site.' -errtxt[6]=' DATA FILE(s) - Multiple selection permitted. The file(s) must show the datasets in the single column' -errtxt[7]=' format described in the documentation.' -errtxt[8]=' TAV FILE - the current non-encrypted Table Attribute Values file.' -errtxt[9]=' OUTPUT DIRECTORY - Directory for any HDF, netCDF and log files created. Shortcut values of ''M'' or ''D''' -errtxt[10]=' will output files to the Metadata or Data file directories respectively.' -errtxt[12]='Alternative Input Option for IDL LIC Versions:' -errtxt[13]=' GLOBAL ATTRIBUTES ARRAY (GA) - a string array containing the Global Attributes in the form' -errtxt[14]=' ''label=value''.' -errtxt[15]=' DATA STRUCTURE (DS) - a heap structure using pointers containing the Variable Attribute Labels' -errtxt[16]=' (DS.VA_L), the Variable Attribute Values (DS.VA_V), and the Data (DS.Data), for a single output file.' -errtxt[17]=' TAV FILE - the current non-encrypted Table Attribute Values file.' -errtxt[18]=' OUTPUT DIRECTORY - Directory for any HDF, netCDF and log files created.' -errtxt[19]=' RETERR - String variable to which any error(s) are written.' -errtxt[21]='For IDL VM, input is by ''DIALOG_BOXES''. For IDL LIC, input can be by ''DIALOG_BOXES'' or passed' -errtxt[22]='by calling the program with one of the following command line options:' -errtxt[23]=' 1. idlcr8hdf (Opens this box, and allows the user the option to continue with file inputs).' -errtxt[24]=' 2. idlcr8hdf,METAFILE,DATAFILE(s),TAVFILE,OUTDIR[,RETERR][,/H5][,/Cn][,/NC][,/AVK][,/Log][,/Popup] or' -errtxt[25]=' idlcr8hdf,'''','''','''',''''[,/H5][,/Cn][,/NC][,/AVK][,/Log][,/Popup] (For null string, DIALOG_BOXES will prompt for input).' -errtxt[26]=' 3. idlcr8hdf,GA,DS,TAVFILE,OUTDIR[,RETERR][,/H5][,/Cn][,/NC][,/AVK][,/Log][,/Popup] or -errtxt[27]=' idlcr8hdf,GA,DS[,/H5][,/Cn][,/NC][,/AVK][,/Log][,/Popup] (Inputs are from session memory, DIALOG_BOX(s) will' -errtxt[28]=' prompt for input if TAVFILE or OUTDIR are not included).' -errtxt[29]=' /H5, /Cn, /NC, /AVK, /Log and /Popup keywords are used in place of the options given below (HDF4 is default).' -errtxt[30]='The /Cn keyword enables compression and shuffling of HDF5 files only (C1=low, C9=high). Default is no compression.' -errtxt[32]='Contacts -' -errtxt[33]=' Ian Boyd, Bryan Scientific Consulting LLC (iboyd@bryanscientific.org)' -errtxt[34]=' 9 Cambridge Terrace' -errtxt[35]=' Masterton 5810, New Zealand' -errtxt[37]=' Ann Mari Fjaeraa, EVDC Project Manager (amf@nilu.no)' -errtxt[38]=' Norwegian Institute for Air Research, Instituttveien 18' -errtxt[39]=' Postbox 100, N-2027 KJELLER, NORWAY' -errtxt[41]='EVDC Website: Tools and documentation available from http://evdc.esa.int/' -errtxt[43]='To continue, please choose from the options below (Note: HDF5 only available on IDL6.2 or greater).' -errtxt=' '+errtxt - -;Set-up text display widget -IF intype EQ -2 THEN xtxt=' - Command Line Input Error' ELSE xtxt='' -IF intype EQ -3 THEN optsens=0 ELSE optsens=1 -base=WIDGET_BASE(Title='idlcr8hdf '+vertxt[1]+xtxt,Tlb_Frame_Attr=1,/Column) ;,Tab_Mode=1) -wtxt=WIDGET_TEXT(base,xsize=102,ysize=25,/Scroll) -base3=WIDGET_BASE(base,/Nonexclusive) -logtext='Append log output to the file ''idlcr8hdf.log'' ' -IF intype EQ -3 THEN logtext=logtext+'(No log or netCDF file output permitted in IDL DEMO Mode)' $ -ELSE logtext=logtext+'(will create the file IF it doesn''t exist)' -poptext='Open a Pop-Up Window to display log output' -avktext='If Avg. Kernel data are present, append sentence to VAR_NOTES giving first three values of ' -avktext=avktext+'the first kernel' -avktip='Sentence reads ''First three values of the first kernel are: x.xx x.xx x.xx''' -b4=WIDGET_BUTTON(base3,value=logtext,uvalue='idlcr8hdf.log',frame=3,SENSITIVE=optsens) -b5=WIDGET_BUTTON(base3,value=poptext,uvalue='Pop',frame=3) -b6=WIDGET_BUTTON(base3,value=avktext,uvalue='AVK',frame=3) ;,Tooltip=AVKTip) -base2=WIDGET_BASE(base,/Row) -tip='Left Mouse Click or Tab to entry and hit <Spacebar>' -b1=WIDGET_BUTTON(base2,value='HDF4',uvalue='H4',frame=3) ;,Tooltip=Tip) -b7=WIDGET_BUTTON(base2,value='netCDF3',uvalue='NC',frame=3,SENSITIVE=optsens) -IF FLOAT(!Version.Release) GE 6.2 THEN $ - b2=WIDGET_BUTTON(base2,value='HDF5',uvalue='H5',frame=3) $;,Tooltip=Tip) $ -ELSE b2=WIDGET_BUTTON(base2,value='HDF5',Sensitive=0,frame=3) -b8=Widget_Label(base2,Value=' Compression ',Sensitive=0) -b9=Widget_Combobox(base2,Value=lvals,Sensitive=0,uvalue='0') - -b3=WIDGET_BUTTON(base2,value='Stop',uvalue='CANCEL',frame=3) ;,ToolTip=Tip) -WIDGET_CONTROL,base,/Realize -WIDGET_CONTROL,b4,/Input_Focus -FOR i=0,N_ELEMENTS(errtxt)-1 DO $ - WIDGET_CONTROL,wtxt,set_value=errtxt[i],/Append -XMANAGER,'intro',base - -END ;Intro - - - -PRO idlcr8hdf_event, ev -;Procedure to close the pop-up logging window after user selects 'Finish'. -; ---------- -;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org -; -;History: -; 20061012: Introduced to IDLCR8HDF - Version 2.0 -; -;Input: Selected widget event structure -; -;Output: Nil -; -;Called by: XMANAGER in IDLCR8HDF and STOP_WITH_ERROR -; -;Subroutines Called: None - -WIDGET_CONTROL,ev.top,/DESTROY -RETALL & HEAP_GC - -END ;IDLcr8HDF_Event - - - -FUNCTION is_a_number_hdf, value, ind - -IF N_PARAMS() EQ 2 THEN BEGIN - ;Check each individual character is numeric - nlen=STRLEN(value) - retval=1B - FOR i=0,nlen-1 DO BEGIN - ah=STRMID(value,i,1) - isan=(BYTE(ah) GE 48) AND (BYTE(ah) LE 57) ;0-9 - IF ~isan THEN retval=0B - ENDFOR - RETURN, retval -ENDIF ELSE BEGIN - ON_IOERROR, ConversionError - IF STRTRIM(value,2) EQ '' THEN RETURN, 0B - n=DOUBLE(value) - RETURN, 1B - ConversionError: - RETURN, 0B -ENDELSE -END - - - -PRO stop_with_error, txt1, txt2, lu -;Procedure called when an error in the program inputs is detected. An error message is displayed -;and the program stopped and reset. If necessary, open files are closed, and memory associated -;with a structure is freed. The error message is displayed in one or more of the following: -;a Pop-up dialog window; the Pop-up logging window; the Output Logging window in the IDLDE; -;a log file (idlcr8hdf.log) -; ---------- -;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org -; -;History: -; 20050802: Original IDLCR8HDF Routine - Version 1.0 -; 20061012: Set-up so that the error output is displayed in the output window(s) dependent on -; the options chosen by the user, the point in the program that the error is detected, -; and the method that IDLCR8HDF is called. If txt1 is preceeded by 'D_' or is null, -; the error output is to a Pop-up Dialog window. Other error output options are -; determined by the values in the (Common) dux array - Version 2.0 -; 20100205: Allow routine to return to the calling routine, rather than stop the application, if -; a 'reterr' argument is included in the call to idlcr8hdf - Version 3.09 -; 20110401: Change end text dependent on whether the program is creating a GEOMS file or doing QA -; on a GEOMS file - Version 4.0b1 -; 20111220: Change text referring to HDF to GEOMS - Version 4.0b7 -; 20171121: Add comment advising that program stopped before all checks were completed if doing -; QA; Add COMMON DATA to routine and remove ds from the list of input parameters -; - Version 4.0b43 -; -;Inputs: txt1 - the initial text line of the error message. Generally contains the name of the routine -; where the error was detected. -; txt2 - the second text line which generally describes the error state. -; lu - Where applicable, the file unit that needs to be closed at the termination of the program, -; otherwise set to -1. -; ds - Where applicable, the name of the data structure set-up by the program (dependent on -; the point in the program that the error is detected), so that memory associated with the -; structure can be freed (COMMON variable). -; dux - an integer array used to determine the output options for the error message (COMMON) variable). -; qa_yes - a boolean to indicate whether idlcr8hdf called in QA mode or not (COMMON variable). -; -;Output: Error message displayed/output dependent on the requested output options. -; -;Called by: The routine in which the error was detected. The following routines call STOP_WITH_ERROR: -; READ_TABLEFILE; TEST_FILE_INPUT; READ_METADATA; EXTRACT_AND_TEST; CHECK_METADATA; -; SET_UP_STRUCTURE; CHECK_MIN_MAX_FILL; EXTRACT_DATA; FIND_HDF_FILENAME; IDLCR8HDF. -; -;Subroutines Called: IDLCR8HDF_EVENT (via XMANAGER) - -COMMON DATA -COMMON WIDGET_WIN - -IF lu NE -1L THEN FREE_LUN,lu - -IF txt1 EQ '' THEN BEGIN ;<cancel> chosen on Intro box - res=DIALOG_MESSAGE('IDLcr8HDF Stopped!',/Information,Title='EVDC IDLcr8HDF') -ENDIF ELSE BEGIN - ;If necessary free up memory by destroying the heap variables pointed at by its pointer arguments - IF N_ELEMENTS(ds) NE 0 THEN PTR_FREE,ds.data - ;Write error to file and/or IDL output window - IF STRMID(txt1,0,2) EQ 'D_' THEN txtx=STRMID(txt1,2) ELSE txtx=txt1 - FOR i=dux[0],dux[1],dux[2] DO BEGIN - IF i EQ -1 THEN BEGIN - PRINT,' ERROR in '+txtx & PRINT,' '+txt2 - PRINT,'' & PRINT,'IDLcr8HDF stopped - Program Ended on '+SYSTIME(0) - ENDIF ELSE BEGIN - PRINTF,i,' ERROR in '+txtx & PRINTF,i,' '+txt2 - IF (i EQ dux[0]) OR ((i EQ dux[1]) AND (STRPOS(o3[2],'idlcr8hdf.log') NE -1)) THEN BEGIN - PRINTF,i,'' & PRINTF,i,'IDLcr8HDF stopped - Program Ended on '+SYSTIME(0) - ENDIF ELSE IF qa_yes THEN BEGIN - PRINTF,i,'' & PRINTF,i,' INFORMATION: QA stopped before all checks could be completed' - ENDIF - ENDELSE - ENDFOR - IF dux[1] NE dux[0] THEN FREE_LUN,dux[1] - IF (STRMID(txt1,0,2) EQ 'D_') AND (rerr[0] 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]='IDLcr8HDF Stopped!' - res=DIALOG_MESSAGE(errtxt2,/Error,Title='EVDC IDLcr8HDF Error') - ENDIF ELSE IF rerr[0] EQ 'NA' THEN BEGIN ;write error to Popup window - IF o3[4] EQ '0' THEN endtxt='GEOMS file creation ' $ - ELSE endtxt='GEOMS file testing ' - 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=endtxt+'stopped - hit <Finish> to close program',/Append - WIDGET_CONTROL,b1,Sensitive=1,/Input_Focus - XMANAGER,'stop_with_error',base,Event_Handler='idlcr8hdf_event' - ENDIF -ENDELSE -HEAP_GC -IF rerr[0] EQ 'NA' THEN RETALL $ -ELSE BEGIN - IF o3[4] EQ '0' THEN endtxt='create GEOMS file' $ - ELSE endtxt='complete GEOMS file test' - rerr[0]='Unable to '+endtxt+' - '+txtx+txt2 -ENDELSE - -END ;Procedure Stop_With_Error - - - -PRO infotxt_output, infotxt -;Procedure called when the program makes a change to the input meta data or reports information -;relevant to the creation of the HDF/netCDF 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 IDLCR8HDF - Version 3.06 -; 20110401: infotxt INFORMATION header changed to a number, to allow changes to the comment -; header depending on how idlcr8hdf has been called, as follows: 0 = INFORMATION; -; 1 = NON-STANDARD COMPLIANCE NOTIFICATION (QA) o/w INFORMATION; 2 = ERROR (QA) -; o/w INFORMATION; 3 = ERROR; 4 = DEBUG - Version 4.01 -; -;Inputs: infotxt - the text line(s) of the information message. Can be either a scalar string -; or string array -; -;Output: Message displayed/output dependent on the requested output options -; -;Called by: The routine in which the reported change was made. The following routines call -; INFOTXT_OUTPUT: READ_TABLEFILE; GEOMS_RULE_CHANGES; PRE_DEFINED_ATT_CHECKS; -; READ_METADATA; EXTRACT_AND_TEST; CHECK_METADATA; EXTRACT_DATA; SET_UP_STRUCTURE; -; CHECK_STRING_DATATYPE; CHECK_MIN_MAX_FILL; READ_DATA; FIND_HDF_FILENAME; -; AVDC_HDF_WRITE; IDLCR8HDF -; -;Subroutines Called: None - -COMMON DATA -COMMON WIDGET_WIN - -dm=SIZE(infotxt,/N_ELEMENTS) -qaval=FIX(STRMID(infotxt[0],0,1)) -write_rerr=0 ;Boolean to generate a return error message - -IF qa_yes THEN BEGIN ;program called in QA mode - ;Add correct message title - CASE 1 OF - qaval EQ 0: infotxt[0]=' INFORMATION:'+STRMID(infotxt[0],1) - qaval EQ 1: infotxt[0]=' NON-STANDARD COMPLIANCE NOTIFICATION:'+STRMID(infotxt[0],1) - qaval EQ 4: infotxt[0]=' DEBUG:'+STRMID(infotxt[0],1) - ELSE: infotxt[0]=' ERROR:'+STRMID(infotxt[0],1) - ENDCASE - ;truncate message from the '|' - bs_found=0 - FOR n=0,dm-1 DO BEGIN - IF bs_found EQ 1 THEN infotxt[n]='' $ - ELSE BEGIN - bspos=STRPOS(infotxt[n],'|') - IF bspos NE -1 THEN BEGIN - infotxt[n]=STRMID(infotxt[n],0,bspos) - bs_found=1 - ENDIF - ENDELSE - ENDFOR - ;recalculate number of good infotxt values - gi=WHERE(infotxt NE '',dm) -ENDIF ELSE BEGIN ;program called in HDF file create mode - CASE 1 OF - qaval EQ 3: BEGIN - infotxt[0]=' ERROR:'+STRMID(infotxt[0],1) - IF rerr[1] NE 'NA' THEN BEGIN ;generate return error message - IF rerr[1] EQ '' THEN BEGIN - IF o3[4] EQ '0' THEN endtxt='create GEOMS file' $ - ELSE endtxt='complete GEOMS file test' - ENDIF - write_rerr=1 - ENDIF - o3[4]='NOHDF' ;Error in Input so do not create HDF file - END - qaval EQ 4: infotxt[0]=' DEBUG:'+STRMID(infotxt[0],1) - ELSE: infotxt[0]=' INFORMATION:'+STRMID(infotxt[0],1) - ENDCASE - ;remove '|' from the message - FOR n=0,dm-1 DO BEGIN - bspos=STRPOS(infotxt[n],'|') - IF bspos NE -1 THEN BEGIN - infotxt[n]=STRMID(infotxt[n],0,bspos)+STRMID(infotxt[n],bspos+1) - ENDIF - ENDFOR -ENDELSE - -lineno=lineno+dm -FOR n=0,dm-1 DO BEGIN - IF o3[3] EQ '' THEN WIDGET_CONTROL,wtxt,set_value=infotxt[n],/Append,Set_text_top_line=lineno - FOR m=dux[0],dux[1],dux[2] DO BEGIN - IF m EQ -1 THEN PRINT,infotxt[n] ELSE PRINTF,m,infotxt[n] ;write out to log - ENDFOR -ENDFOR - -IF write_rerr EQ 1 THEN BEGIN - IF rerr[1] EQ '' THEN rerr[1]='Unable to '+endtxt+' -'+STRMID(infotxt[0],8) $ - ELSE rerr[1]=rerr[1]+';'+STRMID(infotxt[0],8) - IF dm GT 1 THEN FOR n=1,dm-1 DO rerr[1]=rerr[1]+STRMID(infotxt[n],3) -ENDIF - -END ;Procedure InfoTxt_Output - - - -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 var_units_test, vuvalue, rd, tab_type, var_si_conv, errstate -;Procedure to perform checks on the VAR_UNITS value in the metadata and, based on the VAR_UNITS input, -;calculate and return the VAR_SI_CONVERSION value. -; ---------- -;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org -; -; History: -; 20061012: Introduced to IDLCR8HDF - Version 2.0 -; (Previously these checks were performed in the Extract_and_Test routine) -; 20080302: var_unit_arr and unit_pre_arr added which hold, respectively, the valid VAR_UNITS and -; corresponding VAR_SI_CONVERSION values, and the set of UNIT_PREFIXs (previously -; values from the EVDC/AVDC TAV file have been used). This has been done so that the -; routine can be stand-alone (i.e. not dependent on also having to read in a TAV file), -; and also because the original Envisat table.dat file does not contain the -; VAR_SI_CONVERSION values. The input parameters have been changed to reflect this: bu -; and up arrays (previously containing the VAR_UNIT and UNIT_PREFIX values from the TAV -; file), and nbu and nup (the number of elements in the bu and up arrays) are no longer -; used. The integer flag tab_type has been added to account for the different handling of -; some of the VAR_UNIT and VAR_SI_CONVERSION values between AVDC and original Envisat. -; The STOP_WITH_ERROR routine is no longer called in the event of an error, but an Error -; State string is returned instead. Bug fixed when testing for a UNIT_PREFIX - previously -; only the first character of the VAR_UNIT value was checked for a possible UNIT_PREFIX, -; thus 'deka' (da) was excluded. Bug Fixed when the calculated Power Value of the last -; base SI unit shown in VAR_SI_CONVERSION is '1', this is now set so that the power value -; does not appear - Version 3.0 -; 20090611: Galileo added to var_unit_arr (AVDC); Last VAR_SI_CONVERSION value for ppmv, ppbv, -; pptv, and ppv changed to DIMENSIONLESS (was ppv) in var_unit_arr (AVDC); -; VAR_SI_CONVERSION values for molec changed to 0;1.66054E-24;mol (was 0;1;molec) in -; var_unit_arr (AVDC); VAR_SI_CONVERSION values for DU changed to 0;4.4615E-4;mol m-2 -; (was 0;2.6867E20;molec m-2) in var_unit_arr; Stop power units being added to -; DIMENSIONLESS (e.g. for VAR_UNITS=ppmv2); Change EVDC VAR_SI_CONVERSION values in -; var_unit_arr to match Envisat Metadata Guidelines values - Version 3.01 -; 20091117: EVDC VAR_SI_CONVERSION values set to the same as AVDC (only one var_unit_arr set); -; Fix bug which, in some cases, does not account for repeated units in VAR_UNITS when -; determining the VAR_SI_CONVERSION (e.g. VAR_UNITS=W m-2 sr-1 m-1); Put VAR_SI_CONVERSION -; units in power value order; Correctly scale multiple units by the power -; e.g. W2 = (m2 kg s-3)^2 (previously assumed only a single unit was being scaled); -; Generate error if third VAR_SI_CONVERSION value is 'DIMENSIONLESS' or 'NONE' but also -; includes extra values - Version 4.0b1 -; 20101001: Set up for GEOMS compliance e.g. DIMENSIONLESS changed to 1; MJD2000 changed to MJD2K; -; NONE removed - Version 4.0b2 -; 20101221: Bug fix - need to correctly account for when units cancel each other out. Was still -; assigned the value DIMENSIONLESS - Version 4.0b3 -; 20110303: Bug fix - when a dimensionless unit includes a power value (e.g. ppmv2), the base unit -; in VAR_SI_CONVERSION stays as '1'; Add 'dB' to var_unit_arr - Version 4.0b4 -; 20110719: Add 'pH' - Version 4.0b5 -; 20240908: Bug fix - Using the SORT command to put units in order of power value can give different -; results depending on the operating system when power values are identical. Change routine -; so the MAX command is used instead to create an array in descending power order -; - Version 4.0b6 -; 20240912: Reinstitute sorting power units with the SORT command and save this version as a second -; var_si_conv option (var_si_conv becomes a string array of size 2). This means the ordering -; of the units between the two var_si_conv values may differ - Version 4.0b7 -; -; Inputs: vuvalue - a string containing the Metadata VAR_UNITS value to be checked (everything -; after the '=') -; rd - integer flag to indicate if VAR_DATA_TYPE is real/double (-1) or not (1). Used to -; make VAR_SI_CONVERSION values of the same type -; tab_type - integer flag differentiating between AVDC (0) and original Envisat (1) styles -; -; Output: var_si_conv - a string array of size 2 containing the VAR_SI_CONVERSION value determined -; by the program (returns '' if an error is encountered). The 2 values may -; differ if there are multiple units with the same power value, due to using -; two different sort methods -; errstate - string describing an error state encountered during testing (o/w set to '') -; -; Called by: CHECK_METADATA -; -; External Subroutines Called: None - -var_unit_arr=['%;0;0.01;1','A;0;1;A','C;0;1;s A','cd;0;1;cd','d;0;86400;s','deg;0;1.74533E-2;rad',$ - 'degC;273.15;1;K','1;0;1;1','h;0;3600;s','Hz;0;1;s-1','J;0;1;m2 kg s-2','K;0;1;K',$ - 'l;0;1E-3;m3','lm;0;1;cd sr','lx;0;1;cd sr m-2','m;0;1;m','min;0;60;s',$ - 'MJD2K;0;86400;s','mol;0;1;mol','molec;0;1.66054E-24;mol','Np;0;1;1',$ - 'N;0;1E3;m kg s-2','Pa;0;1;m-1 kg s-2','pH;0;1E-12;m2 kg s-2 A-2','photons;0;1;photons',$ - 'ppbv;0;1E-9;1','ppmv;0;1E-6;1','pptv;0;1E-12;1','ppv;0;1;1','psu;0;1;psu',$ - 'rad;0;1;rad','s;0;1;s','sr;0;1;sr','V;0;1;m2 kg s-3 A-1','W;0;1;m2 kg s-3','kg;0;1;kg',$ - 'DU;0;4.4614E-4;mol m-2','Gal;0;1E-2;m s-2','dB;0;1;1'] - -unit_pre_arr=['Y;yotta;1E24','Z;zetta;1E21','E;exa;1E18','P;peta;1E15','T;tera;1E12','G;giga;1E9',$ - 'M;mega;1E6','k;kilo;1E3','h;hecto;1E2','da;deka;1E1','d;deci;1E-1','c;centi;1E-2',$ - 'm;milli;1E-3','u;micro;1E-6','n;nano;1E-9','p;pico;1E-12','f;femto;1E-15',$ - 'a;atto;1E-18','z;zepto;1E-21','y;yocto;1E-24'] - -;Set up text in case an error is found in the input VAR_UNITS value -errtxt=STRARR(2) -IF tab_type EQ 1 THEN errtxt[0]='No match with Table.Dat BASE_UNIT/UNIT_PREFIX values' $ -ELSE errtxt[0]='No match with Table Attribute Values file VAR_UNITS/UNIT_PREFIX values' -errtxt[1]='Not valid' -errtxt='VAR_UNITS='+vuvalue+': '+errtxt -var_si_conv=STRARR(2) & errstate='' ;initialize outputs - -nta=N_ELEMENTS(var_unit_arr) -;extract var_unit_arr/unit_pre_arr sub-values into vu/up arrays -vuhold=STRSPLIT(var_unit_arr[0],';',/Extract) ;test for number of sub-values -nvu=N_ELEMENTS(vuhold) & vu=STRARR(nvu,nta) -FOR j=0,nta-1 DO BEGIN - vuhold=STRSPLIT(var_unit_arr[j],';',/Extract) - vu[*,j]=vuhold -ENDFOR -nta=N_ELEMENTS(unit_pre_arr) -vuhold=STRSPLIT(unit_pre_arr[0],';',/Extract) ;test for number of sub-values -nup=N_ELEMENTS(vuhold) & up=STRARR(nup,nta) -FOR j=0,nta-1 DO BEGIN - vuhold=STRSPLIT(unit_pre_arr[j],';',/Extract) - up[*,j]=vuhold -ENDFOR - -;separate out metadata sub-values into component parts, and set-up holding arrays -vp=STRSPLIT(STRTRIM(vuvalue,2),' ',/Extract) & vpn=N_ELEMENTS(vp) -vpx=STRARR(2,vpn) ;0 holds VAR_UNIT value, 1 holds POWER component -bpx=STRARR(vpn) ;holding string array for base unit -ex=INTARR(vpn)+1 ;holding integer array for power value (defaults to 1) -tm=DBLARR(vpn) ;holding array for scale factor -j=0 -WHILE (j LE vpn-1) AND (errstate EQ '') DO BEGIN - ;test to see if the sub-value is a base unit in the TAV file - ti=WHERE(vp[j] EQ vu[0,*],tcnt) - IF tcnt NE 0 THEN vpx[0,j]=vp[j] $ it is a base unit - ELSE BEGIN ;separate out into unit and power values as required - vpx[0,j]=STRMID(vp[j],0,1) ;save first character of vp(j) - stopchk=0 - IF (STRLEN(vp[j]) GE 2) THEN BEGIN - FOR k=1,STRLEN(vp[j])-1 DO BEGIN - ah=STRMID(vp[j],k,1) - test1=(BYTE(ah) GE 65) AND (BYTE(ah) LE 90) ;A-Z - test2=(BYTE(ah) GE 97) AND (BYTE(ah) LE 122) ;a-z - IF (test1[0]) OR (test2[0]) THEN BEGIN - IF stopchk EQ 0 THEN vpx[0,j]=vpx[0,j]+ah ELSE stopchk=2 - ;if stopchk EQ 2 THEN this means that VAR_UNITS is not legal - ENDIF ELSE IF stopchk EQ 0 THEN BEGIN - stopchk=1 ;first non-alpha character so check for numeric or '-' character - test1=(BYTE(ah) EQ 45) OR ((BYTE(ah) GE 49) AND (BYTE(ah) LE 57)) ;- or 1-9 - IF NOT test1[0] THEN stopchk=2 ELSE vpx[1,j]=ah - ENDIF ELSE IF stopchk EQ 1 THEN BEGIN - ;need to check for a numeric character only - test1=(BYTE(ah) GE 48) AND (BYTE(ah) LE 57) ;0-9 - IF NOT test1[0] THEN stopchk=2 ELSE vpx[1,j]=vpx[1,j]+ah - ENDIF - ENDFOR - IF vpx[1,j] NE '' THEN ex[j]=FIX(vpx[1,j]) - ENDIF - IF stopchk EQ 2 THEN vpx[0,j]=vp[j] ;in the event that the value is not valid, so will create error - ;Do TAV check on the VAR_UNIT value - ti=WHERE(vpx[0,j] EQ vu[0,*],tcnt) - ENDELSE - - IF tcnt NE 0 THEN BEGIN ;VAR_UNIT is a BASE_VALUE - bemult=(DOUBLE(vu[nvu-2,ti[0]]))^ex[j] & tm[j]=bemult - bpx[j]=vu[nvu-3,ti[0]]+';'+STRTRIM(STRING(format='(E8.1)',bemult),2)+';'+vu[nvu-1,ti[0]] - ENDIF ELSE IF vpx[0,j] EQ 'g' THEN BEGIN ;check for VAR_UNIT EQ g (gram) for AVDC style TAV file - bemult=0.001d^ex[j] & tm[j]=bemult - bpx[j]='0;'+STRTRIM(STRING(format='(E8.1)',bemult),2)+';kg' - ENDIF ELSE BEGIN ;separate out vpx value into prefix and base-value and test - ;check for valid prefix - first check for 'deka' (da) - pref=STRMID(vpx[0,j],0,2) & bas=STRMID(vpx[0,j],2) - pi=WHERE(pref EQ up[0,*],pcnt) - IF pcnt EQ 0 THEN BEGIN ;test for the remaining prefixes - pref=STRMID(vpx[0,j],0,1) & bas=STRMID(vpx[0,j],1) - pi=WHERE(pref EQ up[0,*],pcnt) - ENDIF - IF pcnt NE 0 THEN BEGIN - pmult=DOUBLE(up[nup-1,pi[0]]) - ;check for valid base - ti=WHERE(bas EQ vu[0,*],tcnt) - IF tcnt NE 0 THEN BEGIN - bpmult=(DOUBLE(vu[nvu-2,ti[0]])*pmult)^ex[j] & tm[j]=bpmult - bpx[j]=vu[nvu-3,ti[0]]+';'+STRTRIM(STRING(format='(E8.1)',bpmult),2)+';'+vu[nvu-1,ti[0]] - ENDIF ELSE IF bas EQ 'g' THEN BEGIN ;check for VAR_UNIT EQ g (gram) for AVDC style TAV file - bpmult=(pmult*0.001D)^ex[j] & tm[j]=bpmult - bpx[j]='0;'+STRTRIM(STRING(format='(E8.1)',bpmult),2)+';kg' - ENDIF ELSE errstate=errtxt[0] - ENDIF ELSE errstate=errtxt[0] - ENDELSE - j=j+1 -ENDWHILE - -IF errstate EQ '' THEN BEGIN ;No errors detected so continue - ;Create VAR_SI_CONVERSION value - tmult=1.0D - FOR j=0,vpn-1 DO tmult=tmult*tm[j] - - ;reformat the multiplier e.g. 1.000E+003 becomes 1E3 - ;convert to Exponential form if necessary - IF (tmult EQ 273.15D) OR (tmult MOD 60.D EQ 0.D) OR ((tmult GE 0.01D) AND (tmult LT 1.D2) $ - AND (tmult*1.D4 MOD 1.D2 EQ 0.D)) THEN tmults=STRTRIM(STRUPCASE(tmult),2) $ ;keep the same format - ELSE tmults=STRTRIM(STRING(format='(E14.6)',tmult),2) - - epos=STRPOS(tmults,'E') & ppos=STRPOS(tmults,'.') - IF epos NE -1 THEN BEGIN ;remove unnecessary characters after 'E' - ep=FIX(STRMID(tmults,epos+1)) & epx=STRTRIM(ep,2) - tmults=STRMID(tmults,0,epos+1)+epx - ENDIF - IF ppos NE -1 THEN BEGIN ;remove any trailing zeroes after the decimal place - IF epos NE -1 THEN ep=STRMID(tmults,ppos+1,epos-(ppos+1)) ELSE ep=STRMID(tmults,ppos+1) - WHILE STRMID(ep,STRLEN(ep)-1,1) EQ '0' DO ep=STRMID(ep,0,STRLEN(ep)-1) - IF ep NE '' THEN tmults=STRMID(tmults,0,ppos+1)+ep ELSE tmults=STRMID(tmults,0,ppos) - IF epos NE -1 THEN tmults=tmults+'E'+epx - ENDIF - - ;Scale the units by the power e.g. W2 = (m2 kg s-3)^2 - FOR j=0,vpn-1 DO BEGIN - vsc=STRSPLIT(bpx[j],';',/EXTRACT) - vspl=STRSPLIT(vsc[2],' ',/EXTRACT,COUNT=vscnt) - sichk=STRARR(vscnt) & pwchk=sichk - IF (vpx[1,j] NE '') AND (vpx[1,j] NE '1') AND (vsc[2] NE '1') THEN BEGIN - bpx[j]=vsc[0]+';'+vsc[1]+';' - FOR k=0,vscnt-1 DO BEGIN - ;separate out SI units and power values - sires=STRSPLIT(vspl[k],'-0123456789',/Extract) - sichk[k]=sires[0] ;SI Unit - IF STRLEN(sichk[k]) NE STRLEN(vspl[k]) THEN $ - pwchk[k]=STRMID(vspl[k],STRLEN(sichk[k])) $ - ELSE pwchk[k]='1' ;Power value - pwm=FIX(pwchk[k])*FIX(vpx[1,j]) - IF k EQ 0 THEN sp='' ELSE sp=' ' - bpx[j]=bpx[j]+sp+sichk[k]+STRTRIM(pwm,2) - ENDFOR - ENDIF - ENDFOR - - ;Put together VAR_SI_CONVERSION - vsc=STRSPLIT(bpx[0],';',/EXTRACT) - ;IF vsc[2] EQ '1' THEN vpx[1,0]='' - var_si_conv[0]=vsc[0]+';'+tmults+';'+vsc[2] - ;add remaining base units to VAR_SI_CONVERSION - IF vpn GT 1 THEN BEGIN - FOR j=1,vpn-1 DO BEGIN - vsc=STRSPLIT(bpx[j],';',/Extract) - var_si_conv[0]=var_si_conv[0]+' '+vsc[2] - ENDFOR - ENDIF - var_si_conv[1]=var_si_conv[0] ;Default is that the 2nd entry is the same as the first - - ;check VAR_SI_CONVERSION for repeated SI units e.g. m m-3 becomes m-2 - schk=STRSPLIT(var_si_conv[0],' ;',/Extract) & scnt=N_ELEMENTS(schk) - IF scnt GT 3 THEN BEGIN ;more than one SI unit in VAR_SI_CONVERSION - sichk=STRARR(scnt-2) & pwchk=sichk - FOR j=0,scnt-3 DO BEGIN ;separate out SI units and power values - sires=STRSPLIT(schk[j+2],'-0123456789',/Extract) - sichk[j]=sires[0] ;SI Unit - IF STRLEN(sichk[j]) NE STRLEN(schk[j+2]) THEN $ - pwchk[j]=STRMID(schk[j+2],STRLEN(sichk[j])) $ - ELSE pwchk[j]='1' ;Power value - ENDFOR - j=0 & pwval=0 - WHILE j LT scnt-3 DO BEGIN - si=WHERE((sichk[j] NE '') AND (sichk[j] EQ sichk[j+1:scnt-3]),sicnt) - IF sicnt NE 0 THEN BEGIN - FOR k=0,sicnt-1 DO BEGIN - si[k]=si[k]+j+1 - pwval=FIX(pwchk[j])+FIX(pwchk[si[k]]) - IF (pwval[0] EQ 0) AND (k EQ sicnt-1) THEN BEGIN - sichk[j]='' & pwchk[j]='' - ENDIF ELSE IF (pwval[0] EQ 1) and (k EQ sicnt-1) THEN pwchk[j]='' $ - ELSE pwchk[j]=STRTRIM(pwval[0],2) - ;make null all the other SI values - sichk[si[k]]='' & pwchk[si[k]]='' - ENDFOR - ENDIF ELSE IF pwchk[j] EQ '1' THEN pwchk[j]='' - j=j+1 - ENDWHILE - ;Also do check on the power value of the last SI Unit - IF pwchk[scnt-3] EQ '1' THEN pwchk[scnt-3]='' - - ;Put units in power value order - ;Create two possible versions, one using the MAX command (primary) and another using SORT - ;Note: When using the SORT command, identical elements are sorted arbitrary and may vary between operating systems - FOR k=0,1 DO BEGIN - pwhold=pwchk - oi=WHERE(pwhold EQ '',ocnt) - IF ocnt NE 0 THEN pwhold[oi]='1' - IF k EQ 0 THEN BEGIN ;Do MAX order method - pws=INTARR(scnt-2) - ;determine minimum power value - mnp=MIN(FIX(pwhold)) - FOR j=0,scnt-3 DO BEGIN - mxp=MAX(FIX(pwhold),mxs) - pws[j]=mxs - pwhold[mxs]=mnp - 1 ;make it the lowest value - ENDFOR - sichk=sichk[pws] & pwchk=pwchk[pws] - ENDIF ELSE BEGIN ;Do SORT method (results are operating system dependent) - pws=SORT(FIX(pwhold)) - sichk=sichk[REVERSE(pws)] & pwchk=pwchk[REVERSE(pws)] - ENDELSE - var_si_conv[k]=schk[0]+';'+schk[1]+';'+sichk[0]+pwchk[0] - si=WHERE(sichk NE '',sicnt) - IF sicnt EQ 0 THEN var_si_conv[k]=var_si_conv[k]+'1' $ ;i.e. values cancelled out - ELSE BEGIN - FOR j=1,scnt-3 DO BEGIN - si=WHERE(sichk[0:j-1] NE '',sicnt) - IF (sichk[j] EQ '') OR (sicnt EQ 0) THEN var_si_conv[k]=var_si_conv[k]+sichk[j]+pwchk[j] $ - ELSE var_si_conv[k]=var_si_conv[k]+' '+sichk[j]+pwchk[j] - ENDFOR - ENDELSE - ENDFOR - ENDIF - - IF rd LT 0 THEN BEGIN - ;VAR_DATA_TYPE is Real or Double so make VAR_SI_CONVERSION values floating point - FOR k=0,1 DO BEGIN - tchkh=STRSPLIT(var_si_conv[k],';',/Extract) & tup=STRUPCASE(tchkh) - FOR j=0,1 DO BEGIN - IF STRPOS(tup[j],'.') EQ -1 THEN BEGIN - IF STRPOS(tup[j],'E') EQ -1 THEN tchkh[j]=tchkh[j]+'.0' $ - ELSE tchkh[j]=STRMID(tup[j],0,STRPOS(tup[j],'E'))+'.0'+STRMID(tup[j],STRPOS(tup[j],'E')) - ENDIF - ENDFOR - var_si_conv[k]=tchkh[0]+';'+tchkh[1]+';'+tchkh[2] - ENDFOR - ENDIF - - ;Check for invalid VAR_UNITS - third VAR_SI_CONVERSION is 1 plus extra values - FOR k=0,1 DO BEGIN - vsc=STRSPLIT(var_si_conv[k],';',/EXTRACT) - vsc[2]=STRTRIM(vsc[2],2) - IF (errstate EQ '') AND (STRMID(vsc[2],0,1) EQ '1') AND (STRLEN(vsc[2]) GT 1) THEN BEGIN - errstate=errtxt[1] & var_si_conv[k]='' - ENDIF - ENDFOR -ENDIF - -END ;procedure Var_Units_Test - - - -PRO read_tablefile, tablefile -;Procedure to identify the version number of the TAV file, read the contents of the -;LABELS/FIELDS (tab_arr), and determine the FILE_META_VERSION in the global attributes (tab_ver). -;This routine also creates a flag (tab_type) to determine whether the input file is an original -;table.dat file used by Envisat or the GEOMS TAV file, with the HDF/netCDF file generated -;accordingly. -; ---------- -;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org -; -;History: -; 20050802: Original IDLCR8HDF Routine - Version 1.0 -; 20061012: Bug-fix to separate semi-colons with a space when one immediately follows the other, -; so that the number of subvalues is correctly determined by the program. Common -; variable definition WIDGET_WIN added - Version 2.0 -; 20080302: Added code to differentiate between the AVDC TAV file and original Envisat table.dat. -; The tab_type flag is used to differentiate between the two formats. Note that some of the -; table.dat labels are renamed to match the equivalent TAV file labels for compatibility -; when testing Metadata entries - refer to EnviName/AVDCName arrays; Ensure that the TAV -; Version value is correctly formatted and is version 03 or greater - Version 3.0 -; 20100205: Add RETURN command after all STOP_WITH_ERROR calls, which allows program to return to the -; calling program if the reterr argument is included in the idlcr8hdf call - Version 3.09 -; 20110401: Change AVDC references to GEOMS; Test AVDC TAV file version is version 04 or greater; -; Remove check on format of TAV file version; Conform to new INFOTXT_OUTPUT reporting -; - Version 4.0b1 -; -;Input: Tablefile - Name of the file containing the Table Attributes. -; -;Outputs: tab_ver - String containing FILE_META_VERSION value. -; tab_arr - 2-D string array of size nf*mcnt+2, where nf=Number of Fields, and mcnt=maximum -; number of values detected in any one field. tab_arr(*,0) is the name of each field, -; and tab_arr(*,1) is the number of values in each field. -; tab_type - 0/1 where 0 identifies an AVDC format TAV file and 1 identifies an original -; Envisat format table.dat file. -; -;Called by: IDLCR8HDF -; -;Subroutines Called: STOP_WITH_ERROR (if error state detected); INFOTXT_OUTPUT -; Possible Conditions for STOP_WITH_ERROR call (plus [line number] where called): -; 1. Table Attribute Values file version not identified -; 2. Envisat table.dat ASC2HDF program version not found -; 3. First Envisat table.dat field value not found -; 4. Table Attribute Values file version is not in a valid format -; -; Information Conditions (when the program reports issues and continues): -; 1. [Original EVDC]/[GEOMS] Reporting Guidelines Apply (depending on type of Table Attribute -; Values file read in) -; 2. Old version of the TAV file in use. Update from http://avdc.gsfc.nasa.gov/Tools - -COMMON TABLEDATA -COMMON WIDGET_WIN - -;Possible error messages for this procedure -proname='Read_TableFile procedure: ' -errtxt=STRARR(4) -errtxt[0]='Table Attribute Values file version not identified' -errtxt[1]='Envisat table.dat ASC2HDF program version not found' -errtxt[2]='First Envisat table.dat field value not found' -errtxt[3]='Table Attribute Values file version is invalid (should be ddRddd or ddRdddd): ' -FOR i=0,2 do errtxt[i]=errtxt[i]+' with the search criteria used by this program' - -;Array of Envisat Field names to be changed to equivalent AVDC Field names -enviname=['_NAME','_AFFILIATION','DATA_VARIABLES_00_00','BASE_UNIT'] -avdcname=['ORIGINATOR','AFFILIATION','DATA_VAR_ALL_00_00','VAR_UNITS'] - -ON_IOERROR,TypeConversionError -dum='' & tab_ver='' -min_fmv=4 ;TAV Version must be GE this value e.g. 04R001, 06R002 but not 03R004 -OPENR,lu,tablefile,/GET_LUN -;determine TAV file version -REPEAT BEGIN - READF,lu,dum - dumup=STRUPCASE(dum) - envitest=STRPOS(STRCOMPRESS(dumup,/Remove_all),'!TABLE.DATVERSION') NE -1 - avdctest=STRPOS(STRCOMPRESS(dumup,/Remove_all),'!VERSION') NE -1 -ENDREP UNTIL (envitest) OR (avdctest) OR (EOF(lu)) -IF EOF(lu) THEN BEGIN - STOP_WITH_ERROR,o3[3]+proname,errtxt[0],lu & RETURN -ENDIF -res=STRSPLIT(dumup,' ',/Extract) & vi=WHERE(res EQ 'VERSION') -IF N_ELEMENTS(res) LE vi[0]+1 THEN BEGIN - STOP_WITH_ERROR,o3[3]+proname,errtxt[0]+': '+dum,lu & RETURN -ENDIF -IF envitest THEN BEGIN - tab_type=1 & infotxt='0 EVDC original style table.dat' - n_title=4 -ENDIF ELSE BEGIN - tab_type=0 & infotxt='0 GEOMS compliant Table Attribute Values' - n_title=5 -ENDELSE -infotxt=infotxt+' file input. '+STRMID(infotxt,2,n_title)+' Reporting Guidelines apply' -INFOTXT_OUTPUT,infotxt - -;Ensure Meta Version has a valid format -;i. Check third character is an 'R' -;ii. Check number of characters is 6 or 7 -;iii. Check dd and ddd(d) are numeric -;iv. Check that version number is 03 or greater for AVDC file type -;v. Issue warning and convert to ddRddd if format is ddRdddd -fmv=res[vi[0]+1] ;File_Meta_Version -valid=0 ;set to test for valid string to number conversion -FOR i=0,STRLEN(fmv)-1 DO IF i NE 2 THEN fmvtest=FIX(STRMID(fmv,i,1)) -fmvtest=FIX(STRMID(fmv,0,2)) ;To test for TAV version 'min_fmv' or greater -valid=1 ;FILE_META_VERSION characters are numeric (except for the 'R') if program gets to here -TypeConversionError: -IF (STRMID(fmv,2,1) NE 'R') OR (STRLEN(fmv) lt 6) OR (STRLEN(fmv) gt 7) OR $ - (valid EQ 0) THEN BEGIN - STOP_WITH_ERROR,o3[3]+proname,errtxt[3]+fmv,lu & RETURN -ENDIF - -IF (tab_type EQ 0) AND (fmvtest LT min_fmv) THEN BEGIN - infotxt=STRARR(2) - infotxt[0]='3 Old version of the Table Attribute Values file used as input: ' - infotxt[0]=infotxt[0]+' TAV Version '+fmv - infotxt[1]=' Please update from http://avdc.gsfc.nasa.gov/Tools' - INFOTXT_OUTPUT,infotxt -ENDIF - -IF envitest THEN BEGIN ;identify asc2hdf version in table.dat and read past the CHECK_ATTRIBUTE line - WHILE (STRPOS(STRUPCASE(dum),'ASC2HDF') EQ -1) AND (NOT EOF(lu)) DO READF,lu,dum - IF EOF(lu) THEN BEGIN - STOP_WITH_ERROR,o3[3]+proname,errtxt[1],lu & RETURN - ENDIF - cr8_hdf_ver=';IDLCR8HDF' - dum=STRTRIM(dum,2) - WHILE ((STRMID(dum,0,1) EQ '!') OR (STRMID(dum,0,1) EQ '#') OR (dum EQ '')) AND $ - (NOT EOF(lu)) DO BEGIN - READF,lu,dum & dum=STRTRIM(dum,2) - ENDWHILE - IF EOF(lu) THEN BEGIN - STOP_WITH_ERROR,o3[3]+proname,errtxt[2],lu & RETURN - ENDIF - READF,lu,dum ;to get to the line after 'CHECK_ATTRIBUTE' -ENDIF ELSE cr8_hdf_ver=';IDLCR8HDF' -tab_ver=fmv+cr8_hdf_ver ;= the FILE_META_VERSION input value in the global attributes - -;determine no. of FIELDS/LABELS as well as the maximum number of elements -nf=0 & mcnt=0 & firstfield='' -dum=STRTRIM(dum,2) -WHILE NOT EOF(lu) DO BEGIN - ncnt=-1 - IF (STRMID(dum,0,1) NE '!') AND (STRMID(dum,0,1) NE '#') AND $ - (STRMID(dum,0,1) NE '=') AND (dum NE '') THEN BEGIN - - FOR i=0,N_ELEMENTS(enviname)-1 DO BEGIN - epos=STRPOS(STRUPCASE(dum),enviname[i]) - IF (epos NE -1) AND (epos LE 3) THEN ncnt=i - ENDFOR - IF (ncnt EQ 0) OR (ncnt EQ 1) THEN BEGIN - IF STRMID(STRUPCASE(dum),0,3) NE 'PI_' THEN nf=nf-1 ELSE ecnt=0 - ;This puts all PI_,DO_, and DS_NAME or AFFILIATION values into either the ORIGINATOR or AFFILIATION fields - ENDIF ELSE ecnt=0 - - nf=nf+1 - IF firstfield EQ '' THEN firstfield=dum - IF ncnt NE -1 THEN dum=avdcname[ncnt] ;change the name of the Envisat field to AVDC equivalent - REPEAT BEGIN - READF,lu,dum & dum=STRTRIM(dum,2) - IF STRMID(dum,0,1) EQ '=' THEN ecnt=ecnt+1 - ENDREP UNTIL (STRMID(dum,0,1) NE '=') OR (EOF(lu)) - IF ecnt GT mcnt THEN mcnt=ecnt - ENDIF ELSE IF NOT EOF(lu) THEN BEGIN - READF,lu,dum & dum=STRTRIM(dum,2) - ENDIF -ENDWHILE -FREE_LUN,lu - -;read in the contents of the file -tab_arr=STRARR(nf,mcnt+2) ;note tab_arr(*,0) EQ FIELD/LABEL name and -;tab_arr(*,1) EQ N_ELEMENTS under each FIELD/LABEL -tab_hold=STRARR(mcnt) - -OPENR,lu,tablefile,/GET_LUN -READF,lu,dum & dum=STRTRIM(dum,2) -WHILE dum NE firstfield DO BEGIN - READF,lu,dum & dum=STRTRIM(dum,2) -ENDWHILE - -i=0 -WHILE i LE nf-1 DO BEGIN - ncnt=-1 - FOR j=0,N_ELEMENTS(enviname)-1 DO BEGIN - epos=STRPOS(STRUPCASE(dum),enviname[j]) - IF (epos NE -1) AND (epos LE 3) THEN ncnt=j - ENDFOR - IF (ncnt EQ 0) OR (ncnt EQ 1) THEN BEGIN - IF STRMID(STRUPCASE(dum),0,3) NE 'PI_' THEN i=i-1 ELSE ecnt=0 - ENDIF ELSE ecnt=0 - IF ncnt NE -1 THEN dum=avdcname[ncnt] ;change the name of the Envisat field to AVDC equivalent - tab_arr[i,0]=dum - REPEAT BEGIN - READF,lu,dum & dum=STRTRIM(dum,2) - IF STRMID(dum,0,1) EQ '=' THEN BEGIN - tab_hold[ecnt]=STRMID(dum,1) ;strip the '=' sign - ;add space between adjacent semi-colons so StrSplit finds correct number of sub-values - REPEAT BEGIN - cpos=STRPOS(tab_hold[ecnt],';;') - IF cpos NE -1 THEN tab_hold[ecnt]=STRMID(tab_hold[ecnt],0,cpos+1)+ $ - ' '+STRMID(tab_hold[ecnt],cpos+1) - ENDREP UNTIL cpos EQ -1 - ecnt=ecnt+1 - ENDIF - ENDREP UNTIL (STRMID(dum,0,1) NE '=') OR (EOF(lu)) - tab_arr[i,1]=STRTRIM(ecnt,2) - tab_arr[i,2:ecnt+1]=tab_hold[0:ecnt-1] - IF i NE nf-1 THEN BEGIN - WHILE (STRMID(dum,0,1) EQ '!') OR (STRMID(dum,0,1) EQ '#') OR (dum EQ '') DO BEGIN - READF,lu,dum & dum=STRTRIM(dum,2) - ENDWHILE - ENDIF - i=i+1 -ENDWHILE -FREE_LUN,lu - -END ;Procedure Read_TableFile - - - -PRO test_file_input,aname,fentry -;Procedure to test that a file name given as an entry to a free text attribute is valid, based on Envisat -;Metadata Guidelines (not currently used by AVDC). Note: No longer permitted under GEOMS guidelines. -; ---------- -;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org -; -; History: -; 20050802: Original IDLCR8HDF Routine - Version 1.0 -; 20061012: Common variable definition WIDGET_WIN added - Version 2.0 -; 20100205: Add RETURN command after all STOP_WITH_ERROR calls, which allows program to return to the -; calling program if the reterr argument is included in the idlcr8hdf call - Version 3.09 -; -; Inputs: aname - Global or Variable Attribute Label -; fentry - Filename entry used as the Global or Variable Attribute value -; -; Outputs: None -; -; Called by: READ_METADATA -; -; Subroutines Called: STOP_WITH_ERROR (if error state detected) -; Possible Conditions for STOP_WITH_ERROR call (plus [line number] where called): -; 1. Syntax of Filename entry incorrect -; 2. Filename entry not found or not usable -; 3. File size is too large - -COMMON WIDGET_WIN - -sfile=4096 ;maximum permitted file size - -;possible error message for this procedure -proname='Test_File_Input procedure: ' -errtxt2=STRARR(3) & lu=-1 -errtxt2[0]='Syntax of Filename entry incorrect: ' -errtxt2[1]='Filename entry not found or not usable: ' -errtxt2[2]='File size is too large (maximum permitted: '+STRTRIM(sfile,2)+' bytes)' - -si=STRPOS(fentry,'"')+1 & ei=STRPOS(fentry,'"',/Reverse_Search)-si -IF ei EQ si THEN BEGIN - STOP_WITH_ERROR,o3[3]+proname+aname+': ',errtxt2[0]+fentry,lu & RETURN -ENDIF -faname=STRMID(fentry,si,ei) -ftest=FILE_TEST(faname,/Read,/Regular) -IF ftest EQ 0 THEN BEGIN - STOP_WITH_ERROR,o3[3]+proname+aname+': ',errtxt2[1]+faname,lu & RETURN -ENDIF -OPENR,fu,faname,/GET_LUN -ftest=FSTAT(fu) & FREE_LUN,fu -IF ftest.size GT sfile THEN BEGIN - STOP_WITH_ERROR,o3[3]+proname+AName+'='+fentry+': ',errtxt2[2],lu & RETURN -ENDIF - -END ;procedure Test_File_Input - - - -PRO geoms_rule_changes, code, in1, in2, in3, in4 -;Procedure to check Metadata for old/redundant rules, labels and/or values and update/report -;as required -; ---------- -;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org -; -; History: -; 20110401: Introduced. Incorporates GEOMS rules changes from v3.0 to v4.0 - Version 4.01 -; 20111220: Add ISO646-US ASCII character set check - Version 4.0b7 -; 20120313: Add UVVIS.DOAS plus additional gases to Code 6 checks - Version 4.0b8 -; 20120703: Bug Fix when checking for non-ASCII characters (rule 10) - Version 4.0b11 -; 20131023: Add GEOMS rule change for MIXING.RATIO[.VOLUME][.MASS], and UNCERTAINTY (rule 5); -; Fixed bug that caused incorrect rep_list_2 values to be written to file (rule 5); -; Stopped checks for illegal characters if ORIGINATOR values are present in the -; TAV file (rule 10) - Version 4.0b16 -; 20131029: Add array of invalid ASCII characters that can be replaced with valid ISO646 US -; ASCII characters (rule 10) - Version 4.0b17 -; 20131029: Modify UNCERTAINTY rules to allow more exceptions (rule 5); Allow for renaming of -; LIDAR.DIAL DATA_SOURCE to LIDAR.WATERVAPORDIAL if conditions met (rule 6) - -; Version 4.0b18 -; 20140226: Add 'DU' to obsolete descriptor value and fix bug causing no new variable name to -; be generated if more than 3 sub-values in the name (rule 5); Add 'RO.SAC.C' to -; the obsolete DATA_SOURCE list (rule 6) - Version 4.0b19 -; 20140521: Account for extra parameter in the UVVIS-DOAS data source when trying to determine -; if file is based on a DATA_TEMPLATE (rule 9) - Version 4.0b21 -; 20140806: Modify UNCERTAINTY rules to allow more exceptions including additional instrument -; types and check on molec cm-2 to allow for prefix values (rule 5) - Version 4.0b22 -; 20140909: Add RO.F3C.FM[1][2][3] to the obsolete list of DATA_SOURCES (replaced by -; F3C.FM[1][2][3]) (rule 6) - Version 4.0b23 -; 20141110: Numerous changes while testing harmonization of EVDC files - additional -; DATA_SOURCEs and units added to identify correct new UNCERTAINTY values, including -; identification of covariance uncertainties; Bug fix when doing checks on -; AIR.CONCENTRATION; LIDAR.BACKSCATTER conversion and units definition added; List -; of non-ASCII characters to be checked expanded; Bug fix when correcting -; for non-ASCII characters when the replacement value has more than one character -; e.g. ' deg. ' - Version 4.0b24 -; 20150409: Strip trailing and leading spaces from DATA_TEMPLATE value when doing checks -; (rule 9) - Version 4.0b28 -; 20160213: Add extra satellite instruments to the obsolete list for DATA_SOURCE (rule 6); -; Fix issue with DATA_TEMPLATE identification (rule 9) - Version 4.0b37 -; 20171121: Fix bug that selected the incorrect DATA_TEMPLATE in some situations (rule 9) -; - Version 4.0b43 -; 20190506: Add FILE_META_VERSION information to INFORMATION/ERROR comment when -; DATA_TEMPLATE value is not as expected; Fix bug that caused a crash if the -; file does not use a DATA_TEMPLATE (rule 9) - Version 4.0b50 -; 20191205: Add rule 11 which does checks on optional VERSION_NAME sub-field of -; DATA_SOURCE - Version 4.0b53 -; 20200709: Fix in option 5 when looking for obsolete variable names. Change from doing a -; STRPOS search to looking for an exact change for DATA_VARIABLES_01 names due to -; introduction of DRY.AIR.... names to the TAV - Version 4.0b55 -; 20201020: Change infotxt status from 1 to 2 in option 5; Change to LIDAR.H2O data source -; if needing to update out-of-date H2O LIDAR DATA_SOURCE values in option 6 -; - Version 4.0b56 -; 20220805: Change INFOTXT message in rule 10 (non-ASCII character check) so it includes -; the entire Metadata entry - Version 4.0b60 -; -; Inputs: code - Integer value identifying type of check to carry out -; in1 - First set of inputs required for checks (optional, dependent on code value) -; in2 - Second set of inputs required for checks (optional, dependent on code value) -; in3 - Third set of inputs required for checks (optional, dependent on code value) -; in4 - Fourth set of inputs required for checks (optional, dependent on code value) -; -; Outputs: Any or all of the inputs can be changed dependent on the code value -; -; Called by: READ_METADATA; CHECK_METADATA; SET_UP_STRUCTURE; FIND_HDF_FILENAME -; -; Subroutines Called: INFOTXT_OUTPUT -; Information Conditions (when the program is able to make changes): -; 1. Attribute entry is not GEOMS compliant, and removed from the metadata saved to the output file -; 2. Dataset name renamed according to new DATETIME reporting conventions -; 3. Double underscore replaced with a single underscore in the variable name -; 4. VAR_DEPEND value made self-referencing for axis variable -; 5. Axis variable cannot be dependent on another variable -; 6. VAR_UNITS=MJD2000 not GEOMS compliant, changed to VAR_UNITS=MJD2K -; 7. DATA_FILE_VERSION must be in the form 'nnn' -; 8. Obsolete Dataset name renamed -; 9. Obsolete DATA_SOURCE value renamed -; 10. Obsolete FILE_ACCESS values renamed -; 11. Rename obsolete VAR_UNITS=DIMENSIONLESS/NONE values -; 12. DATA_TEMPLATE field is not present in the TAV file -; 13. DATA_TEMPLATE value renamed based on DATA_SOURCE value -; 14. Entry must only include valid characters from the ISO646-US ASCII character set -; -; Code 0: Check for Attributes that have become redundant between v3.0 and v4.0. -; Inputs: in1=mh, in2=mhgood -; Code 1: Check for redundant _START/STOP/INTEGRATION.TIME variable names and replace with -; DATETIME.START/STOP/INTEGRATION.TIME (for single occurences only). Also check -; for double underscores in the variable names and replace with single underscore -; Inputs: in1=vncnt, in2=dv -; Code 2: Check for Axis Variables and, if found, make VAR_DEPEND=INDEPENDENT self-referencing -; Inputs: in1=vardeptest, in2=vn[vc], in3=resvd[1], in4=holdvd -; Code 3: Change VAR_UNIT value MJD2000 to MJD2K -; Inputs: in1=meta_arr[i], in2=res[1], in3=writeonce -; Code 4: Do DATA_FILE_VERSION checks -; Inputs: in1=dfvv (DATA_FILE_VERSION value) -; Code 5: Look for obsolete DATA_VARIABLES (based on list) and, if possible, modify values to -; GEOMS compliance -; Inputs: in1=vncnt, in2=dv, in3=vuv, in4=data_source -; Code 6: Look for obsolete DATA_SOURCE (based on list) and, if possible, modify values to -; GEOMS compliance -; Inputs: in1=vncnt, in2=dv, in3=data_source -; Code 7: Looks for and replaces obsolete CALVAL and NDSC FILE_ACCESS values -; Inputs: in1=facnt, in2=fav -; Code 8: Looks for VAR_UNITS=DIMENSIONLESS or NONE and replace with '' or '1' -; Inputs: in1=vucnt, in2=vuv, in3=vdv -; Code 9: Do DATA_TEMPLATE/DATA_QUALITY checks -; Inputs: in1=m_v0, in2=m_v1, in3=ga_chk -; Code 10: Do checks on ISO646-US ASCII character set -; Inputs: in1=meta_arr or dtest, in2=vn[vc] (Variable Name for datasets only) -; Code 11: Do checks on DATA_VERSION_NAME part of DATA_SOURCE value -; Inputs: in1=DATA_VERSION_NAME, in2=meta_arr - -COMMON TABLEDATA -COMMON METADATA -COMMON DATA -COMMON WIDGET_WIN - -CASE 1 OF - code EQ 0: BEGIN - ;Check for redundant attributes - redundant=['DATA_TYPE','DATA_LEVEL','VAR_MONOTONE','VAR_DIMENSION',$ - 'VAR_AVG_TYPE','VIS_LABEL','VIS_FORMAT','VIS_PLOT_TYPE',$ - 'VIS_SCALE_TYPE','VIS_SCALE_MIN','VIS_SCALE_MAX'] - nrd=N_ELEMENTS(redundant) - - FOR i=0,nrd-1 DO BEGIN - rdl=STRLEN(redundant[i]) - fai=WHERE(STRMID(STRUPCASE(in1),0,rdl) EQ redundant[i],facnt) - IF facnt NE 0 THEN BEGIN - in2[fai]=0 ;Metadata lines not wanted - test1=(STRMID(redundant[i],0,3) EQ 'VAR') OR (STRMID(redundant[i],0,3) EQ 'VIS') - IF test1 THEN att_type='Variable' ELSE att_type='Global' - infotxt='2 '+redundant[i]+' not a GEOMS '+att_type+' attribute|.' - infotxt=infotxt+' Removed from the metadata saved to the output file' - INFOTXT_OUTPUT, infotxt - ENDIF - ENDFOR - END ;Case 0 - - code EQ 1: BEGIN - ;Check for redundant _START/STOP sub-values and replace with - ;DATETIME.START/STOP/INTEGRATION.TIME (for single occurences only) - time_attr=['START','STOP','INTEGRATION','RESOLUTION'] - n_tim=N_ELEMENTS(time_attr) - iscnt=0 ;Used for RESOLUTION/INTEGRATION.TIME checks - FOR i=0,n_tim-1 DO BEGIN - si=WHERE(STRPOS(in2,'_'+time_attr[i]+'.TIME') NE -1,scnt) - IF (scnt EQ 1) AND (iscnt EQ 0) THEN BEGIN ;change name and also relevant VAR_NAME if present - IF i EQ 2 THEN iscnt=1 ;used in the event both INTEGRATION and RESOLUTION.TIME are present - vnchange[si[0]]=in2[si[0]] - IF i GE 2 THEN in2[si[0]]=time_attr[2]+'.TIME' $ - ELSE in2[si[0]]='DATETIME.'+time_attr[i] - IF qa_yes THEN itxt=' expected to be ' ELSE itxt=' renamed ' - infotxt='2 Dataset name '+vnchange[si[0]]+itxt+in2[si[0]] - INFOTXT_OUTPUT,infotxt - ENDIF - ENDFOR - ;Check for double underscore and replace with single underscore - FOR i=0,in1-1 DO BEGIN - IF STRPOS(in2[i],'__') NE -1 THEN BEGIN - vnchange[i]=in2[i] - infotxt=STRARR(2) - infotxt[0]='2 Double underscore present in Variable Name '+in2[i]+'|' - infotxt[1]=' replaced with a single underscore' - INFOTXT_OUTPUT, infotxt - vns=STRSPLIT(in2[i],'_',/Extract,COUNT=n_vns) - FOR j=0,n_vns-1 DO IF j EQ 0 THEN in2[i]=vns[j] ELSE in2[i]=in2[i]+'_'+vns[j] - ENDIF - ENDFOR - END ;Case 1 - - code EQ 2: BEGIN ;Check for and Apply GEOMS rule changes for INDEPENDENT and axis variables - ;RULE: Any variable that is mentioned in VAR_DEPEND is by definition an axis variable - vdtest=STRUPCASE(in1) & vnv=STRUPCASE(in2) & vdv=STRUPCASE(in3) ;vdtest is an array of VAR_DEPEND variables - avi=WHERE(vnv EQ vdtest,avcnt) ;i.e. is the VAR_NAME an axis variable - IF avcnt NE 0 THEN BEGIN ;possible axis variable so do axis variable checks - test1=(vdv EQ 'INDEPENDENT') OR (vdv EQ vnv) ;value is independent or self-referencing - test2=(vdv NE 'INDEPENDENT') AND (vdv NE vnv) AND (vdv NE 'CONSTANT') ;neither independent, self-referencing, nor constant - test6=(vdv EQ 'CONSTANT') AND (vnv EQ 'DATETIME') ;i.e. VAR_NAME=DATETIME and VAR_DEPEND=CONSTANT - IF (test1) OR (test6) THEN BEGIN - IF vdv NE vnv THEN BEGIN - infotxt='2 '+in4+' should be self-referencing for axis variable VAR_NAME='+vnv+'|.' - infotxt=infotxt+' VAR_DEPEND value changed in metadata' - INFOTXT_OUTPUT,infotxt - in3=vnv ;self-referencing VAR_DEPEND value - ENDIF - ENDIF ELSE IF test2 THEN BEGIN - in4[0]='E1' ;i.e. axis variable cannot be dependent on another variable so return error - ENDIF - ENDIF ELSE BEGIN ;not an axis variable so ensure VAR_DEPEND value is not self-referencing - IF vdv EQ vnv THEN in4[0]='E3' ;i.e. VAR_DEPEND value cannot be self-referencing - ENDELSE - END ;Case 2 - - code EQ 3: BEGIN ;Change MJD2000 to MJD2K - in1='VAR_UNITS=MJD2K' & in2='MJD2K' - IF in3 EQ 0 THEN BEGIN ;First instance of MJD2000 so include comment - infotxt='2 VAR_UNITS=MJD2000 not GEOMS compliant|. Changed to VAR_UNITS=MJD2K' - INFOTXT_OUTPUT,infotxt - ENDIF - END - - code EQ 4: BEGIN ;checks on DATA_FILE_VERSION - in1=STRTRIM(in1,2) - vp=0 & errval=0 & in1h=in1 - ON_IOERROR,TypeConversionError - IF STRMID(STRUPCASE(in1),0,1) EQ 'V' THEN BEGIN - vp=1 & in1=STRMID(in1,1) - ENDIF - dpos=STRPOS(in1,'.') - valid=0 ;to check for conversion error - IF dpos NE -1 THEN BEGIN - IF LONG(STRMID(in1,dpos+1)) EQ 0L THEN atxt='' ELSE atxt=STRMID(in1,dpos+1) - in1=STRMID(in1,0,dpos)+atxt - IF FIX(in1) GE 100 THEN in1=STRMID(in1,0,dpos) ;just use version up to the decimal point - ENDIF - IF (FIX(in1) GT 999) OR (FIX(in1) LT 1) THEN errval=1 - valid=1 ;OK if got to here - - TypeConversionError: ;Check for invalid DATA_FILE_VERSION value - IF (valid EQ 0) OR (errval EQ 1) THEN BEGIN - infotxt='3 DATA_FILE_VERSION='+in1h+' must be in the form ''nnn''' - INFOTXT_OUTPUT, infotxt - in1=in1h - ENDIF ELSE BEGIN ;format value correctly (nnn) - IF FIX(in1) LT 10 THEN in1='00'+STRTRIM(FIX(in1),2) $ - ELSE IF FIX(in1) LT 100 THEN in1='0'+STRTRIM(FIX(in1),2) $ - ELSE in1=STRTRIM(in1,2) - IF (vp EQ 1) OR (in1h NE in1) THEN BEGIN - infotxt='2 DATA_FILE_VERSION must be in the form ''nnn''|. Renamed from ' - infotxt=infotxt+in1h+' to '+in1 - INFOTXT_OUTPUT, infotxt - ENDIF - ENDELSE - END - - code EQ 5: BEGIN ;Look for obsolete DATA_VARIABLES - ;Notes = what to do about ALTITUDE.LAYER.INDEX, CHL.A.CONCENTRATION, - ; PRESSURE.LEVEL.INDEX (still in 04R001), TSM.CONCENTRATION - ;List of obsolete DATA_VARIABLE_01 values - obs_list_1=['AIR.TEMPERATURE','TEMPERATURE.AIR','COLUMN.VERTICAL','H2O.RELATIVE.HUMIDITY',$ - 'PRESSURE.SURFACE','TEMPERATURE.SURFACE','TEMPERATURE.INTERNAL.BOX',$ - 'TEMPERATURE.INTERNAL.INSTRUMENT','TEMPERATURE.SEA.SURFACE','AIR.NUMBER.DENSITY',$ - 'SEA.SURFACE.TEMPERATURE','OPACITY.ATMOSPHERIC.VERTICAL'] - ;Corresponding list of replacement DATA_VARIABLE_1 values (direct replacement) - rep_list_1=['TEMPERATURE','TEMPERATURE','COLUMN','HUMIDITY.RELATIVE',$ - 'SURFACE.PRESSURE','SURFACE.TEMPERATURE','INTERNAL.BOX.TEMPERATURE',$ - 'INTERNAL.INSTRUMENT.TEMPERATURE','SEA.SURFACE.TEMPERATURE','NUMBER.DENSITY',$ - 'SEA.SURFACE.TEMPERATURE.SKIN','OPACITY.ATMOSPHERIC'] - ;Obsolete DATA_VARIABLE_01 which need special handling - obs_list_spec_1=['.AMF','.AVK','.CONCENTRATION','.MIXING.RATIO'] - ;Obsolete DATA_VARIABLE_02 values - obs_list_2=['SOLAR','VERTICAL.SOLAR',$ - 'EMISSION.VERTICAL','VERTICAL.EMISSION',$ - 'VERTICAL','SLANT','SOLAR.OCCULTATION','VERTICAL.ZENITH','VERTICAL.NADIR',$ - 'VERTICAL.LUNAR','VERTICAL.SOLAR.FOCUS'] - ;[0,1] for FTIR/UVVIS (except Brewer and Dobson) only, [2,3] for MWR only - ;Corresponding list of replacement DATA_VARIABLE_2 values (direct replacement) - rep_list_2=['ABSORPTION.SOLAR|SCATTER.SOLAR','ABSORPTION.SOLAR|SCATTER.SOLAR',$ - 'EMISSION','EMISSION',$ - '','','OCCULTATION.SOLAR','ZENITH','NADIR',$ - 'ABSORPTION.LUNAR','ABSORPTION.SOLAR.FOCUS'] - ;Replacements for .CONCENTRATION - dependent on VAR_UNITS - rep_list_conc=['.MIXING.RATIO.VOLUME','.MIXING.RATIO.MASS','.NUMBER.DENSITY','.PARTIAL.PRESSURE'] - ;Conditional Miscellaneous changes - misc_list_1=['TEMPERATURE_SKIN','AIR.MASS.FACTOR'] - ;Corresponding change to miscellaneous variables - misc_list_2=['SEA.SURFACE.TEMPERATURE.SKIN','O3.COLUMN_AMF'] - ;Obsolete DATA_VARIABLE_03 descriptors (may or may not be able to fix these) - obs_list_3=['DU','UNCERTAINTY.STDEV','UNCERTAINTY.RMS','UNCERTAINTY.RELATIVE','UNCERTAINTY.TOTAL', $ - 'UNCERTAINTY.RANDOM','UNCERTAINTY.SYSTEMATIC'] - ;Corresponding list of replacement DATA_VARIABLE_03 descriptors - rep_list_3=['','UNCERTAINTY.COMBINED.STANDARD','UNCERTAINTY.COMBINED.STANDARD', $ - 'UNCERTAINTY.COMBINED.STANDARD.RELATIVE','UNCERTAINTY.COMBINED.STANDARD', $ - 'UNCERTAINTY.RANDOM.STANDARD','UNCERTAINTY.SYSTEMATIC.STANDARD'] - rep_list_4=['','','','','UNCERTAINTY.COMBINED.COVARIANCE','UNCERTAINTY.RANDOM.COVARIANCE', $ - 'UNCERTAINTY.SYSTEMATIC.COVARIANCE'] - vn_v=STRARR(in1,3) & vn_new=STRARR(in1) - ;Separate out Variable Name/[Mode or Descriptor]/[Descriptor] - FOR i=0,in1-1 DO BEGIN - res=STRSPLIT(in2[i],'_',/EXTRACT,COUNT=nres) - IF nres GT 3 THEN nres=3 - FOR j=0,nres-1 DO vn_v[i,j]=res[j] - ENDFOR - ;Test for values in obs_list_1 - FOR i=0,N_ELEMENTS(obs_list_1)-1 DO BEGIN - ;ri=WHERE(STRPOS(STRUPCASE(vn_v[*,0]),obs_list_1[i]) NE -1,rcnt) - ;Change to exact match from v4.0b55 (20200709) - due to introduction of DRY.AIR.... variable names - ri=WHERE(STRUPCASE(vn_v[*,0]) EQ obs_list_1[i],rcnt) - IF rcnt NE 0 THEN BEGIN ;obsolete values found so replace - vlen=STRLEN(obs_list_1[i]) - FOR j=0,rcnt-1 DO BEGIN - change_val=1 - ;Special case for SEA.SURFACE.TEMPERATURE (only change if mode is _SKIN) - IF obs_list_1[i] EQ 'SEA.SURFACE.TEMPERATURE' THEN BEGIN - IF STRUPCASE(vn_v[ri[j],1]) EQ 'SKIN' THEN vn_v[ri[j],1]='' ELSE change_val=0 - ENDIF - IF change_val EQ 1 THEN BEGIN - vpos=STRPOS(STRUPCASE(vn_v[ri[j],0]),obs_list_1[i]) - vn_v[ri[j],0]=STRMID(vn_v[ri[j],0],0,vpos)+rep_list_1[i]+STRMID(vn_v[ri[j],0],vpos+vlen) - ENDIF - ENDFOR - ENDIF - ENDFOR - ;Special cases for DATA_VARIABLE_01 values .AMF, .AVK, .CONCENTRATION, and .MIXING.RATIO - nols1=N_ELEMENTS(obs_list_spec_1) - FOR i=0,nols1-1 DO BEGIN - ri=WHERE(STRPOS(STRUPCASE(vn_v[*,0]),obs_list_spec_1[i]) NE -1,rcnt) - IF (i EQ nols1-1) AND (rcnt NE 0) THEN BEGIN - ;do check for .MIXING.RATIO. in which case no correction required - rxi=WHERE(STRPOS(STRUPCASE(vn_v[ri,0]),'.MIXING.RATIO.') EQ -1,rxcnt) - IF rxcnt NE 0 THEN BEGIN ;some values are just .MIXING.RATIO so correct these only - rcnt=rxcnt & ri=ri[rxi] - ENDIF ELSE rcnt=0 - ENDIF - IF rcnt NE 0 THEN BEGIN ;obsolete values found - vlen=STRLEN(obs_list_spec_1[i]) - IF i LE 1 THEN BEGIN ;move .AMF and .AVK to DESCRIPTOR section (2 or 3) - FOR j=0,rcnt-1 DO BEGIN - vpos=STRPOS(STRUPCASE(vn_v[ri[j],0]),obs_list_spec_1[i]) - ai=WHERE(vn_v[ri[j],*] EQ '',acnt) - IF acnt NE 0 THEN BEGIN - ;the DESCRIPTOR field is empty, so can convert value OK (otherwise no change made) - vn_v[ri[j],0]=STRMID(vn_v[ri[j],0],0,vpos) - vn_v[ri[j],ai[0]]=STRMID(obs_list_spec_1[i],1) ;write to first empty field - ENDIF - ENDFOR - ENDIF ELSE BEGIN - ;.CONCENTRATION or .MIXING.RATIO so attempt to identify actual type of measurement - ;(mixing ratio etc) by looking at corresponding VAR_UNIT values - mrvi=WHERE((STRPOS(STRLOWCASE(in3[ri]),'pp') NE -1) OR $ - (STRPOS(STRLOWCASE(in3[ri]),'m-1 sr-1') NE -1),mrvcnt) ;testing for volume mixing ratio - mrmi=WHERE(STRPOS(STRLOWCASE(in3[ri]),' g ') NE -1,mrmcnt) ;test for mass mixing ratio - ndi=WHERE(STRPOS(STRLOWCASE(in3[ri]),'mol') NE -1,ndcnt) ;test for number density - ppi=WHERE(STRPOS(STRLOWCASE(in3[ri]),'pa') NE -1,ppcnt) ;test for partial pressure - IF i EQ nols1-1 THEN cnti=[mrvcnt,mrmcnt,0,0] ELSE cnti=[mrvcnt,mrmcnt,ndcnt,ppcnt] - ci=WHERE(cnti GT 0,ccnt) - IF ccnt EQ 1 THEN BEGIN ;type of measurement successfully found - ;Note: not set up to handle if more than one type of measurement found in the file - vlen=STRLEN(obs_list_spec_1[i]) - FOR j=0,rcnt-1 DO BEGIN - IF (STRUPCASE(vn_v[ri[j],0]) EQ 'AIR.CONCENTRATION') AND (ci[0] EQ 2) THEN $ - ;Do special case check for AIR.CONCENTRATION - can only change to NUMBER.DENSITY - vn_v[ri[j],0]='NUMBER.DENSITY' $ - ELSE BEGIN - vpos=STRPOS(STRUPCASE(vn_v[ri[j],0]),obs_list_spec_1[i]) - vn_v[ri[j],0]=STRMID(vn_v[ri[j],0],0,vpos)+rep_list_conc[ci[0]]+STRMID(vn_v[ri[j],0],vpos+vlen) - ENDELSE - ENDFOR - ENDIF - ENDELSE - ENDIF - ENDFOR - FOR i=0,N_ELEMENTS(obs_list_2)-1 DO BEGIN ;Check for obsolete mode values - ri=WHERE(STRUPCASE(vn_v[*,1]) EQ obs_list_2[i],rcnt) - IF rcnt NE 0 THEN BEGIN - test1=(i LE 1) AND (STRPOS(STRUPCASE(in4),'FTIR') NE -1) - test2=(i LE 1) AND (STRPOS(STRUPCASE(in4),'UVVIS') NE -1) AND $ - (STRPOS(STRUPCASE(in4),'DOBSON') EQ -1) AND (STRPOS(STRUPCASE(in4),'BREWER') EQ -1) - test3=((i EQ 2) OR (i EQ 3)) AND ((STRPOS(STRUPCASE(in4),'MWR') NE -1) OR $ - (STRPOS(STRUPCASE(in4),'MICROWAVE') NE -1)) - IF i LE 1 THEN mi=STRSPLIT(rep_list_2[i],'|',/EXTRACT) - ;test1, test2, and test3 are special case obsolete values - IF test1 THEN vn_v[ri,1]=mi[0] $ - ELSE IF test2 THEN vn_v[ri,1]=mi[1] $ - ELSE IF test3 THEN vn_v[ri,1]=rep_list_2[i] $ ;direct replacement - ELSE BEGIN ;all other obsolete mode values - IF i LE 1 THEN vn_v[ri,1]=mi[0] ELSE vn_v[ri,1]=rep_list_2[i] ;direct replacement - IF i EQ 5 THEN BEGIN ;SLANT (if column measurement then add to main variable name) - FOR j=0,rcnt-1 DO BEGIN - IF STRLEN(vn_v[ri[j],0])-STRPOS(vn_v[ri[j],0],'COLUMN') EQ 6 THEN $ - vn_v[ri[j],0]=vn_v[ri[j],0]+'.'+obs_list_2[i] - ENDFOR - ENDIF - ENDELSE - ENDIF - ENDFOR - ;Check conditional miscellaneous changes - FOR i=0,N_ELEMENTS(misc_list_1)-1 DO BEGIN - IF i EQ 0 THEN ri=WHERE(STRPOS(STRUPCASE(in2),misc_list_1[i]) NE -1,rcnt) $ - ELSE ri=WHERE(STRUPCASE(in2) EQ misc_list_1[i],rcnt) - IF rcnt NE 0 THEN BEGIN - IF i EQ 0 THEN BEGIN ;ensure DATA_SOURCE is BUOY before changing - IF STRPOS(STRUPCASE(in4),'BUOY') NE -1 THEN BEGIN - FOR j=0,rcnt-1 DO BEGIN - vn_v[ri[j],0]=misc_list_2[i] & vn_v[ri[j],1]='' - ENDFOR - ENDIF - ENDIF ELSE BEGIN ;ensure O3.COLUMN is also present as a DATA_VARIABLE before changing - mi=WHERE(STRUPCASE(vn_v[*,0]) EQ 'O3.COLUMN',mcnt) - IF mcnt NE 0 THEN BEGIN - vn_v[ri[0],0]='O3.COLUMN' & vn_v[ri[0],1]='AMF' - ENDIF - ENDELSE - ENDIF - ENDFOR - - FOR i=0,N_ELEMENTS(obs_list_3)-1 DO BEGIN ;Check for obsolete descriptor values (UNCERTAINTY) - ;acceptable units for identifying correct replacement name (test1 instruments only) - vuok=['ppv','ppmv','ppbv','pptv','k','du','molec cm-2','molec cm-3','degc','m-1 sr-1','dimensionless'] - vuok2=['ppv2','ppmv2','ppbv2','pptv2'] ;covariance - test1=(STRPOS(STRUPCASE(in4),'MWR') NE -1) OR (STRPOS(STRUPCASE(in4),'MICROWAVE') NE -1) OR $ - (STRPOS(STRUPCASE(in4),'HAGAR') NE -1) OR (STRPOS(STRUPCASE(in4),'MIPAS.STR') NE -1) OR $ - (STRPOS(STRUPCASE(in4),'SIOUX') NE -1) OR (STRPOS(STRUPCASE(in4),'UVVIS.AMAXDOAS') NE -1) OR $ - (STRPOS(STRUPCASE(in4),'UVVIS.SAOZ') NE -1) OR (STRPOS(STRUPCASE(in4),'ATMOINSPECTOR') NE -1) OR $ - (STRPOS(STRUPCASE(in4),'FISH') NE -1) OR (STRPOS(STRUPCASE(in4),'AMON') NE -1) OR $ - (STRPOS(STRUPCASE(in4),'LPMA') NE -1) OR (STRPOS(STRUPCASE(in4),'SALOMON') NE -1) OR $ - (STRPOS(STRUPCASE(in4),'RADIOMETER.IR.CIMEL') NE -1) OR (STRPOS(STRUPCASE(in4),'UVVIS') NE -1) OR $ - (STRPOS(STRUPCASE(in4),'SPECTROMETER') NE -1) OR (STRPOS(STRUPCASE(in4),'LIDAR.WATERVAPORRAMAN_UNIVAQ') NE -1) - ;check for DATA_SOURCE where CoVariance as well as Standard uncertainties are reported - test2=(STRPOS(STRUPCASE(in4),'FTIR.CO_KIT') NE -1) OR (STRPOS(STRUPCASE(in4),'FTIR.CO_ULG') NE -1) OR $ - (STRPOS(STRUPCASE(in4),'FTIR.HNO3_KIT') NE -1) OR (STRPOS(STRUPCASE(in4),'FTIR.HNO3_ULG') NE -1) OR $ - (STRPOS(STRUPCASE(in4),'FTIR.N2O_KIT') NE -1) OR (STRPOS(STRUPCASE(in4),'FTIR.N2O_ULG') NE -1) OR $ - (STRPOS(STRUPCASE(in4),'FTIR.NO2_KIT') NE -1) OR (STRPOS(STRUPCASE(in4),'FTIR.NO2_ULG') NE -1) - vnvi=1 ;determines the location index of the descriptor variable - ri=WHERE(STRUPCASE(vn_v[*,1]) EQ obs_list_3[i],rcnt) - IF rcnt EQ 0 THEN BEGIN - vnvi=2 & ri=WHERE(STRUPCASE(vn_v[*,2]) EQ obs_list_3[i],rcnt) - ENDIF - IF rcnt NE 0 THEN BEGIN - IF i LE 3 THEN vn_v[ri,vnvi]=rep_list_3[i] $ ;direct replacement - ELSE BEGIN ;need to identify whether the UNITS are % or fit vuok criteria - FOR j=0,rcnt-1 DO BEGIN - IF STRTRIM(in3[ri[j]],2) EQ '%' THEN vn_v[ri[j],vnvi]=rep_list_3[i]+'.RELATIVE' $ - ELSE BEGIN - ;test for Standard Uncertainties - vi=WHERE(STRTRIM(STRLOWCASE(in3[ri[j]]),2) EQ vuok,vcnt) - IF vcnt EQ 0 THEN $ ;do test for [Prefix]molec cm-2/molec cm-3 - IF (STRPOS(STRLOWCASE(in3[ri[j]]),'molec cm-2') NE -1) OR $ - (STRPOS(STRLOWCASE(in3[ri[j]]),'molec cm-3') NE -1) THEN vcnt=1 - IF ((test1) OR (test2)) AND (vcnt NE 0) THEN $ can assume that the given error values are Standard Deviation - vn_v[ri[j],vnvi]=rep_list_3[i] - ;test for Covariance Uncertainties - vi=WHERE(STRTRIM(STRLOWCASE(in3[ri[j]]),2) EQ vuok2,vcnt) - IF (test2) AND (vcnt NE 0) THEN vn_v[ri[j],vnvi]=rep_list_4[i] - ENDELSE - ENDFOR - ENDELSE - ENDIF - ENDFOR - ;create any comments for INFOTXT_OUTPUT - FOR i=0,in1-1 DO BEGIN - ;put full DATA_VARIABLE names back together - vn_new[i]=vn_v[i,0] - FOR j=1,2 DO IF vn_v[i,j] NE '' THEN vn_new[i]=vn_new[i]+'_'+vn_v[i,j] - ;Check to see if it is different to the original DATA_VARIABLE - IF vn_new[i] NE in2[i] THEN BEGIN - vnchange[i]=in2[i] & in2[i]=vn_new[i] - IF qa_yes THEN itxt=' expected to be ' ELSE itxt=' renamed ' - infotxt='2 Dataset name '+vnchange[i]+itxt+in2[i] - INFOTXT_OUTPUT,infotxt - ENDIF - ENDFOR - END - - code EQ 6: BEGIN ;Look for obsolete DATA_SOURCE - ;List of obsolete DATA_SOURCE_01 values - obs_list=['RO.CHAMP','RO.SAC.C','RO.F3C.FM','RO.CNOFS','RO.GRACE.A','FTIR_', $ - 'UVVIS.DOAS_','MICROWAVE.RADIOMETER','LIDAR.BACKSCATTER',$ - 'LIDAR.DIAL','LIDAR.OLEX','LIDAR.RIEGL','LIDAR.RMR'] - ;Corresponding replacement list - rep_list=['CHAMP','SAC.C','F3C.FM','CNOFS','GRACE.A','FTIR.','UVVIS.DOAS.','MWR.','LIDAR.'] - ;Possible list of species for the above instruments (note: gases will take precedence - ;over Aerosol and Temperature, o/w the first species found will be considered the - ;primary species measured) - spc_list=['BrO.','C2H2.','C2H6.','CCl2F2.','CCl3F.','CH4.','CHF2Cl.','CHOCHO.','ClO.','ClONO2.','CO.',$ - 'CO2.','COF2.','H2CO.','H2O.','HCFC22.','HCl.','HCN.','HCOOH.','HF.','HNO3.','HONO.','IO.',$ - 'N2O.','NO.','NO2.','O3.','OClO.','OCS.','SF6.','SO2.','AEROSOL','TEMPERATURE'] - - oli=-1 ;will change to obs_list index value if obsolete DATA_SOURCE_01 found - n_obsl=N_ELEMENTS(obs_list) - lid_i=n_obsl-5 ;needs to be start index of the lidar measurements in obs_list - spc_i=n_obsl-8 ;needs to be start index of the instruments requiring species information in obs_list - uv_i=n_obsl-7 ;needs to be UVVIS.DOAS index in obs_list - FOR i=0,N_ELEMENTS(obs_list)-1 DO IF STRPOS(STRUPCASE(in3),obs_list[i]) NE -1 THEN oli=i - IF oli NE -1 THEN BEGIN ;obsolete DATA_SOURCE value found - IF oli GE lid_i THEN oli=lid_i ;to correspond with rep_list index for lidar - pri=-1 ;will change if species found - IF oli GE spc_i THEN BEGIN ;if not an RO DATA_SOURCE then need to determine species - n_sl=N_ELEMENTS(spc_list) - ;Look through VAR_NAME values to try and identify new DATA_SOURCE from species list - spc_found=INTARR(in1)-1 ;identifies index of found species names in the Datasets - FOR i=0,n_sl-1 DO BEGIN - fi=WHERE(STRPOS(STRMID(in2,0,STRLEN(spc_list[i])),spc_list[i]) NE -1,fcnt) - IF fcnt NE 0 THEN spc_found[fi]=i - ENDFOR - ;Check list of found species - don't include Aerosol and Temperature - fi=WHERE((spc_found NE -1) AND (spc_found LT n_sl-2),fcnt) - IF fcnt NE 0 THEN pri=spc_found[fi[0]] $ ;primary species index determined - ELSE IF (oli EQ uv_i) OR (oli EQ lid_i) THEN BEGIN ;Test for Temperature (Lidar Only) and Aerosol - fi=WHERE(spc_found NE -1,fcnt) - IF fcnt NE 0 THEN BEGIN - pri=MIN(spc_found[fi]) ;i.e. Aerosol takes precedence over Temperature - IF (oli EQ uv_i) AND (pri EQ n_sl-1) THEN pri=-1 ;UVVIS.DOAS can't be Temperature - ENDIF - ENDIF - ENDIF ELSE BEGIN ;cases with straight replacement - old_ds=STRTRIM(in3,2) & in3=rep_list[oli]+STRMID(old_ds,STRLEN(obs_list[oli])) & pri=-2 - ENDELSE - IF pri NE -1 THEN BEGIN ;update DATA_SOURCE - IF pri GE 0 THEN BEGIN - old_ds=in3 - ;check special cases for LIDAR water vapor measurements - IF (spc_list[pri] EQ 'H2O.') AND (STRPOS(STRUPCASE(in3),'LIDAR.DIAL') NE -1) THEN $ - in3='H2O' $ - ELSE IF (spc_list[pri] EQ 'H2O.') AND (STRPOS(STRUPCASE(in3),'LIDAR.BACKSCATTER') NE -1) THEN $ - in3='H2O' $ - ELSE in3=spc_list[pri] - IF STRPOS(in3,'.') NE -1 THEN in3=STRMID(in3,0,STRLEN(in3)-1) - in3=rep_list[oli]+in3+STRMID(old_ds,STRPOS(old_ds,'_')) - ENDIF - IF qa_yes THEN itxt=' expected to be ' ELSE itxt=' renamed ' - infotxt='2 DATA_SOURCE value '+old_ds+itxt+in3 - INFOTXT_OUTPUT,infotxt - ENDIF - ENDIF - END - - code EQ 7: BEGIN ;Look for obsolete CALVAL and NDSC FILE_ACCESS values - obs_list=['CALVAL','NDSC'] - rep_list=['EVDC','NDACC'] - - FOR i=0,in1-1 DO BEGIN - oi=WHERE(in2[i] EQ obs_list,ocnt) - IF ocnt NE 0 THEN BEGIN - IF qa_yes THEN itxt=' expected to be ' ELSE itxt=' renamed ' - infotxt='2 FILE_ACCESS value '+in2[i]+itxt+rep_list[oi[0]] - INFOTXT_OUTPUT,infotxt - in2[i]=rep_list[oi[0]] - ENDIF - ENDFOR - END - - code EQ 8: BEGIN ;Look for VAR_UNITS=DIMENSIONLESS or NONE and replace with '' or '1' - vuv=STRUPCASE(in2) - FOR i=0,in1-1 DO BEGIN - IF (vuv[i] EQ 'DIMENSIONLESS') OR (vuv[i] EQ 'NONE') THEN BEGIN - IF in3[i] EQ 'STRING' THEN newv='' ELSE newv='1' - IF qa_yes THEN itxt=' expected to be ' ELSE itxt=' renamed ' - infotxt='2 VAR_UNITS='+vuv[i]+itxt+'VAR_UNITS='+newv+' based on VAR_DATA_TYPE='+in3[i] - INFOTXT_OUTPUT,infotxt - in2[i]=newv - ENDIF - ENDFOR - END - - code EQ 9: BEGIN ;DATA_QUALITY/DATA_TEMPLATE checks - attr_arr_glob_prov=['DATA_TEMPLATE','DATA_QUALITY'] - geoms_te='' & meta_te='' & std_txt=' Required Metadata Template: ' - ;Check if DATA_TEMPLATE field is present in the TAV file - dti=WHERE(tab_arr[*,0] EQ attr_arr_glob_prov[0],dtcnt) - ;Check if DATA_TEMPLATE label is present in the Metadata - gi=WHERE(in1 EQ attr_arr_glob_prov[0],gcnt) - IF gcnt EQ 1 THEN IF in2[gi[0]] NE '' THEN meta_te=STRUPCASE(STRTRIM(in2[gi[0]],2)) - IF dtcnt NE 0 THEN tab_arr_sub=tab_arr[dti[0],2:FIX(tab_arr[dti[0],1]+1)] ;Valid Template values from TAV file - IF dtcnt EQ 0 THEN BEGIN - infotxt='3'+std_txt+'DATA_TEMPLATE field is not present in the TAV file. Checks cannot be performed' - INFOTXT_OUTPUT,infotxt - std_txt='' - ENDIF ELSE IF meta_te NE '' THEN BEGIN - ;check if value is present in the TAV file - ci=WHERE(STRUPCASE(tab_arr_sub) EQ meta_te,ccnt) - IF ccnt NE 0 THEN BEGIN - geoms_te=tab_arr_sub[ci[0]] & in2[gi[0]]=geoms_te - infotxt='0'+std_txt+geoms_te - INFOTXT_OUTPUT,infotxt - std_txt='' - ENDIF - ENDIF - IF std_txt NE '' THEN BEGIN ;template value not present in the metadata or value not in the TAV file - ;Determine DATA_TEMPLATE value based on DATA_SOURCE value (if applicable) - di=WHERE(in1 EQ 'DATA_SOURCE',dcnt) - IF dcnt EQ 1 THEN BEGIN ;DATA_SOURCE found - ;Extract DATA_SOURCE value - IF in2[di[0]] NE '' THEN BEGIN - res=STRSPLIT(STRTRIM(in2[di[0]],2),'_',/EXTRACT) - ;First check - First part of DATA_SOURCE matches text in TAV entry - res1=STRUPCASE(STRSPLIT(res[0],'.',/EXTRACT,COUNT=res1cnt)) - ci=WHERE(STRPOS(STRUPCASE(tab_arr_sub),res1[0]) NE -1,ccnt) - IF ccnt EQ 1 THEN geoms_te=tab_arr_sub[ci[0]] $ ;Template found - ELSE IF (ccnt GT 1) AND (N_ELEMENTS(res1) GT 1) THEN BEGIN - ;2nd Check - More than one option so try and match 2nd part of DATA_SOURCE with 4th part in TAV entry - tab_arr_sub=tab_arr_sub[ci] - tab_arr_part_sub=STRARR(ccnt) - numi=-1 - FOR i=0,ccnt-1 DO BEGIN - rest=STRSPLIT(tab_arr_sub[i],'-',/EXTRACT) - rest=[rest,'','',''] ;ensure there are at least 4 sub-values - tab_arr_part_sub[i]=rest[3] - IF IS_A_NUMBER_HDF(rest[3]) THEN numi=i - ENDFOR - ci=WHERE(STRUPCASE(tab_arr_part_sub) EQ res1[1],ccnt) - IF (ccnt EQ 0) AND (numi NE -1) THEN BEGIN - ;2nd part is likely a gas e.g. FTIR.CO, so use template where 4th part of TAV entry is a number - geoms_te=tab_arr_sub[numi] - ENDIF ELSE IF ccnt EQ 1 THEN geoms_te=tab_arr_sub[ci[0]] $ ;unique template found - ELSE IF ccnt GT 1 THEN BEGIN ;ccnt GT 1, Try alternatives for H2O and O3, or test for fourth part - tasx=tab_arr_sub[ci] - CASE 1 OF - (res1[0] EQ 'UVVIS') AND (res1[1] EQ 'DOAS'): BEGIN - IF res1cnt GE 4 THEN BEGIN - IF res1[3] EQ 'AEROSOL' THEN res1x=STRUPCASE(res1[2])+'-AEROSOL' $ - ELSE res1x=STRUPCASE(res1[2])+'-GAS' - ENDIF ELSE res1x=res1[1] - END - res1[1] EQ 'H2O': res1x='WATERVAPOR' - res1[1] EQ 'O3': res1x='OZONE' - ELSE: res1x=res1[1] - ENDCASE - ci=WHERE(STRPOS(STRUPCASE(tasx),res1x) NE -1,ccnt) - IF ccnt EQ 1 THEN geoms_te=tasx[ci[0]] - ENDIF - ENDIF - IF geoms_te NE '' THEN BEGIN ;DATA_TEMPLATE value found based on DATA_SOURCE - IF gcnt EQ 1 THEN BEGIN ;DATA_TEMPLATE label is present - fmvi=WHERE(in1 EQ 'FILE_META_VERSION',fmvcnt) - fmvtxt='' - IF fmvcnt EQ 1 THEN BEGIN - resfmv=STRSPLIT(in2[fmvi[0]],' ;',/EXTRACT,COUNT=fmvcnt) - IF fmvcnt GE 1 THEN BEGIN - fmv_ver=resfmv[0] - fmvtxt=' and '+fmv_ver+' metadata definitions' - ENDIF - ENDIF - in2[gi[0]]=geoms_te ;Add value to DATA_TEMPLATE label - IF qa_yes THEN itxt=' expected to be ' ELSE itxt=' renamed ' - infotxt='2 DATA_TEMPLATE value'+itxt+geoms_te+' based on DATA_SOURCE value'+fmvtxt - INFOTXT_OUTPUT,infotxt - std_txt='' - ENDIF ELSE IF gcnt EQ 0 THEN in3[0]=geoms_te ;Need to add new Global Attribute Information - ENDIF - ENDIF - ENDIF - ENDIF - IF geoms_te NE '' THEN BEGIN ;Check if DATA_QUALITY label is present - gi=WHERE(in1 EQ attr_arr_glob_prov[1],gcnt) - IF gcnt EQ 0 THEN in3[1]='DATA_QUALITY' ;Need to add new Global Attribute Information - ENDIF - IF std_txt NE '' THEN BEGIN ;No template file required based on selection criteria - infotxt='0'+std_txt+'DATA_TEMPLATE value not required based on selection criteria' - INFOTXT_OUTPUT,infotxt - ENDIF - END - - code EQ 10: BEGIN ;check printable characters in Metadata and Datasets (of type String) - ;Note: Also allowed are Control Characters Null Character (0B), Horizontal Tab (9B), - ;Line Feed (10B), and Carriage Return (13B) - Note: 10B and 13B for Metadata only - exempt=['PI_NAME','DO_NAME','DS_NAME','PI_AFFILIATION','DO_AFFILIATION','DS_AFFILIATION',$ - 'PI_ADDRESS','DO_ADDRESS','DS_ADDRESS','PI_EMAIL','DO_EMAIL','DS_EMAIL'] - non_char=[27B, 128B, 147B, 176B, 186B, 197B, 216B, 226B, 232B, 233B, 248B, 252B] ;128B = Euro - rep_char=['',' ','"',' deg. ',' deg. ','A','O','a','e','e','o','u'] ;replace above non-US ASCII chars with these values - - ;Do check for the Full TAV which includes ORIGINATOR attributes - ochk=STRPOS(tab_arr(*,0),'ORIGINATOR') NE -1 - oi=WHERE(ochk NE 0,ocnt) - mentry=0 ;Default assumes String Dataset Entry - n_nchar=N_ELEMENTS(non_char) - IF N_PARAMS() EQ 3 THEN in2='Dataset '+STRTRIM(in2,2) $ ;Dataset Entry - ELSE mentry=1 ;Metadata entry - FOR i=0L,N_ELEMENTS(in1)-1L DO BEGIN - exempt_att=0 ;default assumes attribute is not exempt from checks - IF mentry EQ 1 THEN BEGIN - res=STRSPLIT(in1[i],'=',/EXTRACT) - in2='Metadata entry '+in1[i] ;res[0] - IF ocnt NE 0 THEN BEGIN - ;Do not need to check 'exempt' attributes if using the full version of the TAV file - ei=WHERE(STRUPCASE(res[0]) EQ exempt,ecnt) - IF ecnt NE 0 THEN exempt_att=1 ;invalid entries can be corrected by the code - ENDIF - ENDIF - - IF (~qa_yes) AND (exempt_att EQ 0) THEN BEGIN - ;If not doing QA then check for characters that can be replaced - non_char_i=[-1] - FOR j=0,n_nchar-1 DO BEGIN - testb=BYTE(in1[i]) - ni=WHERE(testb EQ non_char[j],ncnt) - IF ncnt NE 0 THEN BEGIN - IF non_char_i[0] EQ -1 THEN non_char_i=[j] $ - ELSE non_char_i=[non_char_i,j] - inval=in1[i] - FOR k=0,ncnt-1 DO BEGIN - IF k NE 0 THEN ni[k]=ni[k]+((STRLEN(rep_char[j])-1)*k) - inval=STRMID(inval,0,ni[k])+rep_char[j]+STRMID(inval,ni[k]+1) - ENDFOR - in1[i]=inval - ENDIF - ENDFOR - IF non_char_i[0] NE -1 THEN BEGIN - infotxt='2 Illegal character(s) ' - FOR j=0,N_ELEMENTS(non_char_i)-1 DO BEGIN - IF j EQ 0 THEN infotxt=infotxt+STRTRIM(non_char[non_char_i[j]],2) $ - ELSE infotxt=infotxt+', '+STRTRIM(non_char[non_char_i[j]],2) - ENDFOR - infotxt=infotxt+' in '+in2+' replaced with valid ISO646-US ASCII character(s)' - INFOTXT_OUTPUT,infotxt - ENDIF - ENDIF - testb=BYTE(in1[i]) - - IF exempt_att EQ 0 THEN BEGIN - IF mentry EQ 1 THEN $ - test=(testb NE 0B) AND (testb NE 9B) AND (testb NE 10B) AND (testb NE 13B) $ - ELSE test=(testb NE 0B) AND (testb NE 9B) ;Check for Dataset Control Characters - ;check for non-char value and replace - bi=WHERE(((testb LT 32B) OR (testb GT 126B)) AND (test),bcnt) - IF bcnt NE 0 THEN BEGIN ;Illegal character present - IF bcnt GT 1L THEN BEGIN ;check for repeat illegal characters and only display once - bix=[bi[0]] - FOR j=1L,bcnt-1L DO BEGIN - ri=WHERE(testb[bi[j]] EQ testb[bi[0L:j-1L]],rcnt) - IF rcnt EQ 0 THEN bix=[bix,bi[j]] - ENDFOR - bi=bix & bcnt=N_ELEMENTS(bi) - ENDIF - infotxt=STRARR(2) - infotxt[0]='3 Entry must only include valid characters from the ISO646-US ASCII character set.' - infotxt[1]=' Illegal character(s) ' - FOR j=0L,bcnt-1L DO BEGIN - cval=STRTRIM(testb[bi[j]],2) - ;print,testb[bi[j]] ;to output actual byte value - IF (cval EQ STRTRIM(8B,2)) OR (cval EQ '') THEN BEGIN - ;Illegal non-printable character so print in Decimal Byte form - cval=STRING(format='(I4)',testb[bi[j]]) & cval=STRTRIM(cval,2)+'B' - ENDIF - IF j NE bcnt-1L THEN itxt=', ' ELSE itxt='' - infotxt[1]=infotxt[1]+cval+itxt - ENDFOR - infotxt[1]=infotxt[1]+' in '+in2+' (may not display correctly)' - INFOTXT_OUTPUT,infotxt - ENDIF - ENDIF - ENDFOR - END - code EQ 11: BEGIN ;Check DATA_VERSION_NAME part of the DATA_SOURCE - ;Check that entry is made up of dot-separated alpha_numeric words - n_char=STRLEN(in1) - strok=1B ;default is that entry is OK - test2=0B & test3=0B ;will change to 1 if test conditions are true - charchkm1=0B ;to check test character against the previous character - FOR i=0,n_char-1 DO BEGIN - charchk=BYTE(STRMID(in1,i,1)) - test1=(charchk EQ 46B) OR ((charchk GE 65B) AND (charchk LE 90B)) OR $ ;'.' or A-Z - ((charchk GE 97B) AND (charchk LE 122B)) OR $ ;a-z - ((charchk GE 48B) AND (charchk LE 57B)) ;0-9 - IF (i EQ 0) OR (i EQ n_char-1) THEN test2=charchk EQ 46B ;i.e. first or last character is a '.' - test3=(charchk EQ 46B) AND (charchkm1 EQ 46B) ;i.e. two consecutive dots - IF (~test1) OR (test2) OR (test3) THEN strok=0B - charchkm1=charchk - ENDFOR - IF ~strok THEN BEGIN - ;write error message - infotxt='3 VERSION_NAME sub-value of the DATA_SOURCE must consist of dot-separated alpha-numeric words: ' - infotxt=infotxt+in1 - INFOTXT_OUTPUT,infotxt - ENDIF - END -ENDCASE - -END ;Procedure GEOMS_Rule_Changes - - - -PRO pre_defined_att_checks, ct, nav, vav, vnx, vdtv,verror -;Procedure to perform checks on the HDF4 pre-defined attributes, as well as check that -;the numeric variable attribute values are of the same data type as that given by the -;corresponding VAR_DATA_TYPE value (if input is via session memory) -; ---------- -;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org -; -; History: -; 20101120: Introduced - Version 4.0 -; -; Inputs: ct - Identifies the type of check to be performed on the inputs -; nav - The HDF pre-defined attribute to check -; vav - The Variable Attribute corresponding to the pre-defined attribute -; vnx - The Dataset from which the attributes are derived -; vdtv - The Data Type of the Dataset -; verror - Boolean to indicate whether the Variable Attribute data type error has -; already been written -; -; Outputs: None - Any Pre-defined attributes written to an HDF file are recreated by the program -; -; Called by: IDLCR8HDF, READ_METADATA -; -; Subroutines Called: INFOTXT_OUTPUT -; Information Conditions (when the program is able to make changes): -; 1. VAR_VALID_MIN/MAX or VAR_FILL_VALUE data types do not match VAR_DATA_TYPE -; 2. HDF4 pre-defined attribute data type does not match VAR_DATA_TYPE -; 3. HDF4 pre-defined attribute units value does not match VAR_UNITS -; 4. HDF4 pre-defined attribute value does not equal equivalent variable -; attribute value (valid_range, _FillValue) -; 5. HDF4 pre-defined attribute [long_name/format/coordsys] present in the HDF file -; 6. Datasets that have been scaled or had an offset applied are not permitted - -COMMON DATA -COMMON WIDGET_WIN - -IF ct NE -1 THEN BEGIN ;check units, _FillValue, and valid_range attributes only - ncsa=['units','_Fillvalue','valid_range','valid_range'] - var_atts=['VAR_UNITS','VAR_FILL_VALUE','VAR_VALID_MIN','VAR_VALID_MAX'] - - ;Change VAR_DATA_TYPE value to IDL Type code - CASE 1 OF - vdtv EQ 'BYTE': idltc=1 - vdtv EQ 'SHORT': idltc=2 - (vdtv EQ 'INTEGER') OR (vdtv EQ 'LONG'): idltc=3 - (vdtv EQ 'REAL') OR (vdtv EQ 'FLOAT'): idltc=4 - vdtv EQ 'DOUBLE': idltc=5 - vdtv EQ 'STRING': idltc=7 - ELSE: idltc=0 - ENDCASE - test1=(SIZE(vav,/TYPE) EQ 7) AND (STRTRIM(vav,2) NE '') - test2=SIZE(vav,/TYPE) NE 7 - test3=(SIZE(nav,/TYPE) EQ 7) AND (STRTRIM(nav,2) NE '') - test4=SIZE(nav,/TYPE) NE 7 - IF ((test1) OR (test2)) AND (ct NE 0) THEN BEGIN - IF (SIZE(vav,/TYPE) NE idltc) AND (idltc NE 0) AND (verror[0] EQ 0) THEN BEGIN - ;Check data type of Variable Attribute - infotxt=STRARR(2) - itxt='VAR_VALID_MIN, VAR_VALID_MAX or VAR_FILL_VALUE' - infotxt[0]='2 '+itxt+' data types do not match VAR_DATA_TYPE='+vdtv - IF vnx NE '' THEN infotxt[1]=' for Dataset '+vnx+'|.' $ - ELSE BEGIN - infotxt[0]=infotxt[0]+'|.' & infotxt[1]=' ' - ENDELSE - infotxt[1]=infotxt[1]+' Data type will be changed to match VAR_DATA_TYPE value in HDF file' - IF ~qa_yes THEN INFOTXT_OUTPUT,infotxt ;error for QA output separately - verror[0]=1 - ENDIF - ENDIF - IF ((test3) OR (test4)) AND (ct NE 0) THEN BEGIN - IF (SIZE(nav,/TYPE) NE idltc) AND (idltc NE 0) AND (o3[0] EQ 'H4') AND (verror[1] EQ 0) THEN BEGIN - ;Check data type of pre-defined attribute - infotxt=STRARR(2) - infotxt[0]='2 HDF4 pre-defined attribute '+ncsa[ct]+' data type does not match VAR_DATA_TYPE='+vdtv - IF vnx NE '' THEN infotxt[0]=infotxt[0]+' for Dataset '+vnx+'|.' $ - ELSE infotxt[0]=infotxt[0]+'|.' - infotxt[1]=' Data type will be changed to match VAR_DATA_TYPE value in HDF file' - INFOTXT_OUTPUT,infotxt - IF ncsa[ct] EQ 'valid_range' THEN verror[1]=1 ;to only report 1 valid_range error - ENDIF - ENDIF - IF ((test1) OR (test2)) AND ((test3) OR (test4)) THEN BEGIN - ;Compare Variable Attributes against pre-defined attributes - IF ct EQ 0 THEN BEGIN ;units check - IF (STRTRIM(nav,2) NE STRTRIM(vav,2)) AND (o3[0] EQ 'H4') THEN BEGIN - infotxt=STRARR(2) - infotxt[0]='2 HDF4 pre-defined attribute units='+nav+' does not match VAR_UNITS='+vav - IF vnx NE '' THEN infotxt[0]=infotxt[0]+' for Dataset '+vnx+'|.' $ - ELSE infotxt[0]=infotxt[0]+'|.' - infotxt[1]=' HDF4 units will be changed to match VAR_UNITS value in HDF file' - INFOTXT_OUTPUT,infotxt - ENDIF - ENDIF ELSE BEGIN - IF (nav NE vav) AND (idltc NE 7) AND (idltc NE 0) AND (o3[0] EQ 'H4') THEN BEGIN - infotxt=STRARR(2) - infotxt[0]='2 HDF4 pre-defined attribute '+ncsa[ct]+' value does not equal '+var_atts[ct]+' value' - IF vnx NE '' THEN infotxt[0]=infotxt[0]+' for Dataset '+vnx+'|.' $ - ELSE infotxt[0]=infotxt[0]+'|.' - infotxt[1]=' HDF4 '+ncsa[ct]+' will be changed to match '+var_atts[ct]+' value in HDF file' - INFOTXT_OUTPUT,infotxt - ENDIF - ENDELSE - ENDIF -ENDIF ELSE BEGIN ;Do checks on remaining pre-defined attributes - ncsa_str=['long_name','format','coordsys'] - ncsa_cal=['scale_factor','scale_factor_err','add_offset','add_offset_err','calibrated_nt'] - FOR i=0,N_ELEMENTS(ncsa_str)-1 DO BEGIN - ni=WHERE(STRLOWCASE(nav) EQ ncsa_str[i],ncnt) - IF ncnt NE 0 THEN BEGIN - IF qa_yes THEN itxt=' present in' ELSE itxt=' will not be written to' - infotxt='0 HDF4 pre-defined attribute '+ncsa_str[i]+itxt+' the HDF file' - INFOTXT_OUTPUT,infotxt - ENDIF - ENDFOR - calfound=0 - FOR i=0,N_ELEMENTS(ncsa_cal)-1 DO BEGIN - ni=WHERE((STRLOWCASE(nav) EQ ncsa_cal[i]) AND (calfound EQ 0),ncnt) - IF ncnt NE 0 THEN BEGIN - calfound=1 - infotxt='3 Datasets that have been scaled or had an offset applied are not permitted' - INFOTXT_OUTPUT,infotxt - ENDIF - ENDFOR -ENDELSE - -END ;Procedure Pre_Defined_Att_Checks - - - -PRO read_metadata, metafile, inf -;Procedure to check Metadata contents and return as meta_arr - also: -;1. Ensures uniform input (making ATTRIBUTE_NAME uppercase, deleting unwanted spaces etc). -;2. Checks that attribute length falls within allowable limits -;3. Checks for repeated, missing, new, obsolete attributes -;4. Update FILE_META_VERSION with TAV and software version (tab_ver) - -;Please also note the following: -;1. Any extra valid Global Attributes will be included (and added to the attr_arr_glob array). -;2. If redundant attributes are detected they will be omitted from the Metadata written -; to the HDF file. -; ---------- -;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org -; -; History: -; 20050802: Original IDLCR8HDF Routine - Version 1.0 -; 20061012: Error messages modified; Allow VAR_MONOTONE as a Variable Attribute if it is present in the -; Metadata; If DATA_TYPE is present in the Metadata then change it to DATA_LEVEL; Allow -; additional Global Attributes; Puts Variable Attribute names in the same order as -; attr_arr_data; Common variable definition WIDGET_WIN added - Version 2.0 -; 20080302: VAR_MONOTONE not included in the output Metadata if it is present in the input Metadata -; and an AVDC style TAV file is read in; if DATA_TYPE is present in the Metadata it is changed -; to DATA_LEVEL if an AVDC style TAV file is read in, and vice versa if an original style -; Envisat table.dat file read in; Warnings added if VAR_MONOTONE attribute is omitted, when -; changing DATA_TYPE to DATA_LEVEL (or vice versa), and if extra Global Attributes are -; included in the Metadata; Checks for missing or repeated Global Attributes; Global -; Attributes can now be listed in any order in the Metadata (will be written to the HDF file -; in a standard order) - Version 3.0 -; 20091203: Stop using DATA_TYPE for EVDC style input (if found change to DATA_LEVEL); If VAR_MONOTONE -; is present in the Metadata it is removed - Version 3.08 -; 20100205: Add RETURN command after all STOP_WITH_ERROR calls, which allows program to return to the -; calling program if the reterr argument is included in the idlcr8hdf call - Version 3.09 -; 20101120: Add DATA_STOP_DATE and remove DATA_LEVEL from Global Attributes; Remove VAR_AVG_TYPE, -; VAR_DIMENSION, and VIS attributes from Variable Attributes; Add call to GEOMS_RULE_CHANGES -; to account for changes in Metadata setup between v3.0 and v4.0; Remove option to have a -; filename as a value for free text attributes; Increase checks between DATA_VARIABLES and -; VAR_NAME values - Version 4.0b0 -; 20111220: Call additional GEOMS_RULE_CHANGES; Perform checks on HDF4 pre-defined attributes -; - Version 4.0b7 -; 20150127: Stop removing leading and trailing spaces from variable attribute values (when there is -; only 1 value) to allow for possibility of VAR_FILL_VALUEs being an empty string -; - Version 4.0b25 -; 20150811: Add checks for invalid FILE_META_VERSION entries - Version 4.0b29 -; 20150924: Change to v4.0b25 so that leading and trailing spaces are not removed from VAR_FILL_VALUE -; entries only (see attr_arr_str) - Version 4.0b30 -; 20151012: Improve/fix checks for invalid whitespace in Metadata labels or values and add log output when -; changes are made. Checks previously done in CHECK_METADATA moved to this routine -; - Version 4.0b31 -; 20151021: Do not do checks on invalid whitespace for attributes that have numeric values - Version 4.0b32 -; 20151109: Add log message when adding/checking for missing mandatory variable attributes - Version 4.0b34 -; 20151116: Fix bug when doing whitespace checks and there is no metadata entry - Version 4.0b35 -; 20180528: Fix bug when adding non-standard global attributes to meta_arr - Version 4.0b47 -; 20191205: Do checks for sub-value separators (. or ; or _) being at the start or end of attribute -; values, and for consecutive sub-value separators (e.g. ;;). This can occur as the STRSPLIT -; command can remove these while testing sub-values, so they are not detected as part of -; any tests - Version 4.0b53 -; 20200311: Do not include '.' separator when doing checks for sub-value separators at the start or end of -; the attribute values e.g. allow DO_NAME=Boyd;Ian S. - Version 4.0b54 -; 20201020: Generate message for invalid metadata attributes, previously removed them 'quietly' -; - Version 4.0b56 -; 20201026: Fix bug from 4.0b56 which caused an error message if _FillValue was present in the GEOMS file -; - Version 4.0b57 -; -; Inputs: metafile - Either, the filename of the input file containing the Metadata or, if program -; input is via string array and Structure, a string array containing the Global -; and Variable Attributes. -; inf - flag, where 0=Metafile is an array; 1=Metafile is a file. -; -; Output: meta_arr - a string array containing the Global and Variable Attributes, which have been -; checked and uniformally formatted. -; -; Called by: IDLCR8HDF -; -; Subroutines Called: STOP_WITH_ERROR (if error state detected); -; INFOTXT_OUTPUT, GEOMS_RULE_CHANGES -; Possible Conditions for STOP_WITH_ERROR call (plus [line number] where called): -; 1. Metadata value contains too many characters -; 2. No or invalid DATA_VARIABLES and VAR_NAME attributes present in the metadata -; 3. DATA_VARIABLES or VAR_NAME value appears more than once -; 4. No valid Metadata in command line parameter file or arrays -; 5. VAR_NAME does not match corresponding DATA_VARIABLES value -; 6. Number of DATA_VARIABLES values does not match the number of VAR_NAME occurrences -; 7. A required Global Attribute Label is not present in the Metadata -; 8. A required Global Attribute Label is repeated in the Metadata -; 9. Number of Attribute occurrences does not match the number of datasets -; 10. DATA_QUALITY is not present but is a mandatory attribute when DATA_TEMPLATE is reported -; -; Information Conditions (when the program is able to make changes): -; 1. Invalid Attribute Label ignored -; 2. Missing Global Attribute added -; 3. DATA_VARIABLES attribute added to metadata based on VAR_NAME values -; 4. Values for DATA_VARIABLES appended to the metadata based on VAR_NAME values -; 5. VAR_NAME value appended to label based on DATA_VARIABLES value -; 6. New Global Attribute added - -COMMON TABLEDATA -COMMON METADATA -COMMON DATA -COMMON WIDGET_WIN - -attr_arr_glob=['PI_NAME','PI_AFFILIATION','PI_ADDRESS','PI_EMAIL',$ - 'DO_NAME','DO_AFFILIATION','DO_ADDRESS','DO_EMAIL',$ - 'DS_NAME','DS_AFFILIATION','DS_ADDRESS','DS_EMAIL',$ - 'DATA_DESCRIPTION','DATA_DISCIPLINE','DATA_GROUP','DATA_LOCATION',$ - 'DATA_SOURCE','DATA_VARIABLES','DATA_START_DATE','DATA_STOP_DATE',$ - 'DATA_FILE_VERSION','DATA_MODIFICATIONS','DATA_CAVEATS','DATA_RULES_OF_USE',$ - 'DATA_ACKNOWLEDGEMENT','DATA_QUALITY','DATA_TEMPLATE','DATA_PROCESSOR',$ - 'FILE_NAME','FILE_GENERATION_DATE','FILE_ACCESS','FILE_PROJECT_ID','FILE_DOI', $ - 'FILE_ASSOCIATION','FILE_META_VERSION'] - -attr_arr_glob_opt=['DATA_DESCRIPTION','DATA_MODIFICATIONS','DATA_CAVEATS','DATA_RULES_OF_USE',$ - 'DATA_ACKNOWLEDGEMENT','DATA_QUALITY','DATA_TEMPLATE','DATA_PROCESSOR',$ - 'FILE_PROJECT_ID','FILE_ASSOCIATION'] - -attr_arr_glob_ins=['DATA_START_DATE','DATA_STOP_DATE','FILE_GENERATION_DATE', $ - 'FILE_DOI','FILE_META_VERSION'] - -attr_arr_glob_prov=['DATA_TEMPLATE','DATA_QUALITY'] - -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'] - -attr_arr_data_opt=['VAR_NOTES'] - -;attr_arr_str=['VAR_FILL_VALUE','_fillvalue'] ;do not take STRTRIM of these values - -;Attributes that have user defined values -attr_free=['DATA_DESCRIPTION','DATA_MODIFICATIONS','DATA_CAVEATS',$ - 'DATA_RULES_OF_USE','DATA_ACKNOWLEDGEMENT','DATA_QUALITY', $ - 'DATA_PROCESSOR','FILE_PROJECT_ID','FILE_ASSOCIATION', $ - 'VAR_DESCRIPTION','VAR_NOTES'] - -;Atrributes that have numeric values (except for string datatype) -num_atts=['VAR_VALID_MIN','VAR_VALID_MAX','VAR_FILL_VALUE','_fillvalue','valid_range'] - -;To check for pre-defined attributes -ncsa=['long_name','units','format','coordsys','valid_range','_fillvalue','scale_factor', $ - 'scale_factor_err','add_offset','add_offset_err','calibrated_nt'] - -;Possible sub-value separators -sv_sep=['.','_',';'] - -nchar=65535L ;limit on number of characters - -;possible error messages for this procedure -errtxt=STRARR(9) -proname='Read_Metadata procedure: ' -errtxt[0]=' value contains too many characters (maximum permitted: '+STRTRIM(nchar,2)+')' -errtxt[1]='No or invalid DATA_VARIABLES and VAR_NAME attributes present in the metadata' -errtxt[2]=' value appears more than once: ' -errtxt[3]='No valid Metadata in input' -errtxt[4]='VAR_NAME does not match corresponding DATA_VARIABLES value: ' -errtxt[5]='Number of DATA_VARIABLES values does not match the number of VAR_NAME occurrences ' -errtxt[6]='Global Attribute Label not present in the Metadata: ' ;now INFOTXT -errtxt[7]='Global Attribute Label repeated in the Metadata: ' -errtxt[8]=' occurrences does not match the number of datasets (should be ' - -dum='' & nrline=0L & dvfound=-1L -nd=N_ELEMENTS(attr_arr_data) -ng=N_ELEMENTS(attr_arr_glob) -nna=N_ELEMENTS(num_atts) -dsei=WHERE(attr_arr_glob EQ 'DS_EMAIL') ;any new global attributes should be after this attribute -vni=WHERE(attr_arr_data EQ 'VAR_NAME') ;used in VAR_NAME checks -ndv=0 ;will reset to the number of datasets - -IF inf EQ 1 THEN BEGIN ;count number of lines in the file - OPENR,lu,metafile,/GET_LUN - WHILE NOT EOF(lu) DO BEGIN - READF,lu,dum & nrline=nrline+1L - ENDWHILE - FREE_LUN,lu - mh=STRARR(nrline) & mv_lng=LON64ARR(nrline) & mv_dbl=DBLARR(nrline) - OPENR,lu,metafile,/GET_LUN - READF,lu,mh - FREE_LUN,lu -ENDIF ELSE mh=metafile ;metadata is already in an array -mhhold=STRTRIM(mh,1) ;create temporary array and remove leading blanks from all the entries -mhgood=INTARR(N_ELEMENTS(mh))+1 ;will be assigned zero for unwanted metadata lines - -;Check for Redundant Attribute Labels -GEOMS_RULE_CHANGES,0,mhhold,mhgood - -;test1 - check first character is A-Z or a-z; test2 - check for '=' and not a redundant label -test1=(((BYTE(STRMID(mhhold,0,1)) GE 65) AND (BYTE(STRMID(mhhold,0,1)) LE 90)) OR $ - ((BYTE(STRMID(mhhold,0,1)) GE 97) AND (BYTE(STRMID(mhhold,0,1)) LE 122)) OR $ - (STRMID(STRUPCASE(mhhold),0,10) EQ '_FILLVALUE')) -test2=(STRPOS(mhhold,'=') GE 1) AND (mhgood EQ 1) - -nri=WHERE((test1) AND (test2),nrline) -IF nrline EQ 0 THEN BEGIN - STOP_WITH_ERROR,o3[3]+proname,errtxt[3],lu & RETURN -ENDIF - -;Generate an error for invalid metadata attributes -bnri=WHERE(~test1,bnrline) -IF bnrline NE 0 THEN BEGIN - FOR i=0L,bnrline-1L DO BEGIN - infotxt='2 Invalid Metadata Attribute: '+mhhold[bnri[i]]+'|' - infotxt=infotxt+' removed from the metadata saved to the output file' - INFOTXT_OUTPUT,infotxt - ENDFOR -ENDIF - -;Set holding arrays for metadata -mh=mh[nri] & mv_lng=mv_lng[nri] & mv_dbl=mv_dbl[nri] -lu=-1 -m_v0=STRARR(nrline) & m_v1=m_v0 ;to hold metadata labels and values -m_v2=INTARR(nrline) ;global/variable attribute switch - -;Separate out Metadata entries -FOR i=0L,nrline-1L DO BEGIN - ;separate out the Meta entry into two components - eqpos=STRPOS(mh[i],'=') - m_v0[i]=STRMID(mh[i],0,eqpos) - IF eqpos EQ STRLEN(mh[i])-1 THEN m_v1[i]='' $ ;ie. the last character is the '=' - ELSE m_v1[i]=STRMID(mh[i],eqpos+1) -ENDFOR - -;Check for any attribute label that is not uppercase (and not an HDF pre-defined label) or has leading or trailing spaces -ui=WHERE(m_v0 NE STRUPCASE(m_v0),ucnt) -lti=WHERE(m_v0 NE STRTRIM(m_v0,2),ltcnt) - -IF ltcnt NE 0L THEN BEGIN ;some attribute labels have leading or trailing spaces - m_v0=STRTRIM(m_v0,2) - IF qa_yes THEN BEGIN ;only report if doing QA, otherwise fix quietly - infotxt='2 Leading or trailing spaces in Metadata Attribute Labels not permitted' - INFOTXT_OUTPUT,infotxt - ENDIF -ENDIF - -IF ucnt NE 0L THEN BEGIN ;some attribute labels are not fully uppercase - writeonce=0 ;write out information text once only - FOR i=0L,ucnt-1L DO BEGIN - ;check for HDF predefined labels which should be lowercase - pi=WHERE(STRLOWCASE(m_v0[ui[i]]) EQ ncsa,pcnt) - IF pcnt EQ 0 THEN BEGIN ;label expected to be in uppercase - m_v0[ui[i]]=STRUPCASE(m_v0[ui[i]]) - IF writeonce EQ 0 THEN BEGIN ;write INFORMATION text to log - writeonce=1 - IF qa_yes THEN itxt='must be' ELSE itxt='made' - infotxt='2 Metadata Attribute Labels '+itxt+' UPPERCASE' - INFOTXT_OUTPUT,infotxt - ENDIF - ENDIF - ENDFOR -ENDIF - -;Check for and remove leading and trailing spaces in Metadata (sub-)values (except for free-text attributes -;and VAR_FILL_VALUEs). Also for non-free-text attributes check if the first and last characters are -;sub-value separators, or if there are consecutive sub-value separators in the attribute value. -;Also check for values with excessive length. -IF qa_yes THEN wstxt='are not permitted' ELSE wstxt='removed' ;used when reporting white space in sub-values - -FOR i=0L,nrline-1L DO BEGIN - ;si=WHERE(STRUPCASE(m_v0[i]) EQ STRUPCASE(num_atts),scnt) - ;IF scnt EQ 0 THEN si=WHERE(m_v0[i] EQ attr_free,scnt) - si=WHERE(m_v0[i] EQ attr_free,scnt) - IF scnt EQ 0 THEN BEGIN ;not a free text attribute - nmi=WHERE(STRUPCASE(m_v0[i]) EQ STRUPCASE(num_atts),nmcnt) ;is it an attribute that should have a numeric value - res=STRSPLIT(m_v1[i],';',/Extract,COUNT=svcnt) - spi=WHERE(STRTRIM(res,2) NE res,spcnt) - IF spcnt NE 0 THEN BEGIN - IF (svcnt EQ 1) AND (qa_yes) THEN BEGIN - IF (nmcnt NE 0) AND (STRTRIM(res[0],2) NE '') THEN infotxt='' $ value should be a number not a string so do not check for spaces - ELSE IF res[0] EQ ' ' THEN infotxt='' $ there could be a single space in the HDF file if there is no entry - ELSE infotxt='2 Leading or trailing spaces in '+m_v0[i]+'='+m_v1[i]+' value '+wstxt - ENDIF ELSE IF svcnt GT 1 THEN BEGIN - infotxt='2 Leading or trailing spaces between '+m_v0[i]+' sub-values '+wstxt - ENDIF ELSE infotxt='' ;Quietly fix if only single value and not doing QA - IF infotxt NE '' THEN INFOTXT_OUTPUT,infotxt - res=STRTRIM(res,2) - m_v1[i]=res[0] - IF svcnt GT 1 THEN FOR j=1,svcnt-1 DO m_v1[i]=m_v1[i]+';'+res[j] - ENDIF - - IF nmcnt EQ 0 THEN BEGIN - ;Check that first and last characters of the metadata value aren't sub-value separators - ;and that there are no consecutive sub-value separators - mv1len=STRLEN(m_v1[i]) ;string length of attribute value - mv1start=STRMID(m_v1[i],0,1) ;first character - mv1end=STRMID(m_v1[i],mv1len-1L,1) ;last character - FOR k=0,1 DO BEGIN - writeonce=1B ;so that error message is only written once for each attribute - IF k EQ 0 THEN $ - infotxt='3 Sub-value separators at the start or end of the attribute value are not permitted' $ - ELSE infotxt='3 Consecutive sub-value separators are not permitted' - FOR j=0,N_ELEMENTS(sv_sep)-1 DO BEGIN - spchk=sv_sep[j]+sv_sep[j] - ;Note: in some cases a '.' may be present at the end of a sub-value e.g. DO_NAME=Boyd;Ian S. - IF (k EQ 0) AND (sv_sep[j] NE '.') THEN test=(mv1start EQ sv_sep[j]) OR (mv1end EQ sv_sep[j]) $ - ELSE IF k EQ 1 THEN test=STRPOS(m_v1[i],spchk) NE -1 $ - ELSE test=0B - IF (test) AND (writeonce) THEN BEGIN - IF mv1len GT 50 THEN infotxt=infotxt+' in '+m_v0[i]+' attribute' $ - ELSE infotxt=infotxt+': '+m_v0[i]+'='+m_v1[i] - INFOTXT_OUTPUT,infotxt - writeonce=0B - ENDIF - ENDFOR - ENDFOR - ENDIF - ENDIF - ;check for string length of attribute value - IF STRLEN(m_v1[i]) GT nchar THEN BEGIN - infotxt='3 '+m_v0[i]+errtxt[0] - INFOTXT_OUTPUT,infotxt - ENDIF -ENDFOR - -FOR i=0L,nrline-1L DO BEGIN - ;Do check for DATA_VARIABLES and separate out the values - IF m_v0[i] EQ 'DATA_VARIABLES' THEN BEGIN - dv=STRSPLIT(m_v1[i],' ;',/Extract,COUNT=ndv) & dvfound=i - ENDIF - - ;Check for FILE_META_VERSION and add value derived from READ_TABLEFILE if not doing QA - IF m_v0[i] EQ 'FILE_META_VERSION' THEN BEGIN - IF (~qa_yes) AND (m_v1[i] NE tab_ver) THEN BEGIN - m_v1[i]=tab_ver - infotxt='0 FILE_META_VERSION updated with corrected TAV file version and tool name' - INFOTXT_OUTPUT,infotxt - ENDIF ELSE IF qa_yes THEN BEGIN - ;Check Revision number is less than or equal to the TAV revision number, must be '04''R''xxx' - revno=STRSPLIT(m_v1[i],';',/EXTRACT,count=fmvcnt) & revno=STRTRIM(revno,2) - tavno=STRSPLIT(tab_ver,';',/EXTRACT) & tavno=STRTRIM(tavno,2) - IF fmvcnt NE 2 THEN BEGIN - infotxt='2 FILE_META_VERSION values must include the TAV file version and the HDF file ' - infotxt=infotxt+'creation tool name e.g. FILE_META_VERSION='+tavno[0]+';CUSTOM' - INFOTXT_OUTPUT,infotxt - ENDIF - revok=0 ;default is to report a TAV file version error unless revok eq 3 - IF STRLEN(revno[0]) EQ STRLEN(tavno[0]) THEN BEGIN - vrev=STRMID(revno[0],0,2) & vtav=STRMID(tavno[0],0,2) - rrev=STRMID(revno[0],2,1) & rtav=STRMID(tavno[0],2,1) - srev=STRMID(revno[0],3) & stav=STRMID(tavno[0],3) - IF IS_A_NUMBER_HDF(vrev) THEN BEGIN - IF (FIX(vrev) GE 4) AND (FIX(vrev) LE FIX(vtav)) THEN revok++ ;TAV version OK - ENDIF - IF rrev EQ rtav THEN revok++ ;'R' is present as the third character - IF (IS_A_NUMBER_HDF(srev)) AND (revok eq 2) THEN BEGIN - IF (FIX(srev) LE FIX(stav)) OR ((FIX(srev) GT FIX(stav)) AND (FIX(vrev) LT FIX(vtav))) THEN revok++ ;TAV sub-version OK - ENDIF - ENDIF - IF revok NE 3 THEN BEGIN - infotxt='2 FILE_META_VERSION sub-value '+revno[0]+' is not valid. It must be the TAV file version e.g. '+tavno[0] - INFOTXT_OUTPUT,infotxt - ENDIF - ENDIF - ENDIF - - ;Check whether a Global Attribute (1), GEOMS Variable Attribute (2), or pre-defined Variable Attribute (3) - gi=WHERE(m_v0[i] EQ attr_arr_glob,gcnt) - vi=WHERE(m_v0[i] EQ attr_arr_data,vcnt) - pi=WHERE(STRLOWCASE(m_v0[i]) EQ ncsa,pcnt) - IF gcnt NE 0 THEN m_v2[i]=1 $ - ELSE IF vcnt NE 0 THEN m_v2[i]=2 $ - ELSE IF pcnt NE 0 THEN m_v2[i]=3 $ - ELSE BEGIN ;check for new Global Attribute - IF i GT 0 THEN test1=m_v2[i-1] EQ 1 ELSE test1=1 ;i.e. the previous attribute was also a global attribute - test2=(STRMID(m_v0[i],0,3) NE 'VAR') AND (STRMID(m_v0[i],0,3) NE 'VIS') ;does not start with 'VAR' or 'VIS' - test3=STRPOS(m_v0[i],' ') EQ -1 ;No spaces in the label - ;Check for possible mispellings of an actual attribute by removing '_' and comparing - gv_chk=STRUPCASE(STRJOIN(STRSPLIT(m_v0[i],' _',/EXTRACT),/SINGLE)) - test4=1 - FOR j=0,ng-1 DO BEGIN - ga_j=STRUPCASE(STRJOIN(STRSPLIT(attr_arr_glob[j],' _',/EXTRACT),/SINGLE)) - IF gv_chk EQ ga_j THEN test4=0 ;Match with actual Global Attribute so do not keep - ENDFOR - IF (test1) AND (test2) AND (test3) AND (test4) THEN m_v2[i]=1 ;it is considered a new global attribute - ENDELSE -ENDFOR - -;Check for any invalid attribute labels and remove -bi=WHERE(m_v2 EQ 0,bcnt) -IF bcnt NE 0 THEN BEGIN - FOR i=0,bcnt-1 DO BEGIN - infotxt='2 Unknown Attribute Label '+m_v0[bi[i]]+'| ignored' - INFOTXT_OUTPUT, infotxt - ENDFOR -ENDIF -;resize arrays, or stop the program if no valid data -gi=WHERE(m_v2 NE 0,nrline) -IF nrline NE 0 THEN BEGIN - m_v0=m_v0[gi] & m_v1=m_v1[gi] & m_v2=m_v2[gi] - mv_lng=mv_lng[gi] & mv_dbl=mv_dbl[gi] -ENDIF ELSE BEGIN - STOP_WITH_ERROR,o3[3]+proname,errtxt[3],lu & RETURN -ENDELSE - -;Check to see that Global and Variable Attributes are present -gi=WHERE(m_v2 EQ 1,gcnt) -vi=WHERE(m_v2 EQ 2,vcnt) -pi=WHERE(m_v2 EQ 3,pcnt) ;to do pre-defined attribute checks if necessary -IF (gcnt EQ 0) OR (vcnt EQ 0) THEN BEGIN - STOP_WITH_ERROR,o3[3]+proname,errtxt[3],lu & RETURN -ENDIF - -;Do QA on pre-defined attributes -IF pcnt NE 0 THEN PRE_DEFINED_ATT_CHECKS,-1,m_v0[pi] - -;Do DATA_VARIABLES checks -IF ndv EQ 0 THEN BEGIN - ;If no DATA_VARIABLES values found then try to determine values from VAR_NAME values - dvi=WHERE((m_v0 EQ 'VAR_NAME') AND (m_v1 NE ''),ndv) - IF ndv NE 0 THEN BEGIN - dv=m_v1[dvi] - ;Create or append to DATA_VARIABLES attribute - IF dvfound EQ -1L THEN BEGIN ;increase array sizes by 1 to accommodate DATA_VARIABLES - dvfound=nrline & nrline=nrline+1L - m_v0=[m_v0,'DATA_VARIABLES'] & m_v1=[m_v1,''] & m_v2=[m_v2,1] - mv_lng=[mv_lng,0LL] & mv_dbl=[mv_dbl,0.D] - infotxt='2 Missing DATA_VARIABLES Global Attribute| added to metadata based on VAR_NAME values' - ENDIF ELSE BEGIN - infotxt='2 Missing DATA_VARIABLES attribute values| appended to the metadata based on VAR_NAME values' - ENDELSE - INFOTXT_OUTPUT, infotxt - ENDIF ELSE BEGIN - STOP_WITH_ERROR,o3[3]+proname,errtxt[1],lu & RETURN - ;No or invalid DATA_VARIABLES and VAR_NAME attributes present in the metadata so cannot proceed - ENDELSE -ENDIF -;Check for repeated DATA_VARIABLES -FOR i=0,ndv-1 DO BEGIN - ri=WHERE(STRUPCASE(dv[i]) EQ STRUPCASE(dv),rcnt) - IF (rcnt GT 1) AND (dv[i] NE '') THEN BEGIN - STOP_WITH_ERROR,o3[3]+proname,'DATA_VARIABLES'+errtxt[2]+dv[i],lu & RETURN - ENDIF -ENDFOR - -;Do VAR_NAME checks -vi=WHERE(m_v0 EQ attr_arr_data[vni[0]],vcnt) ;identify VAR_NAME indices -IF vcnt NE 0 THEN BEGIN - ;Check if VAR_NAME values are present, if not add value from DATA_VARIABLES (if number of DATA_VARIABLES values match vcnt) - IF ndv EQ vcnt THEN BEGIN - FOR i=0,vcnt-1 DO BEGIN - IF (m_v1[vi[i]] NE '') AND (m_v1[vi[i]] NE dv[i]) THEN BEGIN - STOP_WITH_ERROR,o3[3]+proname,errtxt[4]+'('+m_v1[vi[i]]+'/'+dv[i]+')',lu & RETURN - ENDIF ELSE IF m_v1[vi[i]] EQ '' THEN BEGIN - m_v1[vi[i]]=dv[i] - infotxt='2 Missing VAR_NAME value for '+m_v0[vi[i]]+'|. '+dv[i]+'| appended to label based on DATA_VARIABLES value - INFOTXT_OUTPUT,infotxt - ENDIF - ENDFOR - ENDIF ELSE BEGIN ;number of DATA_VARIABLES values does not match the number of VAR_NAME occurrences - STOP_WITH_ERROR,o3[3]+proname,errtxt[5]+'('+STRTRIM(ndv,2)+'/'+STRTRIM(vcnt,2)+')',lu & RETURN - ENDELSE - ;Check for repeated VAR_NAMES - FOR i=0,vcnt-1 DO BEGIN - ri=WHERE(STRUPCASE(m_v1[vi[i]]) EQ STRUPCASE(m_v1[vi]),rcnt) - IF (rcnt GT 1) AND (m_v1[vi[i]] NE '') THEN BEGIN - STOP_WITH_ERROR,o3[3]+proname,'VAR_NAME'+errtxt[2]+m_v1[vi[i]],lu & RETURN - ENDIF - ENDFOR -ENDIF ELSE BEGIN ;No VAR_NAME labels so create from DATA_VARIABLES - FOR i=0,ndv-1 DO BEGIN - m_v0=[m_v0,'VAR_NAME'] & m_v1=[m_v1,dv[i]] & m_v2=[m_v2,2] - mv_lng=[mv_lng,0LL] & mv_dbl=[mv_dbl,0.D] - IF i EQ 0 THEN vi=[nrline] ELSE vi=[vi,nrline+1L] - ENDFOR - infotxt='1 Missing VAR_NAME attribute labels and values| added to metadata based on DATA_VARIABLES values' - INFOTXT_OUTPUT,infotxt -ENDELSE - -;Check for obsolete FTIR, UVVIS.DOAS, LIDAR, and MWR DATA_SOURCE_01 values and modify of possible -dsi=WHERE(m_v0 EQ 'DATA_SOURCE',dsicnt) -vni=WHERE(m_v0 EQ 'VAR_NAME',vncnt) -;extract VAR_NAME values -dv=STRARR(vncnt) & data_source='' -FOR i=0,vncnt-1 DO dv[i]=m_v1[vni[i]] -IF dsicnt EQ 1 THEN BEGIN - IF m_v1[dsi[0]] NE '' THEN BEGIN - data_source=m_v1[dsi[0]] & dschange=data_source - GEOMS_RULE_CHANGES,6,vncnt,dv,data_source - IF data_source NE dschange THEN m_v1[dsi[0]]=data_source - ENDIF -ENDIF - -;Check for missing Global Attributes which have values which can be calculated by the program -FOR i=0,N_ELEMENTS(attr_arr_glob_ins)-1 DO BEGIN - gaii=WHERE(m_v0 EQ attr_arr_glob_ins[i],gaicnt) - IF gaicnt EQ 0 THEN BEGIN ;missing mandatory global attribute so add to the array - IF attr_arr_glob_ins[i] EQ 'FILE_META_VERSION' THEN itxt=tab_ver ELSE itxt='' - nrline=nrline+1L - m_v0=[m_v0,attr_arr_glob_ins[i]] & m_v1=[m_v1,itxt] & m_v2=[m_v2,1] - mv_lng=[mv_lng,0LL] & mv_dbl=[mv_dbl,0.D] - infotxt='2 Missing mandatory Global Attribute '+attr_arr_glob_ins[i]+'| added' - INFOTXT_OUTPUT, infotxt - ENDIF -ENDFOR -;Do DATA_TEMPLATE/DATA_QUALITY checks -ngp=N_ELEMENTS(attr_arr_glob_prov) -ga_chk=STRARR(ngp) ;initialize DATA_TEMPLATE/QUALITY values -GEOMS_RULE_CHANGES,9,m_v0,m_v1,ga_chk -;Check to see if Global Attributes have to be added -FOR i=0,ngp-1 DO BEGIN - IF ga_chk[i] NE '' THEN BEGIN - m_v0=[m_v0,attr_arr_glob_prov[i]] - IF i EQ 0 THEN m_v1=[m_v1,ga_chk[i]] ELSE m_v1=[m_v1,''] - m_v2=[m_v2,1] & nrline=nrline+1L - mv_lng=[mv_lng,0LL] & mv_dbl=[mv_dbl,0.D] - IF i EQ 0 THEN itxt='='+ga_chk[i] ELSE itxt='' - infotxt='2 Missing Global Attribute '+attr_arr_glob_prov[i]+itxt+'| added' - INFOTXT_OUTPUT, infotxt - ENDIF -ENDFOR - -;Calculate expected size of meta_arr array -gi=WHERE(m_v2 EQ 1,gcnt) -IF gcnt GT ng THEN ngv=gcnt ELSE ngv=ng -n_arr=ngv+(nd*ndv) -meta_arr=STRARR(n_arr) & mlh=LON64ARR(n_arr) & mdh=DBLARR(n_arr) -;mlh and mdh are holding arrays to hold numeric values in the metadata (will be renamed mv_lng and mv_dbl) - -;Check for missing or repeated standard Global Attributes -i=0 -WHILE i LE ng-1 DO BEGIN - gai=WHERE(m_v0[gi] EQ attr_arr_glob[i],gacnt) - IF gacnt EQ 0 THEN BEGIN ;Check to see if Global Attribute Optional or Mandatory - gaoi=WHERE(attr_arr_glob[i] EQ attr_arr_glob_opt, gaocnt) - IF gaocnt EQ 0 THEN BEGIN ;it is mandatory, therefore missing - infotxt='3 Global Attribute Label not present in the Metadata: '+attr_arr_glob[i] - INFOTXT_OUTPUT,infotxt - ENDIF - ENDIF ELSE IF gacnt GT 1 THEN BEGIN ;Global Attribute Repeated - STOP_WITH_ERROR,o3[3]+proname,errtxt[7]+attr_arr_glob[i],lu & RETURN - ENDIF - IF gacnt EQ 0 THEN BEGIN ;remove attribute from the attr_arr_glob array - attr_arr_glob=[attr_arr_glob[0:i-1],attr_arr_glob[i+1:ng-1]] - ng=ng-1 - ENDIF ELSE i=i+1 -ENDWHILE - -;Check for all Standard Global Attributes, add any new ones, and put in expected order -xg=0 & mi=0 -FOR i=0,gcnt-1 DO BEGIN - gai=WHERE(m_v0[gi[i]] EQ attr_arr_glob,gacnt) - CASE 1 OF - gacnt EQ 0: BEGIN ;New Global Attribute - IF i EQ 0 THEN BEGIN - attr_arr_glob=[m_v0[gi[i]],attr_arr_glob] - ENDIF ELSE IF i EQ ng THEN BEGIN - attr_arr_glob=[attr_arr_glob,m_v0[gi[i]]] - ENDIF ELSE BEGIN - attr_arr_glob=[attr_arr_glob[0:mi],m_v0[gi[i]],attr_arr_glob[mi+1:N_ELEMENTS(attr_arr_glob)-1]] - mi=mi+1 - ENDELSE - ;IF (i GE ng-(1+xg)) OR (i LE dsei) THEN BEGIN - ; attr_arr_glob=[attr_arr_glob,m_v0[gi[i]]] & mi=ng - ; IF i LE dsei THEN xg=xg+1 - ;ENDIF ELSE BEGIN - ; attr_arr_glob=[attr_arr_glob[0:mi],m_v0[gi[i]],attr_arr_glob[mi+1:ng-1]] - ; mi=mi+1 - ;ENDELSE - ng=ng+1 - infotxt='1 Non-standard Global Attribute '+m_v0[gi[i]]+'| added' - INFOTXT_OUTPUT, infotxt - END - ELSE: mi=i ;gai[0] - ENDCASE -ENDFOR -FOR i=0,gcnt-1 DO BEGIN - gai=WHERE(m_v0[gi[i]] EQ attr_arr_glob,gacnt) - meta_arr[gai[0]]=m_v0[gi[i]]+'='+m_v1[gi[i]] -ENDFOR - -;Check for repeated new Global Attributes -FOR i=0,ng-1 DO BEGIN - gai=WHERE(m_v0[gi] EQ attr_arr_glob[i],gacnt) - IF gacnt GT 1 THEN BEGIN ;Global Attribute Repeated - STOP_WITH_ERROR,o3[3]+proname,errtxt[7]+attr_arr_glob[i],lu & RETURN - ENDIF -ENDFOR - -;Do checks for all the variable attributes and put in expected order -vni=-1 -FOR i=0,nd-1 DO BEGIN - si=ng+i ;start index of meta_arr for variable attributes - di=WHERE(m_v0 EQ attr_arr_data[i],dcnt) - IF attr_arr_data[i] EQ 'VAR_NAME' THEN vni=di ;to enable checks on optional variable attributes - IF dcnt EQ ndv THEN BEGIN ;i.e. correct number of attribute labels found so add to meta_arr in correct order - FOR j=0,ndv-1 DO BEGIN - meta_arr[si+(nd*j)]=m_v0[di[j]]+'='+m_v1[di[j]] - mlh[si+(nd*j)]=mv_lng[di[j]] & mdh[si+(nd*j)]=mv_dbl[di[j]] - ENDFOR - ENDIF ELSE IF dcnt EQ 0 THEN BEGIN ;attribute not present - so add with no label - oi=WHERE(attr_arr_data[i] EQ attr_arr_data_opt,ocnt) ;check whether missing attribute is optional - IF ocnt EQ 0 THEN BEGIN - FOR j=0,ndv-1 DO meta_arr[si+(nd*j)]=attr_arr_data[i]+'=' - IF qa_yes THEN itxt=' must be present in the metadata' ELSE itxt=' added to the metadata with no value' - infotxt='2 Mandatory variable attribute '+attr_arr_data[i]+itxt - INFOTXT_OUTPUT,infotxt - ENDIF - ENDIF ELSE BEGIN ;incorrect number of attributes - report with error unless attribute is optional - oi=WHERE(attr_arr_data[i] EQ attr_arr_data_opt,ocnt) - IF (ocnt NE 0) AND (vni[0] GE 0) AND (di[0]-vni[0] GT 0) THEN BEGIN ;match VAR_NOTES attributes to the corresponding VAR_NAME index value - FOR j=0,dcnt-1 DO BEGIN - vnih=di[j]-vni[0] & jh=0 - FOR k=0,N_ELEMENTS(vni)-1 DO BEGIN - dvn=di[j]-vni[k] - IF (dvn LT vnih) AND (dvn GT 0) THEN BEGIN - vnih=di[j]-vni[k] & jh=k - ENDIF - ENDFOR - meta_arr[si+(nd*jh)]=m_v0[di[j]]+'='+m_v1[di[j]] - mlh[si+(nd*jh)]=mv_lng[di[j]] & mdh[si+(nd*jh)]=mv_dbl[di[j]] - ENDFOR - ENDIF ELSE BEGIN ;Not optional or too much out of order, therefore report as an error - STOP_WITH_ERROR,o3[3]+proname,'Number of '+attr_arr_data[i]+errtxt[8]+STRTRIM(ndv,2)+')',lu & RETURN - ENDELSE - ENDELSE -ENDFOR - -;remove any empty meta_arr cells (can happen due to optional attributes) -;and save numeric metadata values in correct order -gi=WHERE(STRTRIM(meta_arr,2) NE '') -meta_arr=meta_arr[gi] & mv_lng=mlh[gi] & mv_dbl=mdh[gi] - -;Call GEOMS_RULE_CHANGES to check for obsolete Dataset names and update if possible -;Identify VAR_NAME and VAR_UNITS indices -vni=WHERE(STRMID(meta_arr,0,8) EQ 'VAR_NAME',vncnt) -vui=WHERE(STRMID(meta_arr,0,9) EQ 'VAR_UNITS',vucnt) -vnchange=STRARR(vncnt) ;array to hold original VAR_NAME values if they have been changed -;extract VAR_NAME values -dv=STRARR(vncnt) -FOR i=0,vncnt-1 DO BEGIN - res=STRSPLIT(meta_arr[vni[i]],' =',/EXTRACT,COUNT=nres) - IF nres EQ 2 THEN dv[i]=res[1] -ENDFOR -;Check for obsolete _START/STOP/INTEGRATION sub-values and replace with DATETIME.START/STOP -;or INTEGRATION.TIME (for single occurences only). Also check for double underscores and -;replace with single underscores in the Variable Names -GEOMS_RULE_CHANGES,1,vncnt,dv - -;Check for other obsolete variable names -IF vncnt EQ vucnt THEN BEGIN - vuv=STRARR(vucnt) - FOR i=0,vucnt-1 DO BEGIN - res=STRSPLIT(meta_arr[vui[i]],'=',/EXTRACT,COUNT=nres) - IF nres EQ 2 THEN vuv[i]=STRTRIM(res[1],2) - ENDFOR - GEOMS_RULE_CHANGES,5,vncnt,dv,vuv,data_source -ENDIF -;Update meta_arr in the case of modified Dataset name values -vci=WHERE(vnchange NE '',vcres) -IF vcres NE 0 THEN BEGIN ;need to update VAR_NAME and DATA_VARIABLES attribute values - dvi=WHERE(STRMID(meta_arr,0,14) EQ 'DATA_VARIABLES') - meta_arr[dvi[0]]='DATA_VARIABLES=' - FOR i=0,vncnt-1 DO BEGIN - ;Check for new VAR_NAME value and update - IF vnchange[i] NE '' THEN meta_arr[vni[i]]='VAR_NAME='+dv[i] - ;Append to new DATA_VARIABLES list - IF i EQ 0 THEN itxt='' ELSE itxt=';' - meta_arr[dvi[0]]=meta_arr[dvi[0]]+itxt+dv[i] - ENDFOR -ENDIF - -;Check for obsolete CALVAL and EVDC FILE_ACCESS values and replace -fai=WHERE(STRMID(meta_arr,0,11) EQ 'FILE_ACCESS') -res=STRSPLIT(meta_arr[fai[0]],' =;',/EXTRACT,COUNT=facnt) -IF facnt GE 2 THEN BEGIN - fav=STRUPCASE(res[1:facnt-1]) & fachange=fav - GEOMS_RULE_CHANGES,7,facnt-1,fav - IF ARRAY_EQUAL(fav,fachange) EQ 0 THEN BEGIN ;FILE_ACCESS values changed so update meta_arr - meta_arr[fai[0]]='FILE_ACCESS=' - FOR i=0,facnt-2 DO BEGIN - IF i EQ 0 THEN itxt='' ELSE itxt=';' - meta_arr[fai[0]]=meta_arr[fai[0]]+itxt+fav[i] - ENDFOR - ENDIF -ENDIF - -;Check for VAR_UNITS=DIMENSIONLESS or NONE and fix if possible. -;Need to check VAR_DATA_TYPE. If string then VAR_UNITS value should be blank, o/w replace with '1' -vdi=WHERE(STRMID(meta_arr,0,13) EQ 'VAR_DATA_TYPE',vdcnt) -IF (vdcnt NE 0) AND (vdcnt EQ vucnt) THEN BEGIN - vdv=STRARR(vdcnt) - FOR i=0,vdcnt-1 DO BEGIN - res=STRSPLIT(meta_arr[vdi[i]],'=',/EXTRACT,COUNT=nres) - IF nres EQ 2 THEN vdv[i]=STRUPCASE(STRTRIM(res[1],2)) - ENDFOR - vuchange=vuv - GEOMS_RULE_CHANGES,8,vucnt,vuv,vdv - FOR i=0,vucnt-1 DO BEGIN ;Check for changes and update meta_arr - IF STRUPCASE(vuchange[i]) NE vuv[i] THEN meta_arr[vui[i]]='VAR_UNITS='+vuv[i] - ENDFOR -ENDIF - -;Check for non-permitted characters (must be ISO646-US character set) -GEOMS_RULE_CHANGES,10,meta_arr - -END ;Procedure Read_Metadata - - - -PRO extract_and_test, dentry, cpos, nel, ta1, ta2, taname, aname, ti -;Procedure called by Check_Metadata to extract the relevant parts of tab_arr and test against -;a corresponding Metadata value. -; ---------- -;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org -; -; History: -; 20050802: Original IDLCR8HDF Routine - Version 1.0 -; 20050909: Make VAR_SI_CONVERSION values match the VAR_DATA_TYPE, and add fix for VAR_SI_CONVERSION -; calculation rules e.g. if VAR_UNITS=cm m-3 then VAR_SI_CONVERSION=0;0.01;m-2 - Version 1.1 -; 20061012: Procedure simplified, with all portions of the code related to the VAR_UNITS and -; VAR_SI_CONVERSION calculations moved to the Var_Units_Test routine; Common variable -; definition WIDGET_WIN added - Version 2.0 -; 20100205: Add RETURN command after all STOP_WITH_ERROR calls, which allows program to return to the -; calling program if the reterr argument is included in the idlcr8hdf call - Version 3.09 -; 20120313: Add option to check for Metadata Entries that are only different to the TAV entry -; because of case sensitivity and/or use of incorrect apostrophe ("`" instead of "'") -; - Version 4.0b8 -; 20140226: For AFFILIATION, if there is no match, do additional check of the acronym only and, -; if present, correct the value - Version 4.0b19 -; 20150811: Add infoerr array. For these Global Attributes the program will no longer stop, but -; will generate an ERROR message and continue if there is a problem with any of the -; entries - Version 4.0b29 -; 20161230: Add COUNTRY and AFFILIATION to infoerr array and allow for this when creating infotxt -; message - Version 4.0b41 -; 20171121: Fix error caused by incorrectly referring to the variable infoerr as inforerr -; - Version 4.0b43 -; 20191205: Account for DATA_SOURCE being able to have 2 or 3 sub-values, where the third sub- -; value is a VERSION_NAME - Version 4.0b53 -; 20220805: Ensure the 3-digit instrument ID is numeric (each character is now checked in -; IS_A_NUMBER_HDF) - Version 4.0b60 -; -; Inputs: dentry - the Metadata entry to be checked (everything after the '=') -; cpos - a pattern used by StrSplit on DEntry to split it into its component parts e.g. ';' or '_' -; nel - the number of sub-values in the set of tab_arr entries being checked (ta1 only) -; ta1 - the set of tab_arr values used to check against the Metadata contents -; ta2 - a second set of tab_arr values used for DATA_SOURCE checks, o/w set to [''] -; taname - a combination of the Metadata Attribute name and Table Field/Label used for the -; error messages -; aname - the Metadata attribute name to be used for the error message -; ti - the index value of the ta2 values to be checked. Used for DATA_SOURCE checks only -; -; Output: ti - the index value(s) of tab_arr which match the Metadata value (used in Check_Metadata -; to do further checks on the ORIGINATOR values) -; -; Called by: CHECK_METADATA -; -; Subroutines Called: STOP_WITH_ERROR (if error state detected) -; Possible Conditions for STOP_WITH_ERROR call (plus [line number] where called): -; 1. Incorrect number of (sub-)values for a particular Metadata attribute -; 2. Metadata value does not match any values in the Table Attribute Values file - -COMMON TABLEDATA -COMMON WIDGET_WIN - -proname='Check_Metadata/Extract_And_Test procedures: ' & lu=-1L -errtxt2=STRARR(2) -errtxt2[0]='Number of (sub-)values for this attribute should be ' -errtxt2[1]='Doesn''t match any Table Attribute Values under FIELD: ' - -infoerr=['COUNTRY','AFFILIATION','FILE_ACCESS','FILE_META_VERSION','DATA_LOCATION', $ - 'DATA_DISCIPLINE','DATA_GROUP'] ;do not stop the program for errors in these attributes - -;Check number of Metadata entry components is as expected -achk=STRSPLIT(dentry,cpos,/Extract,COUNT=n_achk) -oki=WHERE(n_achk EQ nel,okcnt) ;Note: DATA_SOURCE can have 2 or 3 sub-values (nel=[2,3] from 4.0b53) -IF okcnt EQ 0 THEN BEGIN - STOP_WITH_ERROR,o3[3]+proname+taname+'='+dentry+': ',errtxt2[0]+STRTRIM(nel,2)+'.',lu - RETURN -ENDIF - -;allow fix to be made to the affiliation if the acronym is correct but institute name is wrong -n_ta1=N_ELEMENTS(ta1) -IF (STRPOS(taname,'AFFILIATION') NE -1) AND (cpos NE '_') THEN BEGIN - affchk=1 & acroval=STRTRIM(achk[1],2) & t_acrovals=STRARR(n_ta1) -ENDIF ELSE affchk=0 - -;check Metadata entry with relevant TAV entry or entries -IF cpos EQ '_' THEN BEGIN ;DATA_SOURCE entry so need to treat components separately - achk=STRCOMPRESS(achk,/Remove_All) & nel=1 - achkh=' '+achk[1] - ;strip the last three characters (instrument ID) off achk(1) - achk[1]=STRMID(achk[1],0,STRLEN(achk[1])-3) - ;Check that the instrument ID is valid - instidchk=STRMID(achkh,STRLEN(achkh)-3,3) - IF ~IS_A_NUMBER_HDF(instidchk,2) THEN BEGIN - infotxt='3 DATA_SOURCE 3-digit instrument identifier missing or invalid' - INFOTXT_OUTPUT,infotxt - ENDIF -ENDIF ELSE achk=STRCOMPRESS(dentry,/Remove_All) -FOR j=0,n_ta1-1 DO BEGIN - ;extract valid portion of the entries (depending on ATTRIBUTE name) - tchk=STRSPLIT(ta1[j],';',/Extract) - ta1[j]=STRCOMPRESS(tchk[0],/Remove_All) - IF nel GT 1 THEN $ - FOR k=1,nel-1 DO ta1[j]=ta1[j]+';'+STRCOMPRESS(tchk[k],/Remove_All) - IF affchk EQ 1 THEN t_acrovals[j]=tchk[1] -ENDFOR -IF ta2[0] NE '' THEN BEGIN - FOR j=0,N_ELEMENTS(ta2)-1 DO BEGIN - ;extract valid portion of the entries (depending on ATTRIBUTE name) - tchk=STRSPLIT(ta2[j],';',/Extract) - ta2[j]=STRCOMPRESS(tchk[ti],/Remove_All) - ENDFOR -ENDIF -IF cpos EQ '_' THEN BEGIN - gi=WHERE(ta1 EQ achk[0],gcnt) - IF gcnt NE 0 THEN gi=WHERE(ta2 EQ achk[1],gcnt) -ENDIF ELSE BEGIN ;Check for Case Sensitive or incorrect apostrophe problem - gi=WHERE(ta1 EQ achk,gcnt) - IF gcnt EQ 0 THEN BEGIN - ;Check for Case Sensitivity and/or incorrect apostrophe usage or correct affiliation acronym only - ;Will be fixed in CHECK_METADATA - gi=WHERE(STRLOWCASE(ta1) EQ STRLOWCASE(achk),gcnt) - IF gcnt NE 0 THEN dentry=ta1[gi[0]] ELSE BEGIN - ;Check for "`" instead of "'" in metadata entry - IF STRPOS(achk,'`') NE -1 THEN BEGIN - achkh=achk - WHILE STRPOS(achkh,'`') NE -1 DO STRPUT,achkh,'''',STRPOS(achkh,'`') - gi=WHERE(STRLOWCASE(ta1) EQ STRLOWCASE(achkh),gcnt) - IF gcnt NE 0 THEN dentry=ta1[gi[0]] - ENDIF - ;If AFFILIATION check to see if the acronym only is valid - IF affchk EQ 1 THEN BEGIN - gi=WHERE(STRLOWCASE(acroval) EQ STRLOWCASE(t_acrovals),gcnt) - IF gcnt NE 0 THEN dentry=ta1[gi[0]] - ENDIF - ENDELSE - ENDIF -ENDELSE - -IF gcnt EQ 0 THEN BEGIN - ei=-1 - FOR i=0,N_ELEMENTS(infoerr)-1 DO IF STRPOS(taname,infoerr[i]) NE -1 THEN ei=i - - IF ei NE -1 THEN BEGIN ;Log message instead of STOP_WITH_ERROR - IF ei LE 1 THEN metalabel=STRMID(taname,STRPOS(taname,'/')+1) ELSE metalabel=infoerr[ei] - infotxt='3 '+metalabel+'='+aname+' doesn''t match any ' - infotxt=infotxt+'Table Attribute Values under '+infoerr[ei]+' field' - INFOTXT_OUTPUT,infotxt - ENDIF ELSE BEGIN - STOP_WITH_ERROR,o3[3]+proname+aname+': ',errtxt2[1]+taname,lu & RETURN - ENDELSE -ENDIF -ti=gi ;ti returned to the Check_MetaData procedure to check AFFILIATION, ADDRESS and EMAIL values - -END ;procedure Extract_and_Test - - - -PRO check_metadata -;Procedure to check the validity of Metadata values by checking them against the corresponding -;entries in the Table Attribute Values File -; ---------- -;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org -; -; History: -; 20050802: Original IDLCR8HDF Routine - Version 1.0 -; 20050909: 'rd' flag included to convert VAR_SI_CONVERSION values to floating point if the -; corresponding VAR_DATA_TYPE is real or double; Section included to check for -; repeated SI units in the calculated VAR_SI_CONVERSION - Version 1.1 -; 20061012: Section added to do simple checks on Metadata ORIGINATOR values if the information -; is not otherwise available in the TAV file; Warning messages written to output -; windows and/or log file depending on the options chosen by the user; Section relating -; to checks on VAR_UNITS and VAR_SI_CONVERSION moved to the VAR_UNITS_TEST routine; -; Common variable definition WIDGET_WIN added - Version 2.0 -; 20080302: Sections included to handle checks when an Envisat table.dat file is used, including; -; ensuring metadata labels DATA_TYPE and VAR_DATA_TYPE are not both found when using the -; Strpos procedure; Handling different table.dat format of ORIGINATOR attributes; using -; DATA_SOURCE_02 instead of AFFILIATION for the DATA_SOURCE checks. Section which puts -; TAV file VAR_UNITS and UNIT_PREFIX values into arrays for the VAR_UNITS_TEST call is -; no longer needed and is removed; Change made so that Free Attribute Metadata values -; which include semi-colons do not have white space removed (i.e. the semi-colons are -; not defined as separating sub-values) - Version 3.0 -; 20091203: Account for the situation when a VAR_NAME does not include a Variable Mode, but still -; includes a Variable Descriptor ('__' will be present in the name) - Version 3.08 -; 20100205: Add RETURN command after all STOP_WITH_ERROR calls, which allows program to return to the -; calling program if the reterr argument is included in the idlcr8hdf call - Version 3.09 -; 20101120: Do check for MJD2000 VAR_UNIT, and change to MJD2K; Check for LONG datatype (now refers to -; LONG64) and change to INTEGER (now means LONG32) for TAV checks - Version 4.0b0 -; 20121015: Change VAR_SI_CONVERSION information/error message so will also report if the -; VAR_SI_CONVERSION label does not have an accompanying value - Version 4.0b13 -; 20141110: Add check for DATETIME where VAR_SI_CONVERSION=0.0;86400.0;s, but VAR_UNITS is not MJD2K. -; Will change VAR_UNITS to MJD2K - Version 4.0b24 -; 20150127: Stop removing leading and trailing spaces from variable attribute values (when there is -; only 1 value) to allow for possibility of VAR_FILL_VALUEs being an empty string -; - Version 4.0b25 -; 20150811: Remove STOP_WITH_ERROR message if FILE_META_VERSION doesn't have 2 values. This check is -; now done in the READ_METADATA routine - Version 4.0b29 -; 20151012: Remove checks on white space in Metadata values. This check is now done in the -; READ_METADATA routine - Version 4.0b31 -; 20161130: Allow the program to continue when an error is generated after doing VAR_UNITS and -; VAR_SI_CONVERSION checks (previously called STOP_WITH_ERROR) - Version 4.0b40 -; 20161230: Modify checks on ORIGINATOR values as this section removed from the TAV file from -; 04R025. Add tests for COUNTRY in XX_ADDRESS, and check for '@' in XX_EMAIL - -; Version 4.0b41 -; 20170331: Allow for COUNTRY value in XX_ADDRESS to be all uppercase or have initial capitalization -; (allow for special cases e.g. Cote d'Ivoire; words such as the, and, of also not -; capitalized) - Version 4.0b42 -; 20191205: Add check for optional VERSION_NAME sub-value of DATA_SOURCE. If present then call -; GEOMS_RULE_CHANGES to do checks on the value - Version 4.0b53 -; 20240912: Allow for VAR_SI_CONVERSION to have two possible values depending on the ordering of units -; that have the same power value e.g. m-1 sr-1 or sr-1 m-1 - Version 4.0b64 -; -; Inputs: meta_arr - a string array containing the Global and Variable Attributes -; tab_arr - a string array containing the TAV or table.dat file attributes - -; Output: meta_arr - some meta_arr values may be corrected or calculated in this routine. White space -; between metadata sub-values is also removed. -; -; Called by: IDLCR8HDF -; -; Subroutines Called: EXTRACT_AND_TEST -; VAR_UNITS_TEST -; GEOMS_RULE_CHANGES -; STOP_WITH_ERROR (if error state detected); INFOTXT_OUTPUT -; Possible Conditions for STOP_WITH_ERROR call (plus [line number] where called): -; 1. An attribute value is expected but is absent or invalid -; 2. Incorrect number of (sub-)values for a particular Metadata attribute -; 3. NAME and AFFILIATION values don't match (only applies if reading in the full AVDC -; TAV file) -; 4. AFFILIATION (AVDC) or DATA_SOURCE_02 (Envisat) field not found in the TAV file -; 5. There is no match between a given VAR_UNIT, and the list of valid units and prefixes -; present in the TAV file -; 6. VAR_UNITS value is not valid -; -; Information Conditions (when the program is able to make changes): -; 1. ORIGINATOR values for ADDRESS or EMAIL replaced with those from the TAV file -; 2. VAR_SI_CONVERSION values replaced with those calculated by the program - -COMMON TABLEDATA -COMMON METADATA -COMMON DATA -COMMON WIDGET_WIN - -;Possible error messages for this procedure -proname='Check_Metadata procedure: ' & lu=-1L -errtxt=STRARR(4) -errtxt[0]='No or Invalid value for this Attribute: ' -errtxt[1]='Number of (sub-)values for this Attribute should be ' ;changed to information message -errtxt[2]='AFFILIATION value doesn''t match NAME value: ' -errtxt[3]=' field not found in the input Table Attribute Values file' - -;originator values to be checked -orig_attr=['PI_','DO_','DS_','NAME','AFFILIATION','ADDRESS','EMAIL'] -countrysp1=['Congo, The Democratic Republic of the','Cote d''Ivoire'] ;special cases for initial capitalization of the COUNTRY value -countrysp2=['the','and','of'] ;special cases for initial capitalization of the COUNTRY value (individual words) - -;initialize variables for VAR_UNITS/VAR_SI_CONVERSION checks -firstvu=0 & tchk=[''] & rd=1 & writeonce=0 -xval=[2,2,3,1] ;expected number of sub-values for the four originator attributes -new_name=[''] ;Entry which will contain any ORIGINATOR name values not present in the TAV file - -FOR i=0,N_ELEMENTS(meta_arr)-1 DO BEGIN - res=STRSPLIT(meta_arr[i],'=',/Extract,COUNT=nres) - IF res[0] EQ 'VAR_NAME' THEN vnval=meta_arr[i] - FOR j=0,2 DO BEGIN - FOR k=3,6 DO BEGIN - ;need to change ORIGINATOR NAME ATTRIBUTE names to match TAV File - IF res[0] EQ orig_attr[j]+orig_attr[k] THEN BEGIN - res[0]='ORIGINATOR' & jh=j & kh=k - ENDIF - ENDFOR - ENDFOR - ;Test whether metadata label is also a TAV field - ;First test checks for exact match between metadata label and TAV field - ;(This avoids possible problem using STRPOS (e.g. DATA_TYPE won't also pick up VAR_DATA_TYPE)) - mi=WHERE(tab_arr[*,0] EQ res[0],mcnt) - ;Back-up test in the event that label has more than one TAV field (e.g. DATA_VARIABLES) - IF mcnt EQ 0 THEN mi=WHERE(STRPOS(tab_arr[*,0],res[0]) EQ 0,mcnt) ;was NE -1 - - IF res[0] EQ 'ORIGINATOR' THEN BEGIN - ;Do checks on _NAME, _AFFILIATION, _ADDRESS, and _EMAIL sub-values - resx=STRSPLIT(meta_arr[i],'=;',/Extract) - nel=N_ELEMENTS(resx)-1 - - IF (kh EQ 4) AND (nel EQ xval[1]) THEN BEGIN ;check on AFFILIATION value - resy=STRSPLIT(meta_arr[i],'=',/Extract) - mci=WHERE(tab_arr[*,0] EQ 'AFFILIATION',mccnt) - IF mccnt NE 0 THEN BEGIN - ta1=tab_arr[mci[0],2:tab_arr[mci[0],1]+1] ;subset of relevant FIELD ENTRIES - taname=tab_arr[mci[0],0]+'/'+resy[0] ;name combination for error message - dentry=resy[1] & ti=-1 - EXTRACT_AND_TEST,dentry,';',xval[1],ta1,[''],taname,resy[1],ti - IF STRLEN(rerr[0]) GT 2 THEN RETURN - IF dentry NE resy[1] THEN BEGIN - ;Case and/or apostrophe differences between Metadata entry and TAV entry - achk2=STRSPLIT(tab_arr[mci[0],ti[0]+2],';',/Extract) - achk=STRTRIM(achk2[0],2) - FOR m=1,xval[1]-1 DO achk=achk+';'+STRTRIM(achk2[m],2) - IF qa_yes THEN itxt=' should be ' ELSE itxt=' replaced with ' - infotxt='2 '+meta_arr[i]+itxt+resy[0]+'='+achk+' based on the TAV entry' - INFOTXT_OUTPUT,infotxt - meta_arr[i]=resy[0]+'='+achk - ENDIF - ENDIF - ENDIF - IF (kh EQ 5) AND (nel EQ xval[2]) THEN BEGIN ;check on COUNTRY value - mci=WHERE(tab_arr[*,0] EQ 'COUNTRY',mccnt) - IF mccnt NE 0 THEN BEGIN - ;check the COUNTRY value against the TAV values (note: can be all uppercase or initial capitalization) - aname=res[1] & ti=-1 - ta1=tab_arr[mci[0],2:tab_arr[mci[0],1]+1] ;subset of relevant FIELD ENTRIES - taname=tab_arr[mci[0],0]+'/'+orig_attr[jh]+'ADDRESS' ;name combination for error message - EXTRACT_AND_TEST,STRUPCASE(resx[nel]),';',1,ta1,[''],taname,aname,ti - IF STRLEN(rerr[0]) GT 2 THEN RETURN - ;Test for upper case or initial capitalization - IF resx[nel] NE STRUPCASE(resx[nel]) THEN BEGIN - counterr=0 ;error code to detect problem with initial capitalization - sci=WHERE(STRUPCASE(resx[nel]) EQ STRUPCASE(countrysp1),spcnt) - IF spcnt NE 0 THEN BEGIN ;Test special case countries e.g. Cote d'Ivoire - IF resx[nel] NE countrysp1[sci[0]] THEN counterr=1 - ENDIF ELSE BEGIN ;Test individual words in the country value - sepcount=STRSPLIT(resx[nel],' ,',COUNT=spcnt,/EXTRACT) - FOR j=0,spcnt-1 DO BEGIN - slen=STRLEN(sepcount[j]) - sci=WHERE(STRUPCASE(sepcount[j]) EQ STRUPCASE(countrysp2),sp2cnt) - IF sp2cnt NE 0 THEN test=countrysp2[sci[0]] $ ;special case lowercase word e.g. the, and, of - ELSE IF slen EQ 1 THEN test=STRUPCASE(sepcount[j]) $ ;only one letter in the word so make uppercase - ELSE test=STRUPCASE(STRMID(sepcount[j],0,1))+STRLOWCASE(STRMID(sepcount[j],1)) ;capitalize the first letter - IF sepcount[j] NE test THEN counterr=1 - ENDFOR - ENDELSE - IF counterr EQ 1 THEN BEGIN - uccountry=STRUPCASE(resx[nel]) - IF qa_yes THEN itxt=' should be ' ELSE itxt=' replaced with ' - infotxt='2 COUNTRY value for '+meta_arr[i]+itxt+uccountry - INFOTXT_OUTPUT,infotxt - meta_arr[i]=STRMID(meta_arr[i],0,STRPOS(meta_arr[i],';',/REVERSE_SEARCH)+1)+uccountry - ENDIF - ENDIF - ENDIF - ENDIF - IF (kh EQ 6) AND (nel EQ xval[3]) THEN BEGIN ;check @ present in the e-mail value - amppos=STRPOS(res[1],'@') - IF (amppos LE 0) OR (amppos EQ STRLEN(res[1])-1) THEN BEGIN - infotxt='3 Invalid format for '+orig_attr[jh]+'EMAIL value' - INFOTXT_OUTPUT,infotxt - ENDIF - ENDIF - IF (nel NE xval[kh-3]) OR (STRMID(meta_arr[i],STRLEN(meta_arr[i])-1,1) EQ ';') THEN BEGIN - IF xval[kh-3] GT 1 THEN itxt='sub-' ELSE itxt='' - infotxt='3 Number of '+itxt+'values for attribute '+meta_arr[i]+' should be '+STRTRIM(xval[kh-3],2) - INFOTXT_OUTPUT,infotxt - ENDIF - mcnt=0 - ENDIF - - IF mcnt NE 0 THEN BEGIN ;check Metadata value(s) against TAV file entries - IF (nres NE 2) AND (rd NE 0) THEN BEGIN - STOP_WITH_ERROR,o3[3]+proname,errtxt[0]+meta_arr[i],lu & RETURN - ENDIF ELSE IF res[0] EQ 'DATA_SOURCE' THEN BEGIN - ;need to test against 'DATA_SOURCE' and 'AFFILIATION' (AVDC) or 'DATA_SOURCE_02' - ;(original Envisat) entries - ta1=tab_arr[mi[0],2:tab_arr[mi[0],1]+1] ;DATA_SOURCE ENTRIES - IF tab_type EQ 0 THEN BEGIN - atxt='AFFILIATION' & atx='' & ti=1 - ENDIF ELSE BEGIN - atxt='DATA_SOURCE_02' & atx='_01' & ti=0 - ENDELSE - ai=WHERE(STRPOS(tab_arr[*,0],atxt) NE -1,acnt) - IF acnt EQ 0 THEN BEGIN - STOP_WITH_ERROR,o3[3]+proname,atxt+errtxt[3],lu & RETURN - ENDIF - ta2=tab_arr[ai[0],2:tab_arr[ai[0],1]+1] ;AFFILIATION/DATA_SOURCE_02 ENTRIES - taname=res[0]+atx+'/'+tab_arr[ai[0],0] ;name combination for error message - n_sv=[2,3] ;possible number of DATA_SOURCE sub-values - EXTRACT_AND_TEST,res[1],'_',n_sv,ta1,ta2,taname,res[1],ti - IF STRLEN(rerr[0]) GT 2 THEN RETURN - - achk=STRSPLIT(res[1],'_',/EXTRACT,COUNT=n_achk) - IF n_achk EQ 3 THEN BEGIN - ;Check DATA_VERSION_NAME if there are 3 sub-values in DATA_SOURCE (introduced v4.0b53) - GEOMS_RULE_CHANGES,11,achk[2],meta_arr - ENDIF - - ENDIF ELSE IF res[0] EQ 'DATA_VARIABLES' THEN BEGIN - ;make into 2-D array and separate out the components of the VAR_NAMES - achk=STRSPLIT(res[1],';',/Extract,COUNT=nel) - achk2=STRARR(mcnt,nel) - FOR j=0,nel-1 DO BEGIN - dv=STRSPLIT(achk[j],'_',/Extract,Count=n_dv) - IF n_dv GT mcnt THEN BEGIN ;invalid VAR_NAME composition - STOP_WITH_ERROR,o3[3]+proname,errtxt[0]+res[0]+'='+achk[j],lu & RETURN - ENDIF - achk2[0:N_ELEMENTS(dv)-1,j]=dv - ENDFOR - FOR j=0,nel-1 DO BEGIN - dv=WHERE(achk2[*,j] NE '',dvcnt) - FOR k=0,mcnt-1 DO BEGIN - IF achk2[k,j] NE '' THEN BEGIN - taname=tab_arr[mi[k],0] - ta1=REFORM(tab_arr[mi[k],2:tab_arr[mi[k],1]+1]) ;subset of TAV DATA_VARIABLES entries - IF (k NE 0) AND (dvcnt EQ 2) THEN BEGIN - ;need to check both variable mode and descriptor options - FOR m=k+1,mcnt-1 DO BEGIN - taname=taname+'/'+tab_arr[mi[m],0] - ta1=[ta1,REFORM(tab_arr[mi[m],2:tab_arr[mi[m],1]+1])] - ENDFOR - ENDIF - EXTRACT_AND_TEST,achk2[k,j],';',1,ta1,[''],taname,achk[j],0 - IF STRLEN(rerr[0]) GT 2 THEN RETURN - ENDIF - ENDFOR - ENDFOR - ENDIF ELSE IF res[0] EQ 'FILE_ACCESS' THEN BEGIN - ;need to check each Metadata entry individually - achk=STRSPLIT(res[1],';',/Extract) - nel=N_ELEMENTS(achk) - ta1=tab_arr[mi[0],2:tab_arr[mi[0],1]+1] ;FILE_ACCESS entries - FOR j=0,nel-1 DO BEGIN - EXTRACT_AND_TEST,achk[j],';',1,ta1,[''],res[0],achk[j],0 - IF STRLEN(rerr[0]) GT 2 THEN RETURN - ENDFOR - ENDIF ELSE IF res[0] EQ 'FILE_META_VERSION' THEN BEGIN - ;only need to look at the second value - achk=STRSPLIT(res[1],';',/Extract) - IF N_ELEMENTS(achk) NE 2 THEN achk=[achk,'x','x'] ;ensure there are at least 2 values - achk=[STRTRIM(achk[1],2)] - ta1=tab_arr[mi[0],2:tab_arr[mi[0],1]+1] ;FILE_META_VERSION entries - EXTRACT_AND_TEST,achk[0],';',1,ta1,[''],res[0],achk[0],0 - IF STRLEN(rerr[0]) GT 2 THEN RETURN - ENDIF ELSE IF res[0] EQ 'VAR_UNITS' THEN BEGIN - ;VAR_UNITS and VAR_SI_CONVERSION entries (not needed for STRING data type) - IF rd NE 0 THEN BEGIN - IF STRUPCASE(res[1]) EQ 'MJD2000' THEN BEGIN ;change to MJD2K - in1=meta_arr[i] & in2=res[1] - GEOMS_RULE_CHANGES,3,in1,in2,writeonce - meta_arr[i]=in1 & res[1]=in2 - writeonce=1 - ENDIF - errstate='' & datetimechk=STRPOS(STRUPCASE(vnval),'DATETIME') NE -1 - VAR_UNITS_TEST,res[1],rd,tab_type,tchk,errstate - IF errstate NE '' THEN BEGIN ;VAR_UNIT value not valid - ;STOP_WITH_ERROR,o3[3]+proname+STRMID(errstate,0,STRPOS(errstate,':')+2), $ - ; STRMID(errstate,STRPOS(errstate,':')+2),lu - ;RETURN - IF ~datetimechk THEN itxt='. VAR_SI_CONVERSION value not checked' ELSE itxt='' - infotxt='3 '+errstate+itxt - INFOTXT_OUTPUT,infotxt - ENDIF - ;Find VAR_SI_CONVERSION attribute - k=1 - WHILE STRMID(meta_arr[i+k],0,17) NE 'VAR_SI_CONVERSION' DO k++ - achk=STRSPLIT(meta_arr[i+k],'=',/Extract) ;VAR_SI_CONVERSION values - IF N_ELEMENTS(achk) EQ 1 THEN achk=[achk,' '] - IF datetimechk THEN BEGIN - ;Check if VAR_SI_CONVERSION values are 0.0;86400.0;s regardless of VAR_UNITS. If so, then don't change - achk1=STRSPLIT(achk[1],' ;',/EXTRACT) - achk1=[achk1,'','',''] ;Ensure there are at least 3 values extracted - IF (is_a_number_hdf(achk1[0])) AND (is_a_number_hdf(achk1[1])) THEN BEGIN - IF (FIX(achk1[0]) EQ 0) AND (LONG(achk1[1]) EQ 86400L) AND $ - (STRLOWCASE(achk1[2]) EQ 's') THEN tchk[*]='0.0;86400.0;s' - ENDIF ELSE IF errstate NE '' THEN tchk[*]='0.0;86400.0;s' - ENDIF - ;check calculated VAR_SI_CONVERSION value(s) against existing value in the metadata - IF (errstate EQ '') OR (datetimechk) THEN BEGIN - vscgi=WHERE(achk[1] EQ tchk,vscgcnt) - IF vscgcnt EQ 0 THEN BEGIN - IF qa_yes THEN itxt=' should be ' ELSE itxt=' replaced with ' - infotxt='2 '+meta_arr[i+k]+itxt+'VAR_SI_CONVERSION='+tchk[0]+' for '+vnval - INFOTXT_OUTPUT, infotxt - meta_arr[i+k]=achk[0]+'='+tchk[0] - ENDIF ELSE meta_arr[i+k]=achk[0]+'='+tchk[vscgi[0]] - ENDIF - ENDIF - ENDIF ELSE BEGIN ;perform checks on the remaining ATTRIBUTE_NAMES - achk=STRSPLIT(res[1],';',/Extract) - achk=STRTRIM(achk,2) - nel=N_ELEMENTS(achk) & ta2=[''] - IF res[0] EQ 'VAR_DATA_TYPE' THEN BEGIN - ;to convert VAR_SI_CONVERSION values to floating point if necessary - CASE 1 OF - (STRUPCASE(res[1]) EQ 'REAL') OR (STRUPCASE(res[1]) EQ 'DOUBLE'): rd=-1 - STRUPCASE(res[1]) EQ 'STRING': rd=0 - ELSE: rd=1 - ENDCASE - IF STRUPCASE(achk[0]) EQ 'LONG' THEN achk[0]='INTEGER' - ;Changed to INTEGER for EXTRACT_AND_TEST only - actual change occurs in SET_UP_STRUCTURE - ENDIF - IF (nel NE mcnt) OR (STRMID(meta_arr[i],STRLEN(meta_arr[i])-1,1) EQ ';') THEN BEGIN - IF mcnt GT 1 THEN itxt='sub-' ELSE itxt='' - infotxt='3 Number of '+itxt+'values for attribute '+meta_arr[i]+' should be '+STRTRIM(mcnt,2) - INFOTXT_OUTPUT,infotxt - ;STOP_WITH_ERROR,o3[3]+proname+meta_arr[i]+': ',errtxt[1]+STRTRIM(mcnt,2)+'.',lu - ;RETURN - ENDIF - nel=1 - FOR j=0,mcnt-1 DO BEGIN - ta1=tab_arr[mi[j],2:tab_arr[mi[j],1]+1] ;subset of relevant ENTRIES - dentry=achk[j] & ti=mi[j] - EXTRACT_AND_TEST,dentry,';',nel,ta1,ta2,tab_arr[mi[j],0],achk[j],ti - IF STRLEN(rerr[0]) GT 2 THEN RETURN - IF (mcnt EQ 1) AND (dentry NE achk[j]) THEN BEGIN - ;Case and/or apostrophe differences between Metadata entry and TAV entry - achk2=STRSPLIT(tab_arr[mi[j],ti[0]+2],';',/Extract) - achk=STRTRIM(achk2[0],2) - IF qa_yes THEN itxt=' should be ' ELSE itxt=' replaced with ' - infotxt='2 '+meta_arr[i]+itxt+res[0]+'='+achk+' based on the TAV entry' - INFOTXT_OUTPUT,infotxt - meta_arr[i]=res[0]+'='+achk - ENDIF - IF j EQ 0 THEN ta2[0]='' - ENDFOR - ENDELSE - ENDIF -ENDFOR - -END ;Procedure Check_Metadata - - - -PRO extract_data, sds, inf, vc, dtest, ndl, infowrite -;Procedure to extract the set of data corresponding to a particular Variable Attribute -; ---------- -;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org -; -; History: -; 20050802: Introduced to IDLCR8HDF, originally part of the READ_DATA procedure - Version 1.0 -; 20050909: After reading in a data segment according to the VAR_SIZE values, check the next line to -; ensure it is not a further data point (i.e. there are more data points than specified by -; VAR_SIZE); If sds is a structure, and there is only one data point then return dtest as -; an array rather than a scalar - Version 1.1 -; 20061012: The portion of READ_DATA dealing with extracting a specific data segment renamed -; EXTRACT_DATA - Version 2.0 -; 20080302: The sets of data values in a file can now be in any order, rather than the same order as -; that given by the DATA_VARIABLES attribute; dtest used as an input flag to indicate -; whether the procedure call is from SET_UP_STRUCTURE [-1] or READ_DATA [0]. If from -; SET_UP_STRUCTURE then only the number of data lines (ndl) is determined by the routine; -; an error is generated in the case where the same VAR_NAME is repeated in the data file; -; Error messages clarified when an incorrect number of data values are found in the -; input data file - Version 3.0 -; 20100205: Add RETURN command after all STOP_WITH_ERROR calls, which allows program to return to the -; calling program if the reterr argument is included in the idlcr8hdf call - Version 3.09 -; 20130116: Account for empty string data values in a dataset - previously '' values ignored in an -; input data file - Version 4.0b15 -; 20150227: Do not automatically run STRTRIM on the dataset when it is read in to allow checks on -; string datasets - Version 4.0b26 -; 20150811: Add infowrite parameter so that if DATETIME type dataset is in ISO8601 format then the -; information/error message will only be written once instead of for each value -; - Version 4.0b29 -; 20171130: Remove ds from STOP_WITH_ERROR parameters - Version 4.0b44 -; -; Inputs: sds - Either a structure containing the Variable Attributes and Data, or the input -; data file -; inf - flag identifying sds type where, 0=structure; 1=data file -; vc - the index value of the vn array holding the VAR_NAME being searched for in the data -; file, or the index value of the sds structure which holds the data -; dtest - a single string array to show which procedure called this routine, either -; SET_UP_STRUCTURE [-1], or READ_DATA [0] -; infowrite - optional input to stop ISO8601 to MJD2K information message being written multiple -; times (when called by READ_DATA only) -; -; Outputs: dtest - a string array of size ndl that holds the data values -; ndl - The total number of data lines i.e. the product of the VAR_SIZE values -; -; Called by: SET_UP_STRUCTURE; READ_DATA -; -; Subroutines Called: STOP_WITH_ERROR (if error state detected) -; Possible Conditions for STOP_WITH_ERROR call (plus [line number] where called): -; 1. EOF encountered when trying to find the VAR_NAME or while attempting to read in data -; [2146,2220,2221] -; 2. Invalid data point found in the dataset [2193] -; 3. Two instances of the same VAR_NAME found in the input data file [2204] -; 4. Too many data values present in the dataset or the next line in the file is invalid [2211] -; 5. Number of data values does not match product of VAR_SIZE [2237] -; 6. ISO8601 datetime format not valid [2253] - -COMMON DATA -COMMON WIDGET_WIN - -;possible error messages for this procedure -errtxt=STRARR(10) -errtxt[0]='EOF encountered but not expected' -errtxt[1]=' when trying to find VAR_NAME in ' -errtxt[2]=' when trying to determine VAR_SIZE for VAR_NAME=' -errtxt[3]=' - Number of data lines should be ' -errtxt[4]='Invalid data point found in the dataset at line ' -errtxt[5]='Two instances of this VAR_NAME present in the Data File' -errtxt[6]='Too many data values present in the dataset or invalid data line entry following the dataset' -errtxt[7]='Number of data values does not match product of VAR_SIZE: ' -errtxt[8]='ISO8601 format not valid: ' -errtxt[9]='Unable to determine correct number of data values due to VAR_SIZE calculation error' -proname='Extract_Data procedure: ' - -ndl=1L ;initialize data line count -vsi=WHERE(vserror EQ vn[vc],vcnt) ;check whether there is a VAR_SIZE calculation error for this dataset -sdata=ndm(vc,8) EQ 6 ;Check for String data type - -IF inf EQ 1 THEN BEGIN ;Find the first data value for the requested VAR_NAME - ON_IOERROR,TypeConversionError - dum='' & vntest=vn[vc] - ;Add check in case VAR_NAME has been changed from original input - IF vnchange[vc] NE '' THEN vntest=vnchange[vc] - OPENR,lu,sds,/GET_LUN - IF NOT EOF(lu) THEN BEGIN ;read through data file until the correct Variable Name is found - REPEAT READF,lu,dum UNTIL (EOF(lu)) OR (STRTRIM(STRUPCASE(dum),2) EQ STRUPCASE(vntest)) OR $ - (STRTRIM(STRUPCASE(dum),2) EQ STRUPCASE(vn[vc])) - ENDIF - IF STRTRIM(STRUPCASE(dum),2) EQ STRUPCASE(vn[vc]) THEN vntest=vn[vc] - IF EOF(lu) THEN BEGIN - STOP_WITH_ERROR,o3[3]+proname+vn[vc],errtxt[0]+errtxt[1]+sds,lu & RETURN - ENDIF - - ;Find first data point - dum='!' - valid=-1 ;set to test for successful line read - WHILE (STRMID(dum,0,1) EQ '!') OR (STRMID(dum,0,1) EQ '#') OR ((~sdata) AND (strtrim(dum,2) EQ '')) DO BEGIN - READF,lu,dum ;& dum=STRTRIM(dum,2) - ENDWHILE - valid=1 ;i.e. got to here without prematurely reaching the eof so first data point found OK - - IF dtest[0] EQ '-1' THEN BEGIN ;need to determine the total number of data lines only (ndl) - IF NOT EOF(lu) THEN BEGIN - ;read through until eof or comment line or another VAR_NAME is reached - REPEAT BEGIN - READF,lu,dum - dum=STRTRIM(STRUPCASE(dum),2) - bi=WHERE(dum EQ STRUPCASE(vn)) - IF (STRMID(dum,0,1) NE '!') AND (STRMID(dum,0,1) NE '#') AND $ - ((dum NE '') OR ((sdata) AND (dum EQ ''))) AND (bi[0] EQ -1) THEN incnt=1L $ - ELSE incnt=0L - ndl=ndl+incnt ;presume it is a valid data line so add to total - ENDREP UNTIL (incnt EQ 0L) OR (EOF(lu)) - ENDIF - ENDIF ELSE BEGIN ;return dtest and ndl - di=WHERE(ndm[vc,0:7] NE 0L,ndim) - ;multiply together the array dimensions to determine expected total number of datalines - FOR i=0,ndim-1 DO ndl=ndl*ndm[vc,di[i]] - ;attempt to read any remaining data values - IF ndl GT 1L THEN BEGIN - valid=0 ;set to test for successful block read - dtest=STRARR(ndl-1) & READF,lu,dtest - valid=1 ;i.e. got to here without prematurely reaching the EOF so block read was OK - dtest=[dum,dtest] ;add first value to dtest - ENDIF ELSE dtest=[dum] - dtestup=STRUPCASE(STRTRIM(dtest,2)) ;& dtest=STRTRIM(dtest,2) - ;Test to see if any of the 'data' read in is a VAR_NAME - if so bcnt will NE 0 (and will generate error message) - bcnt=0 & i=0 - WHILE (bcnt EQ 0) AND (i LE nvn-1) DO BEGIN - bi=WHERE(dtestup EQ STRUPCASE(vn[i]),bcnt) - IF bcnt EQ 0 THEN i=i+1 - ENDWHILE - ;Test to see if any of the 'data' read in is not a valid data point - if so bcnt will NE 0 - IF bcnt EQ 0 THEN $ - bi=WHERE((STRMID(dtestup,0,1) EQ '!') OR (STRMID(dtestup,0,1) EQ '#') OR $ - ((dtestup EQ '') AND (~sdata)) OR (dtestup EQ STRUPCASE(vntest)),bcnt) - - IF bcnt NE 0 THEN BEGIN ;Non-valid data point found in the dataset - STOP_WITH_ERROR,o3[3]+proname+vn[vc]+': ',errtxt[4]+STRTRIM(bi[0]+1,2)+errtxt[3]+ $ - STRTRIM(ndl,2),lu - RETURN - ENDIF - - ;Read next line if not EOF to test for the correct number of data lines - IF NOT EOF(lu) THEN READF,lu,dum ELSE dum='!' - dum=STRTRIM(STRUPCASE(dum),2) - ;Test to see if the next line read in is another VAR_NAME - if not then bi[0] will equal -1 - bi=WHERE(dum EQ STRUPCASE(vn)) - IF bi[0] EQ vc THEN BEGIN ;Same VAR_NAME found twice in the data file - STOP_WITH_ERROR,o3[3]+proname+vn[vc]+': ',errtxt[5],lu & RETURN - ENDIF - IF (bi[0] EQ -1) AND (dum NE '!') THEN bi=WHERE(dum EQ STRUPCASE(vnchange)) - ;Test to check if the next line read in is another data point - if so bcnt will be set to 1 - IF (STRMID(dum,0,1) NE '!') AND (STRMID(dum,0,1) NE '#') AND $ - ((dum NE '') OR ((sdata) AND (dum EQ ''))) AND (bi[0] EQ -1) THEN $ - bcnt=1 - IF bcnt NE 0 THEN BEGIN ;not enough data lines read in or next line in the file is invalid - IF vcnt NE 0 THEN STOP_WITH_ERROR,o3[3]+proname+'VAR_NAME='+vn[vc]+': ',errtxt[9],lu $ - ELSE $ - STOP_WITH_ERROR,o3[3]+proname+'VAR_NAME='+vn[vc]+': ',errtxt[6]+errtxt[3]+STRTRIM(ndl,2),lu - RETURN - ENDIF - ENDELSE - FREE_LUN,lu - - TypeConversionError: - IF valid LE 0 THEN BEGIN ;EOF encountered but not expected - IF (dtest[0] EQ '-1') AND (valid EQ -1) THEN $ - STOP_WITH_ERROR,o3[3]+proname,errtxt[0]+errtxt[2]+vn[vc],lu $ - ELSE BEGIN - ;Check to see if it is due to a VAR_SIZE calculation error or not - IF vcnt NE 0 THEN STOP_WITH_ERROR,o3[3]+proname+'VAR_NAME='+vn[vc]+': ',errtxt[9],lu $ - ELSE $ - STOP_WITH_ERROR,o3[3]+proname+'VAR_NAME='+vn[vc]+': ',errtxt[0]+errtxt[3]+STRTRIM(ndl,2),lu - ENDELSE - RETURN - ENDIF -ENDIF ELSE BEGIN ;sds is a structure - IF dtest[0] EQ '-1' THEN BEGIN ;determine the total number of data lines only - dtest=*sds[vc].data - ndl=LONG(N_ELEMENTS(dtest)) - dtest=['-1'] - ENDIF ELSE BEGIN ;return dtest and ndl - lu=-1 - di=WHERE(ndm[vc,0:7] NE 0L,ndim) - ;multiply together the array dimensions to determine expected total number of datalines - FOR i=0,ndim-1 DO ndl=ndl*ndm[vc,di[i]] - dtest=*sds[vc].data - ;check structure contains the correct number of elements - IF N_ELEMENTS(dtest) NE ndl THEN BEGIN - STOP_WITH_ERROR,o3[3]+proname+vn[vc]+': ',errtxt[7]+STRTRIM(N_ELEMENTS(dtest),2)+'/'+STRTRIM(ndl,2),lu - RETURN - ENDIF - stest=SIZE(dtest) ;test to see if dtest is an array or scalar - IF stest[0] EQ 0 THEN dtest=[dtest] ELSE dtest=REFORM(dtest,ndl,/Overwrite) ;convert to a 1-D array if necessary - ENDELSE -ENDELSE - -;Note: this conditional will only be satisfied if the routine is called by READ_DATA -;(vu still not assigned VAR_UNITS value if called by SET_UP_STRUCTURE) -IF STRUPCASE(vu[vc]) EQ 'MJD2K' THEN BEGIN - ;Convert time from ISO8601 to MJD2000 format if necessary - FOR i=0L,ndl-1L DO BEGIN - IF STRPOS(STRUPCASE(dtest[i]),'Z') NE -1 THEN BEGIN - mjd2000=JULIAN_DATE(dtest[i],/I,/M) ;return time in MJD2000 format - IF mjd2000 EQ -99999.d THEN BEGIN ;conversion error - STOP_WITH_ERROR,o3[3]+proname+vn[vc]+': ',errtxt[8]+dtest[i],lu & RETURN - ENDIF - dtest[i]=STRING(format='(d18.9)',mjd2000) - IF infowrite EQ 1 THEN BEGIN - IF qa_yes THEN itxt='cannot be in ISO8601 format' ELSE itxt='changed from ISO8601 to MJD2K format' - infotxt='2 Dataset values '+itxt+' for VAR_NAME='+vn[vc] - INFOTXT_OUTPUT,infotxt - infowrite=0 - ENDIF - ENDIF - ENDFOR -ENDIF - -END ;Procedure Extract_Data - - - -PRO set_up_structure, sds, inf -;Procedure to do additional checks on VAR_NAME, VAR_DEPEND, VAR_DATA_TYPE, VAR_UNITS, and -;VAR_FILL_VALUE metadata attributes and set up a structure which will be used to hold the -;data values. -; ---------- -;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org -; -; History: -; 20050802: Introduced to IDLCR8HDF, as Determine_Arr_Sizes. Data originally stored in 4 -; arrays according to the data type of the variable attributes - Version 1.0 -; 20050909: Additional checks performed on the metadata attributes as follows: that the -; VAR_DEPEND variable name(s), themselves, have a VAR_DEPEND value of CONSTANT or -; INDEPENDENT, and matching VAR_SIZE values; that VAR_DATA_TYPE is DOUBLE for -; attributes with VAR_UNITS=MJD2000; that VAR_UNITS=MJD2000 when VAR_NAME=DATETIME -; - Version 1.1 -; 20061012: Procedure renamed Set_Up_Structure. A structure using pointers used to store the -; data instead of arrays. VAR_FILL_VALUE value saved in common array and, if -; VAR_UNITS=MJD2000, VAR_FILL_VALUE converted to its MJD2000 form if it is in -; ISO8601 format. Common variable definition WIDGET_WIN added - Version 2.0 -; 20080302: Can calculate the VAR_SIZE value(s) from the input data or from the VAR_SIZE -; value(s) of the VAR_DEPEND variable name(s), if they are not present in the -; metadata. Section added to account for the new variables, ALTITUDE/ALTITUDE.GPH/ -; PRESSURE.BOUNDARIES; Additional checks added for VIS_PLOT_TYPE, VIS_SCALE_TYPE, -; VIS_SCALE_MIN, and VIS_SCALE_MAX values - Version 3.0 -; 20100205: Add RETURN command after all STOP_WITH_ERROR calls, which allows program to return to the -; calling program if the reterr argument is included in the idlcr8hdf call - Version 3.09 -; 20101120: Account for GEOMS changes between v3.0 and v4.0; Remove checks on VAR_DIMENSION, and the -; VIS attributes; Add new VAR_DATA_TYPEs (Byte and Short); Check for self-referencing -; VAR_DEPEND values (which replace INDEPENDENT in that context); Remove requirement for -; DATETIME to be of datatype DOUBLE - Version 4.0 -; 20140521: Account for new Dimension Ordering rules for *.BOUNDARIES variables - should be -; INDEPENDENT;*[;DATETIME]. In the event that the input metadata specifies a data template, -; then the template VAR_DEPEND ordering will apply (and be checked by the template checker), -; otherwise full checks will be made - Version 4.0b21 -; 20140806: Allow VAR_UNITS=d/D to be changed to MJD2K for DATETIME checks - Version 4.0b22 -; 20150811: Bug fix - initialize the infotxt array if reporting an ISO8601 to MJD2K conversion -; issue - Version 4.0b29 -; 20151109: Check that VAR_SIZE values are in INTEGER format, even though they are -; written to the HDF file as a string - Version 4.0b34 -; 20161116: Bug fix - resvd array value set correctly when checking whether a dataset with -; VAR_DEPEND=CONSTANT only has a single value (was 0 should have been 1) - Version 4.0b39 -; 20161130: Allow program to continue when VAR_UNITS is not set to MJD2K for DATETIME -; and related datasets (previously called STOP_WITH_ERROR in some conditions - -; Version 4.0b40 -; 20180218: Change Unexpected number of attribute values extracted error message so that it calls -; INFOTXT_OUTPUT rather than STOP_WITH_ERROR - Version 4.0b45 -; 20210617: Add section to do VAR_DATA_TYPE checks on the datasets, VAR_VALID_MIN, VAR_VALID_MAX and -; VAR_FILL_VALUE values, to ensure they are all matching - Version 4.0b59 -; 20231218: Change check that an axis variable must be listed in DATA_VARIABLES before any variable -; that depends on it. The new check is that the axis variable is present anywhere in the file. -; Text for errtxt[3] changed, code added to identify axis variables and determine their -; VAR_SIZE - Version 4.0b61 -; 20240112: Remove test that INDEPENDENT can only be a second dimension variable if the variable includes -; .BOUNDARIES in the name e.g. ALTITUDE.BOUNDARIES. This is too limiting e.g. for Aerosol Lidar -; RANGE_INDEPENDENT_NORMALIZATION has VAR_DEPEND=INDEPENDENT;DATETIME - Version 4.0b62 -; -; Inputs: sds - Either a structure containing the Variable Attributes and Data, or the input -; data file -; inf - flag identifying SDS type where, 0=structure; 1=data file -; meta_arr - a string array containing the Global and Variable Attributes -; -; Outputs: meta_arr - some meta_arr values may be corrected or calculated in this routine, including: -; VAR_DIMENSION, VAR_SIZE, and VAR_FILL_VALUE -; nvn - an integer giving the number of Variable Attributes present in the input -; vn - a string array of size nvn containing the VAR_NAME values -; vu - a string array of size nvn containing the VAR_UNIT values -; ndm - a (nvn,9) long array, where ndm(*,0:7) describes the dimensions of each dataset -; (maximum of 8 dimensions allowed), and ndm(*,8) describes the VAR_DATA_TYPE -; ds - the structure that will hold the data -; -; Called by: IDLCR8HDF -; -; Subroutines Called: EXTRACT_DATA (if it is necessary to determine VAR_SIZE from the input data) -; STOP_WITH_ERROR (if error state detected); INFOTXT_OUTOUT -; Possible Conditions for STOP_WITH_ERROR call (plus [line number] where called): -; 1. Program expects to find the VAR_NAME in the metadata in the same order as that listed under -; DATA_VARIABLES -; 2. The VAR_DIMENSION value is greater than 8 -; 3. Number of VAR_SIZE and VAR_DEPEND attribute values is different -; 4. The VAR_DEPEND value is not 'CONSTANT', 'INDEPENDENT' or self-referencing, and doesn't -; match a previous VAR_NAME -; 5. The VAR_DEPEND variable name must, itself, have a VAR_DEPEND value of 'CONSTANT' or -; 'INDEPENDENT' or be self-referencing -; 6. The VAR_DEPEND variable name must have a VAR_SIZE value matching the current VAR_NAME -; 7. VAR_DATA_TYPE not BYTE, SHORT, INTEGER, LONG, REAL, DOUBLE, or STRING -; 8. Unexpected number of ATTRIBUTE values extracted -; 9. VAR_DATA_TYPE should be DOUBLE for VAR_UNITS=MJD2K - No longer a requirement -; 10. VAR_UNITS should be MJD2K for VAR_NAME=DATETIME -; 11. VAR_FILL_VALUE not a valid value - occurs on an unsuccessful call to JULIAN_DATE -; 12. VAR_DEPEND values not valid - occurs when INDEPENDENT or CONSTANT is a VAR_DEPEND value -; when it should not be -; 13. First VAR_SIZE value is not '2' when VAR_NAME=ALTITUDE/ALTITUDE.GPH/PRESSURE.BOUNDARIES -; 14. Axis variable cannot be dependent on another variable -; 15. Axis variable in the vertical dimension already found -; 16. Attribute which is not an axis variable has a self-referencing VAR_DEPEND value -; 17. Type conversion error. Called when trying to convert a string, which is expected to be -; numeric, to a number -; -; Information Conditions (when the program is able to make changes): -; 1. 64-bit LONG Data Type is not currently supported in GEOMS. Will attempt to write data in 32-bit -; Integer type instead -; 2. Dataset/VAR_VALID_MIN/VAR_VALID_MAX/VAR_FILL_VALUE value(s) have data type that does not match -; VAR_DATA_TYPE - -COMMON METADATA -COMMON DATA -COMMON WIDGET_WIN - -;possible error messages for this procedure -errtxt=STRARR(17) & lu=-1L -errtxt[0]='Does not match the equivalent VAR_NAME in DATA_VARIABLES: ' -errtxt[1]='Number of dimensions too high to make HDF file (max. allowed 8)' -errtxt[2]='VAR_DEPEND attribute value missing or does not match the number of VAR_SIZE values' -errtxt[3]='VAR_DEPEND value not CONSTANT,INDEPENDENT nor self-referencing, and doesn''t match any VAR_NAME: ' -errtxt[4]='VAR_DEPEND variable name(s) must have VAR_DEPEND value of CONSTANT or be self_referencing: ' -errtxt[5]='VAR_DEPEND variable name must have matching VAR_SIZE value (= ' -errtxt[6]='Data type not BYTE, SHORT, INTEGER, REAL, DOUBLE, or STRING: ' -errtxt[7]='Unexpected number of ATTRIBUTE values extracted: ' -errtxt[8]='VAR_DATA_TYPE must be DOUBLE for VAR_UNITS=MJD2K: ' -errtxt[9]='VAR_UNITS must be MJD2K for ' ;no longer used (infotxt message instead) -errtxt[10]='VAR_FILL_VALUE not a valid value: ' -errtxt[11]=' not valid' -errtxt[12]='Second VAR_SIZE value should be ''2'' for this VAR_NAME' -errtxt[13]='Axis variable cannot be dependent on another variable: ' -errtxt[14]=' is already the axis variable in the vertical dimension' -errtxt[15]='is not an axis variable so VAR_DEPEND value cannot be self-referencing' -errtxt[16]='Type conversion error. Entry is not a valid number: ' -proname='Set_Up_Structure procedure: ' - -lc=0 & vc=0 & writeonce=0 & bounderror=0 -allowable_data_types=['BYTE','SHORT','INTEGER','LONG64','REAL','DOUBLE','STRING'] -idl_data_type=[1,2,3,14,4,5,7] ;IDL type identifiers matching allowable_data_types -num_atts=['VAR_VALID_MIN','VAR_VALID_MAX','VAR_FILL_VALUE'] -nna=N_ELEMENTS(num_atts) - -ON_IOERROR,TypeConversionError - -;Do a search for a DATA_TEMPLATE value. If not present will do all .BOUNDARIES checks, otherwise -;these checks will be done by the Template QA program - interim until .BOUNDARIES dimension ordering -;is consistent for all templates (FTIR retains original convention as of 20140521) -dtfound=0 ;default to template not present -dti=WHERE(STRMID(meta_arr,0,13) EQ 'DATA_TEMPLATE',dtcnt) -IF dtcnt NE 0 THEN BEGIN ;check there is a value - res=STRSPLIT(meta_arr[dti[0]],' =;',/Extract,COUNT=rescnt) - IF rescnt EQ 2 THEN dtfound=1 ;valid DATA_TEMPLATE value found -ENDIF - -;Determine a set of possible Axis Variables to be tested (i.e. all VAR_DEPEND values that aren't CONSTANT or INDEPENDENT) -vdi=WHERE(STRMID(meta_arr,0,10) EQ 'VAR_DEPEND',vdcnt) -vardeptest=[''] & vardepndim=INTARR(vdcnt) & vardepvals=STRARR(8,vdcnt) -IF vdcnt NE 0 THEN BEGIN - FOR i=0,vdcnt-1 DO BEGIN - res=STRSPLIT(meta_arr[vdi[i]],' =;',/Extract,COUNT=rescnt) - vardepndim[i]=rescnt-1 - IF rescnt GT 1 THEN BEGIN ;VAR_DEPEND values found - FOR j=1,rescnt-1 DO BEGIN - resuc=STRUPCASE(res[j]) & vardepvals[j-1,i]=res[j] - ni=WHERE(resuc EQ STRUPCASE(vardeptest),ncnt) - IF (ncnt EQ 0) AND (resuc NE 'INDEPENDENT') AND (resuc NE 'CONSTANT') THEN $ - vardeptest=[vardeptest,res[j]] ;Add VAR_DEPEND value to list of axis-variables to be checked - ENDFOR - ENDIF - ENDFOR -ENDIF -;Remove '' from vardeptest -gi=WHERE(vardeptest NE '',n_axv) -IF n_axv NE 0 THEN BEGIN - vardeptest=vardeptest[gi] & vardepndl=lonarr(n_axv) - vni=WHERE(STRMID(meta_arr,0,8) EQ 'VAR_NAME',vncnt) - ndm=LONARR(vncnt,9) & vu=STRARR(vncnt) ;just needed, at thu stage as dummy inputs to EXTRACT_DATA - IF vncnt NE 0 THEN BEGIN - vn=STRARR(vncnt) & vserror=vn ;vserror needed for dummy input to EXTRACT_DATA - FOR i=0,vncnt-1 DO BEGIN - res=STRSPLIT(meta_arr[vni[i]],' =',/Extract,COUNT=rescnt) - IF rescnt EQ 2 THEN vn[i]=res[1] - ENDFOR - multidim=0B - FOR i=0,n_axv-1 DO BEGIN - vcx=WHERE(vardeptest[i] EQ vn,vcxcnt) - IF vcxcnt NE 0 THEN ndimx=vardepndim[vcx[0]] ELSE ndimx=0 - IF (vcxcnt NE 0) AND (ndimx LE 1) THEN BEGIN - dtest=['-1'] ;-1 identifies to the EXTRACT_DATA routine that ndl is all that is required - EXTRACT_DATA,SDS,inf,vcx,dtest,ndl - vardepndl(i)=ndl - ENDIF ELSE IF ndimx GT 1 THEN multidim=1B ;axis variable has more than one dependency e.g. VAR_DEPEND=DATETIME;ALTITUDE for VAR_NAME=ALTITUDE - ENDFOR - IF multidim THEN BEGIN - FOR i=0,n_axv-1 DO BEGIN - vcx=WHERE(vardeptest[i] EQ vn,vcxcnt) - IF vcxcnt NE 0 THEN ndimx=vardepndim[vcx[0]] ELSE ndimx=0 - IF (vcxcnt NE 0) AND (ndimx GT 1) THEN BEGIN - dtest=['-1'] ;-1 identifies to the EXTRACT_DATA routine that ndl is all that is required - EXTRACT_DATA,SDS,inf,vcx,dtest,ndl - ;Determine actual number of dataset values - FOR j=0,ndimx-1 DO BEGIN - IF vardepvals[j,vcx[0]] NE vn[vcx[0]] THEN BEGIN - di=WHERE(vardepvals[j,vcx[0]] EQ vardeptest,dcnt) - IF dcnt NE 0 THEN ndl=ndl/vardepndl[di[0]] - ENDIF - ENDFOR - vardepndl(i)=ndl - ENDIF - ENDFOR - ENDIF - ENDIF -ENDIF - -;At this point have got an array of size n_axv containing the axis variable names (vardeptest) and their VAR_SIZE (vardepndl) - -notlong=0B ;Boolean to check whether VAR_SIZE values are of integer data type -WHILE lc LT N_ELEMENTS(meta_arr) DO BEGIN - valid=0 ;set to test for successful string conversion to a number - IF STRMID(meta_arr[lc],0,14) EQ 'DATA_VARIABLES' THEN BEGIN ;determine number of variables reported - lcdv=lc - vn=STRSPLIT(meta_arr[lc],' =;',/Extract) ;string containing reported variables - nvn=N_ELEMENTS(vn)-1 ;total number of variables reported - vn=vn[1:nvn] ;remove the DATA_VARIABLES label - vu=STRARR(nvn) ;holding the VAR_UNITS values - vfvd=DBLARR(nna,nvn) ;holding array for floating point num_atts array values - vfvl=LON64ARR(nna,nvn) ;holding array for integer num_atts array values - ndm=LONARR(nvn,9) ;to contain the dimensions and data type for each dataset - vserror=STRARR(nvn) ;to hold variable names for datasets where the VAR_SIZE values cannot be determined - ENDIF - - IF STRMID(meta_arr[lc],0,8) EQ 'VAR_NAME' THEN BEGIN - vname=meta_arr[lc] ;used in case of error - lcz=lc ;VAR_NAME index value - res=STRSPLIT(meta_arr[lc],' =',/Extract) - IF N_ELEMENTS(res) NE 2 THEN res=[res[0],'-1'] - IF res[1] NE vn[vc] THEN BEGIN - STOP_WITH_ERROR,o3[3]+proname+vname+': ',errtxt[0]+vn[vc],lu & RETURN - ;Does not match the equivalent VAR_NAME in DATA_VARIABLES - ENDIF - - REPEAT lc=lc+1 UNTIL STRMID(meta_arr[lc],0,8) EQ 'VAR_SIZE' - holdvs=meta_arr[lc] & lcx=lc ;do checks after determining VAR_DATA_TYPE - REPEAT lc=lc+1 UNTIL STRMID(meta_arr[lc],0,10) EQ 'VAR_DEPEND' - holdvd=meta_arr[lc] & lcy=lc ;do checks after determining VAR_DATA_TYPE - REPEAT lc=lc+1 UNTIL STRMID(meta_arr[lc],0,13) EQ 'VAR_DATA_TYPE' - res=STRSPLIT(STRUPCASE(meta_arr[lc]),' =;',/Extract) - IF N_ELEMENTS(res) EQ 2 THEN gi=WHERE(res[1] EQ allowable_data_types,gcnt) $ ;Check VAR_DATA_TYPE is valid - ELSE gcnt=0 - IF gcnt NE 1 THEN BEGIN - STOP_WITH_ERROR,o3[3]+proname+vname+': ',errtxt[6]+meta_arr[lc],lu & RETURN - ;Data type not BYTE, SHORT, INTEGER, LONG64, REAL, DOUBLE, or STRING - ENDIF - IF gi[0] EQ 3 THEN BEGIN - IF writeonce EQ 0 THEN BEGIN - writeonce=1 - infotxt='2 64-bit LONG Data Type is not currently supported|. ' - infotxt=infotxt+'Will attempt to write data in 32-bit INTEGER Data Type' - INFOTXT_OUTPUT,infotxt - ENDIF - gi[0]=2 & meta_arr[lc]='VAR_DATA_TYPE=INTEGER' - ENDIF - ndm[vc,8]=gi[0] ;index number representing the data type - - ;Do VAR_SIZE and VAR_DEPEND checks - resvs=STRSPLIT(holdvs,' =;',/Extract,COUNT=nvs) ;VAR_SIZE entries - resvd=STRSPLIT(holdvd,' =;',/Extract,COUNT=nd) ;VAR_DEPEND entries - nvs=nvs-1 & nd=nd-1 ;Number of Dimensions based on number of VAR_SIZE/VAR_DEPEND values - vsadded=0 - - IF (nvs EQ 0) AND (nd NE 0) THEN BEGIN - ;Determine resvs values from vardeptest and vardepndl - FOR k=1,nd DO BEGIN - mi=WHERE(resvd[k] EQ vardeptest,mcnt) - IF mcnt NE 0 THEN BEGIN - resvs=[resvs,strtrim(vardepndl[mi[0]],2)] - nvs++ - IF k NE nd THEN sctxt=';' ELSE sctxt='' - holdvs=holdvs+strtrim(vardepndl[mi[0]],2)+sctxt - vsadded=1 - ENDIF - ENDFOR - meta_arr[lcx]=holdvs - ENDIF - IF nvs EQ 0 THEN ndmok=0 ELSE ndmok=1 - ;If there are no VAR_SIZE value entries, need to determine from Data file or structure or - ;from the VAR_SIZE values of the VAR_DEPEND dependencies - IF ((nvs NE nd) AND (ndmok EQ 1)) OR (nd EQ 0) THEN BEGIN - STOP_WITH_ERROR,o3[3]+proname+vname+': ',errtxt[2],lu & RETURN - ;VAR_DEPEND attribute value missing or does not match the number of VAR_SIZE values - ENDIF ELSE IF nd GT 8 THEN BEGIN - STOP_WITH_ERROR,o3[3]+proname+vname+': ',errtxt[1],lu & RETURN - ;Number of dimensions too high to make HDF file (max. allowed 8) - ENDIF ELSE IF ndmok EQ 1 THEN BEGIN - ndm[vc,0:nd-1]=LONG(resvs[1:nvs]) ;save VAR_SIZE values in ndm array - - ;Check that original input values are of integer data type - FOR k=1,nvs DO BEGIN - IF STRTRIM(resvs[k],2) NE STRTRIM(LONG(resvs[k]),2) THEN BEGIN - resvs[k]=STRTRIM(LONG(resvs[k]),2) - notlong=1B - ENDIF - ENDFOR - IF notlong THEN BEGIN - FOR k=1,nvs DO IF k EQ 1 THEN vshold=resvs[k] ELSE vshold=vshold+';'+resvs[k] - meta_arr[lcx]='VAR_SIZE='+vshold - holdvs=meta_arr[lcx] - ENDIF - ENDIF - - vshold=LONARR(nd) ;to hold VAR_SIZE of the dependencies - - ;Do checks on VAR_DEPEND values - ci=WHERE((STRUPCASE(resvd) EQ 'INDEPENDENT') OR (STRUPCASE(resvd) EQ 'CONSTANT'),ccnt) - IF ccnt NE 0 THEN resvd[ci]=STRUPCASE(resvd[ci]) - ;Rules for .BOUNDARY VAR_NAME - IF STRPOS(vn[vc],'.BOUNDARIES') NE -1 THEN BEGIN - test1=0 & test2=0 - IF ((nd EQ 2) OR (nd EQ 3)) AND (dtfound EQ 0) THEN BEGIN - ;First VAR_DEPEND value must be INDEPENDENT - test1=resvd[1] NE 'INDEPENDENT' - ;Second VAR_DEPEND value must be present in the Variable Name e.g. ALTITUDE for ALTITUDE.BOUNDARIES - secvdsplt=STRSPLIT(resvd[2],'.',/Extract) - test2=STRPOS(vn[vc],secvdsplt[0]) EQ -1 - ENDIF ELSE IF (nd LE 1) OR (nd GE 4) THEN test2=1 ;incorrect number of VAR_DEPEND values - IF nd EQ 3 THEN IF resvd[3] NE 'DATETIME' THEN test1=1 ;Third VAR_DEPEND value must be DATETIME - IF (test1) OR (test2) THEN BEGIN - IF test1 EQ 1 THEN itxt=' in IDL/Fortran Dimension Ordering' ELSE itxt='' - infotxt='3 '+holdvd+' not valid for '+vname+itxt - INFOTXT_OUTPUT,infotxt - bounderror=1 - ;STOP_WITH_ERROR,o3[3]+proname+vname+': ',holdvd+errtxt[11]+' for the given VAR_NAME'+itxt,lu - ;VAR_DEPEND value(s) not valid for the given VAR_NAME - ;RETURN - ENDIF - ENDIF - - FOR i=1,nd DO BEGIN - IF i EQ 1 THEN BEGIN - ;Check for and Apply GEOMS rule changes for INDEPENDENT and axis variables - vdval=resvd[i] & vnval=vn[vc] - GEOMS_RULE_CHANGES,2,vardeptest,vnval,vdval,holdvd - IF vdval NE resvd[i] THEN BEGIN ;First VAR_DEPEND value has been changed - ;Rebuild VAR_DEPEND entry - holdvd='VAR_DEPEND='+vdval - IF nd GT 1 THEN FOR j=2,nd DO holdvd=holdvd+';'+resvd[j] - ENDIF - IF STRMID(holdvd,0,1) EQ 'E' THEN BEGIN ;error found while attempting to identify axis variables - CASE 1 OF - holdvd EQ 'E1': STOP_WITH_ERROR,o3[3]+proname+vname+': ',errtxt[13]+vdval,lu - holdvd EQ 'E3': STOP_WITH_ERROR,o3[3]+proname+vname+' ',errtxt[15],lu - ENDCASE - RETURN - ENDIF - resvd[i]=vdval - meta_arr[lcy]=holdvd - ENDIF - test1=(resvd[i] EQ 'CONSTANT') AND ((i GT 1) OR (nd GE 2)) ;CONSTANT can be the only VAR_DEPEND - test2=(resvd[i] EQ vn[vc]) AND ((i GT 1) OR (nd GT 2)) ;e.g. ALTITUDE;DATETIME are possible dependencies for ALTITUDE - test3=0B ;(resvd[i] EQ 'INDEPENDENT') AND ((i GT 1) OR (nd GE 2)) AND (STRPOS(vn[vc],'.BOUNDARIES') EQ -1) ;remove test as too specific - test4=(resvd[i] NE 'CONSTANT') AND (resvd[i] NE 'INDEPENDENT') AND (resvd[i] NE vn[vc]) - ;Check for CONSTANT,INDEPENDENT or self-referencing and more than one VAR_DEPEND value - IF (test1) OR (test2) OR (test3) THEN BEGIN - STOP_WITH_ERROR,o3[3]+proname+vname+': ',holdvd+errtxt[11],lu & RETURN - ;VAR_DEPEND value(s) not valid - ENDIF - ;Check for valid dependencies, and hold VAR_SIZE values of the dependencies if required - ;Section modified 20231218 to allow axis variable to be after the variable that depends on it - CASE 1 OF - (vc EQ 0) AND (nvn GE 2): gi=WHERE(resvd[i] EQ vn[1:nvn-1],gcnt) - (vc EQ nvn-1) AND (nvn GE 2): gi=WHERE(resvd[i] EQ vn[0:nvn-2],gcnt) - nvn GE 3: BEGIN - gi=WHERE(resvd[i] EQ vn[0:vc-1],gcnt) - IF gcnt EQ 0 THEN BEGIN - gi=WHERE(resvd[i] EQ vn[vc+1:nvn-1],gcnt) - IF gcnt NE 0 THEN gi[0]=gi[0]+vc+1 - ENDIF - END - ELSE: gcnt=0 - ENDCASE - ;Original check - ;IF vc GE 1 THEN gi=WHERE(resvd[i] EQ vn[0:vc-1],gcnt) ELSE gcnt=0 - IF (gcnt EQ 0) AND (test4) THEN BEGIN - STOP_WITH_ERROR,o3[3]+proname+vname+': ',errtxt[3]+resvd[i],lu & RETURN - ;VAR_DEPEND value not CONSTANT,INDEPENDENT nor Self-referencing, and doesn't match any VAR_NAME - ENDIF - IF gcnt NE 0 THEN BEGIN ;do remaining checks on VAR_DEPEND values - errfound=0 - gi=WHERE(meta_arr EQ 'VAR_NAME='+resvd[i]) - ;check VAR_DEPEND value of the VAR_DEPEND variable name - vi=WHERE(STRPOS(meta_arr[gi[0]:N_ELEMENTS(meta_arr)-1],'VAR_DEPEND') NE -1) - vex=STRSPLIT(STRUPCASE(meta_arr[gi[0]+vi[0]]),' =;',/Extract,COUNT=nvex) - IF nvex NE 2 THEN vex=[vex,''] ;missing VAR_DEPEND value so add dummy value - IF (vex[1] NE 'CONSTANT') AND (vex[1] NE 'INDEPENDENT') AND (vex[1] NE resvd[i]) THEN BEGIN - vert_var=['ALTITUDE','ALTITUDE.GPH','PRESSURE','DEPTH'] - res=WHERE(resvd[i] EQ vert_var,vcnt) ;does VAR_DEPEND variable name reference a vertical axis - res=WHERE(vex[1] EQ vert_var,xcnt) ;does VAR_DEPEND of the VAR_DEPEND variable name reference a vertical axis - IF (vcnt NE 0) AND (xcnt EQ 0) THEN BEGIN ;Vertical axis so should be self-referencing - infotxt='2 '+meta_arr[gi[0]+vi[0]]+' should be self-referencing for axis variable' - infotxt=infotxt+' VAR_NAME='+resvd[i]+'|. VAR_DEPEND value changed in metadata' - INFOTXT_OUTPUT,infotxt - meta_arr[gi[0]+vi[0]]='VAR_DEPEND='+resvd[i] - IF nvex GT 2 THEN FOR j=2,nvex-1 DO meta_arr[gi[0]+vi[0]]=meta_arr[gi[0]+vi[0]]+';'+vex[j] - ENDIF ELSE BEGIN - ;VAR_DEPEND value of the VAR_DEPEND variable name must be CONSTANT or an axis variable - infotxt=STRARR(2) - infotxt[0]='3 '+meta_arr[lcy]+' variable name(s) for '+vname+' must have VAR_DEPEND' - infotxt[1]=' value of CONSTANT or be self-referencing' - INFOTXT_OUTPUT,infotxt - ENDELSE - ENDIF - IF vsadded EQ 0 THEN BEGIN - ;check VAR_SIZE value of the VAR_DEPEND variable name - vi=WHERE(STRPOS(meta_arr[gi[0]:N_ELEMENTS(meta_arr)-1],'VAR_SIZE') NE -1) - vex=STRSPLIT(meta_arr[gi[0]+vi[0]],' =;',/Extract,COUNT=nvex) - - IF (nvex LT 2) OR (nvex GT 3) THEN errfound=1 $ - ELSE IF (ndmok EQ 1) AND (LONG(vex[1]) NE ndm[vc,i-1]) THEN errfound=1 - - IF errfound EQ 1 THEN BEGIN - STOP_WITH_ERROR,o3[3]+proname+vname+': '+'VAR_DEPEND='+resvd[i]+': ', $ - errtxt[5]+STRTRIM(ndm[vc,i-1],2)+'): '+meta_arr[gi[0]+vi[0]],lu - ;VAR_DEPEND variable name must have matching VAR_SIZE value - RETURN - ENDIF ELSE vshold[i-1]=LONG(vex[1]) - ENDIF - ENDIF - ;If i eq 2 and value is INDEPENDENT then check that the corresponding VAR_SIZE value is 2 - ;(if present), o/w generate errors - IF (i EQ 2) AND (STRUPCASE(resvd[i]) EQ 'INDEPENDENT') THEN BEGIN - IF (ndmok EQ 1) AND (ndm[vc,i-1] NE 2L) THEN BEGIN - STOP_WITH_ERROR,o3[3]+proname+vname+': '+holdvs+': ',errtxt[12],lu & RETURN - ;Second VAR_SIZE value should be '2' for this VAR_NAME - ENDIF - vshold[i-1]=2L ;set VAR_SIZE value in the event that it is not part of the Metadata - ENDIF - ENDFOR - - IF (ndmok EQ 0) OR (vsadded EQ 1) THEN BEGIN ;no VAR_SIZE value so need to determine from the data or the vshold array - IF (vshold[0] EQ 0L) AND (vsadded EQ 0) THEN BEGIN - ;VAR_DEPEND is Self-Referencing or CONSTANT so determine VAR_SIZE from the Data file or structure - dtest=['-1'] ;-1 identifies to the EXTRACT_DATA routine that ndl is all that is required - EXTRACT_DATA,SDS,inf,vc,dtest,ndl - IF STRLEN(rerr[0]) GT 2 THEN RETURN - ;Check if any other VAR_SIZE values have already been determined - ;This could happen in the case VAR_DEPEND=ALTITUDE;DATETIME for VAR_NAME=ALTITUDE for e.g. - gi=WHERE(vshold NE 0L,gcnt) - ndltot=' ('+STRTRIM(ndl,2)+')' - IF gcnt NE 0 THEN BEGIN - FOR i=0,gcnt-1 DO ndl=ndl/FLOAT(vshold[gi[i]]) - ENDIF - ;Make up VAR_SIZE string based on available values - IF vshold[0] EQ 0L THEN vsstr=' (VAR_SIZE=x' ELSE vsstr='VAR_SIZE='+STRTRIM(vshold[0],2) - IF nd GT 1 THEN FOR i=1,nd-1 DO $ - IF vshold[i] EQ 0L THEN vsstr=vsstr+';x' ELSE vsstr=vsstr+';'+STRTRIM(vshold[i],2) - vsstr=vsstr+') ' - vshold[0]=LONG(ndl) - bi=WHERE(vshold EQ 0,bcnt) ;Expected VAR_SIZE values are missing if bcnt not equal to 0 - IF (ndl NE vshold[0]) OR (bcnt NE 0) THEN BEGIN - infotxt=STRARR(2) - infotxt[0]='3 Unable to determine missing VAR_SIZE value(s) for '+vname+' based on' - infotxt[1]=' '+holdvd+vsstr+'and the total number of dataset values'+ndltot - INFOTXT_OUTPUT,infotxt - vserror[vc]=vn[vc] - ENDIF - meta_arr[lcx]='VAR_SIZE='+STRTRIM(vshold[0],2) - ndm[vc,0:nd-1]=vshold - IF nd GT 1 THEN FOR i=1,nd-1 DO meta_arr[lcx]=meta_arr[lcx]+';'+STRTRIM(vshold[i],2) - ENDIF ELSE IF vsadded EQ 0 THEN BEGIN ;can determine VAR_SIZE from the vshold array - meta_arr[lcx]='VAR_SIZE=' - FOR i=0,nd-1 DO BEGIN - IF i EQ 0 THEN meta_arr[lcx]=meta_arr[lcx]+STRTRIM(vshold[i],2) $ - ELSE meta_arr[lcx]=meta_arr[lcx]+';'+STRTRIM(vshold[i],2) - ndm[vc,i]=vshold[i] ;EQ array dimensions from VAR_SIZE of the dependencies - ENDFOR - ENDIF - ;Check to see if VAR_SIZE calculation is valid or not based on contents of vserror - vsok=1 - FOR i=1,nd DO BEGIN - ei=WHERE(resvd[i] EQ vserror,ecnt) - IF ecnt NE 0 THEN vsok=0 - ENDFOR - IF (vsok EQ 1) OR (vsadded EQ 1) THEN BEGIN - IF qa_yes THEN qtxt=' not recorded. Calculated as '+meta_arr[lcx] ELSE qtxt=' added: '+meta_arr[lcx] - infotxt='2 VAR_SIZE value(s) for '+vname+qtxt - INFOTXT_OUTPUT,infotxt - ENDIF - ENDIF - - IF (resvd[1] EQ 'CONSTANT') AND (TOTAL(ndm[vc,0:7]) GT 1.) THEN BEGIN - infotxt='3 Dataset '+vn[vc]+' must only have a single value for VAR_DEPEND=CONSTANT: '+meta_arr[lcx] - INFOTXT_OUTPUT,infotxt - ENDIF - - ;Do DATETIME/MJD2000 checks - REPEAT lc=lc+1 UNTIL STRMID(meta_arr[lc],0,9) EQ 'VAR_UNITS' - resv=STRSPLIT(meta_arr[lc],' =',/Extract,Count=rcnt) - IF rcnt EQ 1 THEN resv=[resv,'[MISSING]'] ;add filler for checks - test1=(vn[vc] EQ 'DATETIME') OR (vn[vc] EQ 'DATETIME.START') OR (vn[vc] EQ 'DATETIME.STOP') - IF (test1) AND (STRUPCASE(resv[1]) NE 'MJD2K') THEN BEGIN - lch=lc ;hold setting for VAR_UNITS - ;Find VAR_SI_CONVERSION values and test for correct values for MJD2K - REPEAT lch=lch+1 UNTIL STRMID(meta_arr[lch],0,17) EQ 'VAR_SI_CONVERSION' - res=STRSPLIT(meta_arr[lch],' =;',/EXTRACT) - test1=(STRLOWCASE(resv[1]) EQ 's') AND (FIX(res[1]) EQ 0) AND (LONG(res[2]) EQ 86400L) AND $ - (STRLOWCASE(res[3]) EQ 's') - IF (resv[1] EQ '[MISSING]') OR (resv[1] EQ '1') OR (STRLOWCASE(resv[1]) EQ 'd') OR (test1) THEN BEGIN - errid='2 ' & itxt='|. Value changed in Metadata' - ENDIF ELSE BEGIN - errid='3 ' & itxt='' - ENDELSE - infotxt=errid+meta_arr[lc]+' must be VAR_UNITS=MJD2K for '+vname+itxt - INFOTXT_OUTPUT,infotxt - meta_arr[lc]='VAR_UNITS=MJD2K' - resv=['VAR_UNITS','MJD2K'] - ;Change VAR_SI_CONVERSION value also - CASE 1 OF - ndm[vc,8] LE 3: meta_arr[lch]='VAR_SI_CONVERSION=0;86400;s - ndm[vc,8] LE 5: meta_arr[lch]='VAR_SI_CONVERSION=0.0;86400.0;s' - ELSE: meta_arr[lch]='VAR_SI_CONVERSION=' - ENDCASE - ENDIF - vu[vc]=resv[1] ;used to check for MJD2K units when reading in data as well as NCSA units value - IF N_ELEMENTS(resv) GT 2 THEN $ - FOR i=2,N_ELEMENTS(resv)-1 DO vu[vc]=vu[vc]+' '+resv[i] - - ;Do checks on the numeric attributes - FOR i=0,nna-1 DO BEGIN - REPEAT lc=lc+1 UNTIL STRMID(meta_arr[lc],0,STRLEN(num_atts[i])) EQ num_atts[i] - resv=STRSPLIT(meta_arr[lc],' =;',/Extract,COUNT=nresv) - test1=(STRUPCASE(vu[vc]) NE 'MJD2K') OR (num_atts[i] EQ 'VAR_FILL_VALUE') - test2=(nresv NE 2) AND (ndm[vc,8] NE 6) - IF (test1) AND (test2) THEN BEGIN - infotxt=STRARR(2) - infotxt[0]='3 Unexpected number of ATTRIBUTE values extracted for '+vname+':' - infotxt[1]=' '+meta_arr[lc] - INFOTXT_OUTPUT,infotxt - resv=[resv,''] ;ensure at least 2 values - meta_arr[lc]=resv[0]+'='+resv[1] & nresv=2 - ;STOP_WITH_ERROR,o3[3]+proname+vname+': ',errtxt[7]+meta_arr[lc],lu & RETURN - ;Unexpected number of ATTRIBUTE values extracted - ENDIF - IF (ndm[vc,8] NE 6) AND (nresv EQ 2) THEN BEGIN - ;If VAR_UNITS=MJD2000 then check for ISO8601 format and change to MJD2000 if necessary - IF (STRUPCASE(vu[vc]) EQ 'MJD2K') AND (STRPOS(STRUPCASE(resv[1]),'Z') NE -1) THEN BEGIN - mjd2000=JULIAN_DATE(resv[1],/I,/M) ;return time in MJD2000 format - IF mjd2000 EQ -99999.d THEN BEGIN - STOP_WITH_ERROR,o3[3]+proname+vn[vc]+': ',errtxt[10]+meta_arr[lc],lu & RETURN - ENDIF - vfvd[i,vc]=mjd2000 - infotxt=STRARR(2) - meta_arr[lc]=resv[0]+'='+STRTRIM(STRING(format='(d18.9)',mjd2000),2) - IF qa_yes THEN qtxt=' must be in MJD2K format|' ELSE qtxt=' replaced with MJD2K formatted value ' - infotxt[0]='2 '+resv[0]+'='+resv[1]+' for VAR_NAME='+vn[vc] - infotxt[1]=qtxt+meta_arr[lc] - INFOTXT_OUTPUT,infotxt - ENDIF ELSE BEGIN - ;Check that the VAR_VALID_MIN/MAX or VAR_FILL_VALUE is numeric and save to vfvl or vfvd arrays - lcx=lc - IF ndm[vc,8] LE 3 THEN BEGIN ;value is integer type - IF mv_lng[lc] NE 0LL THEN vfvl[i,vc]=mv_lng[lc] ELSE vfvl[i,vc]=LONG64(DOUBLE(resv[1])) - ENDIF ELSE BEGIN ;value is floating point type - IF mv_dbl[lc] NE 0.D THEN vfvd[i,vc]=mv_dbl[lc] ELSE vfvd[i,vc]=DOUBLE(resv[1]) - ENDELSE - ENDELSE - ENDIF - ENDFOR - vc=vc+1 - ENDIF - lc=lc+1 & valid=1 -ENDWHILE - -IF notlong THEN BEGIN ;write out INFORMATION or ERROR to log - IF qa_yes THEN itxt=' must be ' ELSE itxt=' changed to ' - infotxt='2 VAR_SIZE values'+itxt+'integer format' - INFOTXT_OUTPUT,infotxt -ENDIF - -;Save numeric metadata values to common arrays -mv_lng=vfvl & mv_dbl=vfvd - -IF qa_yes THEN BEGIN - ON_IOERROR, NULL - ;Check VAR_VALID_MIN, VAR_VALID_MAX, VAR_FILL_VALUE and Dataset data types match VAR_DATA_TYPE - s_dims=SIZE(sds,/DIMENSIONS) - pchkd=PTR_VALID(sds.data) - pchkv=PTR_VALID(sds.va_v) - pchkl=PTR_VALID(sds.va_l) - - IF s_dims[0] GE 1 THEN BEGIN - attnamechk=['VAR_NAME','VAR_DATA_TYPE','VAR_VALID_MIN','VAR_VALID_MAX','VAR_FILL_VALUE'] - n_anc=N_ELEMENTS(attnamechk) - FOR i=0,s_dims[0]-1 DO BEGIN - vdtx=BYTARR(n_anc)+255B ;VAR_DATA_TYPE for the attnamechk values - vnh='' - FOR j=0,s_dims[1]-1 DO BEGIN - IF (pchkl[i,j]) AND (pchkv[i,j]) THEN BEGIN - res=*sds[i,j].va_l & res=STRTRIM(STRUPCASE(res),2) - ai=WHERE(res EQ attnamechk,acnt) - IF acnt NE 0 THEN BEGIN - attval=*sds[i,j].va_v - IF ai[0] EQ 0 THEN vnh=STRTRIM(STRUPCASE(attval),2) $ - ELSE IF ai[0] EQ 1 THEN BEGIN - vdth=STRTRIM(STRUPCASE(attval),2) - vi=WHERE(vdth EQ allowable_data_types,vcnt) - IF vcnt NE 0 THEN vdtx[1]=idl_data_type[vi[0]] - ENDIF ELSE vdtx[ai[0]]=SIZE(*sds[i,j].va_v,/TYPE) - ENDIF - ENDIF - ENDFOR - IF pchkd[i,0] THEN dvdtx=SIZE(*sds[i,0].data,/TYPE) ELSE dvdtx=255B ;Data type of the dataset - ;PRINT,vnh,dvdtx,vdtx[1:4] - IF vdtx[1] NE 255B THEN BEGIN - IF (dvdtx NE vdtx[1]) AND (dvdtx NE 255B) THEN BEGIN - vi=WHERE(dvdtx EQ idl_data_type,vcnt) - IF vcnt NE 0 THEN svdt=allowable_data_types[vi[0]] ELSE svdt='' - IF svdt EQ '' THEN $ - infotxt='3 '+vnh+' dataset has data type that does not match VAR_DATA_TYPE='+vdth $ - ELSE $ - infotxt='3 '+vnh+' dataset has data type '+svdt+', which does not match VAR_DATA_TYPE='+vdth - INFOTXT_OUTPUT,infotxt - ENDIF - FOR j=2,n_anc-1 DO BEGIN - IF vdtx[j] NE vdtx[1] THEN BEGIN - vi=WHERE(vdtx[j] EQ idl_data_type,vcnt) - IF vcnt NE 0 THEN svdt=allowable_data_types[vi[0]] ELSE svdt='' - IF svdt EQ '' THEN $ - infotxt='3 '+vnh+' '+attnamechk[j]+ $ - ' value has data type that does not match VAR_DATA_TYPE='+vdth $ - ELSE $ - infotxt='3 '+vnh+' '+attnamechk[j]+' value has data type '+svdt+ $ - ', which does not match VAR_DATA_TYPE='+vdth - INFOTXT_OUTPUT,infotxt - ENDIF - ENDFOR - ENDIF - ENDFOR - ENDIF -ENDIF - -;Define the HDF storage structure -ds_set={data: PTR_NEW()} ;SDS data array -ds=REPLICATE(ds_set,nvn) ;Dimension the structure to the size of the SDS datasets - -TypeConversionError: -IF valid EQ 0 THEN BEGIN - STOP_WITH_ERROR,o3[3]+proname+vname+': ',errtxt[16]+meta_arr[lcx],lu & RETURN -ENDIF -END ;Procedure Set_Up_Structure - - - -PRO check_string_datatype, vc, dtest, ndl -;Procedure to check that the data, and Variable Attributes are correctly configured when the -;VAR_DATA_TYPE=STRING, including VAR_UNITS, VAR_VALID_MIN/MAX, and VAR_FILL_VALUE. The data -;values are returned in the string datatype. -; ---------- -;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org -; -; History: -; 20091203: Introduced to IDLCR8HDF routine - Version 3.08 -; 20101120: Account for GEOMS rule changes from v3.0 to v4.0 - Version 4.0b0 -; 20111220: Check dataset entries for non-ISO646-US ASCII Characters - Version 4.0b7 -; 20130116: Allow for a string dataset with maximum string length of 0, i.e. a set -; of empty values - Version 4.0b15 -; 20150127: Use right padding to make strings the correct length (previously defaulted to -; left padding); Save maximum string length of dataset in mv_str array -; - Version 4.0b25 -; 20150217: VAR_FILL_VALUE reverts to being an empty space (made same length as the longest -; dataset string in 4.0b25); improve checks on variable attribute values; Make -; the first dataset entry equal to the maximum string length if writing to H5 -; due to apparent bug when writing datasets which means that the string -; length is determined by the first entry (white space is removed when writing to -; the H5 file) - Version 4.0b26 -; 20220805: Improve Information/Error message when attributes for string datasets are not -; valid - Version 4.0b60 -; -; Inputs: meta_arr - a string array containing the Global and Variable Attributes -; vc - the index value of the vn array holding the VARIABLE_NAME being searched for in the data -; file, or the index value of the sds structure which holds the data -; dtest - the set of data values to be tested -; ndl - The total number of data values i.e. the product of the VAR_SIZE values -; -; Outputs: meta_arr - in the event that the values being tested are changed, the array will be updated -; dtest - correctly formatted dataset -; -; Called by: READ_DATA -; -; Subroutines Called: INFOTXT_OUTPUT -; Information Conditions (when the program is able to make changes): -; 1. The Metadata value is not valid for VAR_DATA_TYPE=STRING [2645] - -COMMON METADATA -COMMON DATA -COMMON WIDGET_WIN - -vi=WHERE(meta_arr EQ 'VAR_NAME='+vn[vc]) -dtestx=STRTRIM(dtest,0) ;remove trailing blanks only, for testing that string datasets are left justified -dtest=STRTRIM(dtest,2) - -;Check for non ISO646-US ASCII Characters -GEOMS_RULE_CHANGES,10,dtest,vn[vc] - -IF ~ARRAY_EQUAL(dtest,dtestx) THEN BEGIN ;format of the string dataset has been changed - IF qa_yes THEN $ - infotxt='2 Dataset '+vn[vc]+' incorrectly formatted (text must be left-justified)' $ - ELSE infotxt='2 Dataset '+vn[vc]+' reformatted so that text is left-justified' - INFOTXT_OUTPUT, infotxt -ENDIF - -;Determine maximum string length of the dataset -mxdatalen=0L ;default value of string length -testdatalen=MAX(STRLEN(dtest)) -IF testdatalen GT mxdatalen THEN mxdatalen=testdatalen -;Format the first data value to the maximum string length for H5 only (white space removed when writing to the file) -IF (mxdatalen NE 0L) AND (STRPOS(o3[0],'H5') NE -1) THEN BEGIN - ;Done for H5 only because H5S_CREATE_SIMPLE function uses string length of the 1st entry to set length for all entries - vfsx='(A-'+STRTRIM(mxdatalen,2)+')' ;A- does left justified text instead of right justified - dtest[0]=STRING(format=vfsx,dtest[0]) -ENDIF - -label=['VAR_UNITS','VAR_SI_CONVERSION','VAR_VALID_MIN','VAR_VALID_MAX','VAR_FILL_VALUE'] -n_lab=N_ELEMENTS(label) -mxlablen=MAX(STRLEN(label)) ;determine max label string length -metahold=STRMID(meta_arr,0,mxlablen) ;subset of the meta_arr strings, containing first portion of the Metadata - -;Perform checks on the Attributes -FOR i=0,n_lab-1 DO BEGIN - li=WHERE(STRPOS(metahold[vi[0]:N_ELEMENTS(metahold)-1],label[i]) NE -1) - lvi=li[0]+vi[0] - res=STRSPLIT(meta_arr[lvi],'=',/Extract,COUNT=nres) - IF nres EQ 1 THEN res=[res,''] - IF res[1] EQ ' ' THEN res[1]='' - IF (nres GT 2) OR (res[1] NE '') THEN BEGIN - ;print,'"'+res[1]+'"/"'+itxt+'"/'+strtrim(mxdatalen,2) - IF qa_yes THEN itxt='must be [' ELSE itxt='changed to [' - infotxt='2 '+vn[vc]+' '+meta_arr[lvi]+' attribute '+itxt+res[0]+'= ]' - INFOTXT_OUTPUT, infotxt - mv_str[vc]=mxdatalen - ENDIF - meta_arr[lvi]=res[0]+'= ' ;+string(ltxt) - IF res[0] EQ 'VAR_UNITS' THEN vu[vc]=' ' -ENDFOR - -END ;procedure Check_String_Datatype - - - -PRO check_min_max_fill, vc, dtest, ndl -;Procedure to check that the data, VAR_VALID_MIN/MAX, and VAR_FILL_VALUE fit within the given data -;type. That the VAR_FILL_VALUE falls within the GEOMS definition and that the data values are within -;the valid minimum and maximum values (or the fill value). The VAR_VALID_MIN value is less than or -;equal to the VAR_VALID_MAX value. The data values are returned in the data type given by VAR_DATA_TYPE. -; ---------- -;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org -; -; History: -; 20050802: Original IDLCR8HDF routine - Version 1.0 -; 20050909: If VAR_NAME=DATETIME then checks that the first data value is not a fill value, and is -; the lowest value; Change the methods for determining if a given data value falls within -; the range allowed by integer or long data types; Add fix for the case where a dataset -; consists of only fill values - Version 1.1 -; 20051107: Include VIS_SCALE_MIN/MAX values in the checks; Add fix to remove unwanted precision (to -; prevent rounding errors), by converting values to formatted strings then back to numeric -; values - Version 1.11 -; 20061012: Check that all DATETIME values are in chronological order (if more than one value), and -; contain no fill values; ISO8601 DATETIME conversions to MJD2000 format (if necessary) moved -; to the READ_DATA routine; Add additional checks when extracting the VIS_FORMAT values for -; testing; Common variable definition WIDGET_WIN added - Version 2.0 -; 20080302: Fix bug so that unwanted precision from all data and relevant metadata values is removed -; before doing QA checks, to avoid unnecessary error calls - Version 3.0 -; 20100205: Add RETURN command after all STOP_WITH_ERROR calls, which allows program to return to the -; calling program if the reterr argument is included in the idlcr8hdf call - Version 3.09 -; 20101120: Account for GEOMS rule changes v3.0 to v4.0; Remove checks involving VIS_FORMAT; Modify -; method for checking data type limitations - Version 4.0 -; 20120420: Fix CHECK_MATH check so that combined floating point errors are ignored for long integer -; values being tested (error 160) - Version 4.0b9 -; 20120620: Remove CHECK_MATH checks, and just apply Type conversion checks, as it was not possible for -; CHECK_MATH to accurately indicate the problem numeric value - Version 4.0b10 -; 20120829: Fix call to ROUND when the value being rounded is a string (ROUND can only be applied to -; numeric datatypes) - Version 4.0b12 -; 20130327: Add additional check that VAR_VALID_MIN is less than or equal to the VAR_VALID_MAX in the -; event that the dataset only contains fill values - Version 4.0b20 -; 20141110: Check for NaN in the dataset and change to the Fill Value - Version 4.0b24 -; 20170331: Add checks for VAR_VALID_MIN and MAX values for Latitude, Longitude, Zenith, Azimuth and -; Wind direction datasets; check that the maximum dataset value for Azimuth datasets is -; less than 360.0 degrees - Version 4.0b42 -; 20171130: Remove ds from STOP_WITH_ERROR parameters - Version 4.0b44 -; 20180901: Also check that DATETIME.START and DATETIME.STOP values are in chronological order - -; Version 4.0b48 -; 20201211: Fix bug that caused an IDL Math Error (Floating Illegal Operand) when converting large -; floating point values to integers - Version 4.0b58 -; 20220805: Allow program to continue if data type errors in the dataset and attributes are detected - -; Version 4.0b60 -; -; Inputs: meta_arr - a string array containing the Global and Variable Attributes -; vc - the index value of the vn array holding the VARIABLE_NAME being searched for in the data -; file, or the index value of the sds structure which holds the data -; dtest - the set of data values to be tested -; ndl - The total number of data values i.e. the product of the VAR_SIZE values -; -; Outputs: meta_arr - in the event that the VIS_FORMAT value string or the precision of the attribute -; values being tested are changed, the array will be updated -; dtest - dataset returned in correct data type (VAR_FILL_VALUE/VAR_VALID_MIN/VAR_VALID_MAX -; also appended to the start of the array) -; -; Called by: READ_DATA -; -; Subroutines Called: STOP_WITH_ERROR (if error state detected) -; INFOTXT_OUTPUT -; Possible Conditions for STOP_WITH_ERROR call (plus [line number] where called): -; 1. Value is not valid, or does not match VAR_DATA_TYPE [2826] -; 2. A data value falls outside the range given by VAR_VALID_MIN or VAR_VALID_MAX [2846, 2851] -; 3. The VAR_FILL_VALUE is not outside the valid minimum or maximum values - No longer a requirement -; 4. The metadata or data value being tested is outside the data type range. For example, -; if a data value is 40000, but VAR_DATA_TYPE=INTEGER (maximum allowable value of 32767) -; [2786,2793,2800,2807,2813,2819] -; 5. The DATETIME value cannot be a VAR_FILL_VALUE. Because DATETIME is an axis variable -; it should not contain Fill Values [2862] -; 6. The DATETIME values are not in chronological order [2868] -; 7. Type conversion error. The program cannot convert a data value to a valid number [2874] -; -; Information Conditions (when the program is able to make changes): -; 1. Fill value falls within the data range and is defined as a Default value [2836] -; 2. NaN value in the dataset replaced with the fill value - -COMMON METADATA -COMMON DATA -COMMON WIDGET_WIN - -;Error messages for this procedure -ON_IOERROR,TypeConversionError -proname='Check_Min_Max_Fill procedure: ' -errtxt2=STRARR(7) & lu=-1 -errtxt2[0]=' not valid, or does not match VAR_DATA_TYPE' -errtxt2[1]=' data value outside VAR_VALID_' -errtxt2[2]='Fill data value not outside VAR_VALID_MIN or VAR_VALID_MAX: ' -errtxt2[3]=' outside the data type range: ' -errtxt2[4]='DATETIME value cannot be a VAR_FILL_VALUE. ' -errtxt2[5]='DATETIME values not in chronological order' -errtxt2[6]='Type conversion error. Entry is not a valid value' - -vi=WHERE(meta_arr EQ 'VAR_NAME='+vn[vc]) - -;Determine Var_Fill_Value, Var_Valid_Min, Var_Valid_Max, and data values, -;and check that they fall within the range allowed by VAR_DATA_TYPE -label=['VAR_VALID_MIN','VAR_VALID_MAX','VAR_FILL_VALUE','data value'] -n_lab=N_ELEMENTS(label) -mxlablen=MAX(STRLEN(label[0:n_lab-2])) ;determine max label string length -metahold=STRMID(meta_arr,0,mxlablen) ;subset of the meta_arr strings, containing first portion of the Metadata -lvi=LONARR(n_lab-1) ;to hold index values of the VAR_FILL_VALUE/VALID_MIN/VALID_MAX attributes - -CASE ndm[vc,8] OF - 0: vhold=BYTARR(ndl+(n_lab-1L)) - 1: vhold=INTARR(ndl+(n_lab-1L)) - 2: vhold=LONARR(ndl+(n_lab-1L)) - 3: vhold=LON64ARR(ndl+(n_lab-1L)) - 4: vhold=FLTARR(ndl+(n_lab-1L)) - 5: vhold=DBLARR(ndl+(n_lab-1L)) -ENDCASE - -IF SIZE(dtest,/TYPE) EQ 7 THEN convertstr=1 ELSE convertstr=0 ;dtest is in ASCII string format -IF ndm[vc,8] LE 3 then convtolng=1 else convtolng=0 - -nanfound=0 ;boolean setting so information/error message only written once -FOR i=0L,ndl+(n_lab-2L) DO BEGIN - valid=0 ;set-up for possible Type Conversion Error - IF i LE n_lab-2L THEN BEGIN - lin=i ;label index - li=WHERE(STRPOS(metahold[vi[0]:N_ELEMENTS(metahold)-1],label[lin]) NE -1) - lvi[i]=li[0]+vi[0] - res=STRSPLIT(meta_arr[lvi[i]],'=',/Extract) - IF convtolng THEN lv=mv_lng[i,vc] ELSE lv=mv_dbl[i,vc] - tcerr=': '+meta_arr[lvi[i]] ;used for type conversion error - ENDIF ELSE BEGIN ;check individual data values - res=['',''] & lin=n_lab-1L ;label index - tcerr=' in the Data File: '+STRTRIM(dtest[i-lin],2) ;used for type conversion error - IF convertstr THEN BEGIN - IF convtolng THEN lv=ROUND(DOUBLE(dtest[i-lin]),/L64) ELSE lv=DOUBLE(dtest[i-lin]) ;LONG64(DOUBLE(dtest[i-lin])) ELSE lv=DOUBLE(dtest[i-lin]) - ENDIF ELSE lv=dtest[i-lin] - ENDELSE - IF N_ELEMENTS(res) EQ 2 THEN mok=0 ELSE mok=-1 - - IF mok EQ 0 THEN BEGIN - ;test if value is 'NaN'. If so then change to the fill value if already determined - IF (FINITE(lv,/NAN)) AND (i GT 2L) THEN BEGIN ;data value is NaN - lv=vhold[2] - IF nanfound EQ 0 THEN BEGIN - infotxt='2 '+vn[vc]+' dataset contains NaN value(s)|. Replaced with Fill Value(s)' - INFOTXT_OUTPUT,infotxt - nanfound=1 - ENDIF - ENDIF - CASE ndm[vc,8] OF - 0:BEGIN ;BYTE unsigned 8-bit integer (0 to 255) - ;do difference test to account for possible rounding errors - IF lv NE BYTE(lv) THEN BEGIN - infotxt='3 '+vn[vc]+' '+label[lin]+' outside 8-bit unsigned (BYTE) range: '+STRTRIM(lv,2) - INFOTXT_OUTPUT,infotxt - ENDIF ELSE vhold[i]=BYTE(lv) - END - 1:BEGIN ;SHORT signed 16-bit integer (-32,768 to 32,767) - ;do difference test to account for possible rounding errors - IF lv NE FIX(DOUBLE(lv)) THEN BEGIN - infotxt='3 '+vn[vc]+' '+label[lin]+' outside 16-bit signed (SHORT) range: '+STRTRIM(lv,2) - INFOTXT_OUTPUT,infotxt - ENDIF ELSE vhold[i]=FIX(DOUBLE(lv)) - END - 2:BEGIN ;INTEGER signed 32-bit integer (-2.147e+9 to 2.147e+9) - ;do difference test to account for possible rounding errors - IF lv NE LONG(DOUBLE(lv)) THEN BEGIN - infotxt='3 '+vn[vc]+' '+label[lin]+' outside 32-bit signed (INTEGER) range: '+STRTRIM(lv,2) - INFOTXT_OUTPUT,infotxt - ENDIF ELSE vhold[i]=LONG(DOUBLE(lv)) - END - 3:BEGIN ;INTEGER signed 64-bit integer (-9.223e+18 to 9.223e+18) - ;do difference test to account for possible rounding errors - IF ABS(DOUBLE(lv)-DOUBLE(lv)) GE 1. THEN BEGIN - infotxt='3 '+vn[vc]+' '+label[lin]+' outside 64-bit signed (LONG) range: '+STRTRIM(lv,2) - INFOTXT_OUTPUT,infotxt - ENDIF ELSE vhold[i]=LONG64(DOUBLE(lv)) - END - 4:BEGIN - IF NOT FINITE(FLOAT(lv)) THEN BEGIN - infotxt='3 '+vn[vc]+' '+label[lin]+' outside 32-bit floating point (REAL) range: '+STRTRIM(lv,2) - INFOTXT_OUTPUT,infotxt - ENDIF ELSE vhold[i]=FLOAT(lv) - END - 5:BEGIN - IF NOT FINITE(DOUBLE(lv)) THEN BEGIN - infotxt='3 '+vn[vc]+' '+label[lin]+' outside 64-bit floating point (DOUBLE) range: '+STRTRIM(lv,2) - INFOTXT_OUTPUT,infotxt - ENDIF ELSE vhold[i]=DOUBLE(lv) - END - ENDCASE - ENDIF ELSE BEGIN - STOP_WITH_ERROR,o3[3]+proname+vn[vc],label[lin]+errtxt2[0]+tcerr,lu & RETURN - ENDELSE - valid=1 ;type conversion OK if got to here -ENDFOR - -;Check if VAR_FILL_VALUE falls within VAR_VALID_MIN/MAX - if so it is defined -;as a default, rather than missing/error value -mnv=vhold[0] & mxv=vhold[1] & fv=vhold[2] -IF (fv GE mnv) AND (fv LE mxv) THEN BEGIN - infotxt='0 Fill value for '+meta_arr[vi[0]]+' falls within the data' - infotxt=infotxt+' range and is defined as a ''Default'' value' - INFOTXT_OUTPUT,infotxt -ENDIF - -dtest=vhold[n_lab-1L:ndl+(n_lab-2L)] - -;Do checks on VAR_VALID_MIN and MAX values for Latitude, Longitide, Zenith, Azimuth and Wind Direction datasets -IF STRPOS(vn[vc],'LATITUDE') NE -1 THEN BEGIN - IF (mnv LT -90.0) OR (mxv GT 90.0) THEN BEGIN - itxt='-90.0 (South) and +90.0 (North) degrees' - infotxt='3 Valid Minimum and Maximum range values for '+vn[vc]+' are '+itxt - INFOTXT_OUTPUT,infotxt - ENDIF -ENDIF ELSE IF STRPOS(vn[vc],'LONGITUDE') NE -1 THEN BEGIN - IF (mnv LT -180.0) OR (mxv GT 180.0) THEN BEGIN - itxt='-180.0 (West) and +180.0 (East) degrees' - infotxt='3 Valid Minimum and Maximum range values for '+vn[vc]+' are '+itxt - INFOTXT_OUTPUT,infotxt - ENDIF -ENDIF ELSE IF (STRPOS(vn[vc],'ZENITH') NE -1) AND (STRPOS(vn[vc],'ANGLE') NE -1) THEN BEGIN - IF (mnv LT 0.0) OR (mxv GT 180.0) THEN BEGIN - itxt='0.0 and 180.0 degrees' - infotxt='3 Valid Minimum and Maximum range values for '+vn[vc]+' are '+itxt - INFOTXT_OUTPUT,infotxt - ENDIF -ENDIF ELSE IF (STRPOS(vn[vc],'AZIMUTH') NE -1) AND (STRPOS(vn[vc],'ANGLE') NE -1) THEN BEGIN - IF (mnv LT 0.0) OR (mxv GT 360.0) THEN BEGIN - itxt='0.0 (North) and 360.0 degrees (East = 90 degrees and so on)' - infotxt='3 Valid Minimum and Maximum range values for '+vn[vc]+' are '+itxt - INFOTXT_OUTPUT,infotxt - ENDIF -ENDIF ELSE IF STRPOS(vn[vc],'WIND.DIRECTION') NE -1 THEN BEGIN - IF (mnv NE 0.0) OR (mxv NE 360.0) THEN BEGIN - itxt='0.0 (Calm) and 360.0 degrees (use WMO definition)' - infotxt='3 Valid Minimum and Maximum range values for '+vn[vc]+' are '+itxt - INFOTXT_OUTPUT,infotxt - ENDIF -ENDIF - -;determine whether minimum and maximum data values are within VAR_VALID_MIN/MAX -nfvi=WHERE(dtest NE fv,nfvcnt) -IF nfvcnt NE 0 THEN BEGIN ;i.e. there are non-fill values in the data - minv=MIN(dtest(nfvi),minvi,MAX=maxv,SUBSCRIPT_MAX=maxvi) - IF minv LT mnv THEN BEGIN ;Minimum data value is less than VAR_VALID_MIN - IF ndm[vc,8] EQ 0 THEN itxt=STRTRIM(FIX(dtest[nfvi[minvi]]),2) $ - ELSE itxt=STRTRIM(dtest[nfvi[minvi]],2) - infotxt='3 Minimum data value of '+itxt+' is outside '+meta_arr[lvi[0]] - infotxt=infotxt+' for dataset '+vn[vc] - INFOTXT_OUTPUT,infotxt - ;STOP_WITH_ERROR,o3[3]+proname+vn[vc]+': ','Minimum'+errtxt2[1]+ $ - ; 'MIN: '+STRTRIM(dtest[nfvi[minvi]],2)+' ('+meta_arr[lvi[0]]+').',lu,ds - ;RETURN - ENDIF - IF maxv GT mxv THEN BEGIN ;Maximum data value is greater than VAR_VALID_MAX - IF ndm[vc,8] EQ 0 THEN itxt=STRTRIM(FIX(dtest[nfvi[maxvi]]),2) $ - ELSE itxt=STRTRIM(dtest[nfvi[maxvi]],2) - infotxt='3 Maximum data value of '+itxt+' is outside '+meta_arr[lvi[1]] - infotxt=infotxt+' for dataset '+vn[vc] - INFOTXT_OUTPUT,infotxt - ;STOP_WITH_ERROR,o3[3]+proname+vn[vc]+': ','Maximum'+errtxt2[1]+ $ - ; 'MAX: '+STRTRIM(dtest[nfvi[maxvi]],2)+' ('+meta_arr[lvi[1]]+').',lu,ds - ;RETURN - ENDIF ELSE IF (STRPOS(vn[vc],'AZIMUTH') NE -1) AND (STRPOS(vn[vc],'ANGLE') NE -1) THEN BEGIN - ;also check whether maximum dataset value = 360.0 degrees - IF LONG(maxv) EQ 360L THEN BEGIN - infotxt='3 Maximum data value of 360.0 degrees is an invalid value for dataset '+vn[vc]+' (North = 0.0)' - INFOTXT_OUTPUT,infotxt - ENDIF - ENDIF -ENDIF ELSE BEGIN ;dataset consists of fill values so check that VAR_VALID_MIN LE VAR_VALID_MAX - IF mnv GT mxv THEN BEGIN - infotxt='3 VAR_VALID_MIN value is greater than the VAR_VALID_MAX value for dataset '+vn[vc] - INFOTXT_OUTPUT,infotxt - ENDIF -ENDELSE - -;Do Checks with DATETIME attributes -IF STRPOS(vn[vc],'DATETIME') EQ 0 THEN BEGIN ;i.e. DATETIME, DATETIME.START or DATETIME.STOP - ;Does DATETIME contain any fill values? - di=WHERE(dtest EQ fv,dcnt) - IF (vn[vc] EQ 'DATETIME') AND (dcnt NE 0) THEN BEGIN - ;'DTFVOK' = DATETIME fill value is OK - IF (o3[5] EQ 'DTFVOK') AND (dcnt NE ndl) THEN itxt='1 ' ELSE itxt='3 ' - infotxt=itxt+'DATETIME contains fill value(s)' - INFOTXT_OUTPUT,infotxt - ;STOP_WITH_ERROR,o3[3]+proname,errtxt2[4]+STRTRIM(dtest[di[0]],2),lu,ds & RETURN - ENDIF - di=WHERE(dtest NE fv,dcnt) - IF dcnt GT 1 THEN BEGIN - ;Are DATETIME values in chronological order? - dsort=SORT(dtest[di]) - IF ARRAY_EQUAL(dtest[di],dtest[di[dsort]]) EQ 0 THEN BEGIN - IF o3[5] EQ 'DTFVOK' THEN itxt='1 ' ELSE itxt='3 ' - infotxt=itxt+vn[vc]+' values are not in chronological order' - INFOTXT_OUTPUT,infotxt - ;STOP_WITH_ERROR,o3[3]+proname,errtxt2[5],lu,ds & RETURN - ENDIF - ENDIF -ENDIF - -TypeConversionError: -IF valid EQ 0 THEN STOP_WITH_ERROR,o3[3]+proname+'VAR_NAME='+vn[vc]+': ',errtxt2[6]+tcerr,lu - -END ;procedure Check_Min_Max_Fill - - - -PRO read_data, sds, inf -;Procedure to perform checks on the data file or structure. It looks for missing or invalid -;VAR_VALID_MIN/MAX attribute values for datasets which have VAR_UNITS=MJD2K (including -;start and stop times), and determines these values according to values in the data. It -;also adds a comment to VAR_NOTES if averaging kernel data is present, to indicate the -;proper array order (if this option is chosen on program start-up). -; ---------- -;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org -; -; History: -; 20050802: Original IDLCR8HDF routine - Version 1.0 -; 20050909: Check that the input data file is not empty; test for the correct number of -; datalines in the dataset being searched (e.g. there is not an additional data -; line in the input file); Bug fix to save dtest as an array when there is only -; one data value, and the input data is in a structure - Version 1.1 -; 20061012: Portion of routine originally extracting a dataset from the file or structure -; moved to the EXTRACT_DATA routine; Code added to determine missing or invalid -; minimum or maximum metadata values for DATETIME, or START/STOP.TIME attributes; -; Code added to append a comment to VAR_NOTES if averaging kernel data is present, -; to indicate proper array order (if /AVK option selected); Checked data written -; to the ds structure instead of a series of arrays set-up according to data type; -; Common variable definition WIDGET_WIN added - Version 2.0 -; 20080302: dtest initialized for the EXTRACT_DATA call - Version 3.0 -; 20100205: Add test to allow for RETURN to the calling program after STOP_WITH_ERROR calls -; made by the external routines used by this this procedure - Version 3.09 -; 20101120: Account for new GEOMS rules regarding DATETIME.START/STOP and MJD2K; Do check for -; averaging kernel vector instead of matrix, in which case do not add AVK comment, -; if /AVK option chosen - Version 4.0 -; 20150127: Create mv_str array to hold maximum string length of dataset - Version 4.0b25 -; 20150217: Add check for mv_str so it is only created if input is via files; Change AVK -; comment so it refers to the initial altitude level rather than lowest altitude -; level - Version 4.0b26 -; 20150409: Strip trailing and leading spaces of VAR_VALID_MAX and VAR_VALID_MIN values -; when doing checks for missing values for DATETIME datasets - Version 4.0b28 -; 20150811: Initialize infowrite value for call to EXTRACT_DATA - Version 4.0b29 -; 20150924: Add checks on ALTITUDE/PRESSURE ordering plus ordering of corresponding BOUNDARY -; datasets (only if it is an axis variable). Also perform checks on variables that -; should be grouped (e.g. LATITUDE and LONGITUDE) - Version 4.0b30 -; 20151012: Add ALTITUDE.GPH and DEPTH to the vertical variables to be checked. Check for FTIR -; template version before doing some of the .BOUNDARIES checks - Version 4.0b31 -; 20151109: Allow for the possibility of missing variable attributes (i.e. VAR_NOTES) when -; checking for variable attribute values based on the attr_arr_data array -; ordering; Checks that VAR_NOTES label is present if the /AVK option is selected, -; if not then create log message; Do check on *.BOUNDARIES INDEPENDENT dimension -; regardless of whether the related vertical variable is an axis variable or not -; - Version 4.0b34 -; 20170331: Update comment appended to VAR_NOTES to clarify AVK dimension ordering -; -Version 4.0b42 -; 20171121: Change check identifying FTIR templates with incorrect .BOUNDARIES ordering (FTIR -; changed to FTIR-0 to avoid conflict with other FTIR based templates); Change conditions -; for identifying invalid wind speed or direction values - Version 4.0b43 -; 20201020: Add check for negative random uncertainty values (standard or relative only) -; - Version 4.0b56 -; -; Inputs: sds - Either a structure containing the Variable Attributes and Data, or the input -; data file -; inf - flag identifying SDS type where, 0=structure; 1=data file -; meta_arr - a string array containing the Global and Variable Attributes -; -; Outputs: meta_arr - completes missing or invalid metadata values for attributes with -; VAR_UNITS=MJD2K, as required, and also adds comments to VAR_NOTES -; for averaging kernel data to indicate proper array order, if this -; option is chosen -; ds - checked datasets are written to the ds structure with correct dimensions and -; data type -; -; Called by: IDLCR8HDF -; -; Subroutines Called: EXTRACT_DATA -; CHECK_MIN_MAX_FILL - -COMMON METADATA -COMMON DATA -COMMON WIDGET_WIN - -;Go through variable names and check for DATETIME, DATETIME.START, or DATETIME.STOP -;values and determine representative minimum and maximum datetime values -mint=90000.D & maxt=-90000.D ;initialize min and max datetimes -valfound=0 ;count variable that increments when DATETIME and related datasets are found -errorfound=1 ;variable to indicate error in DATETIME checks -infowrite=0 ;do not write ISO8601 to MJD2K information message in EXTRACT_DATA routine (will write on last EXTRACT_DATA call) -;set-up holding array to hold number of bytes in string VAR_FILL_VALUE (note already created if input is via session memory) -IF mv_str[0] EQ -1L THEN mv_str=LONARR(nvn) -vchk=['VAR_VALID_MIN','VAR_VALID_MAX'] -nvchk=N_ELEMENTS(vchk) & mmi=INTARR(nvchk) -FOR i=0,nvchk-1 DO BEGIN - mi=WHERE(attr_arr_data EQ vchk[i]) & mmi[i]=mi[0] -ENDFOR -vavk=INTARR(nvn) ;holding array for AVK attribute (used below) - -;Determine number of DATETIME values, and set arrays for DATETIME checks -dtfv=DBLARR(3)-mint -dti=WHERE(vn EQ 'DATETIME',dtcnt) -IF dtcnt NE 0 THEN BEGIN - dtest=['0'] ;initializing dtest array for EXTRACT_DATA call - EXTRACT_DATA,sds,inf,dti[0],dtest,ndl,infowrite ;return the dataset - IF STRLEN(rerr[0]) GT 2 THEN RETURN - dtchk=N_ELEMENTS(dtest) - dtvals=DBLARR(3,dtchk)-mint - errorfound=0 -ENDIF ELSE BEGIN - dti=WHERE(vn EQ 'DATETIME.START',dtcnt) - dtsi=WHERE(vn EQ 'DATETIME.STOP',dtscnt) - IF (dtcnt NE 0) AND (dtscnt NE 0) THEN BEGIN - dtest=['0'] ;initializing dtest array for EXTRACT_DATA call - EXTRACT_DATA,sds,inf,dti[0],dtest,ndl,infowrite ;return the dataset - IF STRLEN(rerr[0]) GT 2 THEN RETURN - dtchk=N_ELEMENTS(dtest) - dtest=['0'] ;initializing dtest array for EXTRACT_DATA call - EXTRACT_DATA,sds,inf,dtsi[0],dtest,ndl,infowrite ;return the dataset - IF STRLEN(rerr[0]) GT 2 THEN RETURN - IF dtchk EQ N_ELEMENTS(dtest) THEN BEGIN - dtvals=DBLARR(3,dtchk)-mint - errorfound=0 - ENDIF - ENDIF -ENDELSE - -FOR vc=0,nvn-1 DO BEGIN - vnspl=STRSPLIT(vn[vc],'_',/Extract) - vnspl=[vnspl,'x','x'] ;ensures vnspl has a minimum of 3 values - IF (vnspl[1] EQ 'AVK') OR (vnspl[2] EQ 'AVK') THEN vavk[vc]=1 ;identifies a dataset containing AVK data - IF ((vn[vc] EQ 'DATETIME') OR (vn[vc] EQ 'DATETIME.START') OR $ - (vn[vc] EQ 'DATETIME.STOP')) THEN BEGIN - dtest=['0'] ;initializing dtest array for EXTRACT_DATA call - EXTRACT_DATA,sds,inf,vc,dtest,ndl,infowrite ;return the dataset - IF STRLEN(rerr[0]) GT 2 THEN RETURN - IF errorfound EQ 0 THEN BEGIN - dtnel=N_ELEMENTS(dtest) - CASE 1 OF - vn[vc] EQ 'DATETIME.START': BEGIN - IF dtnel EQ dtchk THEN dtvals[0,*]=DOUBLE(dtest) $ - ELSE IF dtnel EQ 1 THEN dtvals[0,0]=DOUBLE(dtest) $ - ELSE errorfound=1 - dtfv[0]=mv_dbl[2,vc] - END - vn[vc] EQ 'DATETIME': BEGIN - dtvals[1,*]=DOUBLE(dtest) & dtfv[1]=mv_dbl[2,vc] - END - ELSE: BEGIN - IF dtnel EQ dtchk THEN dtvals[2,*]=DOUBLE(dtest) $ - ELSE IF dtnel EQ 1 THEN dtvals[2,dtchk-1]=DOUBLE(dtest) $ - ELSE errorfound=1 - dtfv[2]=mv_dbl[2,vc] - END - ENDCASE - ENDIF - gi=WHERE(DOUBLE(dtest) NE mv_dbl[2,vc],gcnt) ;edit out fill values - IF gcnt NE 0 THEN BEGIN - minth=MIN(DOUBLE(dtest[gi]),MAX=maxth) - IF minth LT mint THEN mint=minth - IF maxth GT maxt THEN maxt=maxth - ;IF vn[vc] EQ 'DATETIME.START' THEN mint=minth $ - ;ELSE IF vn[vc] EQ 'DATETIME.STOP' THEN maxt=maxth - ENDIF - valfound=valfound+1 - ENDIF -ENDFOR - -IF (errorfound EQ 0) AND (valfound GT 1) THEN BEGIN - ;Make all missing values equal to fill values - FOR i=0,2 DO BEGIN - mi=WHERE(dtvals[i,*] EQ -90000.D,mcnt) - IF mcnt NE 0 THEN dtvals[i,mi]=dtfv[i] - ENDFOR - ;Check that DATETIME.START LE DATETIME LE DATETIME.STOP - i=0L - WHILE (errorfound EQ 0) AND (i LE dtchk-1L) DO BEGIN - test1=(dtvals[0,i] NE dtfv[0]) AND (dtvals[1,i] NE dtfv[1]) - test2=(dtvals[1,i] NE dtfv[1]) AND (dtvals[2,i] NE dtfv[2]) - test3=(dtvals[0,i] NE dtfv[0]) AND (dtvals[2,i] NE dtfv[2]) - IF (test1) AND (dtvals[0,i] GT dtvals[1,i]) THEN errorfound=1 - IF (test2) AND (dtvals[1,i] GT dtvals[2,i]) THEN errorfound=2 - IF (test3) AND (dtvals[0,i] GT dtvals[2,i]) THEN errorfound=3 - i=i+1L - ENDWHILE - IF errorfound NE 0 THEN BEGIN - v0=STRTRIM(STRING(format='(d18.9)',dtvals[0,i-1L]),2) - v1=STRTRIM(STRING(format='(d18.9)',dtvals[1,i-1L]),2) - v2=STRTRIM(STRING(format='(d18.9)',dtvals[2,i-1L]),2) - CASE 1 OF - errorfound EQ 1: itxt=['DATETIME.START='+v0,'DATETIME='+v1] - errorfound EQ 2: itxt=['DATETIME='+v1,'DATETIME.STOP='+v2] - ELSE: itxt=['DATETIME.START='+v0,'DATETIME.STOP='+v2] - ENDCASE - infotxt='3 '+itxt[0]+' is greater than '+itxt[1] - INFOTXT_OUTPUT,infotxt - ENDIF -ENDIF - -valfound=0 ;Boolean to indicate whether missing VAR_VALID_MIN/MAX values have been added -;Add minimum and maximum values to Metadata as required -di=WHERE(STRUPCASE(vu) EQ 'MJD2K',dcnt) -IF dcnt NE 0 THEN BEGIN - FOR vc=0,dcnt-1 DO BEGIN - mi=WHERE(meta_arr EQ 'VAR_NAME='+vn[di[vc]]) - FOR i=0,nvchk-1 DO BEGIN - IF STRMID(meta_arr[mi[0]+mmi[i]],0,STRLEN(vchk[i])) NE vchk[i] THEN acti=mmi[i]-1L ELSE acti=mmi[i] ;depends if VAR_NOTES is present or not - res=STRSPLIT(STRTRIM(meta_arr[mi[0]+acti],2),'=',/Extract,COUNT=nres) - IF nres EQ 1 THEN BEGIN ;no value so add Min or Max value to this attribute - IF i MOD 2 EQ 0 THEN BEGIN - IF mint NE 90000.d THEN BEGIN - meta_arr[mi[0]+acti]=vchk[i]+'='+STRTRIM(STRING(format='(d18.9)',mint),2) - valfound=1 - ENDIF - mv_dbl[i,di[vc]]=mint - ENDIF ELSE BEGIN - IF maxt NE -90000.d THEN BEGIN - meta_arr[mi[0]+acti]=vchk[i]+'='+STRTRIM(STRING(format='(d18.9)',maxt),2) - valfound=1 - ENDIF - mv_dbl[i,di[vc]]=maxt - ENDELSE - IF valfound EQ 1 THEN BEGIN - infotxt=STRARR(2) - infotxt[0]='2 Missing '+vchk[i]+' value for '+meta_arr[mi[0]]+'| added based on available' - infotxt[1]=' DATETIME[.START][.STOP] values: '+meta_arr[mi[0]+acti] - INFOTXT_OUTPUT,infotxt - ENDIF - ENDIF - ENDFOR - ENDFOR -ENDIF - -IF o3[1] EQ 'AVK' THEN BEGIN ;append sentence to VAR_NOTES in the AVK attribute - di=WHERE(vavk EQ 1,dcnt) - vnc=STRARR(nvn) ;holding array for comment - IF dcnt NE 0 THEN BEGIN - FOR vc=0,dcnt-1 DO BEGIN - mi=WHERE(meta_arr EQ 'VAR_NAME='+vn[di[vc]]) - ;check to see if AVK is a matrix or vector (no need for comment if it us a vector) - add_comm=0 ;default is for no comment - xi=WHERE(attr_arr_data EQ 'VAR_DEPEND') - IF STRMID(meta_arr[mi[0]+xi[0]],0,10) NE 'VAR_DEPEND' THEN acti=xi[0]-1L ELSE acti=xi[0] ;depends if VAR_NOTES is present or not - res=STRSPLIT(meta_arr[mi[0]+acti],'=; ',/EXTRACT,COUNT=nres) - res=STRUPCASE(res) - IF nres GE 3 THEN BEGIN - FOR i=1,nres-1 DO BEGIN - ri=WHERE(res[i] EQ res,rcnt) ;i.e. repeated dependencies indicate an AVK matrix - IF rcnt GE 2 THEN add_comm=1 - ENDFOR - ENDIF - IF add_comm THEN BEGIN - IF acti NE xi[0] THEN BEGIN ;No VAR_NOTES attribute present so generate error - infotxt='3 VAR_NOTES attribute not present under '+meta_arr[mi[0]]+' so unable to append Averaging Kernel information' - INFOTXT_OUTPUT,infotxt - ENDIF ELSE BEGIN - ;find relevant VAR_NOTES attribute label - xi=WHERE(attr_arr_data EQ 'VAR_NOTES') - eqpos=STRPOS(meta_arr[mi[0]+xi[0]],'=') - vnc[di[vc]]=STRTRIM(STRMID(meta_arr[mi[0]+xi[0]],eqpos+1),2) ;existing VAR_NOTES comment, if any - ;if comment exists, check to see if it ends with a period, if not add one - IF vnc[di[vc]] NE '' THEN BEGIN - IF STRMID(vnc[di[vc]],STRLEN(vnc[di[vc]])-1,1) NE '.' THEN $ - vnc[di[vc]]=vnc[di[vc]]+'. ' ELSE vnc[di[vc]]=vnc[di[vc]]+' ' - ENDIF - ;determine the number of dimensions - ni=WHERE(ndm[di[vc],0:7] NE 0,ndim) - IF ndim GT 2 THEN vncx='for the first measurement are:' ELSE vncx='are:' - ;append comment - vnc[di[vc]]=vnc[di[vc]]+'First three values of the initial averaging kernel '+vncx - ;append meta_arr index to comment - vnc[di[vc]]=STRTRIM(mi[0]+xi[0],2)+'_'+vnc[di[vc]] - ENDELSE - ENDIF - ENDFOR - ENDIF -ENDIF - -;If required do checks on the vertical dimension plus correspondng .BOUNDARIES -;Only do checks if variable is an axis variable - so must be in ascending or descending order -bchks=['ALTITUDE','PRESSURE','ALTITUDE.GPH','DEPTH'] -sdata=ndm(vc,8) EQ 6 - -;Do check for DATA_TEMPLATE and do not do all the .BOUNDARIES checks for FTIR 001 or 002 templates -ti=WHERE(STRMID(meta_arr,0,13) EQ 'DATA_TEMPLATE',tcnt) -ftirchk=0 ;default is that it is not an FTIR-001 or 002 measurement -IF tcnt EQ 1 THEN BEGIN - res=STRSPLIT(meta_arr[ti[0]],'=',/EXTRACT,COUNT=nres) - IF nres EQ 2 THEN BEGIN - IF (STRPOS(res[1],'FTIR-0') NE -1) AND ((STRPOS(res[1],'001') NE -1) OR (STRPOS(res[1],'002') NE -1)) THEN ftirchk=1 - ENDIF -ENDIF - -FOR vc=0,N_ELEMENTS(bchks)-1 DO BEGIN - bci=WHERE(vn EQ bchks[vc],bccnt) & bbci=WHERE(vn EQ bchks[vc]+'.BOUNDARIES',bbccnt) - IF bccnt EQ 1 THEN BEGIN ;ALTITUDE or PRESSURE present so extract VAR_DEPEND and VAR_SIZE values - mi=WHERE(meta_arr EQ 'VAR_NAME='+vn[bci[0]]) - xi=WHERE(attr_arr_data EQ 'VAR_DEPEND') - IF STRMID(meta_arr[mi[0]+xi[0]],0,10) NE 'VAR_DEPEND' THEN acti=xi[0]-1L ELSE acti=xi[0] ;depends if VAR_NOTES is present or not - res=STRSPLIT(meta_arr[mi[0]+acti],'=; ',/EXTRACT,COUNT=nres) - vdvals=STRUPCASE(res[1:nres-1]) - di=WHERE(ndm[bci[0],0:7] NE 0L,ndim) - vsvals=ndm[bci[0],di] - bi=WHERE(vdvals EQ bchks[vc],bcnt) ;determine if the variable of interest is also self-referencing - nbi=WHERE(vdvals NE bchks[vc],nbcnt) ;determine if there are any other dependencies e.g. DATETIME - sdata=ndm[bci[0],8] EQ 6 ;test for string datatype - writeonce=[0,0] ;initialize array for writing information/error messages - IF (bcnt EQ 1) AND (ndim LE 2) AND (~sdata) THEN BEGIN ;it is self-referencing and there are 2 or less dependencies so do the checks - ascending=-1 ;set default value - will change to 0 for descending order and 1 for ascending order - writeonce=[1,1] ;ensure log text is only written once - EXTRACT_DATA,sds,inf,bci[0],dtest,ndl,infowrite - IF STRLEN(rerr[0]) GT 2 THEN RETURN - bvals=DOUBLE(REFORM(dtest,ndm[bci[0],di])) ;put into array order and make numeric - IF nbcnt EQ 1 THEN n_sec=ndm[bci[0],nbi[0]] ELSE n_sec=1L ;Number of datasets to be read through - IF bi[0] NE 0 THEN bvals=TRANSPOSE(bvals) ;make ALTITUDE or PRESSURE array the first index - FOR i=0L,n_sec-1L DO BEGIN ;e.g. number of DATETIME values or single loop if only one dimension - bvtest=bvals[*,i] ;set of PRESSURES or ALTITUDES to be tested - asc=-1 - IF ARRAY_EQUAL(bvtest,bvtest[SORT(bvtest)]) THEN asc=1 $ ;ascending order - ELSE IF ARRAY_EQUAL(bvtest,bvtest[REVERSE(SORT(bvtest))]) THEN asc=0 ;descending order - IF ascending EQ -1 THEN ascending=asc - IF (asc EQ -1) OR (ascending NE asc) THEN BEGIN ;altitudes/pressures not in any order - IF (ascending NE -1) AND (writeonce[1] EQ 1) THEN BEGIN ;more than one dimension and error in one or more of the sets - IF ascending EQ 1 THEN itxt='increasing' ELSE itxt='decreasing' - infotxt='3 '+bchks[vc]+' values not in '+itxt+' order for every '+vdvals[nbi[0]]+' for '+meta_arr[mi[0]] - INFOTXT_OUTPUT,infotxt - writeonce[1]=0 - ENDIF ELSE IF writeonce[0] EQ 1 THEN BEGIN - infotxt='3 '+bchks[vc]+' values not monotonically increasing or decreasing for '+meta_arr[mi[0]] - INFOTXT_OUTPUT,infotxt - writeonce[0]=0 - ENDIF - ENDIF - ENDFOR - ENDIF - - IF bbccnt EQ 1 THEN BEGIN ;*.BOUNDARIES present so check that INDEPENDENT index has VAR_SIZE=2 - mi=WHERE(meta_arr EQ 'VAR_NAME='+vn[bbci[0]]) - xi=WHERE(attr_arr_data EQ 'VAR_DEPEND') - IF STRMID(meta_arr[mi[0]+xi[0]],0,10) NE 'VAR_DEPEND' THEN acti=xi[0]-1L ELSE acti=xi[0] ;depends if VAR_NOTES is present or not - res=STRSPLIT(meta_arr[mi[0]+acti],'=; ',/EXTRACT,COUNT=nres) - vdvals=STRUPCASE(res[1:nres-1]) - di=WHERE(ndm[bbci[0],0:7] NE 0L,ndim) - vsvals=ndm[bbci[0],di] - bi=WHERE(vdvals EQ bchks[vc],bcnt) ;determine index of the variable of interest is also self-referencing - IF bcnt EQ 1 THEN n_alt=ndm[bbci[0],bi[0]] - ibi=WHERE(vdvals EQ 'INDEPENDENT',ibcnt) - IF ibcnt EQ 1 THEN BEGIN - n_ind=ndm[bbci[0],ibi[0]] - IF n_ind NE 2 THEN BEGIN - infotxt='3 INDEPENDENT index must have VAR_SIZE=2 for '+meta_arr[mi[0]] - INFOTXT_OUTPUT,infotxt - ENDIF - ENDIF - ENDIF - - oksofar=(writeonce[0] EQ 1) AND (writeonce[1] EQ 1) - IF (bbccnt EQ 1) AND (oksofar) THEN BEGIN ;Also need to check that xx.BOUNDARIES is in the same order (ascending or descending) - ;Note: Only do checks if checks on axis variable were all OK - nbi=WHERE((vdvals NE bchks[vc]) AND (vdvals NE 'INDEPENDENT'),nbcnt) ;determine if there are any other dependencies e.g. DATETIME - IF nbcnt EQ 1 THEN n_sec=ndm[bbci[0],nbi[0]] ELSE n_sec=1 - sdata=(ndm[bbci[0],8] EQ 6) ;test for string datatype - IF (bcnt EQ 1) AND ((ndim EQ 2) OR (ndim EQ 3)) AND (n_ind EQ 2) AND (~sdata) THEN BEGIN - ;it is dependent on ALTITUDE/PRESSURE and there are 2 or less other dependencies so do the checks - EXTRACT_DATA,sds,inf,bbci[0],dtest,ndl,infowrite - IF STRLEN(rerr[0]) GT 2 THEN RETURN - bvals=DOUBLE(REFORM(dtest,ndm[bbci[0],di])) ;put into array order and make numeric - - ;Determine the actual ordering of the array - ;Option 1 = 2-D e.g. ALTITUDE;INDEPENDENT or INDEPENDENT;ALTITUDE - ;Option 2 = 3-D with axis variable as first or last index and INDEPENDENT is the middle index e.g. ALT;IND;DAT or DAT;IND;ALT - ;Option 3 = 3-D with axis variable as first or last index and INDEPENDENT is the first or last index e.g. ALT;DAT;IND or IND;DAT;ALT - ;Option 4 = 3-D with INDEPENDENT variable as first or last index and ALTITUDE is the middle index e.g. IND;ALT;DAT or DAT;ALT;IND - IF (bcnt EQ 1) AND (ibcnt EQ 1) AND (ndim EQ 2) THEN BEGIN - option=1 - IF bi[0] NE 0 THEN bvals=TRANSPOSE(bvals) ;make ALTITUDE or PRESSURE array the first index - ENDIF ELSE IF (bcnt EQ 1) AND (ibcnt EQ 1) AND (ndim EQ 3) THEN BEGIN - IF ((bi[0] EQ 0) OR (bi[0] EQ 2)) AND (ibi[0] EQ 1) THEN BEGIN - option=2 - IF bi[0] EQ 2 THEN bvals=TRANSPOSE(bvals) ;make ALTITUDE or PRESSURE array the first index - ENDIF ELSE IF ((bi[0] EQ 0) OR (bi[0] EQ 2)) AND (nbi[0] EQ 1) THEN BEGIN - option=3 - IF bi[0] EQ 2 THEN bvals=TRANSPOSE(bvals) ;make ALTITUDE or PRESSURE array the first index - ENDIF ELSE BEGIN - option=4 - IF ibi[0] EQ 2 THEN bvals=TRANSPOSE(bvals) ;make INDEPENDENT array the first index (ALTITUDE or PRESSURE is the 2nd index) - ENDELSE - ENDIF ELSE option=0 - - IF option NE 0 THEN BEGIN - writeonceb=[1,1] - FOR i=0L,n_sec-1L DO BEGIN - FOR j=0,n_ind-1 DO BEGIN ;check that order of the boundary sets matches the axis variable ordering - CASE option of ;set of PRESSURES or ALTITUDES to be tested - 1: bvtest=bvals[*,j] - 2: bvtest=bvals[*,j,i] - 3: bvtest=bvals[*,i,j] - 4: bvtest=bvals[j,*,i] - ENDCASE - asc=-1 - IF ARRAY_EQUAL(bvtest,bvtest[SORT(bvtest)]) THEN asc=1 $ ;ascending order - ELSE IF ARRAY_EQUAL(bvtest,bvtest[REVERSE(SORT(bvtest))]) THEN asc=0 ;descending order - - IF (ascending NE asc) AND (writeonceb[0] EQ 1) THEN BEGIN - ;order of the boundaries dataset does not match axis dataset - IF ascending EQ 1 THEN itxt='increasing' ELSE itxt='decreasing' - infotxt='3 '+bchks[vc]+'.BOUNDARIES order does not match '+bchks[vc]+' '+itxt+' ordering for '+meta_arr[mi[0]] - INFOTXT_OUTPUT,infotxt - writeonceb[0]=0 - ENDIF - ENDFOR - ENDFOR - ;Check that the boundary ordering is also the same as the axis variable for every ALTITUDE/PRESSURE - FOR i=0L,n_sec-1L DO BEGIN - FOR j=0L,n_alt-1L DO BEGIN - CASE option OF ;set of boundary pairs to be tested (INDEPENDENT index) - 1: bvtest=bvals[j,*] - 2: bvtest=bvals[j,*,i] - 3: bvtest=bvals[j,i,*] - 4: bvtest=bvals[*,j,i] - ENDCASE - asc=-1 - IF ARRAY_EQUAL(bvtest,bvtest[SORT(bvtest)]) THEN asc=1 $ ;ascending order - ELSE IF ARRAY_EQUAL(bvtest,bvtest[REVERSE(SORT(bvtest))]) THEN asc=0 ;descending order - - IF (ascending NE asc) AND (writeonceb[1] EQ 1) AND (ftirchk EQ 0) THEN BEGIN - ;order of the boundaries values does not match axis dataset - IF ascending EQ 1 THEN itxt='increasing' ELSE itxt='decreasing' - infotxt='3 Boundary index order does not match '+bchks[vc]+' '+itxt+' ordering for '+meta_arr[mi[0]] - INFOTXT_OUTPUT,infotxt - writeonceb[1]=0 - ENDIF - ENDFOR - ENDFOR - ENDIF - ENDIF - ENDIF - ENDIF -ENDFOR - -;Test that random uncertainty values are positive -posun=['UNCERTAINTY.RANDOM.STANDARD','UNCERTAINTY.RANDOM.STANDARD.RELATIVE'] -n_p=N_ELEMENTS(posun) -vnx=vn -;vnx consists of only the last sub-name of vn -FOR i=0,nvn-1 DO $ - IF STRPOS(vnx[i],'_') NE -1 THEN vnx[i]=STRMID(vnx[i],STRPOS(vnx[i],'_',/REVERSE_SEARCH)+1) -FOR i=0,n_p-1 DO BEGIN ;number of values to check - pi=WHERE(vnx EQ posun[i],pcnt) - IF pcnt NE 0 THEN BEGIN - FOR j=0,pcnt-1 DO BEGIN ;test for negative data values - sdata=(ndm[pi[j],8] EQ 6) ;test for string datatype - IF ~sdata THEN BEGIN - EXTRACT_DATA,sds,inf,pi[j],dtest,ndl,infowrite - IF STRLEN(rerr[0]) GT 2 THEN RETURN - - CASE ndm[pi[j],8] OF ;identifies the VAR_DATA_TYPE - 0:BEGIN - fv=BYTE(mv_lng[2,pi[j]]) & urd=BYTE(dtest) & zero=0 - END - 1:BEGIN - fv=FIX(mv_lng[2,pi[j]]) & urd=FIX(dtest) & zero=0 - END - 2:BEGIN - fv=LONG(mv_lng[2,pi[j]]) & urd=LONG(dtest) & zero=0L - END - 3:BEGIN - fv=mv_lng[2,pi[j]] & urd=LONG64(dtest) & zero=0LL - END - 4:BEGIN - fv=FLOAT(mv_dbl[2,pi[j]]) & urd=FLOAT(dtest) & zero=0. - END - 5:BEGIN - fv=mv_dbl[2,pi[j]] & urd=DOUBLE(dtest) & zero=0.d - END - ENDCASE - ni=WHERE((urd LT zero) AND (urd NE fv),ncnt) - IF ncnt NE 0 THEN BEGIN - infotxt='3 '+vn[pi[j]]+' values cannot be negative' - INFOTXT_OUTPUT,infotxt - ENDIF - ENDIF - ENDFOR - ENDIF -ENDFOR - -;Test 'grouped' datasets, i.e. datasets that must both be in the file if one of them is present -;Note that there might be more than one variable in a particular group e.g. LATITUDE and LATITUDE.INSTRUMENT -grouped=[['LATITUDE','LONGITUDE'],['WIND.DIRECTION','WIND.SPEED']] -g_dim=SIZE(grouped,/DIMENSIONS) -FOR i=0,g_dim(1)-1 DO BEGIN ;number of grouped pairs - p1i=WHERE(STRPOS(STRMID(vn,0,STRLEN(grouped[0,i])),grouped[0,i]) NE -1,p1cnt) - p2i=WHERE(STRPOS(STRMID(vn,0,STRLEN(grouped[1,i])),grouped[1,i]) NE -1,p2cnt) - IF p1cnt NE 0 THEN BEGIN - FOR j=0,p1cnt-1 DO BEGIN ;test all variations - extrapart=STRMID(vn[p1i[j]],STRLEN(grouped[0,i])) & extrapart=STRTRIM(extrapart,2) - mi=WHERE(vn EQ grouped[1,i]+extrapart,mcnt) - IF mcnt EQ 0 THEN BEGIN ;matching variable name not found - infotxt='3 VAR_NAME='+grouped[1,i]+extrapart+' must also be present together with VAR_NAME='+vn[p1i[j]] - INFOTXT_OUTPUT,infotxt - ENDIF ELSE BEGIN - ;Do tests on grouped values - ;1. VAR_SIZE values must be the same. If this is the case then for wind direction and wind speed, - ;2. If wind direction EQ 0 then corresponding wind speed must be 0 - ;3. If wind direction NE 0 then corresponding wind speed can't be 0 - IF ~ARRAY_EQUAL(ndm[p1i[j],0:7],ndm[mi[0],0:7]) THEN BEGIN - infotxt='3 VAR_SIZE values for VAR_NAME='+vn[p1i[j]]+' must be the same as VAR_SIZE values for VAR_NAME='+vn[mi[0]] - INFOTXT_OUTPUT,infotxt - ENDIF ELSE BEGIN ;arrays match up so do wind direction/speed checks - sdata=(ndm[p1i[j],8] EQ 6) OR (ndm[mi[0],8] EQ 6) ;test for string datatype - IF (grouped[0,i] EQ 'WIND.DIRECTION') AND (~sdata) THEN BEGIN - EXTRACT_DATA,sds,inf,p1i[j],dtest,ndl,infowrite - IF STRLEN(rerr[0]) GT 2 THEN RETURN - wdd=DOUBLE(dtest) - EXTRACT_DATA,sds,inf,mi[0],dtest,ndl,infowrite - IF STRLEN(rerr[0]) GT 2 THEN RETURN - wsd=DOUBLE(dtest) - ni=WHERE(wdd EQ 0.d,ncnt) - IF ncnt NE 0 THEN BEGIN - si=WHERE(wsd[ni] GT 0.d,scnt) - IF scnt NE 0 THEN BEGIN ;Wind direction = 0.0 but wind speed NE 0.0 - infotxt='3 '+vn[mi[0]]+' values must be 0.0 for corresponding '+vn[p1i[j]]+' 0.0 values' - INFOTXT_OUTPUT,infotxt - ENDIF - ENDIF - ni=WHERE((wdd GT 0.d) AND (wdd LE 360.d),ncnt) - IF ncnt NE 0 THEN BEGIN - si=WHERE(wsd[ni] EQ 0.d,scnt) - IF scnt NE 0 THEN BEGIN ;Wind direction NE 0.0 but wind speed = 0.0 - infotxt='3 '+vn[p1i[j]]+' values must be 0.0 for corresponding '+vn[mi[0]]+' 0.0 values' - INFOTXT_OUTPUT,infotxt - ENDIF - ENDIF - ENDIF - ENDELSE - ENDELSE - ENDFOR - ENDIF - IF p2cnt NE 0 THEN BEGIN ;now do the reverse check - FOR j=0,p2cnt-1 DO BEGIN ;test all variations - extrapart=STRMID(vn[p2i[j]],STRLEN(grouped[1,i])) & extrapart=STRTRIM(extrapart,2) - mi=WHERE(vn EQ grouped[0,i]+extrapart,mcnt) - IF mcnt EQ 0 THEN BEGIN ;matching variable name not found - infotxt='3 VAR_NAME='+grouped[0,i]+extrapart+' must also be present together with VAR_NAME='+vn[p2i[j]] - INFOTXT_OUTPUT,infotxt - ENDIF - ENDFOR - ENDIF -ENDFOR - -FOR vc=0,nvn-1 DO BEGIN - dtest=['0'] ;initializing dtest array for EXTRACT_DATA call - infowrite=1 ;write information message in EXTRACT_DATA routine if required - ;Extract the dataset - EXTRACT_DATA,sds,inf,vc,dtest,ndl,infowrite - IF STRLEN(rerr[0]) GT 2 THEN RETURN - ;perform checks on the attribute and data values - IF ndm[vc,8] EQ 6 THEN CHECK_STRING_DATATYPE,vc,dtest $ ;Test for String Datatype - ELSE BEGIN - CHECK_MIN_MAX_FILL,vc,dtest,ndl ;Test for Numeric Datatype - IF STRLEN(rerr[0]) GT 2 THEN RETURN - ENDELSE - - ;if AVK dataset extracted then add comment to VAR_NOTES if required - IF o3[1] EQ 'AVK' THEN BEGIN - IF (vnc[vc] NE '') AND (ndm[vc,0] GE 3) THEN BEGIN - res=STRSPLIT(vnc[vc],'_',/Extract) & mi=FIX(res[0]) - meta_arr[mi]='VAR_NOTES='+res[1] - FOR i=0,2 DO meta_arr[mi]=meta_arr[mi]+' '+STRTRIM(dtest[i],2) - ENDIF - ENDIF - - ;Copy data to the data structure with correct dimensions and data type - arrchk=SIZE(dtest) ;put it into array form if it is a scalar - IF arrchk[0] EQ 0 THEN dtest=[dtest] - gi=WHERE(ndm[vc,0:7] NE 0) - vs=TRANSPOSE(ndm[vc,gi]) - dtest=REFORM(dtest,vs,/Overwrite) - ds[vc].data=PTR_NEW(dtest) -ENDFOR - -END ;Procedure Read_Data - - - -PRO find_hdf_filename, hdffilename -;Procedure to do the following: -;1. Compare the DATA_START_DATE and DATA_STOP_DATE with the first and last entries under -; DATETIME. If necessary create/change DATA_START_DATE and DATA_STOP_DATE to match the -; DATETIME entries, and put in ISO8601 format. -;2. Compare filename created by the program from the DATASET_ATTRIBUTES with that under the -; FILE_NAME entry (if present). If necessary create/change the FILE_NAME entry to match the -; created filename -;3. Create/change the FILE_GENERATION_DATE and ensure it is in ISO8601 format. -; ---------- -;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org -; -; History: -; 20050802: Original IDLCR8HDF routine - Version 1.0 -; 20050909: Remove requirement for a 'v' to be at the start of the DATA_FILE_VERSION -; (e.g. v1.0 or 1.0 acceptable); Change the format that DATA_START_DATE and -; FILE_GENERATION_DATE values are saved in the HDF file from MJD2000 to -; ISO8601 - Version 1.1 -; 20051107: Move VIS_SCALE_MIN/MAX checks, for correct format of DATETIME values, to the -; CHECK_MIN_MAX_FILL routine - Version 1.11 -; 20061012: Move the FILE_GENERATION_DATE checks to the main loop which determines the file -; name to facilitate the future option of including the file generation date in the -; file name (this option is not currently implemented by the AVDC); Common variable -; definition WIDGET_WIN added - Version 2.0 -; 20080302: Do search for either DATA_LEVEL or DATA_TYPE, depending on whether an AVDC style -; TAV file or original Envisat table.dat file has been read in; Common variable -; definition TABLEDATA added (tab_type used to differentiate between the databases) -; - Version 3.0 -; 20090610: Allow for the Global Variable DATA_STOP_DATE and calculate the value if required -; - Version 3.07 -; 20100205: Add RETURN command after all STOP_WITH_ERROR calls, which allows program to return to the -; calling program if the reterr argument is included in the idlcr8hdf call - Version 3.09 -; 20101120: Account for new GEOMS changes to filename composition - Version 4.0 -; 20151104: Check that ISO8601 format time is all uppercase - Version 4.0b33 -; 20151109: Check for seconds value of 60 in ISO8601 datetime value - Version 4.0b34 -; 20151215: Fix bug if DATE_TIME_START or DATE_TIME_STOP value is missing - Version 4.0b36 -; 20161130: Allow the program to continue running if an ISO8601 error is found (previously -; called STOP_WITH_ERROR) - Version 4.0b40 -; 20171130: Remove ds from STOP_WITH_ERROR parameters; Change some error and information text -; statements - Version 4.0b44 -; 20180314: Fix bug when checking DATA_START|STOP_DATE values. Criteria for difference with -; MJD2K values change to GT 0.99d/86400.d instead of GE 1.0d/86400.d - -; Version 4.0b46 -; 20190807: Allow .nc and .nc4 filename extensions when doing QA on a file that has been read -; into session memory using the HDF5 routines (idlcr8ascii set up to use H5 routines -; to read netCDF4 files as well as HDF5) - Version 4.0b51 -; 20190824: Fix bug in 4.0b51 that caused the file name to be written to the DATA_FILE_VERSION -; attribute - Version 4.0b52 -; 20231218: Always recalculate FILE_GENERATION_DATE if creating a GEOMS file so that it aligns -; with the FILE_META_VERSION - Version 4.0b61 -; -; Inputs: meta_arr - a string array containing the Global and Variable Attributes -; hfdfilename - a string holding the extension of the eventual HDF filename, either -; '.hdf' for HDF4 or '.h5' for HDF5 or '.nc' for netCDF3/4 -; -; Outputs: meta_arr - DATA_START_DATE, FILE_GENERATION_DATE and FILE_NAME values will be -; (re)written to the array based on values computed by the routine -; hdffilename - a string holding the full HDF file name determined by the routine -; -; Called by: IDLCR8HDF -; -; Subroutines Called: STOP_WITH_ERROR (if error state detected); INFOTXT_OUTPUT -; Possible Conditions for STOP_WITH_ERROR call (plus [line number] where called): -; 1. An incorrect number of sub-values found in meta_arr for DATA_DISCIPLINE, -; DATA_SOURCE, DATA_LOCATION, or DATA_FILE_VERSION [3129] -; 2. Error encountered when converting ISO8601 datetime values to MJD2000 format [3138,3179] -; 3. Type conversion error encountered when a datetime value is expected to be in -; MJD2000 format, but is not numeric [3216] -; -; Information Conditions (when the program is able to make changes): -; 1. DATA_START_DATE or DATA_STOP_DATE changed to match relevant DATETIME entries [3167] -; 2. Filename determined from DATASETS attributes [3208] - -COMMON TABLEDATA -COMMON METADATA -COMMON DATA -COMMON WIDGET_WIN - -;Possible error messages for this procedure -ON_IOERROR,TypeConversionError -errtxt=STRARR(4) & lu=-1L -proname='Find_HDF_Filename procedure: ' -errtxt[0]='Number of sub-values under DATA_' -errtxt[1]=' should be ' -errtxt[2]=' ISO8601 format not valid: ' -errtxt[3]='Type conversion error. MJD2K entry is not valid: ' - -fni=WHERE(attr_arr_glob EQ 'FILE_NAME') -res=STRSPLIT(meta_arr[fni[0]],' =',/Extract) -IF N_ELEMENTS(res) EQ 1 THEN res=[res,'-1'] -fn_val=res[1] - -IF (qa_yes) AND (hdffilename EQ '.h5') AND (fn_val NE '-1') THEN BEGIN - ;netCDF4 files read in using H5 routines so .h5 extension is assumed. Allow files to - ;also have .nc or .nc4 extensions for files being QA'd - hext=['.h5','.nc','.nc4'] - fn_ext=STRMID(fn_val,STRPOS(fn_val,'.',/REVERSE_SEARCH)) - ei=WHERE(fn_ext EQ hext,ecnt) - IF ecnt NE 0 THEN hdffilename=hext[ei[0]] -ENDIF - -fhold='' & iso='' & mjd2000=0.d -dtmjd=0.d & dtiso='' & itxt=' ' -dt=['DISCIPLINE','SOURCE','LOCATION','START_DATE','STOP_DATE','FILE_GENERATION_DATE','FILE_VERSION'] -valid=0 ;test for Type conversion errors -FOR i=0,N_ELEMENTS(dt)-1 DO BEGIN - IF i EQ 0 THEN nei=4 ELSE nei=2 - IF i EQ 5 THEN ai=WHERE(attr_arr_glob EQ dt[i]) $ - ELSE ai=WHERE(attr_arr_glob EQ 'DATA_'+dt[i]) - - res=STRSPLIT(meta_arr[ai[0]],' =;',/Extract,COUNT=cres) - IF (cres NE nei) AND ((i LE 2) OR (i GE 6)) THEN BEGIN - ;infotxt='3 '+errtxt[0]+dt[i]+errtxt[1]+nes - ;INFOTXT_OUTPUT,infotxt - STOP_WITH_ERROR,o3[3]+proname+meta_arr[ai[0]]+': ',errtxt[0]+dt[i]+errtxt[1]+STRTRIM(nei-1,2),lu - RETURN - ENDIF - - IF i EQ 6 THEN BEGIN ;Do DATA_FILE_VERSION checks - dfvv=res[1] - GEOMS_RULE_CHANGES,4,dfvv - res[1]=dfvv & meta_arr[ai[0]]='DATA_FILE_VERSION='+dfvv - ENDIF - - IF (i EQ 3) OR (i EQ 4) THEN BEGIN ;calculate the ISO or MJD2000 value as required - ;Find lowest or highest DATETIME values from Variable Attributes - IF i EQ 3 THEN BEGIN - di=WHERE(vn EQ 'DATETIME.START',dcnt) - itxt= ' first ' & etxt='DATETIME.START ' - ENDIF ELSE BEGIN - di=WHERE(vn EQ 'DATETIME.STOP',dcnt) - itxt= ' last ' & etxt='DATETIME.STOP ' - ENDELSE - IF dcnt NE 0 THEN BEGIN - darr=*ds[di[0]].data ;extract the DATETIME values - ;remove any fill values - fi=WHERE(darr NE mv_dbl[2,di[0]],dcnt) - ENDIF - IF dcnt EQ 0 THEN BEGIN - di=WHERE(vn EQ 'DATETIME',dcnt) & etxt='DATETIME ' - IF dcnt NE 0 THEN BEGIN - darr=*ds[di[0]].data ;extract the DATETIME values - ;remove any fill values - fi=WHERE(darr NE mv_dbl[2,di[0]],dcnt) - ENDIF - ENDIF - IF dcnt NE 0 THEN BEGIN - darr=darr[fi] - IF N_ELEMENTS(darr) EQ 1 THEN itxt=' ' - IF i EQ 3 THEN dtmjd=MIN(darr) ELSE dtmjd=MAX(darr) - dtiso=JDF_2_DATETIME(dtmjd,/M,/S) ;returns datetime in ISO8601 format - dtisochk=0B ;boolean to check for 60 as seconds value - ;Extract DATA_START/STOP_DATE from the Global Attributes, if present - IF N_ELEMENTS(res) EQ nei THEN BEGIN ;a date/time value is present - IF STRPOS(STRUPCASE(res[1]),'Z') NE -1 THEN BEGIN - IF (STRPOS(res[1],'Z') EQ -1) OR (STRPOS(res[1],'T') EQ -1) THEN BEGIN ;ISO8601 string must be in upper case - IF qa_yes then qtxt=' must be ' ELSE qtxt=' made ' - infotxt='2 '+res[0]+' entry'+qtxt+'uppercase for ISO8601 compliance' - INFOTXT_OUTPUT,infotxt - ENDIF - res[1]=STRUPCASE(res[1]) - mjd2000=JULIAN_DATE(res[1],/I,/M) ;return ISO8601 time in MJD2000 format - IF mjd2000 EQ -99999.d THEN BEGIN ;conversion error - infotxt='3 '+res[0]+errtxt[2]+res[1] - INFOTXT_OUTPUT,infotxt - ;STOP_WITH_ERROR,o3[3]+proname,res[0]+errtxt[2]+res[1],lu,ds & RETURN - ENDIF - dtisochk=STRMID(res[1],13,2) EQ '60' ;existing time in ISO8601 format - ENDIF ELSE BEGIN - mjd2000=DOUBLE(res[1]) - iso=JDF_2_DATETIME(mjd2000,/M,/S) ;return MJD2000 time in ISO8601 format - res[1]=iso - IF qa_yes THEN qtxt=' is not in ' ELSE qtxt= ' converted to ' - infotxt='2 '+meta_arr[ai[0]]+qtxt+'ISO8601 format' - INFOTXT_OUTPUT,infotxt - ENDELSE - res1=res[1] - ENDIF ELSE BEGIN - mjd2000=-99999.0D & res1='-99999.0' - ENDELSE - ;Check that DATA_START_DATE matches the first value under DATETIME.START or DATETIME - ;and DATA_STOP_DATE matches the last value under DATETIME.STOP or DATETIME (if present) - IF (ABS(mjd2000-dtmjd) GT 0.99D/86400.D) OR ((dtisochk) AND (dtiso NE res1)) THEN BEGIN - IF N_ELEMENTS(res) EQ nei THEN BEGIN - IF qa_yes THEN qtxt=' must match' ELSE qtxt=' changed to match' - infotxt='2 Date in DATA_'+dt[i]+qtxt+itxt+etxt+'entry: '+res[1]+' -> '+dtiso - ENDIF ELSE BEGIN - IF qa_yes THEN qtxt='should be '+dtiso ELSE qtxt=dtiso+' added' - infotxt='2 Missing DATA_'+dt[i]+' value '+qtxt+' based on'+itxt+etxt+'entry' - ENDELSE - INFOTXT_OUTPUT, infotxt - res=[res[0],dtiso] ;new ISO time for filename - ENDIF - meta_arr[ai[0]]=res[0]+'='+res[1] ;rewrite (correct) value in meta_arr in ISO8601 format - ENDIF ELSE IF N_ELEMENTS(res) EQ 1 THEN res=[res,'[MISSING]'] - ENDIF - IF i EQ 5 THEN BEGIN - ;check format of FILE_GENERATION_DATE value and create/change if necessary - IF ~qa_yes THEN BEGIN - ;Not doing QA so calculate new FILE_GENERATION_DATE of file creation so it aligns with FILE_META_VERSION (added 20231218) - mjd2000=SYSTIME(/Julian,/UTC)-2451544.5D - mjds=JDF_2_DATETIME(mjd2000,/M,/S) ;returns datetime in ISO8601 format - meta_arr[ai[0]]=res[0]+'='+mjds ;write date in ISO8601 format - infotxt='0 '+res[0]+' updated with current system time' - INFOTXT_OUTPUT,infotxt - IF cres EQ 1 THEN res=[res,mjds] ELSE res[1]=mjds - ENDIF ELSE BEGIN - IF N_ELEMENTS(res) EQ 1 THEN res=[res,'-1'] - IF STRPOS(STRUPCASE(res[1]),'Z') NE -1 THEN BEGIN - IF (STRPOS(res[1],'Z') EQ -1) OR (STRPOS(res[1],'T') EQ -1) THEN BEGIN ;ISO8601 string must be in upper case - IF qa_yes THEN qtxt=' must be ' ELSE qtxt=' made ' - infotxt='2 '+res[0]+' entry'+qtxt+'uppercase for ISO8601 compliance' - INFOTXT_OUTPUT,infotxt - ENDIF - mjd2000=JULIAN_DATE(res[1],/I,/M) ;return time in MJD2000 format - IF mjd2000 EQ -99999.d THEN res=[res[0],'-2'] $ ;conversion error - ELSE IF STRMID(res[1],13,2) EQ '60' THEN res=[res[0],'-3'] $ - ELSE meta_arr[ai[0]]=res[0]+'='+STRTRIM(STRUPCASE(res[1]),2) ;rewrite date in ISO8601 format - ENDIF - IF STRPOS(STRUPCASE(res[1]),'Z') EQ -1 THEN BEGIN - ;FILE_GENERATION_DATE not present or not in ISO8601 format so recalculate - mjd2000=SYSTIME(/Julian,/UTC)-2451544.5D - mjds=JDF_2_DATETIME(mjd2000,/M,/S) ;returns datetime in ISO8601 format - meta_arr[ai[0]]=res[0]+'='+mjds ;write date in ISO8601 format - IF res[1] EQ '-1' THEN itxt='not present' $ - ELSE IF res[1] EQ '-3' THEN itxt='must have seconds value between 0 and 59' $ - ELSE itxt='not in ISO8601 format' - infotxt='2 '+res[0]+' value '+itxt+'|. Recalculated using the system time' - INFOTXT_OUTPUT,infotxt - res[1]=mjds - ENDIF - ENDELSE - ;Test to see if DATA_STOP_DATE is greater than FILE_GENERATION_DATE - IF dtmjd GT mjd2000 THEN BEGIN - infotxt='3 Calculated DATA_STOP_DATE='+dtiso+' is later than '+meta_arr[ai[0]] - INFOTXT_OUTPUT,infotxt - ENDIF - ENDIF - IF i NE 5 THEN BEGIN - ;i.e. don't include file generation date in the filename - IF fhold EQ '' THEN fhold=fhold+STRLOWCASE(res[nei-1]) $ - ELSE fhold=fhold+'_'+STRLOWCASE(res[nei-1]) - ENDIF -ENDFOR -hdffilename=fhold+hdffilename - -;check to see if the resulting filename is the same as that under FILE_NAME -IF hdffilename NE fn_val THEN BEGIN - IF fn_val NE '-1' THEN BEGIN - infotxt=STRARR(3) - infotxt[0]='2 FILE_NAME entry under File Attributes does not match the filename' - infotxt[0]=infotxt[0]+' determined from the Global Attributes' - infotxt[1]=' '+fn_val+' -> '+hdffilename+'|' - infotxt[2]=' Filename determined from Global Attributes used' - ENDIF ELSE BEGIN - IF qa_yes THEN qtxt='should be '+hdffilename ELSE qtxt=hdffilename+' added' - infotxt=STRARR(2) - infotxt[0]='2 Missing FILE_NAME value '+qtxt - infotxt[1]=' based on Global Attribute values' - ENDELSE - INFOTXT_OUTPUT, infotxt - meta_arr[fni[0]]='FILE_NAME='+hdffilename ;change FILE_NAME entry -ENDIF -valid=1 ;Type conversions performed OK - -TypeConversionError: -IF valid EQ 0 THEN BEGIN - infotxt='3 '+errtxt[3]+res[1] - INFOTXT_OUTPUT, infotxt - ;STOP_WITH_ERROR,o3[3]+proname+res[0]+': ',errtxt[3]+res[1],lu & RETURN -ENDIF - -END ;Procedure Find_HDF_Filename - - - -PRO makean, hdf_fn, obj_id, obj_type, label, value -;Procedure to read the contents of a file given as an attribute value and write -;it directly to the HDF Scientific Dataset or to the annotation file description -;section (depending on its size). Currently set up to always write the contents -;of the file to HDF_SD as the text file size limit is also 4096 bytes. -; ---------- -;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org -; -; History: -; 20050802: Original IDLCR8HDF routine - Version 1.0 -; -; Inputs: hdf_fn - The file handle identifier for the HDF file -; obj_id - The Scientific Dataset (SD) identifier of the HDF file or a newly -; created dataset -; obj_type - a string identifying the type of annotation (set to 'FILE') -; label - a string containing the Attribute label from the metadata -; value - a string containing the Attribute value from the metadata -; -; Output: Nil -; -; Called by: AVDC_HDF_WRITE -; -; Subroutines Called: None - -nchar=4096 ;strlen maximum for writing directly to SD (instead of AN) - -;Extract name of file -pos1=STRPOS(value,'"') -pos2=STRPOS(value,'"',/REVERSE_SEARCH) -data_notes_file=STRMID(value,pos1+1,pos2-pos1-1) -OPENR,lu,data_notes_file,/GET_LUN - -;read each line and count number of characters in file -dum='' & text_in_file='' -i=0 -WHILE NOT EOF(lu) DO BEGIN - READF,lu,dum - text_in_file=text_in_file+dum+STRING(10B) ;+STRING(13B) - i=i+1 -ENDWHILE -FREE_LUN,lu -n1=STRLEN(text_in_file) - -;The text-buffer contains n1 characters which will be written either -;to the annotation file description section or directly to the HDF file as -;the value of the current attribute -IF n1 LE nchar THEN HDF_SD_ATTRSET,obj_id,label,text_in_file,n1,/DFNT_CHAR $ -ELSE BEGIN - ;open the HDF output file for AN write operations - ;Initialize the HDF AN interface for the specified file - an_id=HDF_AN_START(hdf_fn) - - IF obj_type EQ 'FILE' THEN BEGIN - ;Get id for the file label annotation - an_labl_id=HDF_AN_CREATEF(an_id,2) - ;Get id for the file description annotation - an_desc_id=HDF_AN_CREATEF(an_id,3) - ENDIF ELSE BEGIN - ;Get id for the data object label annotation - an_labl_id=HDF_AN_CREATE(an_id,DFTAG_NDG,HDF_SD_IDTOREF(obj_id),0) - ;Get id for the data object description annoatation - an_desc_id=HDF_AN_CREATE(an_id,DFTAG_NDG,HDF_SD_IDTOREF(obj_id),1) - ENDELSE - - ;Write the label annotation - status=HDF_AN_WRITEANN(an_labl_id,label) - ;Terminate access to the label annotation - HDF_AN_ENDACCESS,an_labl_id - - ;Write the description annotation - status=HDF_AN_WRITEANN(an_desc_id,text_in_file) - ;Terminate access to the description annotation - HDF_AN_ENDACCESS,an_desc_id - - ;Terminate access to the AN interface - HDF_AN_END,an_id -ENDELSE - -END ;Procedure MakeAN - - - -PRO avdc_hdf5_write, hdffilename, natts, av, geoms_output -;IDL subroutine to write AVDC-type global attributes and datasets to the Root -;Group in an HDF5 file. -;########################################################################## -;Note: In versions of IDL earlier than 6.2, this procedure may not compile -;and the HDF5 write option will not be available. -;########################################################################## -; ---------- -;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org -; -; History: -; 20061012: Introduced to IDLCR8HDF - Version 2.0 -; 20080302: Removed portion of the code which created the DIMENSIONLIST attribute -; containing the object reference pointers to the variables referred to -; in the VAR_DEPEND values - Version 3.0 -; 20150127: For string datasets, change the VAR_FILL_VALUE to an empty string that -; is the same length as the individual strings - Version 4.0b25 -; 20150217: Change rule from 4.0b25 so the VAR_FILL_VALUE is an empty string; For -; string datasets do a STRTRIM of the dataset when writing to the file, as -; the first dataset entry may have had whitespace added so that the -; maximum dataset length is correctly determined - Version 4.0b26 -; 20181221: Allow compression and shuffle options when creating HDF5 files -; - Version 4.0b49 -; -; Inputs: hdffilename - a string holding the name of the HDF file being created -; and written to -; natts - an integer giving the number of Variable Attributes in each -; dataset (i.e. the number of elements in attr_arr_data) -; av - a string array of size (nvn,natts+7) containing the attribute and -; NCSA values for each dataset -; ds - structure containing the data -; meta_arr - a string array containing the Global and Variable Attributes -; vn - a string array containing the VAR_NAME values -; ndm - a long array describing the dimensions of each dataset and the -; VAR_DATA_TYPE -; -; Outputs: An HDF5 file with the name hdffilename -; -; Called by: AVDC_HDF_WRITE -; -; Subroutines Called: None - -COMMON METADATA -COMMON DATA - -;Check for compression setting -comp_fac=0 -IF STRLEN(geoms_output) EQ 4 THEN BEGIN - val_chk=STRMID(geoms_output,3,1) - IF IS_A_NUMBER_HDF(val_chk) THEN comp_fac=FIX(val_chk) -ENDIF - -;Create an HDF5 file -hdf_file_id=H5F_CREATE(hdffilename) -H5F_CLOSE, hdf_file_id - -;open the HDF5 file for writing. -hdf_file_id=H5F_OPEN(hdffilename,/WRITE) -;The H5G_OPEN function opens an existing group within an HDF5 file -sd_id=H5G_OPEN(hdf_file_id,'/') - -;Write the Global Attributes to file -;Section 4.1 (Bojkov et al., 2002): Originator attributes -;Section 4.2 (Bojkov et al., 2002): Dataset attributes -;Section 4.3 (Bojkov et al., 2002): File attributes -FOR i=0,N_ELEMENTS(attr_arr_glob)-1 DO BEGIN - ;separate out the Meta Attribute entry - eqpos=STRPOS(meta_arr[i],'=') - IF eqpos EQ STRLEN(meta_arr[i])-1 THEN attribute_value=' ' $ - ELSE attribute_value=STRMID(meta_arr[i],eqpos+1) - ;get attribute type and space, needed to create the attribute - atype_id=H5T_IDL_CREATE(attribute_value) - aspace_id=H5S_CREATE_SCALAR() - ;create attribute in the output file - aset_id=H5A_CREATE(sd_id,attr_arr_glob[i],atype_id,aspace_id) - ;write attribute to dataset - H5A_WRITE,aset_id,attribute_value - ;close all open identifiers - H5A_CLOSE,aset_id - H5S_CLOSE,aspace_id - H5T_CLOSE,atype_id -ENDFOR - -;Write the Datasets (SDS) to file -;Section 5.1 (Bojkov et al, 2002): Variable description attributes. -al=STRARR(natts) -al[0:natts-1]=attr_arr_data -FOR i=0,nvn-1 DO BEGIN - CASE ndm[i,8] OF ;identifies the VAR_DATA_TYPE - 0:BEGIN - valid_range=[BYTE(mv_lng[0,i]),BYTE(mv_lng[1,i])] - fill_value=BYTE(mv_lng[2,i]) - END - 1:BEGIN - valid_range=[FIX(mv_lng[0,i]),FIX(mv_lng[1,i])] - fill_value=FIX(mv_lng[2,i]) - END - 2:BEGIN - valid_range=[LONG(mv_lng[0,i]),LONG(mv_lng[1,i])] - fill_value=LONG(mv_lng[2,i]) - END - 3:BEGIN - valid_range=[mv_lng[0,i],mv_lng[1,i]] - fill_value=mv_lng[2,i] - END - 4:BEGIN - valid_range=[FLOAT(mv_dbl[0,i]),FLOAT(mv_dbl[1,i])] - fill_value=FLOAT(mv_dbl[2,i]) - END - 5:BEGIN - valid_range=[mv_dbl[0,i],mv_dbl[1,i]] - fill_value=mv_dbl[2,i] - END - 6:BEGIN - valid_range=[' ',' '] - fill_value=' ' ;STRING(format='(A-'+STRTRIM(mv_str[i],2)+')','') ;bytarr(mv_str[i]) - END - ENDCASE - - ;Make up data array for writing to HDF5 file - data=*ds[i].data - ;may have lost array VAR_SIZE information if the last value is '1' e.g. 41,1 - gi=WHERE(ndm[i,0:7] NE 0L) & vs=TRANSPOSE(ndm[i,gi]) ;transpose to get array values in a vector - ;get dataset type and space, needed to create the dataset - dtype_id=H5T_IDL_CREATE(data) - dspace_id=H5S_CREATE_SIMPLE(vs) - ;create dataset in the output file - IF comp_fac EQ 0 THEN dset_id=H5D_CREATE(sd_id,vn[i],dtype_id,dspace_id) $ - ELSE BEGIN - ;Perform compression and shuffling of the dataset - dset_id=H5D_CREATE(sd_id,vn[i],dtype_id,dspace_id,CHUNK_DIMENSIONS=vs,GZIP=comp_fac,/SHUFFLE) - ENDELSE - ;write data to dataset - for string datasets remove unwanted white space - IF ndm[i,8] EQ 6 THEN H5D_WRITE,dset_id,STRTRIM(data,2) ELSE H5D_WRITE,dset_id,data - - ;write out variable attributes - ninc=0 & j=0 - mi=WHERE(meta_arr EQ attr_arr_data[0]+'='+vn[i]) - WHILE j LE natts-1 DO BEGIN - ma=mi[0]+ninc - ;Extract Attribute Label - eqpos=STRPOS(meta_arr[ma],'=') - res=STRMID(meta_arr[ma],0,eqpos) - WHILE res[0] NE al[j] DO j=j+1 ;in case of missing optional variable attributes - CASE 1 OF - ;attr_arr_data[j] EQ 'VAR_SIZE': athold=vs - attr_arr_data[j] EQ 'VAR_VALID_MIN': athold=valid_range[0] - attr_arr_data[j] EQ 'VAR_VALID_MAX': athold=valid_range[1] - attr_arr_data[j] EQ 'VAR_FILL_VALUE': athold=fill_value - ELSE: athold=av[i,j] - ENDCASE - ;get attribute type and space, needed to create the attribute - atype_id=H5T_IDL_CREATE(athold) - IF N_ELEMENTS(athold) EQ 1 THEN aspace_id=H5S_CREATE_SCALAR() $ - ELSE aspace_id=H5S_CREATE_SIMPLE(N_ELEMENTS(athold)) - ;create attribute in the output file - aset_id=H5A_CREATE(dset_id,al[j],atype_id,aspace_id) - ;write attribute to dataset - H5A_WRITE,aset_id,athold - ;close all open attribute identifiers - H5A_CLOSE,aset_id - H5S_CLOSE,aspace_id - H5T_CLOSE,atype_id - ninc=ninc+1 & j=j+1 - ENDWHILE - ;close all open dataset identifiers - H5D_CLOSE,dset_id - H5S_CLOSE,dspace_id - H5T_CLOSE,dtype_id -ENDFOR - -;The H5G_CLOSE procedure closes the specified group and releases resources used by it. -H5G_CLOSE,sd_id -;The H5F_CLOSE procedure closes the HDF file associated with the given file handle. -H5F_CLOSE,hdf_file_id - -END ;Procedure AVDC_HDF5_Write - - - -PRO avdc_nc_write, hdffilename, natts, av, var_depend, var_size -;IDL subroutine to write AVDC-type global attributes and datasets to a netCDF file. -; ---------- -;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org -; -; History: -; 20111120: Introduced to IDLCR8HDF - Version 4.0 -; 20130116: Account for string datasets which contain only empty string values (make -; string length = 1) - Version 4.0b15 -; 20150127: For string datasets, change the VAR_FILL_VALUE to an empty string that -; is the same length as the individual strings - Version 4.0b25 -; 20150217: Change rule from 4.0b25 so the VAR_FILL_VALUE is an empty string -; - Version 4.0b26 -; 20161230: Assign a dimension ID to STRING datasets with VAR_DEPEND=CONSTANT so that -; the string length is correctly accounted for (previously only wrote the -; first character of the string) - Version 4.0b41 -; -; Inputs: hdffilename - a string holding the name of the netCDF file being created -; and written to -; natts - an integer giving the number of Variable Attributes in each -; dataset (i.e. the number of elements in attr_arr_data) -; av - a string array of size (nvn,natts+7) containing the attribute and -; NCSA values for each dataset -; var_depend - a string array of size (nvn) holding the VAR_DEPEND attribute -; values, used to assign DIM information -; var_size - a string array of size (nvn) holding the VAR_SIZE attribute -; values, used to assign DIM information -; ds - structure containing the data -; meta_arr - a string array containing the Global and Variable Attributes -; vn - a string array containing the VAR_NAME values -; ndm - a long array describing the dimensions of each dataset and the -; VAR_DATA_TYPE -; -; Outputs: A netCDF file with the name hdffilename -; -; Called by: AVDC_HDF_WRITE -; -; Subroutines Called: None - -COMMON METADATA -COMMON DATA - -;Create a netCDF file -df_file_id=NCDF_CREATE(hdffilename,/CLOBBER) - -;Write the Global Attributes to file -;Section 4.1 (Bojkov et al., 2002): Originator attributes -;Section 4.2 (Bojkov et al., 2002): Dataset attributes -;Section 4.3 (Bojkov et al., 2002): File attributes -FOR i=0,N_ELEMENTS(attr_arr_glob)-1 DO BEGIN - ;separate out the Meta Attribute entry - eqpos=STRPOS(meta_arr[i],'=') - IF eqpos EQ STRLEN(meta_arr[i])-1 THEN attribute_value=' ' $ - ELSE attribute_value=STRMID(meta_arr[i],eqpos+1) - ;get attribute type and space, needed to create the attribute - NCDF_ATTPUT,df_file_id,/GLOBAL,attr_arr_glob[i],attribute_value -ENDFOR - -;Write the Datasets (SDS) to file -;Section 5.1 (Bojkov et al, 2002): Variable description attributes. -al=STRARR(natts) -al[0:natts-1]=attr_arr_data -vdh=['CONSTANT','INDEPENDENT'] -dim_id_list=LONARR(2)-999L -constfound=0 - -FOR i=0,nvn-1 DO BEGIN - - ;Make up data array for writing to netCDF file - data=*ds[i].data - ;may have lost array VAR_SIZE information if the last value is '1' e.g. 41,1 - gi=WHERE(ndm[i,0:7] NE 0L) & vs=TRANSPOSE(ndm[i,gi]) ;transpose to get array values in a vector - - ;Determine Dimension IDs for the Variable - vdl=STRSPLIT(var_depend[i],' ;',/EXTRACT,COUNT=vdcount) - vsl=STRSPLIT(var_size[i],' ;',/EXTRACT) & vsl=LONG(vsl) - IF ndm[i,8] EQ 6 THEN BEGIN ;need to include string length dimension variable - vdcount++ - IF MAX(STRLEN(data)) EQ 0 THEN mxstrlen=1 ELSE mxstrlen=MAX(STRLEN(data)) - vdl=['STRLEN',vdl] & vsl=[mxstrlen,vsl] - ENDIF - dims=LONARR(vdcount)-999L & const=0 - FOR j=0,vdcount-1 DO BEGIN - ndi=WHERE(STRUPCASE(vdl[j]) EQ vdh,ndc) - IF vdl[j] EQ 'STRLEN' THEN BEGIN - ;dataset is of type string so add string length dimension variable to vdh array - dim_id=NCDF_DIMDEF(df_file_id,vn[i]+'_STRLEN',vsl[j]) - vdh=[vdh,STRUPCASE(vn[i])+'_STRLEN'] ;add dimension variable to vdh array - dim_id_list=[dim_id_list,dim_id] - dims[j]=dim_id - ENDIF ELSE IF ndc EQ 0 THEN BEGIN ;new dimension found for file - vdh=[vdh,STRUPCASE(vdl[j])] ;add dimension variable to vdh array - dim_id=NCDF_DIMDEF(df_file_id,vdl[j],vsl[j]) - dim_id_list=[dim_id_list,dim_id] - dims[j]=dim_id - ENDIF ELSE BEGIN ;dimension already has ID or is CONSTANT or INDEPENDENT - IF ndi[0] EQ 0 THEN BEGIN - ;CONSTANT so no dimension is assigned - IF constfound EQ 0 THEN BEGIN - dim_id=NCDF_DIMDEF(df_file_id,'CONSTANT',1) - dim_id_list[0]=dim_id ;assign a dim_id for CONSTANT - needed for STRING datasets - constfound=1 - ENDIF - const=1 - dims[j]=dim_id_list[0] - ENDIF ELSE IF ndi[0] EQ 1 THEN BEGIN - ;INDEPENDENT or BOUNDARIES so assign INDEPENDENT/BOUNDARIES_%vs as dimension name - IF (j EQ 1) AND (STRPOS(vn[i],'.BOUNDARIES') NE -1) THEN itxt='BOUNDARIES_' $ - ELSE itxt='INDEPENDENT_' - dim_id=NCDF_DIMDEF(df_file_id,itxt+STRTRIM(vsl[j],2),vsl[j]) - vdh=[vdh,itxt+STRTRIM(vsl[j],2)] ;add dimension variable to vdh array - dim_id_list=[dim_id_list,dim_id] - dims[j]=dim_id - ENDIF ELSE dims[j]=dim_id_list[ndi[0]] - ENDELSE - ENDFOR - - ;If multi-dimensional reverse dimension IDs as the dataset is automatically transposed - IF N_ELEMENTS(vs) GT 1 THEN dims=REVERSE(dims) - - CASE ndm[i,8] OF ;identifies the VAR_DATA_TYPE - 0:BEGIN - valid_range=[BYTE(mv_lng[0,i]),BYTE(mv_lng[1,i])] - fill_value=BYTE(mv_lng[2,i]) - IF const THEN var_id=NCDF_VARDEF(df_file_id,vn[i],/BYTE) $ - ELSE var_id=NCDF_VARDEF(df_file_id,vn[i],dims,/BYTE) - END - 1:BEGIN - valid_range=[FIX(mv_lng[0,i]),FIX(mv_lng[1,i])] - fill_value=FIX(mv_lng[2,i]) - IF const THEN var_id=NCDF_VARDEF(df_file_id,vn[i],/SHORT) $ - ELSE var_id=NCDF_VARDEF(df_file_id,vn[i],dims,/SHORT) - END - 2:BEGIN - valid_range=[LONG(mv_lng[0,i]),LONG(mv_lng[1,i])] - fill_value=LONG(mv_lng[2,i]) - IF const THEN var_id=NCDF_VARDEF(df_file_id,vn[i],/LONG) $ - ELSE var_id=NCDF_VARDEF(df_file_id,vn[i],dims,/LONG) - END - 3:BEGIN - valid_range=[mv_lng[0,i],mv_lng[1,i]] - fill_value=mv_lng[2,i] - IF const THEN var_id=NCDF_VARDEF(df_file_id,vn[i],/LONG) $ - ELSE var_id=NCDF_VARDEF(df_file_id,vn[i],dims,/LONG) - END - 4:BEGIN - valid_range=[FLOAT(mv_dbl[0,i]),FLOAT(mv_dbl[1,i])] - fill_value=FLOAT(mv_dbl[2,i]) - IF const THEN var_id=NCDF_VARDEF(df_file_id,vn[i],/FLOAT) $ - ELSE var_id=NCDF_VARDEF(df_file_id,vn[i],dims,/FLOAT) - END - 5:BEGIN - valid_range=[mv_dbl[0,i],mv_dbl[1,i]] - fill_value=mv_dbl[2,i] - IF const THEN var_id=NCDF_VARDEF(df_file_id,vn[i],/DOUBLE) $ - ELSE var_id=NCDF_VARDEF(df_file_id,vn[i],dims,/DOUBLE) - END - 6:BEGIN - valid_range=[' ',' '] - fill_value=' ' ;bytarr(mv_str[i]) ;STRING(format='(A-'+STRTRIM(mv_str[i],2)+')','') - var_id=NCDF_VARDEF(df_file_id,vn[i],dims,/CHAR) - END - ENDCASE - - ;write out variable attributes - ninc=0 & j=0 - mi=WHERE(meta_arr EQ attr_arr_data[0]+'='+vn[i]) - WHILE j LE natts-1 DO BEGIN - ma=mi[0]+ninc - ;Extract Attribute Label - eqpos=STRPOS(meta_arr[ma],'=') - res=STRMID(meta_arr[ma],0,eqpos) - WHILE res[0] NE al[j] DO j=j+1 ;in case of missing optional variable attributes - CASE 1 OF - attr_arr_data[j] EQ 'VAR_VALID_MIN': athold=valid_range[0] - attr_arr_data[j] EQ 'VAR_VALID_MAX': athold=valid_range[1] - attr_arr_data[j] EQ 'VAR_FILL_VALUE': athold=fill_value - ELSE: athold=av[i,j] - ENDCASE - NCDF_ATTPUT,df_file_id,var_id,attr_arr_data[j],athold - ninc=ninc+1 & j=j+1 - ENDWHILE - - IF ndm[i,8] NE 6 THEN BEGIN - NCDF_ATTPUT,df_file_id,var_id,'units',vu[i] - NCDF_ATTPUT,df_file_id,var_id,'valid_range',valid_range - NCDF_ATTPUT,df_file_id,var_id,'_Fillvalue',fill_value - ENDIF - - NCDF_CONTROL,df_file_id,/ENDEF ;Enter Data Mode - NCDF_VARPUT,df_file_id,var_id,data ;Write Data to file - NCDF_CONTROL,df_file_id,/REDEF ;Enter Define Mode -ENDFOR - -NCDF_CLOSE,df_file_id - -END ;Procedure AVDC_NC_Write - - - -PRO avdc_hdf_write, hdffilename, geoms_output -;IDL subroutine to write AVDC-type global attributes and datasets to a Scientific -;Dataset (SDS) in HDF4 (Bojkov et al.,2004) or HDF5. This routine creates the HDF4 -;file if requested, and calls the AVDC_HDF5_WRITE routine to create the HDF5 file if -;that option is chosen. -; ---------- -;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org -; -; History: -; 20050802: Originally split into two routines to separately write the Global -; attributes (AVDC_HDF_GLOBAL_ATTRIBUTES) and the Variable attributes -; and data (AVDC_HDF_SDS_VARIABLES) to an HDF4 file - Version 1.0 -; 20061012: Two routines combined into the AVDC_HDF_WRITE routine; Call to the -; AVDC_HDF5_WRITE routine if HDF5 file option is chosen; HDF4 file not -; closed between writing the global and variable attributes to file; -; Data now written to file from the ds structure instead of from arrays -; made according to VAR_DATA_TYPE; the av array made to hold attribute -; values to write to the HDF file, instead of writing the values to -; file directly from the meta_arr array (to avoid duplication of code -; in AVDC_HDF5_WRITE) - Version 2.0 -; 20130116: Account for string datasets which contain only empty string values (make -; string length = 1) - Version 4.0b15 -; 20150127: For string datasets, change the VAR_FILL_VALUE to an empty string that -; is the same length as the individual strings - Version 4.0b25 -; 20150217: Change rule from 4.0b25 so the VAR_FILL_VALUE is an empty string -; - Version 4.0b26 -; -; Inputs: hdffilename - a string holding the name of the HDF file being created -; and written to -; ds - structure containing the data -; meta_arr - a string array containing the Global and Variable Attributes -; vn - a string array containing the VAR_NAME values -; ndm - a long array describing the dimensions of each dataset and the -; VAR_DATA_TYPE -; -; Outputs: An HDF4 file with the name hdffilename -; -; Called by: IDLCR8HDF -; -; Subroutines Called: MAKEAN -; AVDC_HDF5_WRITE (if HDF5 option is chosen) -; INFOTXT_OUTPUT -; Information Conditions (when the program is able to make changes): -; 1. HDF4.2R1 or earlier library and SDS name has greater than 63-characters -; - in which case the SDS name will be truncated [3573] - -COMMON METADATA -COMMON DATA - -nodimset=1 ;added from version 3.06 to stop dimension information being written to the HDF4 file - ;while still retaining original code - -;Determine HDF format (ftype is either 'hdf', 'h5' or 'nc') -ftype=STRMID(hdffilename,STRPOS(hdffilename,'.',/Reverse_Search)+1) - -;List of dim. attributes to be assigned to the SDS dimensions. -da_list=['VAR_NAME','VAR_UNITS'] -da_att=INDGEN(N_ELEMENTS(da_list),/String) & natts=N_ELEMENTS(attr_arr_data) -av=STRARR(nvn,natts) ;array containing attribute and NCSA values for each dataset -var_depend=STRARR(nvn) ;holding array for the VAR_DEPEND attributes, used to assign DIM information -var_size=STRARR(nvn) ;holding array for the VAR_SIZE attributes, used to assign netCDF DIM information - -;Write all Dataset Attribute values to an array -FOR i=0,nvn-1 DO BEGIN ;nvn EQ number of variable names - mi=WHERE(meta_arr EQ attr_arr_data[0]+'='+vn[i]) - ninc=0 & j=0 - WHILE j LE natts-1 DO BEGIN - ma=mi[0]+ninc - ;separate out the Meta entry into two components - eqpos=STRPOS(meta_arr[ma],'=') - res=STRMID(meta_arr[ma],0,eqpos) - WHILE res NE attr_arr_data[j] DO j=j+1 ;in case optional variable attributes are not included - IF eqpos EQ STRLEN(meta_arr[ma])-1 THEN av[i,j]=' ' $ - ELSE av[i,j]=STRMID(meta_arr[ma],eqpos+1) - ;Reverse Values for VAR_DEPEND and VAR_SIZE to match order values will be saved in HDF - IF (res EQ 'VAR_DEPEND') OR (res EQ 'VAR_SIZE') THEN BEGIN - atval=STRSPLIT(av[i,j],' ;',/EXTRACT,COUNT=avcnt) - IF avcnt GT 1 THEN BEGIN - atval=REVERSE(atval) - FOR k=0,avcnt-1 DO IF k EQ 0 THEN ahold=atval[k] ELSE ahold=ahold+';'+atval[k] - av[i,j]=ahold - ENDIF - IF res EQ 'VAR_DEPEND' THEN var_depend[i]=av[i,j] ELSE var_size[i]=av[i,j] - ENDIF - ninc=ninc+1 & j=j+1 - ENDWHILE -ENDFOR - -IF ftype EQ 'hdf' THEN BEGIN - - ;Determine HDF library used by this program - HDF_LIB_INFO,MAJOR=mj,MINOR=mn,RELEASE=rl - hdf4lib=(mj*100)+(mn*10)+rl - hdf4txt='HDF'+STRTRIM(mj,2)+'.'+STRTRIM(mn,2)+'R'+STRTRIM(rl,2) - - ;Create and open the HDF4 file for writing - hdf_file_id=HDF_OPEN(hdffilename,/ALL) - ;The HDF_SD_START function opens or creates an HDF4 file and initializes the SD interface. - sd_id=HDF_SD_START(hdffilename,/RDWR) - - ;Write the Global Attributes to file - ;Section 4.1 (Bojkov et al., 2002): Originator attributes - ;Section 4.2 (Bojkov et al., 2002): Dataset attributes - ;Section 4.3 (Bojkov et al., 2002): File attributes - FOR i=0,N_ELEMENTS(attr_arr_glob)-1 DO BEGIN - ;separate out the Meta Attribute entry - eqpos=STRPOS(meta_arr[i],'=') - IF eqpos EQ STRLEN(meta_arr[i])-1 THEN attribute_value=' ' $ - ELSE attribute_value=STRMID(meta_arr[i],eqpos+1) - ;check for Free text attribute, and filename entry - res=STRMID(meta_arr[i],0,eqpos) - fai=WHERE(res EQ attr_free,facnt) - IF (facnt NE 0) AND (STRMID(STRUPCASE(attribute_value),0,6) EQ 'FILE("') THEN $ - MAKEAN,hdf_file_id,sd_id,'FILE',res,attribute_value $ - ELSE HDF_SD_ATTRSET,sd_id,attr_arr_glob[i],attribute_value - ENDFOR - - ;Write the Datasets (SDS) to file - ;Section 5.1 (Bojkov et al, 2002): Variable description attributes. - FOR i=0,nvn-1 DO BEGIN ;nvn EQ number of variable names - - ;If hdf4 library version is less than 4.2R2 then check length of VAR_NAME - IF hdf4lib LT 422 THEN BEGIN - IF STRLEN(vn[i]) GT 63 THEN BEGIN - infotxt=STRARR(4) - infotxt[0]='2 '+hdf4txt+' truncates the dataset name if its' - infotxt[0]=infotxt[0]+' length is greater than 63-characters.' - infotxt[1]=' If possible update the HDF4 library to HDF4.2R2 or newer| ' - infotxt[1]=infotxt[1]+'(refer to program documentation for procedure to do this). - infotxt[2]=' The Archive Data Center may also be set up to update the file' - infotxt[2]=infotxt[2]+' on submission.' - infotxt[3]=' VAR_NAME='+vn[i] - INFOTXT_OUTPUT, infotxt - ENDIF - ENDIF - - ;create data array from structure and create and define dataset for an HDF4 file - data=*ds[i].data - ;may have lost array VAR_SIZE information if the last value is '1' e.g. 41,1 - gi=WHERE(ndm[i,0:7] NE 0L) & vs=TRANSPOSE(ndm[i,gi]) - type=HDF_IDL2HDFTYPE(SIZE(data,/Type)) - IF ndm[i,8] EQ 6 THEN BEGIN - mxstrlen=MAX(STRLEN(data)) ;determine maximum stength length - IF mxstrlen EQ 0 THEN mxstrlen=1 - sds_id_1=HDF_SD_CREATE(sd_id,vn[i],[mxstrlen,vs],HDF_TYPE=type) - ENDIF ELSE sds_id_1=HDF_SD_CREATE(sd_id,vn[i],vs,HDF_TYPE=type) - - mi=WHERE(meta_arr EQ attr_arr_data[0]+'='+vn[i]) - CASE ndm[i,8] OF ;identifies the VAR_DATA_TYPE - 0:BEGIN - valid_range=[BYTE(mv_lng[0,i]),BYTE(mv_lng[1,i])] - fill_value=BYTE(mv_lng[2,i]) - END - 1:BEGIN - valid_range=[FIX(mv_lng[0,i]),FIX(mv_lng[1,i])] - fill_value=FIX(mv_lng[2,i]) - END - 2:BEGIN - valid_range=[LONG(mv_lng[0,i]),LONG(mv_lng[1,i])] - fill_value=LONG(mv_lng[2,i]) - END - 3:BEGIN - valid_range=[mv_lng[0,i],mv_lng[1,i]] - fill_value=mv_lng[2,i] - END - 4:BEGIN - valid_range=[FLOAT(mv_dbl[0,i]),FLOAT(mv_dbl[1,i])] - fill_value=FLOAT(mv_dbl[2,i]) - END - 5:BEGIN - valid_range=[mv_dbl[0,i],mv_dbl[1,i]] - fill_value=mv_dbl[2,i] - END - 6:BEGIN - valid_range=[' ',' '] - fill_value=' ' ;bytarr(mv_str[i]) ;STRING(format='(A-'+STRTRIM(mv_str[i],2)+')','') - ;Note: this seems to give the same result as using 0B and saving it as type string in the HDF_SD_ATTRSET call - END - ENDCASE - ninc=0 & j=0 - WHILE j LE natts-1 DO BEGIN - ma=mi[0]+ninc - ;Extract Attribute Label - eqpos=STRPOS(meta_arr[ma],'=') - res=STRMID(meta_arr[ma],0,eqpos) - WHILE res[0] NE attr_arr_data[j] DO j=j+1 - CASE 1 OF - ;attr_arr_data[j] EQ 'VAR_SIZE': HDF_SD_ATTRSET,sds_id_1,attr_arr_data[j],vs - attr_arr_data[j] EQ 'VAR_VALID_MIN': HDF_SD_ATTRSET,sds_id_1,attr_arr_data[j],valid_range[0] - attr_arr_data[j] EQ 'VAR_VALID_MAX': HDF_SD_ATTRSET,sds_id_1,attr_arr_data[j],valid_range[1] - attr_arr_data[j] EQ 'VAR_FILL_VALUE': HDF_SD_ATTRSET,sds_id_1,attr_arr_data[j],fill_value - ELSE: HDF_SD_ATTRSET,sds_id_1,attr_arr_data[j],av[i,j] - ENDCASE - ninc=ninc+1 & j=j+1 - ENDWHILE - ;Set information about the dataset (note no pre-defined attributes for String Datatype) - IF ndm[i,8] NE 6 THEN $ - HDF_SD_SETINFO,sds_id_1,UNIT=vu[i],RANGE=valid_range,FILL=fill_value - - IF nodimset EQ 0 THEN BEGIN - ;Assign the VAR_DEPEND dimension(s) attribute information - vd_out=STRSPLIT(var_depend[i],' ;',/Extract) ;Read the variable dependencies - vdim=N_ELEMENTS(vd_out) - vd_out=STRTRIM(vd_out,2) - ;Start search of information - FOR j=0,vdim-1 DO BEGIN - IF (STRUPCASE(vd_out[j]) NE 'CONSTANT') AND (STRUPCASE(vd_out[j]) NE 'INDEPENDENT') THEN BEGIN - ;Return the index of the var_depend array SDS dataset - index=HDF_SD_NAMETOINDEX(sd_id,vd_out[j]) - ;Access the dataset - sds_id_tmp=HDF_SD_SELECT(sd_id,index) - FOR k=0,N_ELEMENTS(da_list)-1 DO BEGIN - ;Find the dataset attribute in da_list - dindex=HDF_SD_ATTRFIND(sds_id_tmp,da_list[k]) - ;Read attribute info and assign to da_att - HDF_SD_ATTRINFO,sds_id_tmp,dindex,DATA=att_data - da_att[k]=att_data - ENDFOR - ;End access of SDS search - HDF_SD_ENDACCESS,sds_id_tmp - - ;Assign dimension to the current SDS - dim_id=HDF_SD_DIMGETID(sds_id_1,j) - HDF_SD_DIMSET,dim_id,/BW_INCOMP,NAME=da_att[0],UNIT=da_att[1] - ENDIF - ENDFOR - ENDIF - - ;Write data to HDF4 file - HDF_SD_ADDDATA,sds_id_1,data - HDF_SD_ENDACCESS,sds_id_1 - ENDFOR - - ;The HDF_SD_END function closes the SD interface to an HDF4 file. - HDF_SD_END,sd_id - ;The HDF_CLOSE procedure closes the HDF4 file associated with the given file handle. - HDF_CLOSE,hdf_file_id -ENDIF ELSE IF ftype EQ 'h5' THEN AVDC_HDF5_WRITE,hdffilename,natts,av,geoms_output $ -ELSE AVDC_NC_WRITE,hdffilename,natts,av,var_depend,var_size - -END ;Procedure AVDC_HDF_Write - - - -PRO idlcr8hdf, ga, sds, tav, odir, reterr, H5=o1, AVK=o2, LOG=o4, POPUP=o5, QA=o6, NOHDF=o7, NC=o8, DATETIME=o9, $ - C1=o11, C2=o12, C3=o13, C4=o14, C5=o15, C6=o16, C7=o17, C8=o18, C9=o19 -;Main IDL program to create HDF4 or HDF5 format files for submission to the AVDC, NDACC, or -;NILU (ESA Envisat) databases. -; -;Program documentation, idlcr8hdf-v4.0_Readme.pdf, available from http://avdc.gsfc.nasa.gov. -; -;Program sub-version 4.0b64 (20240912) -; ---------- -;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org -; -; History: -; 20050802: Original Release - Version 1.0 -; 20050909: No change to routine - Version 1.1 -; 20051107: No change to routine - Version 1.11 -; 20061012: Make the code suitable for running on a licensed version of IDL (using the .pro -; and .sav versions of the code) and on IDL Virtual Machine (using the .sav version -; of the code); Change command line keyword options, remove the /Help (window now -; opened if there are no command line parameters) and /File options (now automatically -; identifies whether input is from session memory or files), and add the /H5 (write -; HDF5), /AVK (for Averaging Kernel Datasets add sentence to VAR_NOTES indicating -; averaging kernel order), /Log (append input/output information to a log file), and -; /Popup options (append input/output, error and warning information to a pop-up -; display window); If input is from files, the program can now handle multiple data -; files (either as a string array or as a file spec), together with a Metadata template -; file and the TAV file; Input/output information as well as errors and warnings -; included in the IDL DE output log window and/or to a log file; The user has the -; option of continuing to run the program with file inputs from the 'Introduction' -; window; Common variable definition WIDGET_WIN added - Version 2.0 -; 20080302: Remove HELP,/TRACEBACK call, which identified how the program was called. This was used -; to stop output to the IDLDE output window in the event that IDL VM was used, but it is -; not required as IDL VM ignores these print calls; Add DIALOG_BOX to show completion -; of the program if program inputs are via the INTRO box, and logging window option -; isn't requested - Version 3.0 -; 20100205: Add optional reterr parameter which allows program to return to a calling program with -; an error message, rather than stop - Version 3.09 -; 20101120: Adopt GEOMS metadata standard; New structure format required when input is by session -; memory; New arrays created to hold numeric metadata values (mv_lng and mv_dbl) in -; either LONG64 or DOUBLE format (changed to their actual format before writing to file); -; Add QA keyword, to perform QA function on HDF file read into session memory (does not -; create an HDF file) - Version 4.0 -; 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 and /NC keywords in DEMO mode - Version 4.0b14 -; 20140327: Add check and log message for VAR_SIZE values not being of type string - Version 4.0b20 -; 20150127: When input is by structure, stop removing leading and trailing spaces from variable -; attribute values when creating metafile array - Version 4.0b25 -; 20150217: Initialize mv_str if input is via files - Version 4.0b26 -; 20150302: Fix Bug which incorrectly set mv_str value if input is via structure - Version 4.0b27 -; 20151012: Create qa_yes common variable to streamline checks on whether program called in QA mode -; or not - Version 4.0b31 -; 20160725: Fix bug so that numeric metadata sub-values read in from a structure will be written to -; the metafile array OK - Version 4.0b38 -; 20161130: Fix issue when a metadata value is not numeric or a string e.g. a structure when the -; inputs are in the form of a structure - Version 4.0b40 -; 20180218: Ensure variable attribute value (vav) is only a single variable when calling -; PRE_DEFINED_ATT_CHECKS - Version 4.0b45 -; 20181221: Allow compression and shuffle options when creating HDF5 files - Version 4.0b49 -; -; Inputs: DIALOG PROMPTS WITH IDL VIRTUAL MACHINE (File input only) -; Metadata template file - The program will fill in missing spaces based on data file and -; TAV inputs -; Data file(s) - the user has the option of picking multiple data files -; tav - the current Table Attribute Values (TAV) file -; outdir - the directory which will hold the HDF file -; -; COMMAND LINE PARAMETER OPTION AVAILABLE WITH FULL IDL VERSION -; ga - either a string array containing the Global Attributes, or a Metadata template file -; (as above) -; sds - either a heap structure using pointers containing the Data (DS.Data) and the Variable -; Attributes (DS.VA_L and DS.VA_V) for a single file, or a string array or file spec of -; file(s) containing the data (as above) -; tav - the current Table Attribute Values (TAV) file -; outdir - the directory to which any HDF and log files will be written -; 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. -; -; OPTIONS -; H5 - output will be as an HDF5 file instead of the standard HDF4 -; NC - output will be as a netCDF3 file instead of the standard HDF4 -; AVK - to add a sentence to VAR_NOTES indicating array order for Averaging Kernel -; datasets -; Log - to append input/output information as well as warnings and errors to a log file -; named idlcr8hdf.log, or idlcr8qa.log if QA option chosen -; Popup - to append input/output information as well as warnings and errors to a pop-up -; display window -; QA - to perform QA function on an HDF file passed to the program using the session -; memory option. The logfile is automatically generated and will be called -; idlcr8qa.log instead of idlcr8hdf.log -; NoHDF - an HDF file will not be created, but the logfile idlcr8hdf.log will automatically -; be generated -; Cn - to enable compression and shuffling of HDF5 files only (ignored for netCDF3 and HDF4) -; -; Output: An HDF file formatted to GEOMS standards. Keyword options can also result -; in an output log file (idlcr8hdf.log), or a pop-up box showing log output. If the -; reterr string input parameter is included then reterr will return an error message to the -; calling program, if encountered. -; -; Called by: Main Routine -; -; Subroutines Called: INTRO (if program called without command line parameters) -; READ_TABLEFILE -; READ_METADATA -; CHECK_METADATA -; SET_UP_STRUCTURE -; READ_DATA -; FIND_HDF_FILENAME -; AVDC_HDF_WRITE -; STOP_WITH_ERROR (if error state detected) -; INFOTXT_OUTPUT (if information conditions detected) -; Possible Conditions for STOP_WITH_ERROR call: -; 1. If Metadata, Data, or Table Attribute Values file(s), or Output Directory -; selection is not valid -; 2. If input is via session memory and the array holding the global attributes -; is not of type string, or is not one dimensional -; 3. If input is via session memory and the input heap structure is not valid -; 4. If input is via session memory and the array sizes of the heap structures -; do not match -; 5. If the H5 option is chosen and the IDL version does not support HDF5 write -; routines (less than v6.2) -; 6. If the NC option is chosen and IDL was called in IDL DEMO mode -; -; Information Conditions (when the program is able to make changes): -; 1. /POPUP keyword cannot be used together with the 'reterr' argument -; 2. Argument 'reterr' must be a scalar variable of type string -; 3. /LOG keyword cannot be used in IDL DEMO mode - -COMMON TABLEDATA -COMMON METADATA -COMMON DATA -COMMON WIDGET_WIN - -;Possible error message for this procedure -proname='IDLcr8HDF procedure: ' & lu=-1L -errtxt=STRARR(6) -errtxt[0]=' selection not valid' -errtxt[1]='Global Attributes Array is not of type string, or is not one dimensional' -errtxt[2]='structure is not valid (' -errtxt[3]='Array sizes of the heap structure do not match: ' -errtxt[4]=' does not support HDF5 Write Routines' -errtxt[5]='IDLcr8HDF requires session memory input to perform the QA function' - -demomode=LMGR(/DEMO) ;Boolean to check for IDL being run in demo mode (disable /LOG option) -IF N_PARAMS() GE 2 THEN intype=SIZE(sds,/TYPE) $ ;Is SDS a structure (8) or a string (7)? -ELSE IF demomode THEN intype=-3 $ -ELSE intype=-1 -IF (intype NE 7) AND (intype NE 8) AND (intype NE -1) AND (intype NE -3) THEN intype=-2 -;Command line parameter neither string nor structure -dux=[-1,-1,1] ;initialize flags for output log to IDLDE Output Window and/or to file -;dux[0] default allows output to the IDLDE output window (ignored by IDL VM) -;dux[1] is the program assigned file unit for sending log output to file -;dux[2] is the step between dux[0] and dux[1] -lineno=0L ;count variable to put curser at end of text pop-up window -rerr=['NA','NA'] ;initialize return error string - -IF intype LT 0 THEN BEGIN ;either no input parameters or invalid second parameter - o3=['0','0','0','0','0','0'] - lvals=['None','1','2','3','4','5','6','7','8','9'] ;compression level for HDF5 option - INTRO,intype ;Open Intro Box and determine HDF output format (HDF4 or HDF5) - IF o3[0] EQ '0' THEN BEGIN - STOP_WITH_ERROR,'','',lu & RETURN - ENDIF - IF o3[3] EQ 'Pop' THEN o3[3]='' ELSE o3[3]='D_' -ENDIF ELSE BEGIN ;Set options (HDF4, HDF5, netCDF, AVK, LOGFILE, POP-UP, QA, NOHDF) - IF KEYWORD_SET(o1) THEN BEGIN - o3=['H5','0','0','D_','0','0'] - CASE 1 OF - KEYWORD_SET(o11): o3[0]='H5_1' - KEYWORD_SET(o12): o3[0]='H5_2' - KEYWORD_SET(o13): o3[0]='H5_3' - KEYWORD_SET(o14): o3[0]='H5_4' - KEYWORD_SET(o15): o3[0]='H5_5' - KEYWORD_SET(o16): o3[0]='H5_6' - KEYWORD_SET(o17): o3[0]='H5_7' - KEYWORD_SET(o18): o3[0]='H5_8' - KEYWORD_SET(o19): o3[0]='H5_9' - ELSE: - ENDCASE - ;PRINT,o3 - ENDIF ELSE IF KEYWORD_SET(o8) THEN o3=['NC','0','0','D_','0','0'] $ - ELSE o3=['H4','0','0','D_','0','0'] - IF KEYWORD_SET(o2) THEN o3[1]='AVK' ;AVK option - IF KEYWORD_SET(o4) THEN o3[2]='idlcr8hdf.log' ;Log option - IF KEYWORD_SET(o5) THEN o3[3]='' ;POP-UP option - IF KEYWORD_SET(o6) THEN o3[2]='idlcr8qa.log' ;QA option - IF KEYWORD_SET(o7) THEN o3[4]='NOHDF' ;NoHDF option - IF KEYWORD_SET(o9) THEN o3[5]='DTFVOK' ;DATETIME Fill Value OK -ENDELSE -IF o3[0] EQ 'NC' THEN dftxt='netCDF' ELSE dftxt='HDF' - -;If reterr included allows error output to return to a calling program -n_it=5 -infotxt=STRARR(2,n_it) -IF N_PARAMS() GE 5 THEN BEGIN - ;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_' - infotxt[0,0]='0 /POPUP keyword cannot be used together with the ''reterr'' argument.' - infotxt[1,0]=' The request for a POPUP window has been ignored.' - ENDIF - ENDIF ELSE BEGIN - ;reterr input included but is of the incorrect type, or parameter cannot be returned - ;to the calling program - infotxt[0,1]='0 Argument ''reterr'' must be of type string.' ;a returnable variable of type string.' - infotxt[1,1]=' idlcr8hdf will stop normally if an error is encountered.' - ENDELSE -ENDIF - -;Do check for /LOG and /NC keywords in IDL DEMO Mode and if necessary disable or stop the program -IF demomode THEN BEGIN - IF o3[2] EQ 'idlcr8hdf.log' THEN BEGIN - o3[2]='0' - infotxt[0,2]='0 /LOG keyword cannot be used in IDL DEMO mode and has been ignored.' - ENDIF - IF o3[0] EQ 'NC' THEN BEGIN - o3[4]='NOHDF' - infotxt[0,3]='3 NetCDF file create feature is disabled in IDL DEMO mode. ' - infotxt[1,3]=' To generate files please use the free IDL Virtual Machine or a licenced version of IDL.' - ENDIF -ENDIF - -qa_yes=STRPOS(o3[2],'idlcr8qa.log') NE -1 ;Set Boolean for doing QA checks - -IF (KEYWORD_SET(o1)) AND (FLOAT(!Version.Release) LT 6.2) THEN BEGIN ;No HDF5 library for IDL6.1 or less - STOP_WITH_ERROR,'D_'+proname,'IDL Version '+!Version.Release+errtxt[4],lu - IF STRLEN(rerr[0]) GT 2 THEN reterr=rerr[0] & RETURN -ENDIF - -;Check TAV file and Output Directory inputs -tablefile='' & outdir='' -CD,CURRENT=path ;identify current working directory -IF N_PARAMS() GE 3 THEN BEGIN ;check TAV file - IF (SIZE(tav,/N_ELEMENTS) EQ 1) AND (SIZE(tav,/TYPE) EQ 7) THEN BEGIN - file_exist=FILE_TEST(tav,/READ) - IF (file_exist EQ 1) AND (tav NE '') THEN tablefile=tav - ENDIF -ENDIF -IF N_PARAMS() GE 4 THEN BEGIN ;check output directory - IF (SIZE(odir,/N_ELEMENTS) EQ 1) AND (SIZE(odir,/TYPE) EQ 7) THEN BEGIN - file_exist=FILE_TEST(odir,/DIRECTORY) - IF (file_exist EQ 1) AND (odir NE '') THEN BEGIN - outdir=FILE_SEARCH(odir,/FULLY_QUALIFY_PATH,/MARK_DIRECTORY) - path=outdir - ENDIF ELSE IF intype NE 8 THEN outdir=STRUPCASE(STRTRIM(odir,2)) - ENDIF -ENDIF - -IF (intype LT 0) OR (intype EQ 7) THEN BEGIN ;Input via files - IF KEYWORD_SET(o6) THEN BEGIN - ;session memory inputs required for QA option - STOP_WITH_ERROR,'D_'+proname,errtxt[5],lu - IF STRLEN(rerr[0]) GT 2 THEN reterr=rerr[0] & RETURN - ENDIF - metafile='' & inf=1 & mv_str=[-1L] - IF N_PARAMS() GE 2 THEN BEGIN - ;Check that input files exist. If not then user will be prompted for new inputs - IF SIZE(ga,/N_ELEMENTS) EQ 1 THEN BEGIN - file_exist=FILE_TEST(ga,/READ) - IF (file_exist EQ 1) AND (ga NE '') THEN $ - metafile=FILE_DIRNAME(ga,/MARK_DIRECTORY)+FILE_BASENAME(ga) - ENDIF - arorfs=SIZE(sds,/DIMENSIONS) ;Is datafile input Filespec or String Array? - IF arorfs[0] EQ 0 THEN BEGIN ;input is filespec (scalar) - IF sds NE '' THEN sds=FINDFILE(sds) - isize=SIZE(sds,/DIMENSIONS) - IF isize[0] EQ 0 THEN sds=[''] ;no files found matching filespec - ENDIF - IF N_ELEMENTS(arorfs) GT 1 THEN sds=REFORM(sds,N_ELEMENTS(sds),/OVERWRITE) ;Convert to 1-D array - file_exist=FILE_TEST(sds,/READ) - gi=WHERE(file_exist EQ 1,nfile) - IF nfile NE 0 THEN BEGIN - sds=sds[gi] & sds=FILE_DIRNAME(sds,/MARK_DIRECTORY)+FILE_BASENAME(sds) - ENDIF ELSE sds=[''] ;contains all valid datafiles - ENDIF ELSE sds=[''] - IF metafile EQ '' THEN BEGIN - metafile=DIALOG_PICKFILE(Filter=['*.meta','meta*.txt','*.txt'], PATH=path, $ - /MUST_EXIST,Title='Select Metadata Template File') - IF metafile EQ '' THEN BEGIN - STOP_WITH_ERROR,'D_'+proname,'Metadata file'+errtxt[0],lu - IF STRLEN(rerr[0]) GT 2 THEN reterr=rerr[0] & RETURN - ENDIF - path=FILE_DIRNAME(metafile,/MARK_DIRECTORY) - END - IF sds[0] EQ '' THEN BEGIN - datafile=DIALOG_PICKFILE(Filter=['*.data','*.dat','*.txt'], PATH=path, $ - /MUST_EXIST,/Multiple_Files,Title='Select Data File(s)') - gi=WHERE(datafile NE '',nfile) - IF nfile EQ 0 THEN BEGIN - STOP_WITH_ERROR,'D_'+proname,'Data file(s)'+errtxt[0],lu - IF STRLEN(rerr[0]) GT 2 THEN reterr=rerr[0] & RETURN - ENDIF ELSE BEGIN - datafile=datafile[gi] & dsort=SORT(datafile) & sds=datafile[dsort] - path=FILE_DIRNAME(sds[0],/MARK_DIRECTORY) - ENDELSE - ENDIF - IF outdir EQ 'M' THEN outdir=FILE_DIRNAME(metafile,/MARK_DIRECTORY) $ - ELSE IF outdir EQ 'D' THEN outdir=FILE_DIRNAME(sds[0],/MARK_DIRECTORY) -ENDIF ELSE BEGIN ;SDS is a data structure, so do initial checks on parameters - ;check GA array - inf=0 & nfile=1 - as=SIZE(ga) & n_ga=N_ELEMENTS(ga) - IF (as[0] NE 1) OR (as[2] NE 7) THEN BEGIN - STOP_WITH_ERROR,'D_'+proname,errtxt[1],lu - IF STRLEN(rerr[0]) GT 2 THEN reterr=rerr[0] & RETURN - ENDIF - - 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) - it5=[''] ;array holding infotxt[*,5] error messages - - ;check pointer array (heap structure) - IF N_TAGS(sds) EQ 0 THEN BEGIN - STOP_WITH_ERROR,'D_'+proname,'Input heap '+errtxt[2]+'expecting VA_L, VA_V, and DATA tags).',lu - IF STRLEN(rerr[0]) GT 2 THEN reterr=rerr[0] & RETURN - ENDIF - tagnames=STRUPCASE(TAG_NAMES(sds)) - si=WHERE((tagnames EQ 'VA_L') OR (tagnames EQ 'VA_V') OR (tagnames EQ 'DATA'),scnt) - IF scnt NE 3 THEN BEGIN - STOP_WITH_ERROR,'D_'+proname,'Input heap '+errtxt[2]+'missing VA_L, VA_V, and/or DATA tags).',lu - IF STRLEN(rerr[0]) GT 2 THEN reterr=rerr[0] & RETURN - ENDIF - ;Check for number and size of dimensions, and invalid pointer arguments for the data - FOR i=0,2 DO BEGIN - CASE 1 OF - i EQ 0: BEGIN - pchk=PTR_VALID(sds.data) & sdstxt='SDS.DATA ' - END - i EQ 1: BEGIN - pchk=PTR_VALID(sds.va_v) & sdstxt='SDS.VA_V ' - pchkv=pchk - END - ELSE: BEGIN - pchk=PTR_VALID(sds.va_l) & sdstxt='SDS.VA_L ' - END - ENDCASE - ;Check that the SDS structure has two dimensions - s_ndim=SIZE(pchk,/N_DIMENSIONS) & s_dim=SIZE(pchk,/DIMENSIONS) - IF s_ndim NE 2 THEN BEGIN - STOP_WITH_ERROR,'D_'+proname,sdstxt+errtxt[2]+'two dimensions expected).',lu - IF STRLEN(rerr[0]) GT 2 THEN reterr=rerr[0] & RETURN - ENDIF ELSE IF i EQ 0 THEN BEGIN - pi=WHERE(pchk[*,0] NE 1,pcnt) - IF pcnt NE 0 THEN BEGIN - STOP_WITH_ERROR,'D_'+proname,sdstxt+errtxt[2]+'null pointer arguments).',lu - IF STRLEN(rerr[0]) GT 2 THEN reterr=rerr[0] & RETURN - ENDIF - ENDIF ELSE BEGIN - IF i EQ 2 THEN test=s_dim[1] NE s_dim0[1] ELSE test=0 - IF (s_dim[0] NE s_dim0[0]) OR (test) THEN BEGIN - dimv='['+STRTRIM(s_dim0[0],2)+','+STRTRIM(sdim0[1],2)+']/['+ $ - STRTRIM(s_dim[0],2)+','+STRTRIM(sdim[1],2)+'].' - STOP_WITH_ERROR,'D_'+proname,errtxt[3]+dimv,lu - IF STRLEN(rerr[0]) GT 2 THEN reterr=rerr[0] & RETURN - ENDIF - ENDELSE - s_dim0=s_dim - ENDFOR - - ;create Metafile by combining global and variable attributes, also make - ;accompanying arrays to hold any numeric metadata values - vi=WHERE(pchk EQ 1,vatot) ;total number of Variable Attribute labels - metafile=STRARR(n_ga+vatot) & mv_lng=LON64ARR(n_ga+vatot) & mv_dbl=DBLARR(n_ga+vatot) - pcnt=LONG(n_ga) & mv_str=LONARR(vatot) - metafile[0:pcnt-1L]=ga - FOR i=0,s_dim[0]-1 DO BEGIN ;s_dim[0] is the number of datasets - dset=*sds[i,0].data & dtype=SIZE(dset,/TYPE) - FOR j=0,s_dim[1]-1 DO BEGIN ;s_dim[1] is the number of attributes - IF pchk[i,j] EQ 1 THEN BEGIN - IF pchkv[i,j] EQ 0 THEN metafile[pcnt]=*sds[i,j].va_l+'=' $ - ELSE BEGIN - vavt=SIZE(*sds[i,j].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 - vav=*sds[i,j].va_v - n_vav=N_ELEMENTS(vav) - FOR k=0,n_vav-1 DO BEGIN - IF k EQ 0 THEN BEGIN - IF vavt EQ 1 THEN metafile[pcnt]=*sds[i,j].va_l+'='+STRING(FIX(vav[k])) $ - ELSE IF vavt EQ 7 THEN metafile[pcnt]=*sds[i,j].va_l+'='+STRING(vav[k]) $ ;save all values as a string representation - ELSE metafile[pcnt]=*sds[i,j].va_l+'='+STRTRIM(vav[k],2) - ENDIF ELSE BEGIN - IF vavt EQ 1 THEN metafile[pcnt]=metafile[pcnt]+';'+STRING(FIX(vav[k])) $ - ELSE IF vavt EQ 7 THEN metafile[pcnt]=metafile[pcnt]+';'+STRING(vav[k]) $ - ELSE metafile[pcnt]=metafile[pcnt]+';'+STRTRIM(vav[k],2) - ENDELSE - ENDFOR - IF vavt NE 7 THEN BEGIN ;i.e. if the metadata value is not of STRING datatype - - ;Do check for VAR_SIZE value written as a numeric type instead of string - vs_chk=*sds[i,j].va_l - IF (STRUPCASE(vs_chk) EQ 'VAR_SIZE') AND (infotxt[0,4] EQ '') THEN BEGIN - ;need to generate infotxt message - IF qa_yes THEN itxt=' must be of ' ELSE itxt=' changed to ' - infotxt[0,4]='2 VAR_SIZE attribute value(s)'+itxt+'type STRING' - ENDIF - - IF n_vav EQ 1 THEN BEGIN ;save numeric values to the appropriate numeric array (LONG64 or DOUBLE) - CASE 1 OF - ((vavt GE 1) AND (vavt LE 3)) OR ((vavt GE 12) AND (vavt LE 14)): mv_lng[pcnt]=vav - (vavt GE 4) AND (vavt LE 5): mv_dbl[pcnt]=vav - ELSE: - ENDCASE - ENDIF - ENDIF - ENDIF ELSE BEGIN ;invalid data type for the attribute label - IF qa_yes THEN itxt='' ELSE itxt=' and will be ignored' - val=*sds[i,j].va_l & val=STRTRIM(val,2) - bli=WHERE(val EQ badlabel,blcnt) - IF blcnt EQ 0 THEN BEGIN - badlabel=[badlabel,val] - IF it5[0] EQ '' THEN it5[0]='2 Value for Attribute Label '+val+' is an invalid Data Type'+itxt $ - ELSE it5=[it5,'2 Value for Attribute Label '+val+' is an invalid Data Type'+itxt] - ENDIF - ENDELSE - ENDELSE - pcnt=pcnt+1L - ENDIF - ENDFOR - ENDFOR -ENDELSE - -;If necessary open DIALOG_BOXES for the TAV File and Output Directory Inputs -IF tablefile EQ '' THEN BEGIN - tablefile=DIALOG_PICKFILE(Filter=['table*.dat','*.dat'], PATH=path, $ - /MUST_EXIST,Title='Select Table Attribute Values File') - IF tablefile EQ '' THEN BEGIN - STOP_WITH_ERROR,'D_'+proname,'Table Attribute Values file'+errtxt[0],lu - IF STRLEN(rerr[0]) GT 2 THEN reterr=rerr[0] & RETURN - ENDIF - path=FILE_DIRNAME(tablefile,/MARK_DIRECTORY) -ENDIF -IF outdir EQ '' THEN BEGIN - outdir=DIALOG_PICKFILE(/DIRECTORY,Title='Select Directory for Output '+dftxt+' file(s)', PATH=path) - IF outdir EQ '' THEN BEGIN - STOP_WITH_ERROR,'D_'+proname,dftxt+' file(s) output directory'+errtxt[0],lu - IF STRLEN(rerr[0]) GT 2 THEN reterr=rerr[0] & RETURN - ENDIF -ENDIF - -;Set dux values according to output options -IF o3[2] NE '0' THEN BEGIN ;open idlcr8hdf/idlcr8qa.log - o3[2]=outdir+o3[2] - res=FILE_TEST(o3[2],/WRITE) - IF res EQ 0 THEN OPENW,du,o3[2],/GET_LUN $ - ELSE BEGIN - OPENW,du,o3[2],/Append,/GET_LUN & FOR i=0,1 DO PRINTF,du,'' - ENDELSE - dux[1]=du & dux[2]=dux[1]-dux[0] -ENDIF -FOR i=dux[0],dux[1],dux[2] DO BEGIN - IF i EQ -1 THEN BEGIN - PRINT,dftxt+' File Input/Output Log - Program Started on '+SYSTIME(0) & PRINT,'' - ENDIF ELSE IF (i EQ dux[0]) OR ((i EQ dux[1]) AND (STRPOS(o3[2],'idlcr8hdf.log') NE -1)) THEN BEGIN - PRINTF,i,dftxt+' File Input/Output Log - Program Started on '+SYSTIME(0) - PRINTF,i,'' - ENDIF -ENDFOR - -IF o3[3] EQ '' THEN BEGIN - ;Set-up output file display widget and log file - base=WIDGET_BASE(Title=dftxt+' File Input/Output Log',Units=2,yoffset=3,xoffset=3,Tlb_Frame_Attr=1,/COLUMN) ;,Tab_Mode=1) - wtxt=WIDGET_TEXT(base,/Scroll,xsize=100,ysize=12) - base2=WIDGET_BASE(base) - b1=WIDGET_BUTTON(base2,value='Finish',uvalue='DONE',frame=3,Sensitive=0) ;Accelerator='Return') - WIDGET_CONTROL,wtxt,/REALIZE - WIDGET_CONTROL,base,/REALIZE - WIDGET_CONTROL,wtxt,set_value='',/APPEND - WIDGET_CONTROL,wtxt,set_value='Input TAV File: '+tablefile,/APPEND -ENDIF - -;Call the procedure to read the TAV file (returns tab_arr) -FOR i=dux[0],dux[1],dux[2] DO $ - IF i EQ -1 THEN PRINT,'Input TAV File: '+tablefile ELSE PRINTF,i,'Input TAV File: '+tablefile -READ_TABLEFILE,tablefile -IF STRLEN(rerr[0]) GT 2 THEN BEGIN - reterr=rerr[0] & RETURN -ENDIF - -;Check (and write out) any earlier infotxt statements -FOR i=0,3 DO BEGIN - IF infotxt[0,i] NE '' THEN BEGIN - IF i EQ 2 THEN INFOTXT_OUTPUT,infotxt[0,i] ELSE INFOTXT_OUTPUT,infotxt[*,i] - ENDIF -ENDFOR - -FOR ndf=0,nfile-1 DO BEGIN - IF o3[3] EQ '' THEN BEGIN - lineno=lineno+3L - WIDGET_CONTROL,wtxt,set_value='',/Append - ENDIF - FOR i=dux[0],dux[1],dux[2] DO IF i EQ -1 THEN PRINT,'' ELSE PRINTF,i,'' - IF inf EQ 1 THEN BEGIN - IF o3[3] EQ '' THEN BEGIN - lineno=lineno+2L - WIDGET_CONTROL,wtxt,set_value='Input Metadata File: '+metafile,/Append - WIDGET_CONTROL,wtxt,set_value='Input Data File: '+sds[ndf],/Append - ENDIF - FOR i=dux[0],dux[1],dux[2] DO BEGIN - IF i EQ -1 THEN BEGIN - PRINT,'Input Metadata File: '+metafile & PRINT,'Input Data File: '+sds[ndf] - ENDIF ELSE BEGIN - PRINTF,i,'Input Metadata File: '+metafile - PRINTF,i,'Input Data File: '+sds[ndf] - ENDELSE - ENDFOR - ENDIF ELSE BEGIN - IF o3[3] EQ '' THEN $ - WIDGET_CONTROL,wtxt,set_value='Reading input Global Attribute array and Data Structure',/Append - FOR i=dux[0],dux[1],dux[2] DO BEGIN - IF i EQ -1 THEN PRINT,'Reading input Global Attribute array and Data Structure' $ - ELSE IF (i EQ dux[0]) OR ((i EQ dux[1]) AND (STRPOS(o3[2],'idlcr8hdf.log') NE -1)) THEN $ - PRINTF,i,'Reading input Global Attribute array and Data Structure' - ENDFOR - ;check for numeric VAR_SIZE value message - IF infotxt[0,4] NE '' THEN INFOTXT_OUTPUT,infotxt[0,4] - - ;check for invalid variable attribute value data type - IF it5[0] NE '' THEN BEGIN - FOR i=0,N_ELEMENTS(it5)-1 DO INFOTXT_OUTPUT,it5[i] - ENDIF - - ;Do stage 1 of HDF Pre-Defined attributes check (checking units,valid_range,_FillValue only) - ncsa=['x','x','units','_fillvalue','valid_range','valid_range'] - var_atts=['VAR_NAME','VAR_DATA_TYPE','VAR_UNITS','VAR_FILL_VALUE','VAR_VALID_MIN','VAR_VALID_MAX'] - FOR i=0,s_dim[0]-1 DO BEGIN ;s_dim[0] is the number of datasets - vdtv='' & vnx='' & verror=INTARR(2) - FOR k=0,N_ELEMENTS(var_atts)-1 DO BEGIN - vav='' & nav='' - FOR j=0,s_dim[1]-1 DO BEGIN ;s_dim[1] is the number of attributes - IF (pchk[i,j] EQ 1) AND (pchkv[i,j] EQ 1) THEN BEGIN - CASE 1 OF - STRUPCASE(STRTRIM(*sds[i,j].va_l,2)) EQ var_atts[k]: vav=*sds[i,j].va_v - STRLOWCASE(STRTRIM(*sds[i,j].va_l,2)) EQ ncsa[k]: nav=*sds[i,j].va_v - ELSE: - ENDCASE - ENDIF - ENDFOR - IF k EQ 0 THEN vnx=STRTRIM(vav[0],2) $ ;VAR_NAME - ELSE IF k EQ 1 THEN vdtv=STRUPCASE(STRTRIM(vav[0],2)) $ ;VAR_DATA_TYPE - ELSE IF k GE 4 THEN BEGIN ;separate out min and max valid_range values - IF ((SIZE(nav[0],/TYPE) EQ 7) AND (STRTRIM(nav[0],2) NE '')) OR (SIZE(nav[0],/TYPE) NE 7) THEN BEGIN - IF k EQ 4 THEN nav=nav[0] $ - ELSE IF N_ELEMENTS(nav) GT 1 THEN nav=nav[1] - ENDIF - vav=vav[0] - ENDIF ELSE BEGIN - vav=vav[0] & nav=nav[0] - ENDELSE - IF k GE 2 THEN PRE_DEFINED_ATT_CHECKS,k-2,nav,vav,vnx,vdtv,verror - ENDFOR - ENDFOR - ENDELSE - - ;Call the procedure to read the metadata (returns meta_arr) - READ_METADATA,metafile,inf - IF STRLEN(rerr[0]) GT 2 THEN BEGIN - reterr=rerr[0] & RETURN - ENDIF - ;Call Procedure to check inputs in meta_arr with those from the TAV file - CHECK_METADATA - IF STRLEN(rerr[0]) GT 2 THEN BEGIN - reterr=rerr[0] & RETURN - ENDIF - ;Call the procedure to set up the data structure and assign arrays for VAR_NAME and VAR_UNITS - IF inf EQ 0 THEN SET_UP_STRUCTURE,sds,inf ELSE SET_UP_STRUCTURE,sds[ndf],inf - IF STRLEN(rerr[0]) GT 2 THEN BEGIN - reterr=rerr[0] & RETURN - ENDIF ;Call the procedure to read the data - IF inf EQ 0 THEN READ_DATA,sds,inf ELSE READ_DATA,sds[ndf],inf - IF STRLEN(rerr[0]) GT 2 THEN BEGIN - reterr=rerr[0] & RETURN - ENDIF - ;Call the Procedure to construct the HDF output filename based on input metadata. - IF STRPOS(o3[0],'H5') NE -1 THEN hdffilename='.h5' $ - ELSE IF o3[0] EQ 'NC' THEN hdffilename='.nc' $ - ELSE hdffilename='.hdf' - FIND_HDF_FILENAME,hdffilename - IF STRLEN(rerr[0]) GT 2 THEN BEGIN - reterr=rerr[0] & RETURN - ENDIF - - ;Create HDF file if the program is not performing QA and NOHDF keyword not selected - IF (~qa_yes) AND (o3[4] EQ '0') THEN BEGIN - ;Check to see if file exists - if so delete (o/w will append to existing file) - IF FILE_TEST(hdffilename) EQ 1 THEN FILE_DELETE,hdffilename - - ;Call the procedure to write the Global Attributes, Variable Attributes and Data - ;to an HDF4 or HDF5 file - geoms_output=o3[0] - AVDC_HDF_WRITE,hdffilename,geoms_output - FILE_MOVE, hdffilename, outdir+hdffilename,/Allow_Same,/Overwrite - hdffilename=outdir+hdffilename - - IF o3[3] EQ '' THEN $ - WIDGET_CONTROL,wtxt,set_value=hdffilename+' successfully created!',/Append,Set_text_top_line=lineno - FOR i=dux[0],dux[1],dux[2] DO BEGIN - IF i EQ -1 THEN PRINT,hdffilename+' successfully created!' $ - ELSE PRINTF,i,hdffilename+' successfully created!' - ENDFOR - ENDIF - ;Free up memory by destroying the heap variables pointed at by its pointer arguments - PTR_FREE,ds.data -ENDFOR - -IF qa_yes THEN endtxt=dftxt+' QA function ' $ -ELSE IF o3[4] EQ '0' THEN endtxt=dftxt+' file creation ' $ -ELSE endtxt='GEOMS file testing ' - -FOR i=dux[0],dux[1],dux[2] DO BEGIN - IF i EQ -1 THEN BEGIN - PRINT,'' & PRINT,endtxt+'completed - Program Ended on '+SYSTIME(0) - ENDIF ELSE IF (i EQ dux[0]) OR ((i EQ dux[1]) AND (STRPOS(o3[2],'idlcr8hdf.log') NE -1)) THEN BEGIN - PRINTF,i,'' & PRINTF,i,endtxt+'completed - Program Ended on '+SYSTIME(0) - ENDIF -ENDFOR -IF dux[1] NE dux[0] THEN FREE_LUN,dux[1] - -ri=WHERE(rerr EQ '',rcnt) -IF (rcnt EQ N_ELEMENTS(rerr)) AND (~qa_yes) AND (o3[4] EQ '0') THEN BEGIN - IF nfile EQ 1 THEN reterr=hdffilename+' successfully created' $ - ELSE reterr=dftxt+' files successfully created' -ENDIF ELSE IF (rerr[1] NE 'NA') AND (rerr[1] NE '') THEN reterr=rerr[1] - -IF o3[3] EQ '' THEN BEGIN - WIDGET_CONTROL,b1,Sensitive=1,/Input_Focus - WIDGET_CONTROL,wtxt,set_value='',/Append,Set_text_top_line=lineno+2L - WIDGET_CONTROL,wtxt,set_value=endtxt+'completed - hit <Finish> to close program',/Append - XMANAGER,'idlcr8hdf',base -ENDIF ELSE IF intype LT 0 THEN BEGIN ;Create Finish Dialog Box - res=DIALOG_MESSAGE(endtxt+'completed successfully!',/Information,Title='EVDC IDLcr8HDF') -ENDIF - -END ;Procedure IDLcr8HDF +;Main Program Version: idlcr8hdf.pro v4.0b65, 20241029 +; Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org +; +;Sub-versions (refer to idlcr8hdf-v4.0_Readme.pdf for full history) + +PRO idlcr8hdf_common +;Procedure to define the COMMON blocks for this program +; ---------- +;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org +; +;History: +; 20050909: Introduced to IDLCR8HDF - Version 1.1 +; 20061012: Variable 'ncsa' added to METADATA; 'iarr', 'larr', 'rarr', 'darr' holding Arrays +; removed from DATA and replaced with Structure 'ds'; Variable 'vfv' added to DATA; +; WIDGET_WIN added for common variables associated with the Graphical User Interface +; - Version 2.0 +; 20080302: tab_type integer added to TABLEDATA - Version 3.0 +; 20100205: rerr string added to WIDGET_WIN - Version 3.09 +; 20110401: vnchange added to DATA; mv_lng and mv_dbl added to METADATA; vserror added to DATA; +; vfv removed from DATA; ncsa removed from METADATA - Version 4.0b1 +; 20150127: mv_str added to METADATA to hold maximum string length of string dataset entries - +; Version 4.0b25 +; 20151012: Rename free_attr to attr_free and add qa_yes - Version 4.0b31 +; +;Input: Nil +; +;Output: Nil +; +;Called by: N/A +; +;Subroutines Called: None + +COMMON TABLEDATA, tab_arr,tab_ver,tab_type +COMMON METADATA, meta_arr,attr_arr_glob,attr_arr_data,attr_free,mv_lng,mv_dbl,mv_str +COMMON DATA, ds,ndm,nvn,vn,vu,vnchange,vserror,qa_yes +COMMON WIDGET_WIN, wtxt,b1,lineno,base,o3,dux,rerr,lvals + +END ;Procedure idlcr8hdf_common + + + +PRO intro_event, ev +;Procedure to define how Events from the Start-up Introduction Window are handled +; ---------- +;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org +; +;History: +; 20061012: Introduced to IDLCR8HDF - Version 2.0 +; +;Input: ev - Selected widget event structure +; +;Output: o3 - Common string array defining the various options for program output +; +;Called by: XMANAGER in INTRO +; +;Subroutines Called: None + +COMMON WIDGET_WIN + +;The uservalue is retrieved from a widget when an event occurs +WIDGET_CONTROL,ev.id,GET_UVALUE=uv +;Assign/Remove AVK button event to/from a variable name +IF uv EQ 'AVK' THEN IF o3[1] EQ uv THEN o3[1]='0' ELSE o3[1]=uv $ +;Assign/Remove Log Output button event to/from a variable name +ELSE IF uv EQ 'idlcr8hdf.log' THEN IF o3[2] EQ uv THEN o3[2]='0' ELSE o3[2]=uv $ +;Assign/Remove Pop-up window for Log Output button event to/from a variable name +ELSE IF uv EQ 'Pop' THEN IF o3[3] EQ uv THEN o3[3]='0' ELSE o3[3]=uv $ +;Assign button event to a variable name +ELSE IF (uv EQ 'H4') OR (uv EQ 'H5') OR (uv EQ 'NC') THEN BEGIN + o3[0]=uv + IF uv EQ 'H5' THEN BEGIN + WIDGET_CONTROL,ev.id+1,Sensitive=1 + WIDGET_CONTROL,ev.id+2,Sensitive=1,Set_Combobox_Select=0 + ENDIF +ENDIF ELSE IF uv EQ '0' THEN IF ev.str EQ 'None' THEN o3[0]='H5' ELSE o3[0]='H5_'+STRTRIM(ev.str,2) $ +ELSE o3[0]='0' ;Cancel button chosen +IF (uv NE 'AVK') AND (uv NE 'idlcr8hdf.log') AND (uv NE 'Pop') AND (uv NE 'H5') THEN WIDGET_CONTROL,ev.top,/DESTROY + +END ;Intro_Event + + + +PRO intro, intype +;Procedure which creates an Introduction Window at start-up when IDLCR8HDF is called without parameters, +;or invalid parameters, or is called by IDL Virtual Machine. The user has a choice of continuing with +;the program after selecting input options, or stopping the program. +; ---------- +;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org +; +;History: +; 20061012: Introduced to IDLCR8HDF - Version 2.0 +; 20080302: Swapped the order of the option windows, so that the options indicated by the check +; boxes appear above the option boxes that close the Introduction Window and continue +; with the program - Version 3.0 +; 20100205: Include text regarding the RETERR argument which can be accepted as an input +; parameter when using the full version of idlcr8hdf - Version 3.09 +; 20110401: Add vertxt string array to hold text which changes between versions of the code; +; Change text to reference GEOMS compliant files instead of AVDC/EVDC/NDACC; Change +; text regarding the format of the structure required for input - Version 4.0b1 +; 20111220: Change text and options to include netCDF; change contact details - Version 4.0b7 +; 20130114: Make insensitive /LOG and /NC options if the program is called in IDL DEMO mode +; -Version 4.0b14 +; +;Input: intype - Integer set to -1 or -2: -1 indicates normal state; -2 indicates that input +; parameters at the IDLCR8HDF call were invalid. +; +;Output: Nil +; +;Called by: IDLCR8HDF +; +;Subroutines Called: INTRO_EVENT (via XMANAGER) + +COMMON WIDGET_WIN + +nhdr=44 & errtxt=STRARR(nhdr) +vertxt=['idlcr8hdf-v4.0_Readme.pdf','v4.0b65 October 2024'] +errtxt[1]='Welcome to IDLcr8HDF. This program creates GEOMS compliant HDF4, HDF5 and netCDF files' +errtxt[2]='(also refer to '+vertxt[0]+').' +errtxt[4]='Inputs to the program (IDL Virtual Machine (VM) and IDL Licensed (LIC) Versions):' +errtxt[5]=' METADATA TEMPLATE FILE - Containing the Global and Variable Attribute information for the site.' +errtxt[6]=' DATA FILE(s) - Multiple selection permitted. The file(s) must show the datasets in the single column' +errtxt[7]=' format described in the documentation.' +errtxt[8]=' TAV FILE - the current non-encrypted Table Attribute Values file.' +errtxt[9]=' OUTPUT DIRECTORY - Directory for any HDF, netCDF and log files created. Shortcut values of ''M'' or ''D''' +errtxt[10]=' will output files to the Metadata or Data file directories respectively.' +errtxt[12]='Alternative Input Option for IDL LIC Versions:' +errtxt[13]=' GLOBAL ATTRIBUTES ARRAY (GA) - a string array containing the Global Attributes in the form' +errtxt[14]=' ''label=value''.' +errtxt[15]=' DATA STRUCTURE (DS) - a heap structure using pointers containing the Variable Attribute Labels' +errtxt[16]=' (DS.VA_L), the Variable Attribute Values (DS.VA_V), and the Data (DS.Data), for a single output file.' +errtxt[17]=' TAV FILE - the current non-encrypted Table Attribute Values file.' +errtxt[18]=' OUTPUT DIRECTORY - Directory for any HDF, netCDF and log files created.' +errtxt[19]=' RETERR - String variable to which any error(s) are written.' +errtxt[21]='For IDL VM, input is by ''DIALOG_BOXES''. For IDL LIC, input can be by ''DIALOG_BOXES'' or passed' +errtxt[22]='by calling the program with one of the following command line options:' +errtxt[23]=' 1. idlcr8hdf (Opens this box, and allows the user the option to continue with file inputs).' +errtxt[24]=' 2. idlcr8hdf,METAFILE,DATAFILE(s),TAVFILE,OUTDIR[,RETERR][,/H5][,/Cn][,/NC][,/AVK][,/Log][,/Popup] or' +errtxt[25]=' idlcr8hdf,'''','''','''',''''[,/H5][,/Cn][,/NC][,/AVK][,/Log][,/Popup] (For null string, DIALOG_BOXES will prompt for input).' +errtxt[26]=' 3. idlcr8hdf,GA,DS,TAVFILE,OUTDIR[,RETERR][,/H5][,/Cn][,/NC][,/AVK][,/Log][,/Popup] or +errtxt[27]=' idlcr8hdf,GA,DS[,/H5][,/Cn][,/NC][,/AVK][,/Log][,/Popup] (Inputs are from session memory, DIALOG_BOX(s) will' +errtxt[28]=' prompt for input if TAVFILE or OUTDIR are not included).' +errtxt[29]=' /H5, /Cn, /NC, /AVK, /Log and /Popup keywords are used in place of the options given below (HDF4 is default).' +errtxt[30]='The /Cn keyword enables compression and shuffling of HDF5 files only (C1=low, C9=high). Default is no compression.' +errtxt[32]='Contacts -' +errtxt[33]=' Ian Boyd, Bryan Scientific Consulting LLC (iboyd@bryanscientific.org)' +errtxt[34]=' 9 Cambridge Terrace' +errtxt[35]=' Masterton 5810, New Zealand' +errtxt[37]=' Ann Mari Fjaeraa, EVDC Project Manager (amf@nilu.no)' +errtxt[38]=' Norwegian Institute for Air Research, Instituttveien 18' +errtxt[39]=' Postbox 100, N-2027 KJELLER, NORWAY' +errtxt[41]='EVDC Website: Tools and documentation available from http://evdc.esa.int/' +errtxt[43]='To continue, please choose from the options below (Note: HDF5 only available on IDL6.2 or greater).' +errtxt=' '+errtxt + +;Set-up text display widget +IF intype EQ -2 THEN xtxt=' - Command Line Input Error' ELSE xtxt='' +IF intype EQ -3 THEN optsens=0 ELSE optsens=1 +base=WIDGET_BASE(Title='idlcr8hdf '+vertxt[1]+xtxt,Tlb_Frame_Attr=1,/Column) ;,Tab_Mode=1) +wtxt=WIDGET_TEXT(base,xsize=102,ysize=25,/Scroll) +base3=WIDGET_BASE(base,/Nonexclusive) +logtext='Append log output to the file ''idlcr8hdf.log'' ' +IF intype EQ -3 THEN logtext=logtext+'(No log or netCDF file output permitted in IDL DEMO Mode)' $ +ELSE logtext=logtext+'(will create the file IF it doesn''t exist)' +poptext='Open a Pop-Up Window to display log output' +avktext='If Avg. Kernel data are present, append sentence to VAR_NOTES giving first three values of ' +avktext=avktext+'the first kernel' +avktip='Sentence reads ''First three values of the first kernel are: x.xx x.xx x.xx''' +b4=WIDGET_BUTTON(base3,value=logtext,uvalue='idlcr8hdf.log',frame=3,SENSITIVE=optsens) +b5=WIDGET_BUTTON(base3,value=poptext,uvalue='Pop',frame=3) +b6=WIDGET_BUTTON(base3,value=avktext,uvalue='AVK',frame=3) ;,Tooltip=AVKTip) +base2=WIDGET_BASE(base,/Row) +tip='Left Mouse Click or Tab to entry and hit <Spacebar>' +b1=WIDGET_BUTTON(base2,value='HDF4',uvalue='H4',frame=3) ;,Tooltip=Tip) +b7=WIDGET_BUTTON(base2,value='netCDF3',uvalue='NC',frame=3,SENSITIVE=optsens) +IF FLOAT(!Version.Release) GE 6.2 THEN $ + b2=WIDGET_BUTTON(base2,value='HDF5',uvalue='H5',frame=3) $;,Tooltip=Tip) $ +ELSE b2=WIDGET_BUTTON(base2,value='HDF5',Sensitive=0,frame=3) +b8=Widget_Label(base2,Value=' Compression ',Sensitive=0) +b9=Widget_Combobox(base2,Value=lvals,Sensitive=0,uvalue='0') + +b3=WIDGET_BUTTON(base2,value='Stop',uvalue='CANCEL',frame=3) ;,ToolTip=Tip) +WIDGET_CONTROL,base,/Realize +WIDGET_CONTROL,b4,/Input_Focus +FOR i=0,N_ELEMENTS(errtxt)-1 DO $ + WIDGET_CONTROL,wtxt,set_value=errtxt[i],/Append +XMANAGER,'intro',base + +END ;Intro + + + +PRO idlcr8hdf_event, ev +;Procedure to close the pop-up logging window after user selects 'Finish'. +; ---------- +;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org +; +;History: +; 20061012: Introduced to IDLCR8HDF - Version 2.0 +; +;Input: Selected widget event structure +; +;Output: Nil +; +;Called by: XMANAGER in IDLCR8HDF and STOP_WITH_ERROR +; +;Subroutines Called: None + +WIDGET_CONTROL,ev.top,/DESTROY +RETALL & HEAP_GC + +END ;IDLcr8HDF_Event + + + +FUNCTION is_a_number_hdf, value, ind + +IF N_PARAMS() EQ 2 THEN BEGIN + ;Check each individual character is numeric + nlen=STRLEN(value) + retval=1B + FOR i=0,nlen-1 DO BEGIN + ah=STRMID(value,i,1) + isan=(BYTE(ah) GE 48) AND (BYTE(ah) LE 57) ;0-9 + IF ~isan THEN retval=0B + ENDFOR + RETURN, retval +ENDIF ELSE BEGIN + ON_IOERROR, ConversionError + IF STRTRIM(value,2) EQ '' THEN RETURN, 0B + n=DOUBLE(value) + RETURN, 1B + ConversionError: + RETURN, 0B +ENDELSE +END + + + +PRO stop_with_error, txt1, txt2, lu +;Procedure called when an error in the program inputs is detected. An error message is displayed +;and the program stopped and reset. If necessary, open files are closed, and memory associated +;with a structure is freed. The error message is displayed in one or more of the following: +;a Pop-up dialog window; the Pop-up logging window; the Output Logging window in the IDLDE; +;a log file (idlcr8hdf.log) +; ---------- +;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org +; +;History: +; 20050802: Original IDLCR8HDF Routine - Version 1.0 +; 20061012: Set-up so that the error output is displayed in the output window(s) dependent on +; the options chosen by the user, the point in the program that the error is detected, +; and the method that IDLCR8HDF is called. If txt1 is preceeded by 'D_' or is null, +; the error output is to a Pop-up Dialog window. Other error output options are +; determined by the values in the (Common) dux array - Version 2.0 +; 20100205: Allow routine to return to the calling routine, rather than stop the application, if +; a 'reterr' argument is included in the call to idlcr8hdf - Version 3.09 +; 20110401: Change end text dependent on whether the program is creating a GEOMS file or doing QA +; on a GEOMS file - Version 4.0b1 +; 20111220: Change text referring to HDF to GEOMS - Version 4.0b7 +; 20171121: Add comment advising that program stopped before all checks were completed if doing +; QA; Add COMMON DATA to routine and remove ds from the list of input parameters +; - Version 4.0b43 +; +;Inputs: txt1 - the initial text line of the error message. Generally contains the name of the routine +; where the error was detected. +; txt2 - the second text line which generally describes the error state. +; lu - Where applicable, the file unit that needs to be closed at the termination of the program, +; otherwise set to -1. +; ds - Where applicable, the name of the data structure set-up by the program (dependent on +; the point in the program that the error is detected), so that memory associated with the +; structure can be freed (COMMON variable). +; dux - an integer array used to determine the output options for the error message (COMMON) variable). +; qa_yes - a boolean to indicate whether idlcr8hdf called in QA mode or not (COMMON variable). +; +;Output: Error message displayed/output dependent on the requested output options. +; +;Called by: The routine in which the error was detected. The following routines call STOP_WITH_ERROR: +; READ_TABLEFILE; TEST_FILE_INPUT; READ_METADATA; EXTRACT_AND_TEST; CHECK_METADATA; +; SET_UP_STRUCTURE; CHECK_MIN_MAX_FILL; EXTRACT_DATA; FIND_HDF_FILENAME; IDLCR8HDF. +; +;Subroutines Called: IDLCR8HDF_EVENT (via XMANAGER) + +COMMON DATA +COMMON WIDGET_WIN + +IF lu NE -1L THEN FREE_LUN,lu + +IF txt1 EQ '' THEN BEGIN ;<cancel> chosen on Intro box + res=DIALOG_MESSAGE('IDLcr8HDF Stopped!',/Information,Title='EVDC IDLcr8HDF') +ENDIF ELSE BEGIN + ;If necessary free up memory by destroying the heap variables pointed at by its pointer arguments + IF N_ELEMENTS(ds) NE 0 THEN PTR_FREE,ds.data + ;Write error to file and/or IDL output window + IF STRMID(txt1,0,2) EQ 'D_' THEN txtx=STRMID(txt1,2) ELSE txtx=txt1 + FOR i=dux[0],dux[1],dux[2] DO BEGIN + IF i EQ -1 THEN BEGIN + PRINT,' ERROR in '+txtx & PRINT,' '+txt2 + PRINT,'' & PRINT,'IDLcr8HDF stopped - Program Ended on '+SYSTIME(0) + ENDIF ELSE BEGIN + PRINTF,i,' ERROR in '+txtx & PRINTF,i,' '+txt2 + IF (i EQ dux[0]) OR ((i EQ dux[1]) AND (STRPOS(o3[2],'idlcr8hdf.log') NE -1)) THEN BEGIN + PRINTF,i,'' & PRINTF,i,'IDLcr8HDF stopped - Program Ended on '+SYSTIME(0) + ENDIF ELSE IF qa_yes THEN BEGIN + PRINTF,i,'' & PRINTF,i,' INFORMATION: QA stopped before all checks could be completed' + ENDIF + ENDELSE + ENDFOR + IF dux[1] NE dux[0] THEN FREE_LUN,dux[1] + IF (STRMID(txt1,0,2) EQ 'D_') AND (rerr[0] 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]='IDLcr8HDF Stopped!' + res=DIALOG_MESSAGE(errtxt2,/Error,Title='EVDC IDLcr8HDF Error') + ENDIF ELSE IF rerr[0] EQ 'NA' THEN BEGIN ;write error to Popup window + IF o3[4] EQ '0' THEN endtxt='GEOMS file creation ' $ + ELSE endtxt='GEOMS file testing ' + 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=endtxt+'stopped - hit <Finish> to close program',/Append + WIDGET_CONTROL,b1,Sensitive=1,/Input_Focus + XMANAGER,'stop_with_error',base,Event_Handler='idlcr8hdf_event' + ENDIF +ENDELSE +HEAP_GC +IF rerr[0] EQ 'NA' THEN RETALL $ +ELSE BEGIN + IF o3[4] EQ '0' THEN endtxt='create GEOMS file' $ + ELSE endtxt='complete GEOMS file test' + rerr[0]='Unable to '+endtxt+' - '+txtx+txt2 +ENDELSE + +END ;Procedure Stop_With_Error + + + +PRO infotxt_output, infotxt +;Procedure called when the program makes a change to the input meta data or reports information +;relevant to the creation of the HDF/netCDF 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 IDLCR8HDF - Version 3.06 +; 20110401: infotxt INFORMATION header changed to a number, to allow changes to the comment +; header depending on how idlcr8hdf has been called, as follows: 0 = INFORMATION; +; 1 = NON-STANDARD COMPLIANCE NOTIFICATION (QA) o/w INFORMATION; 2 = ERROR (QA) +; o/w INFORMATION; 3 = ERROR; 4 = DEBUG - Version 4.01 +; +;Inputs: infotxt - the text line(s) of the information message. Can be either a scalar string +; or string array +; +;Output: Message displayed/output dependent on the requested output options +; +;Called by: The routine in which the reported change was made. The following routines call +; INFOTXT_OUTPUT: READ_TABLEFILE; GEOMS_RULE_CHANGES; PRE_DEFINED_ATT_CHECKS; +; READ_METADATA; EXTRACT_AND_TEST; CHECK_METADATA; EXTRACT_DATA; SET_UP_STRUCTURE; +; CHECK_STRING_DATATYPE; CHECK_MIN_MAX_FILL; READ_DATA; FIND_HDF_FILENAME; +; AVDC_HDF_WRITE; IDLCR8HDF +; +;Subroutines Called: None + +COMMON DATA +COMMON WIDGET_WIN + +dm=SIZE(infotxt,/N_ELEMENTS) +qaval=FIX(STRMID(infotxt[0],0,1)) +write_rerr=0 ;Boolean to generate a return error message + +IF qa_yes THEN BEGIN ;program called in QA mode + ;Add correct message title + CASE 1 OF + qaval EQ 0: infotxt[0]=' INFORMATION:'+STRMID(infotxt[0],1) + qaval EQ 1: infotxt[0]=' NON-STANDARD COMPLIANCE NOTIFICATION:'+STRMID(infotxt[0],1) + qaval EQ 4: infotxt[0]=' DEBUG:'+STRMID(infotxt[0],1) + ELSE: infotxt[0]=' ERROR:'+STRMID(infotxt[0],1) + ENDCASE + ;truncate message from the '|' + bs_found=0 + FOR n=0,dm-1 DO BEGIN + IF bs_found EQ 1 THEN infotxt[n]='' $ + ELSE BEGIN + bspos=STRPOS(infotxt[n],'|') + IF bspos NE -1 THEN BEGIN + infotxt[n]=STRMID(infotxt[n],0,bspos) + bs_found=1 + ENDIF + ENDELSE + ENDFOR + ;recalculate number of good infotxt values + gi=WHERE(infotxt NE '',dm) +ENDIF ELSE BEGIN ;program called in HDF file create mode + CASE 1 OF + qaval EQ 3: BEGIN + infotxt[0]=' ERROR:'+STRMID(infotxt[0],1) + IF rerr[1] NE 'NA' THEN BEGIN ;generate return error message + IF rerr[1] EQ '' THEN BEGIN + IF o3[4] EQ '0' THEN endtxt='create GEOMS file' $ + ELSE endtxt='complete GEOMS file test' + ENDIF + write_rerr=1 + ENDIF + o3[4]='NOHDF' ;Error in Input so do not create HDF file + END + qaval EQ 4: infotxt[0]=' DEBUG:'+STRMID(infotxt[0],1) + ELSE: infotxt[0]=' INFORMATION:'+STRMID(infotxt[0],1) + ENDCASE + ;remove '|' from the message + FOR n=0,dm-1 DO BEGIN + bspos=STRPOS(infotxt[n],'|') + IF bspos NE -1 THEN BEGIN + infotxt[n]=STRMID(infotxt[n],0,bspos)+STRMID(infotxt[n],bspos+1) + ENDIF + ENDFOR +ENDELSE + +lineno=lineno+dm +FOR n=0,dm-1 DO BEGIN + IF o3[3] EQ '' THEN WIDGET_CONTROL,wtxt,set_value=infotxt[n],/Append,Set_text_top_line=lineno + FOR m=dux[0],dux[1],dux[2] DO BEGIN + IF m EQ -1 THEN PRINT,infotxt[n] ELSE PRINTF,m,infotxt[n] ;write out to log + ENDFOR +ENDFOR + +IF write_rerr EQ 1 THEN BEGIN + IF rerr[1] EQ '' THEN rerr[1]='Unable to '+endtxt+' -'+STRMID(infotxt[0],8) $ + ELSE rerr[1]=rerr[1]+';'+STRMID(infotxt[0],8) + IF dm GT 1 THEN FOR n=1,dm-1 DO rerr[1]=rerr[1]+STRMID(infotxt[n],3) +ENDIF + +END ;Procedure InfoTxt_Output + + + +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 var_units_test, vuvalue, rd, tab_type, var_si_conv, errstate +;Procedure to perform checks on the VAR_UNITS value in the metadata and, based on the VAR_UNITS input, +;calculate and return the VAR_SI_CONVERSION value. +; ---------- +;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org +; +; History: +; 20061012: Introduced to IDLCR8HDF - Version 2.0 +; (Previously these checks were performed in the Extract_and_Test routine) +; 20080302: var_unit_arr and unit_pre_arr added which hold, respectively, the valid VAR_UNITS and +; corresponding VAR_SI_CONVERSION values, and the set of UNIT_PREFIXs (previously +; values from the EVDC/AVDC TAV file have been used). This has been done so that the +; routine can be stand-alone (i.e. not dependent on also having to read in a TAV file), +; and also because the original Envisat table.dat file does not contain the +; VAR_SI_CONVERSION values. The input parameters have been changed to reflect this: bu +; and up arrays (previously containing the VAR_UNIT and UNIT_PREFIX values from the TAV +; file), and nbu and nup (the number of elements in the bu and up arrays) are no longer +; used. The integer flag tab_type has been added to account for the different handling of +; some of the VAR_UNIT and VAR_SI_CONVERSION values between AVDC and original Envisat. +; The STOP_WITH_ERROR routine is no longer called in the event of an error, but an Error +; State string is returned instead. Bug fixed when testing for a UNIT_PREFIX - previously +; only the first character of the VAR_UNIT value was checked for a possible UNIT_PREFIX, +; thus 'deka' (da) was excluded. Bug Fixed when the calculated Power Value of the last +; base SI unit shown in VAR_SI_CONVERSION is '1', this is now set so that the power value +; does not appear - Version 3.0 +; 20090611: Galileo added to var_unit_arr (AVDC); Last VAR_SI_CONVERSION value for ppmv, ppbv, +; pptv, and ppv changed to DIMENSIONLESS (was ppv) in var_unit_arr (AVDC); +; VAR_SI_CONVERSION values for molec changed to 0;1.66054E-24;mol (was 0;1;molec) in +; var_unit_arr (AVDC); VAR_SI_CONVERSION values for DU changed to 0;4.4615E-4;mol m-2 +; (was 0;2.6867E20;molec m-2) in var_unit_arr; Stop power units being added to +; DIMENSIONLESS (e.g. for VAR_UNITS=ppmv2); Change EVDC VAR_SI_CONVERSION values in +; var_unit_arr to match Envisat Metadata Guidelines values - Version 3.01 +; 20091117: EVDC VAR_SI_CONVERSION values set to the same as AVDC (only one var_unit_arr set); +; Fix bug which, in some cases, does not account for repeated units in VAR_UNITS when +; determining the VAR_SI_CONVERSION (e.g. VAR_UNITS=W m-2 sr-1 m-1); Put VAR_SI_CONVERSION +; units in power value order; Correctly scale multiple units by the power +; e.g. W2 = (m2 kg s-3)^2 (previously assumed only a single unit was being scaled); +; Generate error if third VAR_SI_CONVERSION value is 'DIMENSIONLESS' or 'NONE' but also +; includes extra values - Version 4.0b1 +; 20101001: Set up for GEOMS compliance e.g. DIMENSIONLESS changed to 1; MJD2000 changed to MJD2K; +; NONE removed - Version 4.0b2 +; 20101221: Bug fix - need to correctly account for when units cancel each other out. Was still +; assigned the value DIMENSIONLESS - Version 4.0b3 +; 20110303: Bug fix - when a dimensionless unit includes a power value (e.g. ppmv2), the base unit +; in VAR_SI_CONVERSION stays as '1'; Add 'dB' to var_unit_arr - Version 4.0b4 +; 20110719: Add 'pH' - Version 4.0b5 +; 20240908: Bug fix - Using the SORT command to put units in order of power value can give different +; results depending on the operating system when power values are identical. Change routine +; so the MAX command is used instead to create an array in descending power order +; - Version 4.0b6 +; 20240912: Reinstitute sorting power units with the SORT command and save this version as a second +; var_si_conv option (var_si_conv becomes a string array of size 2). This means the ordering +; of the units between the two var_si_conv values may differ - Version 4.0b7 +; +; Inputs: vuvalue - a string containing the Metadata VAR_UNITS value to be checked (everything +; after the '=') +; rd - integer flag to indicate if VAR_DATA_TYPE is real/double (-1) or not (1). Used to +; make VAR_SI_CONVERSION values of the same type +; tab_type - integer flag differentiating between AVDC (0) and original Envisat (1) styles +; +; Output: var_si_conv - a string array of size 2 containing the VAR_SI_CONVERSION value determined +; by the program (returns '' if an error is encountered). The 2 values may +; differ if there are multiple units with the same power value, due to using +; two different sort methods +; errstate - string describing an error state encountered during testing (o/w set to '') +; +; Called by: CHECK_METADATA +; +; External Subroutines Called: None + +var_unit_arr=['%;0;0.01;1','A;0;1;A','C;0;1;s A','cd;0;1;cd','d;0;86400;s','deg;0;1.74533E-2;rad',$ + 'degC;273.15;1;K','1;0;1;1','h;0;3600;s','Hz;0;1;s-1','J;0;1;m2 kg s-2','K;0;1;K',$ + 'l;0;1E-3;m3','lm;0;1;cd sr','lx;0;1;cd sr m-2','m;0;1;m','min;0;60;s',$ + 'MJD2K;0;86400;s','mol;0;1;mol','molec;0;1.66054E-24;mol','Np;0;1;1',$ + 'N;0;1E3;m kg s-2','Pa;0;1;m-1 kg s-2','pH;0;1E-12;m2 kg s-2 A-2','photons;0;1;photons',$ + 'ppbv;0;1E-9;1','ppmv;0;1E-6;1','pptv;0;1E-12;1','ppv;0;1;1','psu;0;1;psu',$ + 'rad;0;1;rad','s;0;1;s','sr;0;1;sr','V;0;1;m2 kg s-3 A-1','W;0;1;m2 kg s-3','kg;0;1;kg',$ + 'DU;0;4.4614E-4;mol m-2','Gal;0;1E-2;m s-2','dB;0;1;1'] + +unit_pre_arr=['Y;yotta;1E24','Z;zetta;1E21','E;exa;1E18','P;peta;1E15','T;tera;1E12','G;giga;1E9',$ + 'M;mega;1E6','k;kilo;1E3','h;hecto;1E2','da;deka;1E1','d;deci;1E-1','c;centi;1E-2',$ + 'm;milli;1E-3','u;micro;1E-6','n;nano;1E-9','p;pico;1E-12','f;femto;1E-15',$ + 'a;atto;1E-18','z;zepto;1E-21','y;yocto;1E-24'] + +;Set up text in case an error is found in the input VAR_UNITS value +errtxt=STRARR(2) +IF tab_type EQ 1 THEN errtxt[0]='No match with Table.Dat BASE_UNIT/UNIT_PREFIX values' $ +ELSE errtxt[0]='No match with Table Attribute Values file VAR_UNITS/UNIT_PREFIX values' +errtxt[1]='Not valid' +errtxt='VAR_UNITS='+vuvalue+': '+errtxt +var_si_conv=STRARR(2) & errstate='' ;initialize outputs + +nta=N_ELEMENTS(var_unit_arr) +;extract var_unit_arr/unit_pre_arr sub-values into vu/up arrays +vuhold=STRSPLIT(var_unit_arr[0],';',/Extract) ;test for number of sub-values +nvu=N_ELEMENTS(vuhold) & vu=STRARR(nvu,nta) +FOR j=0,nta-1 DO BEGIN + vuhold=STRSPLIT(var_unit_arr[j],';',/Extract) + vu[*,j]=vuhold +ENDFOR +nta=N_ELEMENTS(unit_pre_arr) +vuhold=STRSPLIT(unit_pre_arr[0],';',/Extract) ;test for number of sub-values +nup=N_ELEMENTS(vuhold) & up=STRARR(nup,nta) +FOR j=0,nta-1 DO BEGIN + vuhold=STRSPLIT(unit_pre_arr[j],';',/Extract) + up[*,j]=vuhold +ENDFOR + +;separate out metadata sub-values into component parts, and set-up holding arrays +vp=STRSPLIT(STRTRIM(vuvalue,2),' ',/Extract) & vpn=N_ELEMENTS(vp) +vpx=STRARR(2,vpn) ;0 holds VAR_UNIT value, 1 holds POWER component +bpx=STRARR(vpn) ;holding string array for base unit +ex=INTARR(vpn)+1 ;holding integer array for power value (defaults to 1) +tm=DBLARR(vpn) ;holding array for scale factor +j=0 +WHILE (j LE vpn-1) AND (errstate EQ '') DO BEGIN + ;test to see if the sub-value is a base unit in the TAV file + ti=WHERE(vp[j] EQ vu[0,*],tcnt) + IF tcnt NE 0 THEN vpx[0,j]=vp[j] $ it is a base unit + ELSE BEGIN ;separate out into unit and power values as required + vpx[0,j]=STRMID(vp[j],0,1) ;save first character of vp(j) + stopchk=0 + IF (STRLEN(vp[j]) GE 2) THEN BEGIN + FOR k=1,STRLEN(vp[j])-1 DO BEGIN + ah=STRMID(vp[j],k,1) + test1=(BYTE(ah) GE 65) AND (BYTE(ah) LE 90) ;A-Z + test2=(BYTE(ah) GE 97) AND (BYTE(ah) LE 122) ;a-z + IF (test1[0]) OR (test2[0]) THEN BEGIN + IF stopchk EQ 0 THEN vpx[0,j]=vpx[0,j]+ah ELSE stopchk=2 + ;if stopchk EQ 2 THEN this means that VAR_UNITS is not legal + ENDIF ELSE IF stopchk EQ 0 THEN BEGIN + stopchk=1 ;first non-alpha character so check for numeric or '-' character + test1=(BYTE(ah) EQ 45) OR ((BYTE(ah) GE 49) AND (BYTE(ah) LE 57)) ;- or 1-9 + IF NOT test1[0] THEN stopchk=2 ELSE vpx[1,j]=ah + ENDIF ELSE IF stopchk EQ 1 THEN BEGIN + ;need to check for a numeric character only + test1=(BYTE(ah) GE 48) AND (BYTE(ah) LE 57) ;0-9 + IF NOT test1[0] THEN stopchk=2 ELSE vpx[1,j]=vpx[1,j]+ah + ENDIF + ENDFOR + IF vpx[1,j] NE '' THEN ex[j]=FIX(vpx[1,j]) + ENDIF + IF stopchk EQ 2 THEN vpx[0,j]=vp[j] ;in the event that the value is not valid, so will create error + ;Do TAV check on the VAR_UNIT value + ti=WHERE(vpx[0,j] EQ vu[0,*],tcnt) + ENDELSE + + IF tcnt NE 0 THEN BEGIN ;VAR_UNIT is a BASE_VALUE + bemult=(DOUBLE(vu[nvu-2,ti[0]]))^ex[j] & tm[j]=bemult + bpx[j]=vu[nvu-3,ti[0]]+';'+STRTRIM(STRING(format='(E8.1)',bemult),2)+';'+vu[nvu-1,ti[0]] + ENDIF ELSE IF vpx[0,j] EQ 'g' THEN BEGIN ;check for VAR_UNIT EQ g (gram) for AVDC style TAV file + bemult=0.001d^ex[j] & tm[j]=bemult + bpx[j]='0;'+STRTRIM(STRING(format='(E8.1)',bemult),2)+';kg' + ENDIF ELSE BEGIN ;separate out vpx value into prefix and base-value and test + ;check for valid prefix - first check for 'deka' (da) + pref=STRMID(vpx[0,j],0,2) & bas=STRMID(vpx[0,j],2) + pi=WHERE(pref EQ up[0,*],pcnt) + IF pcnt EQ 0 THEN BEGIN ;test for the remaining prefixes + pref=STRMID(vpx[0,j],0,1) & bas=STRMID(vpx[0,j],1) + pi=WHERE(pref EQ up[0,*],pcnt) + ENDIF + IF pcnt NE 0 THEN BEGIN + pmult=DOUBLE(up[nup-1,pi[0]]) + ;check for valid base + ti=WHERE(bas EQ vu[0,*],tcnt) + IF tcnt NE 0 THEN BEGIN + bpmult=(DOUBLE(vu[nvu-2,ti[0]])*pmult)^ex[j] & tm[j]=bpmult + bpx[j]=vu[nvu-3,ti[0]]+';'+STRTRIM(STRING(format='(E8.1)',bpmult),2)+';'+vu[nvu-1,ti[0]] + ENDIF ELSE IF bas EQ 'g' THEN BEGIN ;check for VAR_UNIT EQ g (gram) for AVDC style TAV file + bpmult=(pmult*0.001D)^ex[j] & tm[j]=bpmult + bpx[j]='0;'+STRTRIM(STRING(format='(E8.1)',bpmult),2)+';kg' + ENDIF ELSE errstate=errtxt[0] + ENDIF ELSE errstate=errtxt[0] + ENDELSE + j=j+1 +ENDWHILE + +IF errstate EQ '' THEN BEGIN ;No errors detected so continue + ;Create VAR_SI_CONVERSION value + tmult=1.0D + FOR j=0,vpn-1 DO tmult=tmult*tm[j] + + ;reformat the multiplier e.g. 1.000E+003 becomes 1E3 + ;convert to Exponential form if necessary + IF (tmult EQ 273.15D) OR (tmult MOD 60.D EQ 0.D) OR ((tmult GE 0.01D) AND (tmult LT 1.D2) $ + AND (tmult*1.D4 MOD 1.D2 EQ 0.D)) THEN tmults=STRTRIM(STRUPCASE(tmult),2) $ ;keep the same format + ELSE tmults=STRTRIM(STRING(format='(E14.6)',tmult),2) + + epos=STRPOS(tmults,'E') & ppos=STRPOS(tmults,'.') + IF epos NE -1 THEN BEGIN ;remove unnecessary characters after 'E' + ep=FIX(STRMID(tmults,epos+1)) & epx=STRTRIM(ep,2) + tmults=STRMID(tmults,0,epos+1)+epx + ENDIF + IF ppos NE -1 THEN BEGIN ;remove any trailing zeroes after the decimal place + IF epos NE -1 THEN ep=STRMID(tmults,ppos+1,epos-(ppos+1)) ELSE ep=STRMID(tmults,ppos+1) + WHILE STRMID(ep,STRLEN(ep)-1,1) EQ '0' DO ep=STRMID(ep,0,STRLEN(ep)-1) + IF ep NE '' THEN tmults=STRMID(tmults,0,ppos+1)+ep ELSE tmults=STRMID(tmults,0,ppos) + IF epos NE -1 THEN tmults=tmults+'E'+epx + ENDIF + + ;Scale the units by the power e.g. W2 = (m2 kg s-3)^2 + FOR j=0,vpn-1 DO BEGIN + vsc=STRSPLIT(bpx[j],';',/EXTRACT) + vspl=STRSPLIT(vsc[2],' ',/EXTRACT,COUNT=vscnt) + sichk=STRARR(vscnt) & pwchk=sichk + IF (vpx[1,j] NE '') AND (vpx[1,j] NE '1') AND (vsc[2] NE '1') THEN BEGIN + bpx[j]=vsc[0]+';'+vsc[1]+';' + FOR k=0,vscnt-1 DO BEGIN + ;separate out SI units and power values + sires=STRSPLIT(vspl[k],'-0123456789',/Extract) + sichk[k]=sires[0] ;SI Unit + IF STRLEN(sichk[k]) NE STRLEN(vspl[k]) THEN $ + pwchk[k]=STRMID(vspl[k],STRLEN(sichk[k])) $ + ELSE pwchk[k]='1' ;Power value + pwm=FIX(pwchk[k])*FIX(vpx[1,j]) + IF k EQ 0 THEN sp='' ELSE sp=' ' + bpx[j]=bpx[j]+sp+sichk[k]+STRTRIM(pwm,2) + ENDFOR + ENDIF + ENDFOR + + ;Put together VAR_SI_CONVERSION + vsc=STRSPLIT(bpx[0],';',/EXTRACT) + ;IF vsc[2] EQ '1' THEN vpx[1,0]='' + var_si_conv[0]=vsc[0]+';'+tmults+';'+vsc[2] + ;add remaining base units to VAR_SI_CONVERSION + IF vpn GT 1 THEN BEGIN + FOR j=1,vpn-1 DO BEGIN + vsc=STRSPLIT(bpx[j],';',/Extract) + var_si_conv[0]=var_si_conv[0]+' '+vsc[2] + ENDFOR + ENDIF + var_si_conv[1]=var_si_conv[0] ;Default is that the 2nd entry is the same as the first + + ;check VAR_SI_CONVERSION for repeated SI units e.g. m m-3 becomes m-2 + schk=STRSPLIT(var_si_conv[0],' ;',/Extract) & scnt=N_ELEMENTS(schk) + IF scnt GT 3 THEN BEGIN ;more than one SI unit in VAR_SI_CONVERSION + sichk=STRARR(scnt-2) & pwchk=sichk + FOR j=0,scnt-3 DO BEGIN ;separate out SI units and power values + sires=STRSPLIT(schk[j+2],'-0123456789',/Extract) + sichk[j]=sires[0] ;SI Unit + IF STRLEN(sichk[j]) NE STRLEN(schk[j+2]) THEN $ + pwchk[j]=STRMID(schk[j+2],STRLEN(sichk[j])) $ + ELSE pwchk[j]='1' ;Power value + ENDFOR + j=0 & pwval=0 + WHILE j LT scnt-3 DO BEGIN + si=WHERE((sichk[j] NE '') AND (sichk[j] EQ sichk[j+1:scnt-3]),sicnt) + IF sicnt NE 0 THEN BEGIN + FOR k=0,sicnt-1 DO BEGIN + si[k]=si[k]+j+1 + pwval=FIX(pwchk[j])+FIX(pwchk[si[k]]) + IF (pwval[0] EQ 0) AND (k EQ sicnt-1) THEN BEGIN + sichk[j]='' & pwchk[j]='' + ENDIF ELSE IF (pwval[0] EQ 1) and (k EQ sicnt-1) THEN pwchk[j]='' $ + ELSE pwchk[j]=STRTRIM(pwval[0],2) + ;make null all the other SI values + sichk[si[k]]='' & pwchk[si[k]]='' + ENDFOR + ENDIF ELSE IF pwchk[j] EQ '1' THEN pwchk[j]='' + j=j+1 + ENDWHILE + ;Also do check on the power value of the last SI Unit + IF pwchk[scnt-3] EQ '1' THEN pwchk[scnt-3]='' + + ;Put units in power value order + ;Create two possible versions, one using the MAX command (primary) and another using SORT + ;Note: When using the SORT command, identical elements are sorted arbitrary and may vary between operating systems + FOR k=0,1 DO BEGIN + pwhold=pwchk + oi=WHERE(pwhold EQ '',ocnt) + IF ocnt NE 0 THEN pwhold[oi]='1' + IF k EQ 0 THEN BEGIN ;Do MAX order method + pws=INTARR(scnt-2) + ;determine minimum power value + mnp=MIN(FIX(pwhold)) + FOR j=0,scnt-3 DO BEGIN + mxp=MAX(FIX(pwhold),mxs) + pws[j]=mxs + pwhold[mxs]=mnp - 1 ;make it the lowest value + ENDFOR + sichk=sichk[pws] & pwchk=pwchk[pws] + ENDIF ELSE BEGIN ;Do SORT method (results are operating system dependent) + pws=SORT(FIX(pwhold)) + sichk=sichk[REVERSE(pws)] & pwchk=pwchk[REVERSE(pws)] + ENDELSE + var_si_conv[k]=schk[0]+';'+schk[1]+';'+sichk[0]+pwchk[0] + si=WHERE(sichk NE '',sicnt) + IF sicnt EQ 0 THEN var_si_conv[k]=var_si_conv[k]+'1' $ ;i.e. values cancelled out + ELSE BEGIN + FOR j=1,scnt-3 DO BEGIN + si=WHERE(sichk[0:j-1] NE '',sicnt) + IF (sichk[j] EQ '') OR (sicnt EQ 0) THEN var_si_conv[k]=var_si_conv[k]+sichk[j]+pwchk[j] $ + ELSE var_si_conv[k]=var_si_conv[k]+' '+sichk[j]+pwchk[j] + ENDFOR + ENDELSE + ENDFOR + ENDIF + + IF rd LT 0 THEN BEGIN + ;VAR_DATA_TYPE is Real or Double so make VAR_SI_CONVERSION values floating point + FOR k=0,1 DO BEGIN + tchkh=STRSPLIT(var_si_conv[k],';',/Extract) & tup=STRUPCASE(tchkh) + FOR j=0,1 DO BEGIN + IF STRPOS(tup[j],'.') EQ -1 THEN BEGIN + IF STRPOS(tup[j],'E') EQ -1 THEN tchkh[j]=tchkh[j]+'.0' $ + ELSE tchkh[j]=STRMID(tup[j],0,STRPOS(tup[j],'E'))+'.0'+STRMID(tup[j],STRPOS(tup[j],'E')) + ENDIF + ENDFOR + var_si_conv[k]=tchkh[0]+';'+tchkh[1]+';'+tchkh[2] + ENDFOR + ENDIF + + ;Check for invalid VAR_UNITS - third VAR_SI_CONVERSION is 1 plus extra values + FOR k=0,1 DO BEGIN + vsc=STRSPLIT(var_si_conv[k],';',/EXTRACT) + vsc[2]=STRTRIM(vsc[2],2) + IF (errstate EQ '') AND (STRMID(vsc[2],0,1) EQ '1') AND (STRLEN(vsc[2]) GT 1) THEN BEGIN + errstate=errtxt[1] & var_si_conv[k]='' + ENDIF + ENDFOR +ENDIF + +END ;procedure Var_Units_Test + + + +PRO read_tablefile, tablefile +;Procedure to identify the version number of the TAV file, read the contents of the +;LABELS/FIELDS (tab_arr), and determine the FILE_META_VERSION in the global attributes (tab_ver). +;This routine also creates a flag (tab_type) to determine whether the input file is an original +;table.dat file used by Envisat or the GEOMS TAV file, with the HDF/netCDF file generated +;accordingly. +; ---------- +;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org +; +;History: +; 20050802: Original IDLCR8HDF Routine - Version 1.0 +; 20061012: Bug-fix to separate semi-colons with a space when one immediately follows the other, +; so that the number of subvalues is correctly determined by the program. Common +; variable definition WIDGET_WIN added - Version 2.0 +; 20080302: Added code to differentiate between the AVDC TAV file and original Envisat table.dat. +; The tab_type flag is used to differentiate between the two formats. Note that some of the +; table.dat labels are renamed to match the equivalent TAV file labels for compatibility +; when testing Metadata entries - refer to EnviName/AVDCName arrays; Ensure that the TAV +; Version value is correctly formatted and is version 03 or greater - Version 3.0 +; 20100205: Add RETURN command after all STOP_WITH_ERROR calls, which allows program to return to the +; calling program if the reterr argument is included in the idlcr8hdf call - Version 3.09 +; 20110401: Change AVDC references to GEOMS; Test AVDC TAV file version is version 04 or greater; +; Remove check on format of TAV file version; Conform to new INFOTXT_OUTPUT reporting +; - Version 4.0b1 +; +;Input: Tablefile - Name of the file containing the Table Attributes. +; +;Outputs: tab_ver - String containing FILE_META_VERSION value. +; tab_arr - 2-D string array of size nf*mcnt+2, where nf=Number of Fields, and mcnt=maximum +; number of values detected in any one field. tab_arr(*,0) is the name of each field, +; and tab_arr(*,1) is the number of values in each field. +; tab_type - 0/1 where 0 identifies an AVDC format TAV file and 1 identifies an original +; Envisat format table.dat file. +; +;Called by: IDLCR8HDF +; +;Subroutines Called: STOP_WITH_ERROR (if error state detected); INFOTXT_OUTPUT +; Possible Conditions for STOP_WITH_ERROR call (plus [line number] where called): +; 1. Table Attribute Values file version not identified +; 2. Envisat table.dat ASC2HDF program version not found +; 3. First Envisat table.dat field value not found +; 4. Table Attribute Values file version is not in a valid format +; +; Information Conditions (when the program reports issues and continues): +; 1. [Original EVDC]/[GEOMS] Reporting Guidelines Apply (depending on type of Table Attribute +; Values file read in) +; 2. Old version of the TAV file in use. Update from http://avdc.gsfc.nasa.gov/Tools + +COMMON TABLEDATA +COMMON WIDGET_WIN + +;Possible error messages for this procedure +proname='Read_TableFile procedure: ' +errtxt=STRARR(4) +errtxt[0]='Table Attribute Values file version not identified' +errtxt[1]='Envisat table.dat ASC2HDF program version not found' +errtxt[2]='First Envisat table.dat field value not found' +errtxt[3]='Table Attribute Values file version is invalid (should be ddRddd or ddRdddd): ' +FOR i=0,2 do errtxt[i]=errtxt[i]+' with the search criteria used by this program' + +;Array of Envisat Field names to be changed to equivalent AVDC Field names +enviname=['_NAME','_AFFILIATION','DATA_VARIABLES_00_00','BASE_UNIT'] +avdcname=['ORIGINATOR','AFFILIATION','DATA_VAR_ALL_00_00','VAR_UNITS'] + +ON_IOERROR,TypeConversionError +dum='' & tab_ver='' +min_fmv=4 ;TAV Version must be GE this value e.g. 04R001, 06R002 but not 03R004 +OPENR,lu,tablefile,/GET_LUN +;determine TAV file version +REPEAT BEGIN + READF,lu,dum + dumup=STRUPCASE(dum) + envitest=STRPOS(STRCOMPRESS(dumup,/Remove_all),'!TABLE.DATVERSION') NE -1 + avdctest=STRPOS(STRCOMPRESS(dumup,/Remove_all),'!VERSION') NE -1 +ENDREP UNTIL (envitest) OR (avdctest) OR (EOF(lu)) +IF EOF(lu) THEN BEGIN + STOP_WITH_ERROR,o3[3]+proname,errtxt[0],lu & RETURN +ENDIF +res=STRSPLIT(dumup,' ',/Extract) & vi=WHERE(res EQ 'VERSION') +IF N_ELEMENTS(res) LE vi[0]+1 THEN BEGIN + STOP_WITH_ERROR,o3[3]+proname,errtxt[0]+': '+dum,lu & RETURN +ENDIF +IF envitest THEN BEGIN + tab_type=1 & infotxt='0 EVDC original style table.dat' + n_title=4 +ENDIF ELSE BEGIN + tab_type=0 & infotxt='0 GEOMS compliant Table Attribute Values' + n_title=5 +ENDELSE +infotxt=infotxt+' file input. '+STRMID(infotxt,2,n_title)+' Reporting Guidelines apply' +INFOTXT_OUTPUT,infotxt + +;Ensure Meta Version has a valid format +;i. Check third character is an 'R' +;ii. Check number of characters is 6 or 7 +;iii. Check dd and ddd(d) are numeric +;iv. Check that version number is 03 or greater for AVDC file type +;v. Issue warning and convert to ddRddd if format is ddRdddd +fmv=res[vi[0]+1] ;File_Meta_Version +valid=0 ;set to test for valid string to number conversion +FOR i=0,STRLEN(fmv)-1 DO IF i NE 2 THEN fmvtest=FIX(STRMID(fmv,i,1)) +fmvtest=FIX(STRMID(fmv,0,2)) ;To test for TAV version 'min_fmv' or greater +valid=1 ;FILE_META_VERSION characters are numeric (except for the 'R') if program gets to here +TypeConversionError: +IF (STRMID(fmv,2,1) NE 'R') OR (STRLEN(fmv) lt 6) OR (STRLEN(fmv) gt 7) OR $ + (valid EQ 0) THEN BEGIN + STOP_WITH_ERROR,o3[3]+proname,errtxt[3]+fmv,lu & RETURN +ENDIF + +IF (tab_type EQ 0) AND (fmvtest LT min_fmv) THEN BEGIN + infotxt=STRARR(2) + infotxt[0]='3 Old version of the Table Attribute Values file used as input: ' + infotxt[0]=infotxt[0]+' TAV Version '+fmv + infotxt[1]=' Please update from http://avdc.gsfc.nasa.gov/Tools' + INFOTXT_OUTPUT,infotxt +ENDIF + +IF envitest THEN BEGIN ;identify asc2hdf version in table.dat and read past the CHECK_ATTRIBUTE line + WHILE (STRPOS(STRUPCASE(dum),'ASC2HDF') EQ -1) AND (NOT EOF(lu)) DO READF,lu,dum + IF EOF(lu) THEN BEGIN + STOP_WITH_ERROR,o3[3]+proname,errtxt[1],lu & RETURN + ENDIF + cr8_hdf_ver=';IDLCR8HDF' + dum=STRTRIM(dum,2) + WHILE ((STRMID(dum,0,1) EQ '!') OR (STRMID(dum,0,1) EQ '#') OR (dum EQ '')) AND $ + (NOT EOF(lu)) DO BEGIN + READF,lu,dum & dum=STRTRIM(dum,2) + ENDWHILE + IF EOF(lu) THEN BEGIN + STOP_WITH_ERROR,o3[3]+proname,errtxt[2],lu & RETURN + ENDIF + READF,lu,dum ;to get to the line after 'CHECK_ATTRIBUTE' +ENDIF ELSE cr8_hdf_ver=';IDLCR8HDF' +tab_ver=fmv+cr8_hdf_ver ;= the FILE_META_VERSION input value in the global attributes + +;determine no. of FIELDS/LABELS as well as the maximum number of elements +nf=0 & mcnt=0 & firstfield='' +dum=STRTRIM(dum,2) +WHILE NOT EOF(lu) DO BEGIN + ncnt=-1 + IF (STRMID(dum,0,1) NE '!') AND (STRMID(dum,0,1) NE '#') AND $ + (STRMID(dum,0,1) NE '=') AND (dum NE '') THEN BEGIN + + FOR i=0,N_ELEMENTS(enviname)-1 DO BEGIN + epos=STRPOS(STRUPCASE(dum),enviname[i]) + IF (epos NE -1) AND (epos LE 3) THEN ncnt=i + ENDFOR + IF (ncnt EQ 0) OR (ncnt EQ 1) THEN BEGIN + IF STRMID(STRUPCASE(dum),0,3) NE 'PI_' THEN nf=nf-1 ELSE ecnt=0 + ;This puts all PI_,DO_, and DS_NAME or AFFILIATION values into either the ORIGINATOR or AFFILIATION fields + ENDIF ELSE ecnt=0 + + nf=nf+1 + IF firstfield EQ '' THEN firstfield=dum + IF ncnt NE -1 THEN dum=avdcname[ncnt] ;change the name of the Envisat field to AVDC equivalent + REPEAT BEGIN + READF,lu,dum & dum=STRTRIM(dum,2) + IF STRMID(dum,0,1) EQ '=' THEN ecnt=ecnt+1 + ENDREP UNTIL (STRMID(dum,0,1) NE '=') OR (EOF(lu)) + IF ecnt GT mcnt THEN mcnt=ecnt + ENDIF ELSE IF NOT EOF(lu) THEN BEGIN + READF,lu,dum & dum=STRTRIM(dum,2) + ENDIF +ENDWHILE +FREE_LUN,lu + +;read in the contents of the file +tab_arr=STRARR(nf,mcnt+2) ;note tab_arr(*,0) EQ FIELD/LABEL name and +;tab_arr(*,1) EQ N_ELEMENTS under each FIELD/LABEL +tab_hold=STRARR(mcnt) + +OPENR,lu,tablefile,/GET_LUN +READF,lu,dum & dum=STRTRIM(dum,2) +WHILE dum NE firstfield DO BEGIN + READF,lu,dum & dum=STRTRIM(dum,2) +ENDWHILE + +i=0 +WHILE i LE nf-1 DO BEGIN + ncnt=-1 + FOR j=0,N_ELEMENTS(enviname)-1 DO BEGIN + epos=STRPOS(STRUPCASE(dum),enviname[j]) + IF (epos NE -1) AND (epos LE 3) THEN ncnt=j + ENDFOR + IF (ncnt EQ 0) OR (ncnt EQ 1) THEN BEGIN + IF STRMID(STRUPCASE(dum),0,3) NE 'PI_' THEN i=i-1 ELSE ecnt=0 + ENDIF ELSE ecnt=0 + IF ncnt NE -1 THEN dum=avdcname[ncnt] ;change the name of the Envisat field to AVDC equivalent + tab_arr[i,0]=dum + REPEAT BEGIN + READF,lu,dum & dum=STRTRIM(dum,2) + IF STRMID(dum,0,1) EQ '=' THEN BEGIN + tab_hold[ecnt]=STRMID(dum,1) ;strip the '=' sign + ;add space between adjacent semi-colons so StrSplit finds correct number of sub-values + REPEAT BEGIN + cpos=STRPOS(tab_hold[ecnt],';;') + IF cpos NE -1 THEN tab_hold[ecnt]=STRMID(tab_hold[ecnt],0,cpos+1)+ $ + ' '+STRMID(tab_hold[ecnt],cpos+1) + ENDREP UNTIL cpos EQ -1 + ecnt=ecnt+1 + ENDIF + ENDREP UNTIL (STRMID(dum,0,1) NE '=') OR (EOF(lu)) + tab_arr[i,1]=STRTRIM(ecnt,2) + tab_arr[i,2:ecnt+1]=tab_hold[0:ecnt-1] + IF i NE nf-1 THEN BEGIN + WHILE (STRMID(dum,0,1) EQ '!') OR (STRMID(dum,0,1) EQ '#') OR (dum EQ '') DO BEGIN + READF,lu,dum & dum=STRTRIM(dum,2) + ENDWHILE + ENDIF + i=i+1 +ENDWHILE +FREE_LUN,lu + +END ;Procedure Read_TableFile + + + +PRO test_file_input,aname,fentry +;Procedure to test that a file name given as an entry to a free text attribute is valid, based on Envisat +;Metadata Guidelines (not currently used by AVDC). Note: No longer permitted under GEOMS guidelines. +; ---------- +;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org +; +; History: +; 20050802: Original IDLCR8HDF Routine - Version 1.0 +; 20061012: Common variable definition WIDGET_WIN added - Version 2.0 +; 20100205: Add RETURN command after all STOP_WITH_ERROR calls, which allows program to return to the +; calling program if the reterr argument is included in the idlcr8hdf call - Version 3.09 +; +; Inputs: aname - Global or Variable Attribute Label +; fentry - Filename entry used as the Global or Variable Attribute value +; +; Outputs: None +; +; Called by: READ_METADATA +; +; Subroutines Called: STOP_WITH_ERROR (if error state detected) +; Possible Conditions for STOP_WITH_ERROR call (plus [line number] where called): +; 1. Syntax of Filename entry incorrect +; 2. Filename entry not found or not usable +; 3. File size is too large + +COMMON WIDGET_WIN + +sfile=4096 ;maximum permitted file size + +;possible error message for this procedure +proname='Test_File_Input procedure: ' +errtxt2=STRARR(3) & lu=-1 +errtxt2[0]='Syntax of Filename entry incorrect: ' +errtxt2[1]='Filename entry not found or not usable: ' +errtxt2[2]='File size is too large (maximum permitted: '+STRTRIM(sfile,2)+' bytes)' + +si=STRPOS(fentry,'"')+1 & ei=STRPOS(fentry,'"',/Reverse_Search)-si +IF ei EQ si THEN BEGIN + STOP_WITH_ERROR,o3[3]+proname+aname+': ',errtxt2[0]+fentry,lu & RETURN +ENDIF +faname=STRMID(fentry,si,ei) +ftest=FILE_TEST(faname,/Read,/Regular) +IF ftest EQ 0 THEN BEGIN + STOP_WITH_ERROR,o3[3]+proname+aname+': ',errtxt2[1]+faname,lu & RETURN +ENDIF +OPENR,fu,faname,/GET_LUN +ftest=FSTAT(fu) & FREE_LUN,fu +IF ftest.size GT sfile THEN BEGIN + STOP_WITH_ERROR,o3[3]+proname+AName+'='+fentry+': ',errtxt2[2],lu & RETURN +ENDIF + +END ;procedure Test_File_Input + + + +PRO geoms_rule_changes, code, in1, in2, in3, in4 +;Procedure to check Metadata for old/redundant rules, labels and/or values and update/report +;as required +; ---------- +;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org +; +; History: +; 20110401: Introduced. Incorporates GEOMS rules changes from v3.0 to v4.0 - Version 4.01 +; 20111220: Add ISO646-US ASCII character set check - Version 4.0b7 +; 20120313: Add UVVIS.DOAS plus additional gases to Code 6 checks - Version 4.0b8 +; 20120703: Bug Fix when checking for non-ASCII characters (rule 10) - Version 4.0b11 +; 20131023: Add GEOMS rule change for MIXING.RATIO[.VOLUME][.MASS], and UNCERTAINTY (rule 5); +; Fixed bug that caused incorrect rep_list_2 values to be written to file (rule 5); +; Stopped checks for illegal characters if ORIGINATOR values are present in the +; TAV file (rule 10) - Version 4.0b16 +; 20131029: Add array of invalid ASCII characters that can be replaced with valid ISO646 US +; ASCII characters (rule 10) - Version 4.0b17 +; 20131029: Modify UNCERTAINTY rules to allow more exceptions (rule 5); Allow for renaming of +; LIDAR.DIAL DATA_SOURCE to LIDAR.WATERVAPORDIAL if conditions met (rule 6) - +; Version 4.0b18 +; 20140226: Add 'DU' to obsolete descriptor value and fix bug causing no new variable name to +; be generated if more than 3 sub-values in the name (rule 5); Add 'RO.SAC.C' to +; the obsolete DATA_SOURCE list (rule 6) - Version 4.0b19 +; 20140521: Account for extra parameter in the UVVIS-DOAS data source when trying to determine +; if file is based on a DATA_TEMPLATE (rule 9) - Version 4.0b21 +; 20140806: Modify UNCERTAINTY rules to allow more exceptions including additional instrument +; types and check on molec cm-2 to allow for prefix values (rule 5) - Version 4.0b22 +; 20140909: Add RO.F3C.FM[1][2][3] to the obsolete list of DATA_SOURCES (replaced by +; F3C.FM[1][2][3]) (rule 6) - Version 4.0b23 +; 20141110: Numerous changes while testing harmonization of EVDC files - additional +; DATA_SOURCEs and units added to identify correct new UNCERTAINTY values, including +; identification of covariance uncertainties; Bug fix when doing checks on +; AIR.CONCENTRATION; LIDAR.BACKSCATTER conversion and units definition added; List +; of non-ASCII characters to be checked expanded; Bug fix when correcting +; for non-ASCII characters when the replacement value has more than one character +; e.g. ' deg. ' - Version 4.0b24 +; 20150409: Strip trailing and leading spaces from DATA_TEMPLATE value when doing checks +; (rule 9) - Version 4.0b28 +; 20160213: Add extra satellite instruments to the obsolete list for DATA_SOURCE (rule 6); +; Fix issue with DATA_TEMPLATE identification (rule 9) - Version 4.0b37 +; 20171121: Fix bug that selected the incorrect DATA_TEMPLATE in some situations (rule 9) +; - Version 4.0b43 +; 20190506: Add FILE_META_VERSION information to INFORMATION/ERROR comment when +; DATA_TEMPLATE value is not as expected; Fix bug that caused a crash if the +; file does not use a DATA_TEMPLATE (rule 9) - Version 4.0b50 +; 20191205: Add rule 11 which does checks on optional VERSION_NAME sub-field of +; DATA_SOURCE - Version 4.0b53 +; 20200709: Fix in option 5 when looking for obsolete variable names. Change from doing a +; STRPOS search to looking for an exact change for DATA_VARIABLES_01 names due to +; introduction of DRY.AIR.... names to the TAV - Version 4.0b55 +; 20201020: Change infotxt status from 1 to 2 in option 5; Change to LIDAR.H2O data source +; if needing to update out-of-date H2O LIDAR DATA_SOURCE values in option 6 +; - Version 4.0b56 +; 20220805: Change INFOTXT message in rule 10 (non-ASCII character check) so it includes +; the entire Metadata entry - Version 4.0b60 +; +; Inputs: code - Integer value identifying type of check to carry out +; in1 - First set of inputs required for checks (optional, dependent on code value) +; in2 - Second set of inputs required for checks (optional, dependent on code value) +; in3 - Third set of inputs required for checks (optional, dependent on code value) +; in4 - Fourth set of inputs required for checks (optional, dependent on code value) +; +; Outputs: Any or all of the inputs can be changed dependent on the code value +; +; Called by: READ_METADATA; CHECK_METADATA; SET_UP_STRUCTURE; FIND_HDF_FILENAME +; +; Subroutines Called: INFOTXT_OUTPUT +; Information Conditions (when the program is able to make changes): +; 1. Attribute entry is not GEOMS compliant, and removed from the metadata saved to the output file +; 2. Dataset name renamed according to new DATETIME reporting conventions +; 3. Double underscore replaced with a single underscore in the variable name +; 4. VAR_DEPEND value made self-referencing for axis variable +; 5. Axis variable cannot be dependent on another variable +; 6. VAR_UNITS=MJD2000 not GEOMS compliant, changed to VAR_UNITS=MJD2K +; 7. DATA_FILE_VERSION must be in the form 'nnn' +; 8. Obsolete Dataset name renamed +; 9. Obsolete DATA_SOURCE value renamed +; 10. Obsolete FILE_ACCESS values renamed +; 11. Rename obsolete VAR_UNITS=DIMENSIONLESS/NONE values +; 12. DATA_TEMPLATE field is not present in the TAV file +; 13. DATA_TEMPLATE value renamed based on DATA_SOURCE value +; 14. Entry must only include valid characters from the ISO646-US ASCII character set +; +; Code 0: Check for Attributes that have become redundant between v3.0 and v4.0. +; Inputs: in1=mh, in2=mhgood +; Code 1: Check for redundant _START/STOP/INTEGRATION.TIME variable names and replace with +; DATETIME.START/STOP/INTEGRATION.TIME (for single occurences only). Also check +; for double underscores in the variable names and replace with single underscore +; Inputs: in1=vncnt, in2=dv +; Code 2: Check for Axis Variables and, if found, make VAR_DEPEND=INDEPENDENT self-referencing +; Inputs: in1=vardeptest, in2=vn[vc], in3=resvd[1], in4=holdvd +; Code 3: Change VAR_UNIT value MJD2000 to MJD2K +; Inputs: in1=meta_arr[i], in2=res[1], in3=writeonce +; Code 4: Do DATA_FILE_VERSION checks +; Inputs: in1=dfvv (DATA_FILE_VERSION value) +; Code 5: Look for obsolete DATA_VARIABLES (based on list) and, if possible, modify values to +; GEOMS compliance +; Inputs: in1=vncnt, in2=dv, in3=vuv, in4=data_source +; Code 6: Look for obsolete DATA_SOURCE (based on list) and, if possible, modify values to +; GEOMS compliance +; Inputs: in1=vncnt, in2=dv, in3=data_source +; Code 7: Looks for and replaces obsolete CALVAL and NDSC FILE_ACCESS values +; Inputs: in1=facnt, in2=fav +; Code 8: Looks for VAR_UNITS=DIMENSIONLESS or NONE and replace with '' or '1' +; Inputs: in1=vucnt, in2=vuv, in3=vdv +; Code 9: Do DATA_TEMPLATE/DATA_QUALITY checks +; Inputs: in1=m_v0, in2=m_v1, in3=ga_chk +; Code 10: Do checks on ISO646-US ASCII character set +; Inputs: in1=meta_arr or dtest, in2=vn[vc] (Variable Name for datasets only) +; Code 11: Do checks on DATA_VERSION_NAME part of DATA_SOURCE value +; Inputs: in1=DATA_VERSION_NAME, in2=meta_arr + +COMMON TABLEDATA +COMMON METADATA +COMMON DATA +COMMON WIDGET_WIN + +CASE 1 OF + code EQ 0: BEGIN + ;Check for redundant attributes + redundant=['DATA_TYPE','DATA_LEVEL','VAR_MONOTONE','VAR_DIMENSION',$ + 'VAR_AVG_TYPE','VIS_LABEL','VIS_FORMAT','VIS_PLOT_TYPE',$ + 'VIS_SCALE_TYPE','VIS_SCALE_MIN','VIS_SCALE_MAX'] + nrd=N_ELEMENTS(redundant) + + FOR i=0,nrd-1 DO BEGIN + rdl=STRLEN(redundant[i]) + fai=WHERE(STRMID(STRUPCASE(in1),0,rdl) EQ redundant[i],facnt) + IF facnt NE 0 THEN BEGIN + in2[fai]=0 ;Metadata lines not wanted + test1=(STRMID(redundant[i],0,3) EQ 'VAR') OR (STRMID(redundant[i],0,3) EQ 'VIS') + IF test1 THEN att_type='Variable' ELSE att_type='Global' + infotxt='2 '+redundant[i]+' not a GEOMS '+att_type+' attribute|.' + infotxt=infotxt+' Removed from the metadata saved to the output file' + INFOTXT_OUTPUT, infotxt + ENDIF + ENDFOR + END ;Case 0 + + code EQ 1: BEGIN + ;Check for redundant _START/STOP sub-values and replace with + ;DATETIME.START/STOP/INTEGRATION.TIME (for single occurences only) + time_attr=['START','STOP','INTEGRATION','RESOLUTION'] + n_tim=N_ELEMENTS(time_attr) + iscnt=0 ;Used for RESOLUTION/INTEGRATION.TIME checks + FOR i=0,n_tim-1 DO BEGIN + si=WHERE(STRPOS(in2,'_'+time_attr[i]+'.TIME') NE -1,scnt) + IF (scnt EQ 1) AND (iscnt EQ 0) THEN BEGIN ;change name and also relevant VAR_NAME if present + IF i EQ 2 THEN iscnt=1 ;used in the event both INTEGRATION and RESOLUTION.TIME are present + vnchange[si[0]]=in2[si[0]] + IF i GE 2 THEN in2[si[0]]=time_attr[2]+'.TIME' $ + ELSE in2[si[0]]='DATETIME.'+time_attr[i] + IF qa_yes THEN itxt=' expected to be ' ELSE itxt=' renamed ' + infotxt='2 Dataset name '+vnchange[si[0]]+itxt+in2[si[0]] + INFOTXT_OUTPUT,infotxt + ENDIF + ENDFOR + ;Check for double underscore and replace with single underscore + FOR i=0,in1-1 DO BEGIN + IF STRPOS(in2[i],'__') NE -1 THEN BEGIN + vnchange[i]=in2[i] + infotxt=STRARR(2) + infotxt[0]='2 Double underscore present in Variable Name '+in2[i]+'|' + infotxt[1]=' replaced with a single underscore' + INFOTXT_OUTPUT, infotxt + vns=STRSPLIT(in2[i],'_',/Extract,COUNT=n_vns) + FOR j=0,n_vns-1 DO IF j EQ 0 THEN in2[i]=vns[j] ELSE in2[i]=in2[i]+'_'+vns[j] + ENDIF + ENDFOR + END ;Case 1 + + code EQ 2: BEGIN ;Check for and Apply GEOMS rule changes for INDEPENDENT and axis variables + ;RULE: Any variable that is mentioned in VAR_DEPEND is by definition an axis variable + vdtest=STRUPCASE(in1) & vnv=STRUPCASE(in2) & vdv=STRUPCASE(in3) ;vdtest is an array of VAR_DEPEND variables + avi=WHERE(vnv EQ vdtest,avcnt) ;i.e. is the VAR_NAME an axis variable + IF avcnt NE 0 THEN BEGIN ;possible axis variable so do axis variable checks + test1=(vdv EQ 'INDEPENDENT') OR (vdv EQ vnv) ;value is independent or self-referencing + test2=(vdv NE 'INDEPENDENT') AND (vdv NE vnv) AND (vdv NE 'CONSTANT') ;neither independent, self-referencing, nor constant + test6=(vdv EQ 'CONSTANT') AND (vnv EQ 'DATETIME') ;i.e. VAR_NAME=DATETIME and VAR_DEPEND=CONSTANT + IF (test1) OR (test6) THEN BEGIN + IF vdv NE vnv THEN BEGIN + infotxt='2 '+in4+' should be self-referencing for axis variable VAR_NAME='+vnv+'|.' + infotxt=infotxt+' VAR_DEPEND value changed in metadata' + INFOTXT_OUTPUT,infotxt + in3=vnv ;self-referencing VAR_DEPEND value + ENDIF + ENDIF ELSE IF test2 THEN BEGIN + in4[0]='E1' ;i.e. axis variable cannot be dependent on another variable so return error + ENDIF + ENDIF ELSE BEGIN ;not an axis variable so ensure VAR_DEPEND value is not self-referencing + IF vdv EQ vnv THEN in4[0]='E3' ;i.e. VAR_DEPEND value cannot be self-referencing + ENDELSE + END ;Case 2 + + code EQ 3: BEGIN ;Change MJD2000 to MJD2K + in1='VAR_UNITS=MJD2K' & in2='MJD2K' + IF in3 EQ 0 THEN BEGIN ;First instance of MJD2000 so include comment + infotxt='2 VAR_UNITS=MJD2000 not GEOMS compliant|. Changed to VAR_UNITS=MJD2K' + INFOTXT_OUTPUT,infotxt + ENDIF + END + + code EQ 4: BEGIN ;checks on DATA_FILE_VERSION + in1=STRTRIM(in1,2) + vp=0 & errval=0 & in1h=in1 + ON_IOERROR,TypeConversionError + IF STRMID(STRUPCASE(in1),0,1) EQ 'V' THEN BEGIN + vp=1 & in1=STRMID(in1,1) + ENDIF + dpos=STRPOS(in1,'.') + valid=0 ;to check for conversion error + IF dpos NE -1 THEN BEGIN + IF LONG(STRMID(in1,dpos+1)) EQ 0L THEN atxt='' ELSE atxt=STRMID(in1,dpos+1) + in1=STRMID(in1,0,dpos)+atxt + IF FIX(in1) GE 100 THEN in1=STRMID(in1,0,dpos) ;just use version up to the decimal point + ENDIF + IF (FIX(in1) GT 999) OR (FIX(in1) LT 1) THEN errval=1 + valid=1 ;OK if got to here + + TypeConversionError: ;Check for invalid DATA_FILE_VERSION value + IF (valid EQ 0) OR (errval EQ 1) THEN BEGIN + infotxt='3 DATA_FILE_VERSION='+in1h+' must be in the form ''nnn''' + INFOTXT_OUTPUT, infotxt + in1=in1h + ENDIF ELSE BEGIN ;format value correctly (nnn) + IF FIX(in1) LT 10 THEN in1='00'+STRTRIM(FIX(in1),2) $ + ELSE IF FIX(in1) LT 100 THEN in1='0'+STRTRIM(FIX(in1),2) $ + ELSE in1=STRTRIM(in1,2) + IF (vp EQ 1) OR (in1h NE in1) THEN BEGIN + infotxt='2 DATA_FILE_VERSION must be in the form ''nnn''|. Renamed from ' + infotxt=infotxt+in1h+' to '+in1 + INFOTXT_OUTPUT, infotxt + ENDIF + ENDELSE + END + + code EQ 5: BEGIN ;Look for obsolete DATA_VARIABLES + ;Notes = what to do about ALTITUDE.LAYER.INDEX, CHL.A.CONCENTRATION, + ; PRESSURE.LEVEL.INDEX (still in 04R001), TSM.CONCENTRATION + ;List of obsolete DATA_VARIABLE_01 values + obs_list_1=['AIR.TEMPERATURE','TEMPERATURE.AIR','COLUMN.VERTICAL','H2O.RELATIVE.HUMIDITY',$ + 'PRESSURE.SURFACE','TEMPERATURE.SURFACE','TEMPERATURE.INTERNAL.BOX',$ + 'TEMPERATURE.INTERNAL.INSTRUMENT','TEMPERATURE.SEA.SURFACE','AIR.NUMBER.DENSITY',$ + 'SEA.SURFACE.TEMPERATURE','OPACITY.ATMOSPHERIC.VERTICAL'] + ;Corresponding list of replacement DATA_VARIABLE_1 values (direct replacement) + rep_list_1=['TEMPERATURE','TEMPERATURE','COLUMN','HUMIDITY.RELATIVE',$ + 'SURFACE.PRESSURE','SURFACE.TEMPERATURE','INTERNAL.BOX.TEMPERATURE',$ + 'INTERNAL.INSTRUMENT.TEMPERATURE','SEA.SURFACE.TEMPERATURE','NUMBER.DENSITY',$ + 'SEA.SURFACE.TEMPERATURE.SKIN','OPACITY.ATMOSPHERIC'] + ;Obsolete DATA_VARIABLE_01 which need special handling + obs_list_spec_1=['.AMF','.AVK','.CONCENTRATION','.MIXING.RATIO'] + ;Obsolete DATA_VARIABLE_02 values + obs_list_2=['SOLAR','VERTICAL.SOLAR',$ + 'EMISSION.VERTICAL','VERTICAL.EMISSION',$ + 'VERTICAL','SLANT','SOLAR.OCCULTATION','VERTICAL.ZENITH','VERTICAL.NADIR',$ + 'VERTICAL.LUNAR','VERTICAL.SOLAR.FOCUS'] + ;[0,1] for FTIR/UVVIS (except Brewer and Dobson) only, [2,3] for MWR only + ;Corresponding list of replacement DATA_VARIABLE_2 values (direct replacement) + rep_list_2=['ABSORPTION.SOLAR|SCATTER.SOLAR','ABSORPTION.SOLAR|SCATTER.SOLAR',$ + 'EMISSION','EMISSION',$ + '','','OCCULTATION.SOLAR','ZENITH','NADIR',$ + 'ABSORPTION.LUNAR','ABSORPTION.SOLAR.FOCUS'] + ;Replacements for .CONCENTRATION - dependent on VAR_UNITS + rep_list_conc=['.MIXING.RATIO.VOLUME','.MIXING.RATIO.MASS','.NUMBER.DENSITY','.PARTIAL.PRESSURE'] + ;Conditional Miscellaneous changes + misc_list_1=['TEMPERATURE_SKIN','AIR.MASS.FACTOR'] + ;Corresponding change to miscellaneous variables + misc_list_2=['SEA.SURFACE.TEMPERATURE.SKIN','O3.COLUMN_AMF'] + ;Obsolete DATA_VARIABLE_03 descriptors (may or may not be able to fix these) + obs_list_3=['DU','UNCERTAINTY.STDEV','UNCERTAINTY.RMS','UNCERTAINTY.RELATIVE','UNCERTAINTY.TOTAL', $ + 'UNCERTAINTY.RANDOM','UNCERTAINTY.SYSTEMATIC'] + ;Corresponding list of replacement DATA_VARIABLE_03 descriptors + rep_list_3=['','UNCERTAINTY.COMBINED.STANDARD','UNCERTAINTY.COMBINED.STANDARD', $ + 'UNCERTAINTY.COMBINED.STANDARD.RELATIVE','UNCERTAINTY.COMBINED.STANDARD', $ + 'UNCERTAINTY.RANDOM.STANDARD','UNCERTAINTY.SYSTEMATIC.STANDARD'] + rep_list_4=['','','','','UNCERTAINTY.COMBINED.COVARIANCE','UNCERTAINTY.RANDOM.COVARIANCE', $ + 'UNCERTAINTY.SYSTEMATIC.COVARIANCE'] + vn_v=STRARR(in1,3) & vn_new=STRARR(in1) + ;Separate out Variable Name/[Mode or Descriptor]/[Descriptor] + FOR i=0,in1-1 DO BEGIN + res=STRSPLIT(in2[i],'_',/EXTRACT,COUNT=nres) + IF nres GT 3 THEN nres=3 + FOR j=0,nres-1 DO vn_v[i,j]=res[j] + ENDFOR + ;Test for values in obs_list_1 + FOR i=0,N_ELEMENTS(obs_list_1)-1 DO BEGIN + ;ri=WHERE(STRPOS(STRUPCASE(vn_v[*,0]),obs_list_1[i]) NE -1,rcnt) + ;Change to exact match from v4.0b55 (20200709) - due to introduction of DRY.AIR.... variable names + ri=WHERE(STRUPCASE(vn_v[*,0]) EQ obs_list_1[i],rcnt) + IF rcnt NE 0 THEN BEGIN ;obsolete values found so replace + vlen=STRLEN(obs_list_1[i]) + FOR j=0,rcnt-1 DO BEGIN + change_val=1 + ;Special case for SEA.SURFACE.TEMPERATURE (only change if mode is _SKIN) + IF obs_list_1[i] EQ 'SEA.SURFACE.TEMPERATURE' THEN BEGIN + IF STRUPCASE(vn_v[ri[j],1]) EQ 'SKIN' THEN vn_v[ri[j],1]='' ELSE change_val=0 + ENDIF + IF change_val EQ 1 THEN BEGIN + vpos=STRPOS(STRUPCASE(vn_v[ri[j],0]),obs_list_1[i]) + vn_v[ri[j],0]=STRMID(vn_v[ri[j],0],0,vpos)+rep_list_1[i]+STRMID(vn_v[ri[j],0],vpos+vlen) + ENDIF + ENDFOR + ENDIF + ENDFOR + ;Special cases for DATA_VARIABLE_01 values .AMF, .AVK, .CONCENTRATION, and .MIXING.RATIO + nols1=N_ELEMENTS(obs_list_spec_1) + FOR i=0,nols1-1 DO BEGIN + ri=WHERE(STRPOS(STRUPCASE(vn_v[*,0]),obs_list_spec_1[i]) NE -1,rcnt) + IF (i EQ nols1-1) AND (rcnt NE 0) THEN BEGIN + ;do check for .MIXING.RATIO. in which case no correction required + rxi=WHERE(STRPOS(STRUPCASE(vn_v[ri,0]),'.MIXING.RATIO.') EQ -1,rxcnt) + IF rxcnt NE 0 THEN BEGIN ;some values are just .MIXING.RATIO so correct these only + rcnt=rxcnt & ri=ri[rxi] + ENDIF ELSE rcnt=0 + ENDIF + IF rcnt NE 0 THEN BEGIN ;obsolete values found + vlen=STRLEN(obs_list_spec_1[i]) + IF i LE 1 THEN BEGIN ;move .AMF and .AVK to DESCRIPTOR section (2 or 3) + FOR j=0,rcnt-1 DO BEGIN + vpos=STRPOS(STRUPCASE(vn_v[ri[j],0]),obs_list_spec_1[i]) + ai=WHERE(vn_v[ri[j],*] EQ '',acnt) + IF acnt NE 0 THEN BEGIN + ;the DESCRIPTOR field is empty, so can convert value OK (otherwise no change made) + vn_v[ri[j],0]=STRMID(vn_v[ri[j],0],0,vpos) + vn_v[ri[j],ai[0]]=STRMID(obs_list_spec_1[i],1) ;write to first empty field + ENDIF + ENDFOR + ENDIF ELSE BEGIN + ;.CONCENTRATION or .MIXING.RATIO so attempt to identify actual type of measurement + ;(mixing ratio etc) by looking at corresponding VAR_UNIT values + mrvi=WHERE((STRPOS(STRLOWCASE(in3[ri]),'pp') NE -1) OR $ + (STRPOS(STRLOWCASE(in3[ri]),'m-1 sr-1') NE -1),mrvcnt) ;testing for volume mixing ratio + mrmi=WHERE(STRPOS(STRLOWCASE(in3[ri]),' g ') NE -1,mrmcnt) ;test for mass mixing ratio + ndi=WHERE(STRPOS(STRLOWCASE(in3[ri]),'mol') NE -1,ndcnt) ;test for number density + ppi=WHERE(STRPOS(STRLOWCASE(in3[ri]),'pa') NE -1,ppcnt) ;test for partial pressure + IF i EQ nols1-1 THEN cnti=[mrvcnt,mrmcnt,0,0] ELSE cnti=[mrvcnt,mrmcnt,ndcnt,ppcnt] + ci=WHERE(cnti GT 0,ccnt) + IF ccnt EQ 1 THEN BEGIN ;type of measurement successfully found + ;Note: not set up to handle if more than one type of measurement found in the file + vlen=STRLEN(obs_list_spec_1[i]) + FOR j=0,rcnt-1 DO BEGIN + IF (STRUPCASE(vn_v[ri[j],0]) EQ 'AIR.CONCENTRATION') AND (ci[0] EQ 2) THEN $ + ;Do special case check for AIR.CONCENTRATION - can only change to NUMBER.DENSITY + vn_v[ri[j],0]='NUMBER.DENSITY' $ + ELSE BEGIN + vpos=STRPOS(STRUPCASE(vn_v[ri[j],0]),obs_list_spec_1[i]) + vn_v[ri[j],0]=STRMID(vn_v[ri[j],0],0,vpos)+rep_list_conc[ci[0]]+STRMID(vn_v[ri[j],0],vpos+vlen) + ENDELSE + ENDFOR + ENDIF + ENDELSE + ENDIF + ENDFOR + FOR i=0,N_ELEMENTS(obs_list_2)-1 DO BEGIN ;Check for obsolete mode values + ri=WHERE(STRUPCASE(vn_v[*,1]) EQ obs_list_2[i],rcnt) + IF rcnt NE 0 THEN BEGIN + test1=(i LE 1) AND (STRPOS(STRUPCASE(in4),'FTIR') NE -1) + test2=(i LE 1) AND (STRPOS(STRUPCASE(in4),'UVVIS') NE -1) AND $ + (STRPOS(STRUPCASE(in4),'DOBSON') EQ -1) AND (STRPOS(STRUPCASE(in4),'BREWER') EQ -1) + test3=((i EQ 2) OR (i EQ 3)) AND ((STRPOS(STRUPCASE(in4),'MWR') NE -1) OR $ + (STRPOS(STRUPCASE(in4),'MICROWAVE') NE -1)) + IF i LE 1 THEN mi=STRSPLIT(rep_list_2[i],'|',/EXTRACT) + ;test1, test2, and test3 are special case obsolete values + IF test1 THEN vn_v[ri,1]=mi[0] $ + ELSE IF test2 THEN vn_v[ri,1]=mi[1] $ + ELSE IF test3 THEN vn_v[ri,1]=rep_list_2[i] $ ;direct replacement + ELSE BEGIN ;all other obsolete mode values + IF i LE 1 THEN vn_v[ri,1]=mi[0] ELSE vn_v[ri,1]=rep_list_2[i] ;direct replacement + IF i EQ 5 THEN BEGIN ;SLANT (if column measurement then add to main variable name) + FOR j=0,rcnt-1 DO BEGIN + IF STRLEN(vn_v[ri[j],0])-STRPOS(vn_v[ri[j],0],'COLUMN') EQ 6 THEN $ + vn_v[ri[j],0]=vn_v[ri[j],0]+'.'+obs_list_2[i] + ENDFOR + ENDIF + ENDELSE + ENDIF + ENDFOR + ;Check conditional miscellaneous changes + FOR i=0,N_ELEMENTS(misc_list_1)-1 DO BEGIN + IF i EQ 0 THEN ri=WHERE(STRPOS(STRUPCASE(in2),misc_list_1[i]) NE -1,rcnt) $ + ELSE ri=WHERE(STRUPCASE(in2) EQ misc_list_1[i],rcnt) + IF rcnt NE 0 THEN BEGIN + IF i EQ 0 THEN BEGIN ;ensure DATA_SOURCE is BUOY before changing + IF STRPOS(STRUPCASE(in4),'BUOY') NE -1 THEN BEGIN + FOR j=0,rcnt-1 DO BEGIN + vn_v[ri[j],0]=misc_list_2[i] & vn_v[ri[j],1]='' + ENDFOR + ENDIF + ENDIF ELSE BEGIN ;ensure O3.COLUMN is also present as a DATA_VARIABLE before changing + mi=WHERE(STRUPCASE(vn_v[*,0]) EQ 'O3.COLUMN',mcnt) + IF mcnt NE 0 THEN BEGIN + vn_v[ri[0],0]='O3.COLUMN' & vn_v[ri[0],1]='AMF' + ENDIF + ENDELSE + ENDIF + ENDFOR + + FOR i=0,N_ELEMENTS(obs_list_3)-1 DO BEGIN ;Check for obsolete descriptor values (UNCERTAINTY) + ;acceptable units for identifying correct replacement name (test1 instruments only) + vuok=['ppv','ppmv','ppbv','pptv','k','du','molec cm-2','molec cm-3','degc','m-1 sr-1','dimensionless'] + vuok2=['ppv2','ppmv2','ppbv2','pptv2'] ;covariance + test1=(STRPOS(STRUPCASE(in4),'MWR') NE -1) OR (STRPOS(STRUPCASE(in4),'MICROWAVE') NE -1) OR $ + (STRPOS(STRUPCASE(in4),'HAGAR') NE -1) OR (STRPOS(STRUPCASE(in4),'MIPAS.STR') NE -1) OR $ + (STRPOS(STRUPCASE(in4),'SIOUX') NE -1) OR (STRPOS(STRUPCASE(in4),'UVVIS.AMAXDOAS') NE -1) OR $ + (STRPOS(STRUPCASE(in4),'UVVIS.SAOZ') NE -1) OR (STRPOS(STRUPCASE(in4),'ATMOINSPECTOR') NE -1) OR $ + (STRPOS(STRUPCASE(in4),'FISH') NE -1) OR (STRPOS(STRUPCASE(in4),'AMON') NE -1) OR $ + (STRPOS(STRUPCASE(in4),'LPMA') NE -1) OR (STRPOS(STRUPCASE(in4),'SALOMON') NE -1) OR $ + (STRPOS(STRUPCASE(in4),'RADIOMETER.IR.CIMEL') NE -1) OR (STRPOS(STRUPCASE(in4),'UVVIS') NE -1) OR $ + (STRPOS(STRUPCASE(in4),'SPECTROMETER') NE -1) OR (STRPOS(STRUPCASE(in4),'LIDAR.WATERVAPORRAMAN_UNIVAQ') NE -1) + ;check for DATA_SOURCE where CoVariance as well as Standard uncertainties are reported + test2=(STRPOS(STRUPCASE(in4),'FTIR.CO_KIT') NE -1) OR (STRPOS(STRUPCASE(in4),'FTIR.CO_ULG') NE -1) OR $ + (STRPOS(STRUPCASE(in4),'FTIR.HNO3_KIT') NE -1) OR (STRPOS(STRUPCASE(in4),'FTIR.HNO3_ULG') NE -1) OR $ + (STRPOS(STRUPCASE(in4),'FTIR.N2O_KIT') NE -1) OR (STRPOS(STRUPCASE(in4),'FTIR.N2O_ULG') NE -1) OR $ + (STRPOS(STRUPCASE(in4),'FTIR.NO2_KIT') NE -1) OR (STRPOS(STRUPCASE(in4),'FTIR.NO2_ULG') NE -1) + vnvi=1 ;determines the location index of the descriptor variable + ri=WHERE(STRUPCASE(vn_v[*,1]) EQ obs_list_3[i],rcnt) + IF rcnt EQ 0 THEN BEGIN + vnvi=2 & ri=WHERE(STRUPCASE(vn_v[*,2]) EQ obs_list_3[i],rcnt) + ENDIF + IF rcnt NE 0 THEN BEGIN + IF i LE 3 THEN vn_v[ri,vnvi]=rep_list_3[i] $ ;direct replacement + ELSE BEGIN ;need to identify whether the UNITS are % or fit vuok criteria + FOR j=0,rcnt-1 DO BEGIN + IF STRTRIM(in3[ri[j]],2) EQ '%' THEN vn_v[ri[j],vnvi]=rep_list_3[i]+'.RELATIVE' $ + ELSE BEGIN + ;test for Standard Uncertainties + vi=WHERE(STRTRIM(STRLOWCASE(in3[ri[j]]),2) EQ vuok,vcnt) + IF vcnt EQ 0 THEN $ ;do test for [Prefix]molec cm-2/molec cm-3 + IF (STRPOS(STRLOWCASE(in3[ri[j]]),'molec cm-2') NE -1) OR $ + (STRPOS(STRLOWCASE(in3[ri[j]]),'molec cm-3') NE -1) THEN vcnt=1 + IF ((test1) OR (test2)) AND (vcnt NE 0) THEN $ can assume that the given error values are Standard Deviation + vn_v[ri[j],vnvi]=rep_list_3[i] + ;test for Covariance Uncertainties + vi=WHERE(STRTRIM(STRLOWCASE(in3[ri[j]]),2) EQ vuok2,vcnt) + IF (test2) AND (vcnt NE 0) THEN vn_v[ri[j],vnvi]=rep_list_4[i] + ENDELSE + ENDFOR + ENDELSE + ENDIF + ENDFOR + ;create any comments for INFOTXT_OUTPUT + FOR i=0,in1-1 DO BEGIN + ;put full DATA_VARIABLE names back together + vn_new[i]=vn_v[i,0] + FOR j=1,2 DO IF vn_v[i,j] NE '' THEN vn_new[i]=vn_new[i]+'_'+vn_v[i,j] + ;Check to see if it is different to the original DATA_VARIABLE + IF vn_new[i] NE in2[i] THEN BEGIN + vnchange[i]=in2[i] & in2[i]=vn_new[i] + IF qa_yes THEN itxt=' expected to be ' ELSE itxt=' renamed ' + infotxt='2 Dataset name '+vnchange[i]+itxt+in2[i] + INFOTXT_OUTPUT,infotxt + ENDIF + ENDFOR + END + + code EQ 6: BEGIN ;Look for obsolete DATA_SOURCE + ;List of obsolete DATA_SOURCE_01 values + obs_list=['RO.CHAMP','RO.SAC.C','RO.F3C.FM','RO.CNOFS','RO.GRACE.A','FTIR_', $ + 'UVVIS.DOAS_','MICROWAVE.RADIOMETER','LIDAR.BACKSCATTER',$ + 'LIDAR.DIAL','LIDAR.OLEX','LIDAR.RIEGL','LIDAR.RMR'] + ;Corresponding replacement list + rep_list=['CHAMP','SAC.C','F3C.FM','CNOFS','GRACE.A','FTIR.','UVVIS.DOAS.','MWR.','LIDAR.'] + ;Possible list of species for the above instruments (note: gases will take precedence + ;over Aerosol and Temperature, o/w the first species found will be considered the + ;primary species measured) + spc_list=['BrO.','C2H2.','C2H6.','CCl2F2.','CCl3F.','CH4.','CHF2Cl.','CHOCHO.','ClO.','ClONO2.','CO.',$ + 'CO2.','COF2.','H2CO.','H2O.','HCFC22.','HCl.','HCN.','HCOOH.','HF.','HNO3.','HONO.','IO.',$ + 'N2O.','NO.','NO2.','O3.','OClO.','OCS.','SF6.','SO2.','AEROSOL','TEMPERATURE'] + + oli=-1 ;will change to obs_list index value if obsolete DATA_SOURCE_01 found + n_obsl=N_ELEMENTS(obs_list) + lid_i=n_obsl-5 ;needs to be start index of the lidar measurements in obs_list + spc_i=n_obsl-8 ;needs to be start index of the instruments requiring species information in obs_list + uv_i=n_obsl-7 ;needs to be UVVIS.DOAS index in obs_list + FOR i=0,N_ELEMENTS(obs_list)-1 DO IF STRPOS(STRUPCASE(in3),obs_list[i]) NE -1 THEN oli=i + IF oli NE -1 THEN BEGIN ;obsolete DATA_SOURCE value found + IF oli GE lid_i THEN oli=lid_i ;to correspond with rep_list index for lidar + pri=-1 ;will change if species found + IF oli GE spc_i THEN BEGIN ;if not an RO DATA_SOURCE then need to determine species + n_sl=N_ELEMENTS(spc_list) + ;Look through VAR_NAME values to try and identify new DATA_SOURCE from species list + spc_found=INTARR(in1)-1 ;identifies index of found species names in the Datasets + FOR i=0,n_sl-1 DO BEGIN + fi=WHERE(STRPOS(STRMID(in2,0,STRLEN(spc_list[i])),spc_list[i]) NE -1,fcnt) + IF fcnt NE 0 THEN spc_found[fi]=i + ENDFOR + ;Check list of found species - don't include Aerosol and Temperature + fi=WHERE((spc_found NE -1) AND (spc_found LT n_sl-2),fcnt) + IF fcnt NE 0 THEN pri=spc_found[fi[0]] $ ;primary species index determined + ELSE IF (oli EQ uv_i) OR (oli EQ lid_i) THEN BEGIN ;Test for Temperature (Lidar Only) and Aerosol + fi=WHERE(spc_found NE -1,fcnt) + IF fcnt NE 0 THEN BEGIN + pri=MIN(spc_found[fi]) ;i.e. Aerosol takes precedence over Temperature + IF (oli EQ uv_i) AND (pri EQ n_sl-1) THEN pri=-1 ;UVVIS.DOAS can't be Temperature + ENDIF + ENDIF + ENDIF ELSE BEGIN ;cases with straight replacement + old_ds=STRTRIM(in3,2) & in3=rep_list[oli]+STRMID(old_ds,STRLEN(obs_list[oli])) & pri=-2 + ENDELSE + IF pri NE -1 THEN BEGIN ;update DATA_SOURCE + IF pri GE 0 THEN BEGIN + old_ds=in3 + ;check special cases for LIDAR water vapor measurements + IF (spc_list[pri] EQ 'H2O.') AND (STRPOS(STRUPCASE(in3),'LIDAR.DIAL') NE -1) THEN $ + in3='H2O' $ + ELSE IF (spc_list[pri] EQ 'H2O.') AND (STRPOS(STRUPCASE(in3),'LIDAR.BACKSCATTER') NE -1) THEN $ + in3='H2O' $ + ELSE in3=spc_list[pri] + IF STRPOS(in3,'.') NE -1 THEN in3=STRMID(in3,0,STRLEN(in3)-1) + in3=rep_list[oli]+in3+STRMID(old_ds,STRPOS(old_ds,'_')) + ENDIF + IF qa_yes THEN itxt=' expected to be ' ELSE itxt=' renamed ' + infotxt='2 DATA_SOURCE value '+old_ds+itxt+in3 + INFOTXT_OUTPUT,infotxt + ENDIF + ENDIF + END + + code EQ 7: BEGIN ;Look for obsolete CALVAL and NDSC FILE_ACCESS values + obs_list=['CALVAL','NDSC'] + rep_list=['EVDC','NDACC'] + + FOR i=0,in1-1 DO BEGIN + oi=WHERE(in2[i] EQ obs_list,ocnt) + IF ocnt NE 0 THEN BEGIN + IF qa_yes THEN itxt=' expected to be ' ELSE itxt=' renamed ' + infotxt='2 FILE_ACCESS value '+in2[i]+itxt+rep_list[oi[0]] + INFOTXT_OUTPUT,infotxt + in2[i]=rep_list[oi[0]] + ENDIF + ENDFOR + END + + code EQ 8: BEGIN ;Look for VAR_UNITS=DIMENSIONLESS or NONE and replace with '' or '1' + vuv=STRUPCASE(in2) + FOR i=0,in1-1 DO BEGIN + IF (vuv[i] EQ 'DIMENSIONLESS') OR (vuv[i] EQ 'NONE') THEN BEGIN + IF in3[i] EQ 'STRING' THEN newv='' ELSE newv='1' + IF qa_yes THEN itxt=' expected to be ' ELSE itxt=' renamed ' + infotxt='2 VAR_UNITS='+vuv[i]+itxt+'VAR_UNITS='+newv+' based on VAR_DATA_TYPE='+in3[i] + INFOTXT_OUTPUT,infotxt + in2[i]=newv + ENDIF + ENDFOR + END + + code EQ 9: BEGIN ;DATA_QUALITY/DATA_TEMPLATE checks + attr_arr_glob_prov=['DATA_TEMPLATE','DATA_QUALITY'] + geoms_te='' & meta_te='' & std_txt=' Required Metadata Template: ' + ;Check if DATA_TEMPLATE field is present in the TAV file + dti=WHERE(tab_arr[*,0] EQ attr_arr_glob_prov[0],dtcnt) + ;Check if DATA_TEMPLATE label is present in the Metadata + gi=WHERE(in1 EQ attr_arr_glob_prov[0],gcnt) + IF gcnt EQ 1 THEN IF in2[gi[0]] NE '' THEN meta_te=STRUPCASE(STRTRIM(in2[gi[0]],2)) + IF dtcnt NE 0 THEN tab_arr_sub=tab_arr[dti[0],2:FIX(tab_arr[dti[0],1]+1)] ;Valid Template values from TAV file + IF dtcnt EQ 0 THEN BEGIN + infotxt='3'+std_txt+'DATA_TEMPLATE field is not present in the TAV file. Checks cannot be performed' + INFOTXT_OUTPUT,infotxt + std_txt='' + ENDIF ELSE IF meta_te NE '' THEN BEGIN + ;check if value is present in the TAV file + ci=WHERE(STRUPCASE(tab_arr_sub) EQ meta_te,ccnt) + IF ccnt NE 0 THEN BEGIN + geoms_te=tab_arr_sub[ci[0]] & in2[gi[0]]=geoms_te + infotxt='0'+std_txt+geoms_te + INFOTXT_OUTPUT,infotxt + std_txt='' + ENDIF + ENDIF + IF std_txt NE '' THEN BEGIN ;template value not present in the metadata or value not in the TAV file + ;Determine DATA_TEMPLATE value based on DATA_SOURCE value (if applicable) + di=WHERE(in1 EQ 'DATA_SOURCE',dcnt) + IF dcnt EQ 1 THEN BEGIN ;DATA_SOURCE found + ;Extract DATA_SOURCE value + IF in2[di[0]] NE '' THEN BEGIN + res=STRSPLIT(STRTRIM(in2[di[0]],2),'_',/EXTRACT) + ;First check - First part of DATA_SOURCE matches text in TAV entry + res1=STRUPCASE(STRSPLIT(res[0],'.',/EXTRACT,COUNT=res1cnt)) + ci=WHERE(STRPOS(STRUPCASE(tab_arr_sub),res1[0]) NE -1,ccnt) + IF ccnt EQ 1 THEN geoms_te=tab_arr_sub[ci[0]] $ ;Template found + ELSE IF (ccnt GT 1) AND (N_ELEMENTS(res1) GT 1) THEN BEGIN + ;2nd Check - More than one option so try and match 2nd part of DATA_SOURCE with 4th part in TAV entry + tab_arr_sub=tab_arr_sub[ci] + tab_arr_part_sub=STRARR(ccnt) + numi=-1 + FOR i=0,ccnt-1 DO BEGIN + rest=STRSPLIT(tab_arr_sub[i],'-',/EXTRACT) + rest=[rest,'','',''] ;ensure there are at least 4 sub-values + tab_arr_part_sub[i]=rest[3] + IF IS_A_NUMBER_HDF(rest[3]) THEN numi=i + ENDFOR + ci=WHERE(STRUPCASE(tab_arr_part_sub) EQ res1[1],ccnt) + IF (ccnt EQ 0) AND (numi NE -1) THEN BEGIN + ;2nd part is likely a gas e.g. FTIR.CO, so use template where 4th part of TAV entry is a number + geoms_te=tab_arr_sub[numi] + ENDIF ELSE IF ccnt EQ 1 THEN geoms_te=tab_arr_sub[ci[0]] $ ;unique template found + ELSE IF ccnt GT 1 THEN BEGIN ;ccnt GT 1, Try alternatives for H2O and O3, or test for fourth part + tasx=tab_arr_sub[ci] + CASE 1 OF + (res1[0] EQ 'UVVIS') AND (res1[1] EQ 'DOAS'): BEGIN + IF res1cnt GE 4 THEN BEGIN + IF res1[3] EQ 'AEROSOL' THEN res1x=STRUPCASE(res1[2])+'-AEROSOL' $ + ELSE res1x=STRUPCASE(res1[2])+'-GAS' + ENDIF ELSE res1x=res1[1] + END + res1[1] EQ 'H2O': res1x='WATERVAPOR' + res1[1] EQ 'O3': res1x='OZONE' + ELSE: res1x=res1[1] + ENDCASE + ci=WHERE(STRPOS(STRUPCASE(tasx),res1x) NE -1,ccnt) + IF ccnt EQ 1 THEN geoms_te=tasx[ci[0]] + ENDIF + ENDIF + IF geoms_te NE '' THEN BEGIN ;DATA_TEMPLATE value found based on DATA_SOURCE + IF gcnt EQ 1 THEN BEGIN ;DATA_TEMPLATE label is present + fmvi=WHERE(in1 EQ 'FILE_META_VERSION',fmvcnt) + fmvtxt='' + IF fmvcnt EQ 1 THEN BEGIN + resfmv=STRSPLIT(in2[fmvi[0]],' ;',/EXTRACT,COUNT=fmvcnt) + IF fmvcnt GE 1 THEN BEGIN + fmv_ver=resfmv[0] + fmvtxt=' and '+fmv_ver+' metadata definitions' + ENDIF + ENDIF + in2[gi[0]]=geoms_te ;Add value to DATA_TEMPLATE label + IF qa_yes THEN itxt=' expected to be ' ELSE itxt=' renamed ' + infotxt='2 DATA_TEMPLATE value'+itxt+geoms_te+' based on DATA_SOURCE value'+fmvtxt + INFOTXT_OUTPUT,infotxt + std_txt='' + ENDIF ELSE IF gcnt EQ 0 THEN in3[0]=geoms_te ;Need to add new Global Attribute Information + ENDIF + ENDIF + ENDIF + ENDIF + IF geoms_te NE '' THEN BEGIN ;Check if DATA_QUALITY label is present + gi=WHERE(in1 EQ attr_arr_glob_prov[1],gcnt) + IF gcnt EQ 0 THEN in3[1]='DATA_QUALITY' ;Need to add new Global Attribute Information + ENDIF + IF std_txt NE '' THEN BEGIN ;No template file required based on selection criteria + infotxt='0'+std_txt+'DATA_TEMPLATE value not required based on selection criteria' + INFOTXT_OUTPUT,infotxt + ENDIF + END + + code EQ 10: BEGIN ;check printable characters in Metadata and Datasets (of type String) + ;Note: Also allowed are Control Characters Null Character (0B), Horizontal Tab (9B), + ;Line Feed (10B), and Carriage Return (13B) - Note: 10B and 13B for Metadata only + exempt=['PI_NAME','DO_NAME','DS_NAME','PI_AFFILIATION','DO_AFFILIATION','DS_AFFILIATION',$ + 'PI_ADDRESS','DO_ADDRESS','DS_ADDRESS','PI_EMAIL','DO_EMAIL','DS_EMAIL'] + non_char=[27B, 128B, 147B, 176B, 186B, 197B, 216B, 226B, 232B, 233B, 248B, 252B] ;128B = Euro + rep_char=['',' ','"',' deg. ',' deg. ','A','O','a','e','e','o','u'] ;replace above non-US ASCII chars with these values + + ;Do check for the Full TAV which includes ORIGINATOR attributes + ochk=STRPOS(tab_arr(*,0),'ORIGINATOR') NE -1 + oi=WHERE(ochk NE 0,ocnt) + mentry=0 ;Default assumes String Dataset Entry + n_nchar=N_ELEMENTS(non_char) + IF N_PARAMS() EQ 3 THEN in2='Dataset '+STRTRIM(in2,2) $ ;Dataset Entry + ELSE mentry=1 ;Metadata entry + FOR i=0L,N_ELEMENTS(in1)-1L DO BEGIN + exempt_att=0 ;default assumes attribute is not exempt from checks + IF mentry EQ 1 THEN BEGIN + res=STRSPLIT(in1[i],'=',/EXTRACT) + in2='Metadata entry '+in1[i] ;res[0] + IF ocnt NE 0 THEN BEGIN + ;Do not need to check 'exempt' attributes if using the full version of the TAV file + ei=WHERE(STRUPCASE(res[0]) EQ exempt,ecnt) + IF ecnt NE 0 THEN exempt_att=1 ;invalid entries can be corrected by the code + ENDIF + ENDIF + + IF (~qa_yes) AND (exempt_att EQ 0) THEN BEGIN + ;If not doing QA then check for characters that can be replaced + non_char_i=[-1] + FOR j=0,n_nchar-1 DO BEGIN + testb=BYTE(in1[i]) + ni=WHERE(testb EQ non_char[j],ncnt) + IF ncnt NE 0 THEN BEGIN + IF non_char_i[0] EQ -1 THEN non_char_i=[j] $ + ELSE non_char_i=[non_char_i,j] + inval=in1[i] + FOR k=0,ncnt-1 DO BEGIN + IF k NE 0 THEN ni[k]=ni[k]+((STRLEN(rep_char[j])-1)*k) + inval=STRMID(inval,0,ni[k])+rep_char[j]+STRMID(inval,ni[k]+1) + ENDFOR + in1[i]=inval + ENDIF + ENDFOR + IF non_char_i[0] NE -1 THEN BEGIN + infotxt='2 Illegal character(s) ' + FOR j=0,N_ELEMENTS(non_char_i)-1 DO BEGIN + IF j EQ 0 THEN infotxt=infotxt+STRTRIM(non_char[non_char_i[j]],2) $ + ELSE infotxt=infotxt+', '+STRTRIM(non_char[non_char_i[j]],2) + ENDFOR + infotxt=infotxt+' in '+in2+' replaced with valid ISO646-US ASCII character(s)' + INFOTXT_OUTPUT,infotxt + ENDIF + ENDIF + testb=BYTE(in1[i]) + + IF exempt_att EQ 0 THEN BEGIN + IF mentry EQ 1 THEN $ + test=(testb NE 0B) AND (testb NE 9B) AND (testb NE 10B) AND (testb NE 13B) $ + ELSE test=(testb NE 0B) AND (testb NE 9B) ;Check for Dataset Control Characters + ;check for non-char value and replace + bi=WHERE(((testb LT 32B) OR (testb GT 126B)) AND (test),bcnt) + IF bcnt NE 0 THEN BEGIN ;Illegal character present + IF bcnt GT 1L THEN BEGIN ;check for repeat illegal characters and only display once + bix=[bi[0]] + FOR j=1L,bcnt-1L DO BEGIN + ri=WHERE(testb[bi[j]] EQ testb[bi[0L:j-1L]],rcnt) + IF rcnt EQ 0 THEN bix=[bix,bi[j]] + ENDFOR + bi=bix & bcnt=N_ELEMENTS(bi) + ENDIF + infotxt=STRARR(2) + infotxt[0]='3 Entry must only include valid characters from the ISO646-US ASCII character set.' + infotxt[1]=' Illegal character(s) ' + FOR j=0L,bcnt-1L DO BEGIN + cval=STRTRIM(testb[bi[j]],2) + ;print,testb[bi[j]] ;to output actual byte value + IF (cval EQ STRTRIM(8B,2)) OR (cval EQ '') THEN BEGIN + ;Illegal non-printable character so print in Decimal Byte form + cval=STRING(format='(I4)',testb[bi[j]]) & cval=STRTRIM(cval,2)+'B' + ENDIF + IF j NE bcnt-1L THEN itxt=', ' ELSE itxt='' + infotxt[1]=infotxt[1]+cval+itxt + ENDFOR + infotxt[1]=infotxt[1]+' in '+in2+' (may not display correctly)' + INFOTXT_OUTPUT,infotxt + ENDIF + ENDIF + ENDFOR + END + code EQ 11: BEGIN ;Check DATA_VERSION_NAME part of the DATA_SOURCE + ;Check that entry is made up of dot-separated alpha_numeric words + n_char=STRLEN(in1) + strok=1B ;default is that entry is OK + test2=0B & test3=0B ;will change to 1 if test conditions are true + charchkm1=0B ;to check test character against the previous character + FOR i=0,n_char-1 DO BEGIN + charchk=BYTE(STRMID(in1,i,1)) + test1=(charchk EQ 46B) OR ((charchk GE 65B) AND (charchk LE 90B)) OR $ ;'.' or A-Z + ((charchk GE 97B) AND (charchk LE 122B)) OR $ ;a-z + ((charchk GE 48B) AND (charchk LE 57B)) ;0-9 + IF (i EQ 0) OR (i EQ n_char-1) THEN test2=charchk EQ 46B ;i.e. first or last character is a '.' + test3=(charchk EQ 46B) AND (charchkm1 EQ 46B) ;i.e. two consecutive dots + IF (~test1) OR (test2) OR (test3) THEN strok=0B + charchkm1=charchk + ENDFOR + IF ~strok THEN BEGIN + ;write error message + infotxt='3 VERSION_NAME sub-value of the DATA_SOURCE must consist of dot-separated alpha-numeric words: ' + infotxt=infotxt+in1 + INFOTXT_OUTPUT,infotxt + ENDIF + END +ENDCASE + +END ;Procedure GEOMS_Rule_Changes + + + +PRO pre_defined_att_checks, ct, nav, vav, vnx, vdtv,verror +;Procedure to perform checks on the HDF4 pre-defined attributes, as well as check that +;the numeric variable attribute values are of the same data type as that given by the +;corresponding VAR_DATA_TYPE value (if input is via session memory) +; ---------- +;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org +; +; History: +; 20101120: Introduced - Version 4.0 +; +; Inputs: ct - Identifies the type of check to be performed on the inputs +; nav - The HDF pre-defined attribute to check +; vav - The Variable Attribute corresponding to the pre-defined attribute +; vnx - The Dataset from which the attributes are derived +; vdtv - The Data Type of the Dataset +; verror - Boolean to indicate whether the Variable Attribute data type error has +; already been written +; +; Outputs: None - Any Pre-defined attributes written to an HDF file are recreated by the program +; +; Called by: IDLCR8HDF, READ_METADATA +; +; Subroutines Called: INFOTXT_OUTPUT +; Information Conditions (when the program is able to make changes): +; 1. VAR_VALID_MIN/MAX or VAR_FILL_VALUE data types do not match VAR_DATA_TYPE +; 2. HDF4 pre-defined attribute data type does not match VAR_DATA_TYPE +; 3. HDF4 pre-defined attribute units value does not match VAR_UNITS +; 4. HDF4 pre-defined attribute value does not equal equivalent variable +; attribute value (valid_range, _FillValue) +; 5. HDF4 pre-defined attribute [long_name/format/coordsys] present in the HDF file +; 6. Datasets that have been scaled or had an offset applied are not permitted + +COMMON DATA +COMMON WIDGET_WIN + +IF ct NE -1 THEN BEGIN ;check units, _FillValue, and valid_range attributes only + ncsa=['units','_Fillvalue','valid_range','valid_range'] + var_atts=['VAR_UNITS','VAR_FILL_VALUE','VAR_VALID_MIN','VAR_VALID_MAX'] + + ;Change VAR_DATA_TYPE value to IDL Type code + CASE 1 OF + vdtv EQ 'BYTE': idltc=1 + vdtv EQ 'SHORT': idltc=2 + (vdtv EQ 'INTEGER') OR (vdtv EQ 'LONG'): idltc=3 + (vdtv EQ 'REAL') OR (vdtv EQ 'FLOAT'): idltc=4 + vdtv EQ 'DOUBLE': idltc=5 + vdtv EQ 'STRING': idltc=7 + ELSE: idltc=0 + ENDCASE + test1=(SIZE(vav,/TYPE) EQ 7) AND (STRTRIM(vav,2) NE '') + test2=SIZE(vav,/TYPE) NE 7 + test3=(SIZE(nav,/TYPE) EQ 7) AND (STRTRIM(nav,2) NE '') + test4=SIZE(nav,/TYPE) NE 7 + IF ((test1) OR (test2)) AND (ct NE 0) THEN BEGIN + IF (SIZE(vav,/TYPE) NE idltc) AND (idltc NE 0) AND (verror[0] EQ 0) THEN BEGIN + ;Check data type of Variable Attribute + infotxt=STRARR(2) + itxt='VAR_VALID_MIN, VAR_VALID_MAX or VAR_FILL_VALUE' + infotxt[0]='2 '+itxt+' data types do not match VAR_DATA_TYPE='+vdtv + IF vnx NE '' THEN infotxt[1]=' for Dataset '+vnx+'|.' $ + ELSE BEGIN + infotxt[0]=infotxt[0]+'|.' & infotxt[1]=' ' + ENDELSE + infotxt[1]=infotxt[1]+' Data type will be changed to match VAR_DATA_TYPE value in HDF file' + IF ~qa_yes THEN INFOTXT_OUTPUT,infotxt ;error for QA output separately + verror[0]=1 + ENDIF + ENDIF + IF ((test3) OR (test4)) AND (ct NE 0) THEN BEGIN + IF (SIZE(nav,/TYPE) NE idltc) AND (idltc NE 0) AND (o3[0] EQ 'H4') AND (verror[1] EQ 0) THEN BEGIN + ;Check data type of pre-defined attribute + infotxt=STRARR(2) + infotxt[0]='2 HDF4 pre-defined attribute '+ncsa[ct]+' data type does not match VAR_DATA_TYPE='+vdtv + IF vnx NE '' THEN infotxt[0]=infotxt[0]+' for Dataset '+vnx+'|.' $ + ELSE infotxt[0]=infotxt[0]+'|.' + infotxt[1]=' Data type will be changed to match VAR_DATA_TYPE value in HDF file' + INFOTXT_OUTPUT,infotxt + IF ncsa[ct] EQ 'valid_range' THEN verror[1]=1 ;to only report 1 valid_range error + ENDIF + ENDIF + IF ((test1) OR (test2)) AND ((test3) OR (test4)) THEN BEGIN + ;Compare Variable Attributes against pre-defined attributes + IF ct EQ 0 THEN BEGIN ;units check + IF (STRTRIM(nav,2) NE STRTRIM(vav,2)) AND (o3[0] EQ 'H4') THEN BEGIN + infotxt=STRARR(2) + infotxt[0]='2 HDF4 pre-defined attribute units='+nav+' does not match VAR_UNITS='+vav + IF vnx NE '' THEN infotxt[0]=infotxt[0]+' for Dataset '+vnx+'|.' $ + ELSE infotxt[0]=infotxt[0]+'|.' + infotxt[1]=' HDF4 units will be changed to match VAR_UNITS value in HDF file' + INFOTXT_OUTPUT,infotxt + ENDIF + ENDIF ELSE BEGIN + IF (nav NE vav) AND (idltc NE 7) AND (idltc NE 0) AND (o3[0] EQ 'H4') THEN BEGIN + infotxt=STRARR(2) + infotxt[0]='2 HDF4 pre-defined attribute '+ncsa[ct]+' value does not equal '+var_atts[ct]+' value' + IF vnx NE '' THEN infotxt[0]=infotxt[0]+' for Dataset '+vnx+'|.' $ + ELSE infotxt[0]=infotxt[0]+'|.' + infotxt[1]=' HDF4 '+ncsa[ct]+' will be changed to match '+var_atts[ct]+' value in HDF file' + INFOTXT_OUTPUT,infotxt + ENDIF + ENDELSE + ENDIF +ENDIF ELSE BEGIN ;Do checks on remaining pre-defined attributes + ncsa_str=['long_name','format','coordsys'] + ncsa_cal=['scale_factor','scale_factor_err','add_offset','add_offset_err','calibrated_nt'] + FOR i=0,N_ELEMENTS(ncsa_str)-1 DO BEGIN + ni=WHERE(STRLOWCASE(nav) EQ ncsa_str[i],ncnt) + IF ncnt NE 0 THEN BEGIN + IF qa_yes THEN itxt=' present in' ELSE itxt=' will not be written to' + infotxt='0 HDF4 pre-defined attribute '+ncsa_str[i]+itxt+' the HDF file' + INFOTXT_OUTPUT,infotxt + ENDIF + ENDFOR + calfound=0 + FOR i=0,N_ELEMENTS(ncsa_cal)-1 DO BEGIN + ni=WHERE((STRLOWCASE(nav) EQ ncsa_cal[i]) AND (calfound EQ 0),ncnt) + IF ncnt NE 0 THEN BEGIN + calfound=1 + infotxt='3 Datasets that have been scaled or had an offset applied are not permitted' + INFOTXT_OUTPUT,infotxt + ENDIF + ENDFOR +ENDELSE + +END ;Procedure Pre_Defined_Att_Checks + + + +PRO read_metadata, metafile, inf +;Procedure to check Metadata contents and return as meta_arr - also: +;1. Ensures uniform input (making ATTRIBUTE_NAME uppercase, deleting unwanted spaces etc). +;2. Checks that attribute length falls within allowable limits +;3. Checks for repeated, missing, new, obsolete attributes +;4. Update FILE_META_VERSION with TAV and software version (tab_ver) + +;Please also note the following: +;1. Any extra valid Global Attributes will be included (and added to the attr_arr_glob array). +;2. If redundant attributes are detected they will be omitted from the Metadata written +; to the HDF file. +; ---------- +;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org +; +; History: +; 20050802: Original IDLCR8HDF Routine - Version 1.0 +; 20061012: Error messages modified; Allow VAR_MONOTONE as a Variable Attribute if it is present in the +; Metadata; If DATA_TYPE is present in the Metadata then change it to DATA_LEVEL; Allow +; additional Global Attributes; Puts Variable Attribute names in the same order as +; attr_arr_data; Common variable definition WIDGET_WIN added - Version 2.0 +; 20080302: VAR_MONOTONE not included in the output Metadata if it is present in the input Metadata +; and an AVDC style TAV file is read in; if DATA_TYPE is present in the Metadata it is changed +; to DATA_LEVEL if an AVDC style TAV file is read in, and vice versa if an original style +; Envisat table.dat file read in; Warnings added if VAR_MONOTONE attribute is omitted, when +; changing DATA_TYPE to DATA_LEVEL (or vice versa), and if extra Global Attributes are +; included in the Metadata; Checks for missing or repeated Global Attributes; Global +; Attributes can now be listed in any order in the Metadata (will be written to the HDF file +; in a standard order) - Version 3.0 +; 20091203: Stop using DATA_TYPE for EVDC style input (if found change to DATA_LEVEL); If VAR_MONOTONE +; is present in the Metadata it is removed - Version 3.08 +; 20100205: Add RETURN command after all STOP_WITH_ERROR calls, which allows program to return to the +; calling program if the reterr argument is included in the idlcr8hdf call - Version 3.09 +; 20101120: Add DATA_STOP_DATE and remove DATA_LEVEL from Global Attributes; Remove VAR_AVG_TYPE, +; VAR_DIMENSION, and VIS attributes from Variable Attributes; Add call to GEOMS_RULE_CHANGES +; to account for changes in Metadata setup between v3.0 and v4.0; Remove option to have a +; filename as a value for free text attributes; Increase checks between DATA_VARIABLES and +; VAR_NAME values - Version 4.0b0 +; 20111220: Call additional GEOMS_RULE_CHANGES; Perform checks on HDF4 pre-defined attributes +; - Version 4.0b7 +; 20150127: Stop removing leading and trailing spaces from variable attribute values (when there is +; only 1 value) to allow for possibility of VAR_FILL_VALUEs being an empty string +; - Version 4.0b25 +; 20150811: Add checks for invalid FILE_META_VERSION entries - Version 4.0b29 +; 20150924: Change to v4.0b25 so that leading and trailing spaces are not removed from VAR_FILL_VALUE +; entries only (see attr_arr_str) - Version 4.0b30 +; 20151012: Improve/fix checks for invalid whitespace in Metadata labels or values and add log output when +; changes are made. Checks previously done in CHECK_METADATA moved to this routine +; - Version 4.0b31 +; 20151021: Do not do checks on invalid whitespace for attributes that have numeric values - Version 4.0b32 +; 20151109: Add log message when adding/checking for missing mandatory variable attributes - Version 4.0b34 +; 20151116: Fix bug when doing whitespace checks and there is no metadata entry - Version 4.0b35 +; 20180528: Fix bug when adding non-standard global attributes to meta_arr - Version 4.0b47 +; 20191205: Do checks for sub-value separators (. or ; or _) being at the start or end of attribute +; values, and for consecutive sub-value separators (e.g. ;;). This can occur as the STRSPLIT +; command can remove these while testing sub-values, so they are not detected as part of +; any tests - Version 4.0b53 +; 20200311: Do not include '.' separator when doing checks for sub-value separators at the start or end of +; the attribute values e.g. allow DO_NAME=Boyd;Ian S. - Version 4.0b54 +; 20201020: Generate message for invalid metadata attributes, previously removed them 'quietly' +; - Version 4.0b56 +; 20201026: Fix bug from 4.0b56 which caused an error message if _FillValue was present in the GEOMS file +; - Version 4.0b57 +; +; Inputs: metafile - Either, the filename of the input file containing the Metadata or, if program +; input is via string array and Structure, a string array containing the Global +; and Variable Attributes. +; inf - flag, where 0=Metafile is an array; 1=Metafile is a file. +; +; Output: meta_arr - a string array containing the Global and Variable Attributes, which have been +; checked and uniformally formatted. +; +; Called by: IDLCR8HDF +; +; Subroutines Called: STOP_WITH_ERROR (if error state detected); +; INFOTXT_OUTPUT, GEOMS_RULE_CHANGES +; Possible Conditions for STOP_WITH_ERROR call (plus [line number] where called): +; 1. Metadata value contains too many characters +; 2. No or invalid DATA_VARIABLES and VAR_NAME attributes present in the metadata +; 3. DATA_VARIABLES or VAR_NAME value appears more than once +; 4. No valid Metadata in command line parameter file or arrays +; 5. VAR_NAME does not match corresponding DATA_VARIABLES value +; 6. Number of DATA_VARIABLES values does not match the number of VAR_NAME occurrences +; 7. A required Global Attribute Label is not present in the Metadata +; 8. A required Global Attribute Label is repeated in the Metadata +; 9. Number of Attribute occurrences does not match the number of datasets +; 10. DATA_QUALITY is not present but is a mandatory attribute when DATA_TEMPLATE is reported +; +; Information Conditions (when the program is able to make changes): +; 1. Invalid Attribute Label ignored +; 2. Missing Global Attribute added +; 3. DATA_VARIABLES attribute added to metadata based on VAR_NAME values +; 4. Values for DATA_VARIABLES appended to the metadata based on VAR_NAME values +; 5. VAR_NAME value appended to label based on DATA_VARIABLES value +; 6. New Global Attribute added + +COMMON TABLEDATA +COMMON METADATA +COMMON DATA +COMMON WIDGET_WIN + +attr_arr_glob=['PI_NAME','PI_AFFILIATION','PI_ADDRESS','PI_EMAIL',$ + 'DO_NAME','DO_AFFILIATION','DO_ADDRESS','DO_EMAIL',$ + 'DS_NAME','DS_AFFILIATION','DS_ADDRESS','DS_EMAIL',$ + 'DATA_DESCRIPTION','DATA_DISCIPLINE','DATA_GROUP','DATA_LOCATION',$ + 'DATA_SOURCE','DATA_VARIABLES','DATA_START_DATE','DATA_STOP_DATE',$ + 'DATA_FILE_VERSION','DATA_MODIFICATIONS','DATA_CAVEATS','DATA_RULES_OF_USE',$ + 'DATA_ACKNOWLEDGEMENT','DATA_QUALITY','DATA_TEMPLATE','DATA_PROCESSOR',$ + 'FILE_NAME','FILE_GENERATION_DATE','FILE_ACCESS','FILE_PROJECT_ID','FILE_DOI', $ + 'FILE_ASSOCIATION','FILE_META_VERSION'] + +attr_arr_glob_opt=['DATA_DESCRIPTION','DATA_MODIFICATIONS','DATA_CAVEATS','DATA_RULES_OF_USE',$ + 'DATA_ACKNOWLEDGEMENT','DATA_QUALITY','DATA_TEMPLATE','DATA_PROCESSOR',$ + 'FILE_PROJECT_ID','FILE_ASSOCIATION'] + +attr_arr_glob_ins=['DATA_START_DATE','DATA_STOP_DATE','FILE_GENERATION_DATE', $ + 'FILE_DOI','FILE_META_VERSION'] + +attr_arr_glob_prov=['DATA_TEMPLATE','DATA_QUALITY'] + +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'] + +attr_arr_data_opt=['VAR_NOTES'] + +;attr_arr_str=['VAR_FILL_VALUE','_fillvalue'] ;do not take STRTRIM of these values + +;Attributes that have user defined values +attr_free=['DATA_DESCRIPTION','DATA_MODIFICATIONS','DATA_CAVEATS',$ + 'DATA_RULES_OF_USE','DATA_ACKNOWLEDGEMENT','DATA_QUALITY', $ + 'DATA_PROCESSOR','FILE_PROJECT_ID','FILE_ASSOCIATION', $ + 'VAR_DESCRIPTION','VAR_NOTES'] + +;Atrributes that have numeric values (except for string datatype) +num_atts=['VAR_VALID_MIN','VAR_VALID_MAX','VAR_FILL_VALUE','_fillvalue','valid_range'] + +;To check for pre-defined attributes +ncsa=['long_name','units','format','coordsys','valid_range','_fillvalue','scale_factor', $ + 'scale_factor_err','add_offset','add_offset_err','calibrated_nt'] + +;Possible sub-value separators +sv_sep=['.','_',';'] + +nchar=65535L ;limit on number of characters + +;possible error messages for this procedure +errtxt=STRARR(9) +proname='Read_Metadata procedure: ' +errtxt[0]=' value contains too many characters (maximum permitted: '+STRTRIM(nchar,2)+')' +errtxt[1]='No or invalid DATA_VARIABLES and VAR_NAME attributes present in the metadata' +errtxt[2]=' value appears more than once: ' +errtxt[3]='No valid Metadata in input' +errtxt[4]='VAR_NAME does not match corresponding DATA_VARIABLES value: ' +errtxt[5]='Number of DATA_VARIABLES values does not match the number of VAR_NAME occurrences ' +errtxt[6]='Global Attribute Label not present in the Metadata: ' ;now INFOTXT +errtxt[7]='Global Attribute Label repeated in the Metadata: ' +errtxt[8]=' occurrences does not match the number of datasets (should be ' + +dum='' & nrline=0L & dvfound=-1L +nd=N_ELEMENTS(attr_arr_data) +ng=N_ELEMENTS(attr_arr_glob) +nna=N_ELEMENTS(num_atts) +dsei=WHERE(attr_arr_glob EQ 'DS_EMAIL') ;any new global attributes should be after this attribute +vni=WHERE(attr_arr_data EQ 'VAR_NAME') ;used in VAR_NAME checks +ndv=0 ;will reset to the number of datasets + +IF inf EQ 1 THEN BEGIN ;count number of lines in the file + OPENR,lu,metafile,/GET_LUN + WHILE NOT EOF(lu) DO BEGIN + READF,lu,dum & nrline=nrline+1L + ENDWHILE + FREE_LUN,lu + mh=STRARR(nrline) & mv_lng=LON64ARR(nrline) & mv_dbl=DBLARR(nrline) + OPENR,lu,metafile,/GET_LUN + READF,lu,mh + FREE_LUN,lu +ENDIF ELSE mh=metafile ;metadata is already in an array +mhhold=STRTRIM(mh,1) ;create temporary array and remove leading blanks from all the entries +mhgood=INTARR(N_ELEMENTS(mh))+1 ;will be assigned zero for unwanted metadata lines + +;Check for Redundant Attribute Labels +GEOMS_RULE_CHANGES,0,mhhold,mhgood + +;test1 - check first character is A-Z or a-z; test2 - check for '=' and not a redundant label +test1=(((BYTE(STRMID(mhhold,0,1)) GE 65) AND (BYTE(STRMID(mhhold,0,1)) LE 90)) OR $ + ((BYTE(STRMID(mhhold,0,1)) GE 97) AND (BYTE(STRMID(mhhold,0,1)) LE 122)) OR $ + (STRMID(STRUPCASE(mhhold),0,10) EQ '_FILLVALUE')) +test2=(STRPOS(mhhold,'=') GE 1) AND (mhgood EQ 1) + +nri=WHERE((test1) AND (test2),nrline) +IF nrline EQ 0 THEN BEGIN + STOP_WITH_ERROR,o3[3]+proname,errtxt[3],lu & RETURN +ENDIF + +;Generate an error for invalid metadata attributes +bnri=WHERE(~test1,bnrline) +IF bnrline NE 0 THEN BEGIN + FOR i=0L,bnrline-1L DO BEGIN + infotxt='2 Invalid Metadata Attribute: '+mhhold[bnri[i]]+'|' + infotxt=infotxt+' removed from the metadata saved to the output file' + INFOTXT_OUTPUT,infotxt + ENDFOR +ENDIF + +;Set holding arrays for metadata +mh=mh[nri] & mv_lng=mv_lng[nri] & mv_dbl=mv_dbl[nri] +lu=-1 +m_v0=STRARR(nrline) & m_v1=m_v0 ;to hold metadata labels and values +m_v2=INTARR(nrline) ;global/variable attribute switch + +;Separate out Metadata entries +FOR i=0L,nrline-1L DO BEGIN + ;separate out the Meta entry into two components + eqpos=STRPOS(mh[i],'=') + m_v0[i]=STRMID(mh[i],0,eqpos) + IF eqpos EQ STRLEN(mh[i])-1 THEN m_v1[i]='' $ ;ie. the last character is the '=' + ELSE m_v1[i]=STRMID(mh[i],eqpos+1) +ENDFOR + +;Check for any attribute label that is not uppercase (and not an HDF pre-defined label) or has leading or trailing spaces +ui=WHERE(m_v0 NE STRUPCASE(m_v0),ucnt) +lti=WHERE(m_v0 NE STRTRIM(m_v0,2),ltcnt) + +IF ltcnt NE 0L THEN BEGIN ;some attribute labels have leading or trailing spaces + m_v0=STRTRIM(m_v0,2) + IF qa_yes THEN BEGIN ;only report if doing QA, otherwise fix quietly + infotxt='2 Leading or trailing spaces in Metadata Attribute Labels not permitted' + INFOTXT_OUTPUT,infotxt + ENDIF +ENDIF + +IF ucnt NE 0L THEN BEGIN ;some attribute labels are not fully uppercase + writeonce=0 ;write out information text once only + FOR i=0L,ucnt-1L DO BEGIN + ;check for HDF predefined labels which should be lowercase + pi=WHERE(STRLOWCASE(m_v0[ui[i]]) EQ ncsa,pcnt) + IF pcnt EQ 0 THEN BEGIN ;label expected to be in uppercase + m_v0[ui[i]]=STRUPCASE(m_v0[ui[i]]) + IF writeonce EQ 0 THEN BEGIN ;write INFORMATION text to log + writeonce=1 + IF qa_yes THEN itxt='must be' ELSE itxt='made' + infotxt='2 Metadata Attribute Labels '+itxt+' UPPERCASE' + INFOTXT_OUTPUT,infotxt + ENDIF + ENDIF + ENDFOR +ENDIF + +;Check for and remove leading and trailing spaces in Metadata (sub-)values (except for free-text attributes +;and VAR_FILL_VALUEs). Also for non-free-text attributes check if the first and last characters are +;sub-value separators, or if there are consecutive sub-value separators in the attribute value. +;Also check for values with excessive length. +IF qa_yes THEN wstxt='are not permitted' ELSE wstxt='removed' ;used when reporting white space in sub-values + +FOR i=0L,nrline-1L DO BEGIN + ;si=WHERE(STRUPCASE(m_v0[i]) EQ STRUPCASE(num_atts),scnt) + ;IF scnt EQ 0 THEN si=WHERE(m_v0[i] EQ attr_free,scnt) + si=WHERE(m_v0[i] EQ attr_free,scnt) + IF scnt EQ 0 THEN BEGIN ;not a free text attribute + nmi=WHERE(STRUPCASE(m_v0[i]) EQ STRUPCASE(num_atts),nmcnt) ;is it an attribute that should have a numeric value + res=STRSPLIT(m_v1[i],';',/Extract,COUNT=svcnt) + spi=WHERE(STRTRIM(res,2) NE res,spcnt) + IF spcnt NE 0 THEN BEGIN + IF (svcnt EQ 1) AND (qa_yes) THEN BEGIN + IF (nmcnt NE 0) AND (STRTRIM(res[0],2) NE '') THEN infotxt='' $ value should be a number not a string so do not check for spaces + ELSE IF res[0] EQ ' ' THEN infotxt='' $ there could be a single space in the HDF file if there is no entry + ELSE infotxt='2 Leading or trailing spaces in '+m_v0[i]+'='+m_v1[i]+' value '+wstxt + ENDIF ELSE IF svcnt GT 1 THEN BEGIN + infotxt='2 Leading or trailing spaces between '+m_v0[i]+' sub-values '+wstxt + ENDIF ELSE infotxt='' ;Quietly fix if only single value and not doing QA + IF infotxt NE '' THEN INFOTXT_OUTPUT,infotxt + res=STRTRIM(res,2) + m_v1[i]=res[0] + IF svcnt GT 1 THEN FOR j=1,svcnt-1 DO m_v1[i]=m_v1[i]+';'+res[j] + ENDIF + + IF nmcnt EQ 0 THEN BEGIN + ;Check that first and last characters of the metadata value aren't sub-value separators + ;and that there are no consecutive sub-value separators + mv1len=STRLEN(m_v1[i]) ;string length of attribute value + mv1start=STRMID(m_v1[i],0,1) ;first character + mv1end=STRMID(m_v1[i],mv1len-1L,1) ;last character + FOR k=0,1 DO BEGIN + writeonce=1B ;so that error message is only written once for each attribute + IF k EQ 0 THEN $ + infotxt='3 Sub-value separators at the start or end of the attribute value are not permitted' $ + ELSE infotxt='3 Consecutive sub-value separators are not permitted' + FOR j=0,N_ELEMENTS(sv_sep)-1 DO BEGIN + spchk=sv_sep[j]+sv_sep[j] + ;Note: in some cases a '.' may be present at the end of a sub-value e.g. DO_NAME=Boyd;Ian S. + IF (k EQ 0) AND (sv_sep[j] NE '.') THEN test=(mv1start EQ sv_sep[j]) OR (mv1end EQ sv_sep[j]) $ + ELSE IF k EQ 1 THEN test=STRPOS(m_v1[i],spchk) NE -1 $ + ELSE test=0B + IF (test) AND (writeonce) THEN BEGIN + IF mv1len GT 50 THEN infotxt=infotxt+' in '+m_v0[i]+' attribute' $ + ELSE infotxt=infotxt+': '+m_v0[i]+'='+m_v1[i] + INFOTXT_OUTPUT,infotxt + writeonce=0B + ENDIF + ENDFOR + ENDFOR + ENDIF + ENDIF + ;check for string length of attribute value + IF STRLEN(m_v1[i]) GT nchar THEN BEGIN + infotxt='3 '+m_v0[i]+errtxt[0] + INFOTXT_OUTPUT,infotxt + ENDIF +ENDFOR + +FOR i=0L,nrline-1L DO BEGIN + ;Do check for DATA_VARIABLES and separate out the values + IF m_v0[i] EQ 'DATA_VARIABLES' THEN BEGIN + dv=STRSPLIT(m_v1[i],' ;',/Extract,COUNT=ndv) & dvfound=i + ENDIF + + ;Check for FILE_META_VERSION and add value derived from READ_TABLEFILE if not doing QA + IF m_v0[i] EQ 'FILE_META_VERSION' THEN BEGIN + IF (~qa_yes) AND (m_v1[i] NE tab_ver) THEN BEGIN + m_v1[i]=tab_ver + infotxt='0 FILE_META_VERSION updated with corrected TAV file version and tool name' + INFOTXT_OUTPUT,infotxt + ENDIF ELSE IF qa_yes THEN BEGIN + ;Check Revision number is less than or equal to the TAV revision number, must be '04''R''xxx' + revno=STRSPLIT(m_v1[i],';',/EXTRACT,count=fmvcnt) & revno=STRTRIM(revno,2) + tavno=STRSPLIT(tab_ver,';',/EXTRACT) & tavno=STRTRIM(tavno,2) + IF fmvcnt NE 2 THEN BEGIN + infotxt='2 FILE_META_VERSION values must include the TAV file version and the HDF file ' + infotxt=infotxt+'creation tool name e.g. FILE_META_VERSION='+tavno[0]+';CUSTOM' + INFOTXT_OUTPUT,infotxt + ENDIF + revok=0 ;default is to report a TAV file version error unless revok eq 3 + IF STRLEN(revno[0]) EQ STRLEN(tavno[0]) THEN BEGIN + vrev=STRMID(revno[0],0,2) & vtav=STRMID(tavno[0],0,2) + rrev=STRMID(revno[0],2,1) & rtav=STRMID(tavno[0],2,1) + srev=STRMID(revno[0],3) & stav=STRMID(tavno[0],3) + IF IS_A_NUMBER_HDF(vrev) THEN BEGIN + IF (FIX(vrev) GE 4) AND (FIX(vrev) LE FIX(vtav)) THEN revok++ ;TAV version OK + ENDIF + IF rrev EQ rtav THEN revok++ ;'R' is present as the third character + IF (IS_A_NUMBER_HDF(srev)) AND (revok eq 2) THEN BEGIN + IF (FIX(srev) LE FIX(stav)) OR ((FIX(srev) GT FIX(stav)) AND (FIX(vrev) LT FIX(vtav))) THEN revok++ ;TAV sub-version OK + ENDIF + ENDIF + IF revok NE 3 THEN BEGIN + infotxt='2 FILE_META_VERSION sub-value '+revno[0]+' is not valid. It must be the TAV file version e.g. '+tavno[0] + INFOTXT_OUTPUT,infotxt + ENDIF + ENDIF + ENDIF + + ;Check whether a Global Attribute (1), GEOMS Variable Attribute (2), or pre-defined Variable Attribute (3) + gi=WHERE(m_v0[i] EQ attr_arr_glob,gcnt) + vi=WHERE(m_v0[i] EQ attr_arr_data,vcnt) + pi=WHERE(STRLOWCASE(m_v0[i]) EQ ncsa,pcnt) + IF gcnt NE 0 THEN m_v2[i]=1 $ + ELSE IF vcnt NE 0 THEN m_v2[i]=2 $ + ELSE IF pcnt NE 0 THEN m_v2[i]=3 $ + ELSE BEGIN ;check for new Global Attribute + IF i GT 0 THEN test1=m_v2[i-1] EQ 1 ELSE test1=1 ;i.e. the previous attribute was also a global attribute + test2=(STRMID(m_v0[i],0,3) NE 'VAR') AND (STRMID(m_v0[i],0,3) NE 'VIS') ;does not start with 'VAR' or 'VIS' + test3=STRPOS(m_v0[i],' ') EQ -1 ;No spaces in the label + ;Check for possible mispellings of an actual attribute by removing '_' and comparing + gv_chk=STRUPCASE(STRJOIN(STRSPLIT(m_v0[i],' _',/EXTRACT),/SINGLE)) + test4=1 + FOR j=0,ng-1 DO BEGIN + ga_j=STRUPCASE(STRJOIN(STRSPLIT(attr_arr_glob[j],' _',/EXTRACT),/SINGLE)) + IF gv_chk EQ ga_j THEN test4=0 ;Match with actual Global Attribute so do not keep + ENDFOR + IF (test1) AND (test2) AND (test3) AND (test4) THEN m_v2[i]=1 ;it is considered a new global attribute + ENDELSE +ENDFOR + +;Check for any invalid attribute labels and remove +bi=WHERE(m_v2 EQ 0,bcnt) +IF bcnt NE 0 THEN BEGIN + FOR i=0,bcnt-1 DO BEGIN + infotxt='2 Unknown Attribute Label '+m_v0[bi[i]]+'| ignored' + INFOTXT_OUTPUT, infotxt + ENDFOR +ENDIF +;resize arrays, or stop the program if no valid data +gi=WHERE(m_v2 NE 0,nrline) +IF nrline NE 0 THEN BEGIN + m_v0=m_v0[gi] & m_v1=m_v1[gi] & m_v2=m_v2[gi] + mv_lng=mv_lng[gi] & mv_dbl=mv_dbl[gi] +ENDIF ELSE BEGIN + STOP_WITH_ERROR,o3[3]+proname,errtxt[3],lu & RETURN +ENDELSE + +;Check to see that Global and Variable Attributes are present +gi=WHERE(m_v2 EQ 1,gcnt) +vi=WHERE(m_v2 EQ 2,vcnt) +pi=WHERE(m_v2 EQ 3,pcnt) ;to do pre-defined attribute checks if necessary +IF (gcnt EQ 0) OR (vcnt EQ 0) THEN BEGIN + STOP_WITH_ERROR,o3[3]+proname,errtxt[3],lu & RETURN +ENDIF + +;Do QA on pre-defined attributes +IF pcnt NE 0 THEN PRE_DEFINED_ATT_CHECKS,-1,m_v0[pi] + +;Do DATA_VARIABLES checks +IF ndv EQ 0 THEN BEGIN + ;If no DATA_VARIABLES values found then try to determine values from VAR_NAME values + dvi=WHERE((m_v0 EQ 'VAR_NAME') AND (m_v1 NE ''),ndv) + IF ndv NE 0 THEN BEGIN + dv=m_v1[dvi] + ;Create or append to DATA_VARIABLES attribute + IF dvfound EQ -1L THEN BEGIN ;increase array sizes by 1 to accommodate DATA_VARIABLES + dvfound=nrline & nrline=nrline+1L + m_v0=[m_v0,'DATA_VARIABLES'] & m_v1=[m_v1,''] & m_v2=[m_v2,1] + mv_lng=[mv_lng,0LL] & mv_dbl=[mv_dbl,0.D] + infotxt='2 Missing DATA_VARIABLES Global Attribute| added to metadata based on VAR_NAME values' + ENDIF ELSE BEGIN + infotxt='2 Missing DATA_VARIABLES attribute values| appended to the metadata based on VAR_NAME values' + ENDELSE + INFOTXT_OUTPUT, infotxt + ENDIF ELSE BEGIN + STOP_WITH_ERROR,o3[3]+proname,errtxt[1],lu & RETURN + ;No or invalid DATA_VARIABLES and VAR_NAME attributes present in the metadata so cannot proceed + ENDELSE +ENDIF +;Check for repeated DATA_VARIABLES +FOR i=0,ndv-1 DO BEGIN + ri=WHERE(STRUPCASE(dv[i]) EQ STRUPCASE(dv),rcnt) + IF (rcnt GT 1) AND (dv[i] NE '') THEN BEGIN + STOP_WITH_ERROR,o3[3]+proname,'DATA_VARIABLES'+errtxt[2]+dv[i],lu & RETURN + ENDIF +ENDFOR + +;Do VAR_NAME checks +vi=WHERE(m_v0 EQ attr_arr_data[vni[0]],vcnt) ;identify VAR_NAME indices +IF vcnt NE 0 THEN BEGIN + ;Check if VAR_NAME values are present, if not add value from DATA_VARIABLES (if number of DATA_VARIABLES values match vcnt) + IF ndv EQ vcnt THEN BEGIN + FOR i=0,vcnt-1 DO BEGIN + IF (m_v1[vi[i]] NE '') AND (m_v1[vi[i]] NE dv[i]) THEN BEGIN + STOP_WITH_ERROR,o3[3]+proname,errtxt[4]+'('+m_v1[vi[i]]+'/'+dv[i]+')',lu & RETURN + ENDIF ELSE IF m_v1[vi[i]] EQ '' THEN BEGIN + m_v1[vi[i]]=dv[i] + infotxt='2 Missing VAR_NAME value for '+m_v0[vi[i]]+'|. '+dv[i]+'| appended to label based on DATA_VARIABLES value + INFOTXT_OUTPUT,infotxt + ENDIF + ENDFOR + ENDIF ELSE BEGIN ;number of DATA_VARIABLES values does not match the number of VAR_NAME occurrences + STOP_WITH_ERROR,o3[3]+proname,errtxt[5]+'('+STRTRIM(ndv,2)+'/'+STRTRIM(vcnt,2)+')',lu & RETURN + ENDELSE + ;Check for repeated VAR_NAMES + FOR i=0,vcnt-1 DO BEGIN + ri=WHERE(STRUPCASE(m_v1[vi[i]]) EQ STRUPCASE(m_v1[vi]),rcnt) + IF (rcnt GT 1) AND (m_v1[vi[i]] NE '') THEN BEGIN + STOP_WITH_ERROR,o3[3]+proname,'VAR_NAME'+errtxt[2]+m_v1[vi[i]],lu & RETURN + ENDIF + ENDFOR +ENDIF ELSE BEGIN ;No VAR_NAME labels so create from DATA_VARIABLES + FOR i=0,ndv-1 DO BEGIN + m_v0=[m_v0,'VAR_NAME'] & m_v1=[m_v1,dv[i]] & m_v2=[m_v2,2] + mv_lng=[mv_lng,0LL] & mv_dbl=[mv_dbl,0.D] + IF i EQ 0 THEN vi=[nrline] ELSE vi=[vi,nrline+1L] + ENDFOR + infotxt='1 Missing VAR_NAME attribute labels and values| added to metadata based on DATA_VARIABLES values' + INFOTXT_OUTPUT,infotxt +ENDELSE + +;Check for obsolete FTIR, UVVIS.DOAS, LIDAR, and MWR DATA_SOURCE_01 values and modify of possible +dsi=WHERE(m_v0 EQ 'DATA_SOURCE',dsicnt) +vni=WHERE(m_v0 EQ 'VAR_NAME',vncnt) +;extract VAR_NAME values +dv=STRARR(vncnt) & data_source='' +FOR i=0,vncnt-1 DO dv[i]=m_v1[vni[i]] +IF dsicnt EQ 1 THEN BEGIN + IF m_v1[dsi[0]] NE '' THEN BEGIN + data_source=m_v1[dsi[0]] & dschange=data_source + GEOMS_RULE_CHANGES,6,vncnt,dv,data_source + IF data_source NE dschange THEN m_v1[dsi[0]]=data_source + ENDIF +ENDIF + +;Check for missing Global Attributes which have values which can be calculated by the program +FOR i=0,N_ELEMENTS(attr_arr_glob_ins)-1 DO BEGIN + gaii=WHERE(m_v0 EQ attr_arr_glob_ins[i],gaicnt) + IF gaicnt EQ 0 THEN BEGIN ;missing mandatory global attribute so add to the array + IF attr_arr_glob_ins[i] EQ 'FILE_META_VERSION' THEN itxt=tab_ver ELSE itxt='' + nrline=nrline+1L + m_v0=[m_v0,attr_arr_glob_ins[i]] & m_v1=[m_v1,itxt] & m_v2=[m_v2,1] + mv_lng=[mv_lng,0LL] & mv_dbl=[mv_dbl,0.D] + infotxt='2 Missing mandatory Global Attribute '+attr_arr_glob_ins[i]+'| added' + INFOTXT_OUTPUT, infotxt + ENDIF +ENDFOR +;Do DATA_TEMPLATE/DATA_QUALITY checks +ngp=N_ELEMENTS(attr_arr_glob_prov) +ga_chk=STRARR(ngp) ;initialize DATA_TEMPLATE/QUALITY values +GEOMS_RULE_CHANGES,9,m_v0,m_v1,ga_chk +;Check to see if Global Attributes have to be added +FOR i=0,ngp-1 DO BEGIN + IF ga_chk[i] NE '' THEN BEGIN + m_v0=[m_v0,attr_arr_glob_prov[i]] + IF i EQ 0 THEN m_v1=[m_v1,ga_chk[i]] ELSE m_v1=[m_v1,''] + m_v2=[m_v2,1] & nrline=nrline+1L + mv_lng=[mv_lng,0LL] & mv_dbl=[mv_dbl,0.D] + IF i EQ 0 THEN itxt='='+ga_chk[i] ELSE itxt='' + infotxt='2 Missing Global Attribute '+attr_arr_glob_prov[i]+itxt+'| added' + INFOTXT_OUTPUT, infotxt + ENDIF +ENDFOR + +;Calculate expected size of meta_arr array +gi=WHERE(m_v2 EQ 1,gcnt) +IF gcnt GT ng THEN ngv=gcnt ELSE ngv=ng +n_arr=ngv+(nd*ndv) +meta_arr=STRARR(n_arr) & mlh=LON64ARR(n_arr) & mdh=DBLARR(n_arr) +;mlh and mdh are holding arrays to hold numeric values in the metadata (will be renamed mv_lng and mv_dbl) + +;Check for missing or repeated standard Global Attributes +i=0 +WHILE i LE ng-1 DO BEGIN + gai=WHERE(m_v0[gi] EQ attr_arr_glob[i],gacnt) + IF gacnt EQ 0 THEN BEGIN ;Check to see if Global Attribute Optional or Mandatory + gaoi=WHERE(attr_arr_glob[i] EQ attr_arr_glob_opt, gaocnt) + IF gaocnt EQ 0 THEN BEGIN ;it is mandatory, therefore missing + infotxt='3 Global Attribute Label not present in the Metadata: '+attr_arr_glob[i] + INFOTXT_OUTPUT,infotxt + ENDIF + ENDIF ELSE IF gacnt GT 1 THEN BEGIN ;Global Attribute Repeated + STOP_WITH_ERROR,o3[3]+proname,errtxt[7]+attr_arr_glob[i],lu & RETURN + ENDIF + IF gacnt EQ 0 THEN BEGIN ;remove attribute from the attr_arr_glob array + attr_arr_glob=[attr_arr_glob[0:i-1],attr_arr_glob[i+1:ng-1]] + ng=ng-1 + ENDIF ELSE i=i+1 +ENDWHILE + +;Check for all Standard Global Attributes, add any new ones, and put in expected order +xg=0 & mi=0 +FOR i=0,gcnt-1 DO BEGIN + gai=WHERE(m_v0[gi[i]] EQ attr_arr_glob,gacnt) + CASE 1 OF + gacnt EQ 0: BEGIN ;New Global Attribute + IF i EQ 0 THEN BEGIN + attr_arr_glob=[m_v0[gi[i]],attr_arr_glob] + ENDIF ELSE IF i EQ ng THEN BEGIN + attr_arr_glob=[attr_arr_glob,m_v0[gi[i]]] + ENDIF ELSE BEGIN + attr_arr_glob=[attr_arr_glob[0:mi],m_v0[gi[i]],attr_arr_glob[mi+1:N_ELEMENTS(attr_arr_glob)-1]] + mi=mi+1 + ENDELSE + ;IF (i GE ng-(1+xg)) OR (i LE dsei) THEN BEGIN + ; attr_arr_glob=[attr_arr_glob,m_v0[gi[i]]] & mi=ng + ; IF i LE dsei THEN xg=xg+1 + ;ENDIF ELSE BEGIN + ; attr_arr_glob=[attr_arr_glob[0:mi],m_v0[gi[i]],attr_arr_glob[mi+1:ng-1]] + ; mi=mi+1 + ;ENDELSE + ng=ng+1 + infotxt='1 Non-standard Global Attribute '+m_v0[gi[i]]+'| added' + INFOTXT_OUTPUT, infotxt + END + ELSE: mi=i ;gai[0] + ENDCASE +ENDFOR +FOR i=0,gcnt-1 DO BEGIN + gai=WHERE(m_v0[gi[i]] EQ attr_arr_glob,gacnt) + meta_arr[gai[0]]=m_v0[gi[i]]+'='+m_v1[gi[i]] +ENDFOR + +;Check for repeated new Global Attributes +FOR i=0,ng-1 DO BEGIN + gai=WHERE(m_v0[gi] EQ attr_arr_glob[i],gacnt) + IF gacnt GT 1 THEN BEGIN ;Global Attribute Repeated + STOP_WITH_ERROR,o3[3]+proname,errtxt[7]+attr_arr_glob[i],lu & RETURN + ENDIF +ENDFOR + +;Do checks for all the variable attributes and put in expected order +vni=-1 +FOR i=0,nd-1 DO BEGIN + si=ng+i ;start index of meta_arr for variable attributes + di=WHERE(m_v0 EQ attr_arr_data[i],dcnt) + IF attr_arr_data[i] EQ 'VAR_NAME' THEN vni=di ;to enable checks on optional variable attributes + IF dcnt EQ ndv THEN BEGIN ;i.e. correct number of attribute labels found so add to meta_arr in correct order + FOR j=0,ndv-1 DO BEGIN + meta_arr[si+(nd*j)]=m_v0[di[j]]+'='+m_v1[di[j]] + mlh[si+(nd*j)]=mv_lng[di[j]] & mdh[si+(nd*j)]=mv_dbl[di[j]] + ENDFOR + ENDIF ELSE IF dcnt EQ 0 THEN BEGIN ;attribute not present - so add with no label + oi=WHERE(attr_arr_data[i] EQ attr_arr_data_opt,ocnt) ;check whether missing attribute is optional + IF ocnt EQ 0 THEN BEGIN + FOR j=0,ndv-1 DO meta_arr[si+(nd*j)]=attr_arr_data[i]+'=' + IF qa_yes THEN itxt=' must be present in the metadata' ELSE itxt=' added to the metadata with no value' + infotxt='2 Mandatory variable attribute '+attr_arr_data[i]+itxt + INFOTXT_OUTPUT,infotxt + ENDIF + ENDIF ELSE BEGIN ;incorrect number of attributes - report with error unless attribute is optional + oi=WHERE(attr_arr_data[i] EQ attr_arr_data_opt,ocnt) + IF (ocnt NE 0) AND (vni[0] GE 0) AND (di[0]-vni[0] GT 0) THEN BEGIN ;match VAR_NOTES attributes to the corresponding VAR_NAME index value + FOR j=0,dcnt-1 DO BEGIN + vnih=di[j]-vni[0] & jh=0 + FOR k=0,N_ELEMENTS(vni)-1 DO BEGIN + dvn=di[j]-vni[k] + IF (dvn LT vnih) AND (dvn GT 0) THEN BEGIN + vnih=di[j]-vni[k] & jh=k + ENDIF + ENDFOR + meta_arr[si+(nd*jh)]=m_v0[di[j]]+'='+m_v1[di[j]] + mlh[si+(nd*jh)]=mv_lng[di[j]] & mdh[si+(nd*jh)]=mv_dbl[di[j]] + ENDFOR + ENDIF ELSE BEGIN ;Not optional or too much out of order, therefore report as an error + STOP_WITH_ERROR,o3[3]+proname,'Number of '+attr_arr_data[i]+errtxt[8]+STRTRIM(ndv,2)+')',lu & RETURN + ENDELSE + ENDELSE +ENDFOR + +;remove any empty meta_arr cells (can happen due to optional attributes) +;and save numeric metadata values in correct order +gi=WHERE(STRTRIM(meta_arr,2) NE '') +meta_arr=meta_arr[gi] & mv_lng=mlh[gi] & mv_dbl=mdh[gi] + +;Call GEOMS_RULE_CHANGES to check for obsolete Dataset names and update if possible +;Identify VAR_NAME and VAR_UNITS indices +vni=WHERE(STRMID(meta_arr,0,8) EQ 'VAR_NAME',vncnt) +vui=WHERE(STRMID(meta_arr,0,9) EQ 'VAR_UNITS',vucnt) +vnchange=STRARR(vncnt) ;array to hold original VAR_NAME values if they have been changed +;extract VAR_NAME values +dv=STRARR(vncnt) +FOR i=0,vncnt-1 DO BEGIN + res=STRSPLIT(meta_arr[vni[i]],' =',/EXTRACT,COUNT=nres) + IF nres EQ 2 THEN dv[i]=res[1] +ENDFOR +;Check for obsolete _START/STOP/INTEGRATION sub-values and replace with DATETIME.START/STOP +;or INTEGRATION.TIME (for single occurences only). Also check for double underscores and +;replace with single underscores in the Variable Names +GEOMS_RULE_CHANGES,1,vncnt,dv + +;Check for other obsolete variable names +IF vncnt EQ vucnt THEN BEGIN + vuv=STRARR(vucnt) + FOR i=0,vucnt-1 DO BEGIN + res=STRSPLIT(meta_arr[vui[i]],'=',/EXTRACT,COUNT=nres) + IF nres EQ 2 THEN vuv[i]=STRTRIM(res[1],2) + ENDFOR + GEOMS_RULE_CHANGES,5,vncnt,dv,vuv,data_source +ENDIF +;Update meta_arr in the case of modified Dataset name values +vci=WHERE(vnchange NE '',vcres) +IF vcres NE 0 THEN BEGIN ;need to update VAR_NAME and DATA_VARIABLES attribute values + dvi=WHERE(STRMID(meta_arr,0,14) EQ 'DATA_VARIABLES') + meta_arr[dvi[0]]='DATA_VARIABLES=' + FOR i=0,vncnt-1 DO BEGIN + ;Check for new VAR_NAME value and update + IF vnchange[i] NE '' THEN meta_arr[vni[i]]='VAR_NAME='+dv[i] + ;Append to new DATA_VARIABLES list + IF i EQ 0 THEN itxt='' ELSE itxt=';' + meta_arr[dvi[0]]=meta_arr[dvi[0]]+itxt+dv[i] + ENDFOR +ENDIF + +;Check for obsolete CALVAL and EVDC FILE_ACCESS values and replace +fai=WHERE(STRMID(meta_arr,0,11) EQ 'FILE_ACCESS') +res=STRSPLIT(meta_arr[fai[0]],' =;',/EXTRACT,COUNT=facnt) +IF facnt GE 2 THEN BEGIN + fav=STRUPCASE(res[1:facnt-1]) & fachange=fav + GEOMS_RULE_CHANGES,7,facnt-1,fav + IF ARRAY_EQUAL(fav,fachange) EQ 0 THEN BEGIN ;FILE_ACCESS values changed so update meta_arr + meta_arr[fai[0]]='FILE_ACCESS=' + FOR i=0,facnt-2 DO BEGIN + IF i EQ 0 THEN itxt='' ELSE itxt=';' + meta_arr[fai[0]]=meta_arr[fai[0]]+itxt+fav[i] + ENDFOR + ENDIF +ENDIF + +;Check for VAR_UNITS=DIMENSIONLESS or NONE and fix if possible. +;Need to check VAR_DATA_TYPE. If string then VAR_UNITS value should be blank, o/w replace with '1' +vdi=WHERE(STRMID(meta_arr,0,13) EQ 'VAR_DATA_TYPE',vdcnt) +IF (vdcnt NE 0) AND (vdcnt EQ vucnt) THEN BEGIN + vdv=STRARR(vdcnt) + FOR i=0,vdcnt-1 DO BEGIN + res=STRSPLIT(meta_arr[vdi[i]],'=',/EXTRACT,COUNT=nres) + IF nres EQ 2 THEN vdv[i]=STRUPCASE(STRTRIM(res[1],2)) + ENDFOR + vuchange=vuv + GEOMS_RULE_CHANGES,8,vucnt,vuv,vdv + FOR i=0,vucnt-1 DO BEGIN ;Check for changes and update meta_arr + IF STRUPCASE(vuchange[i]) NE vuv[i] THEN meta_arr[vui[i]]='VAR_UNITS='+vuv[i] + ENDFOR +ENDIF + +;Check for non-permitted characters (must be ISO646-US character set) +GEOMS_RULE_CHANGES,10,meta_arr + +END ;Procedure Read_Metadata + + + +PRO extract_and_test, dentry, cpos, nel, ta1, ta2, taname, aname, ti +;Procedure called by Check_Metadata to extract the relevant parts of tab_arr and test against +;a corresponding Metadata value. +; ---------- +;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org +; +; History: +; 20050802: Original IDLCR8HDF Routine - Version 1.0 +; 20050909: Make VAR_SI_CONVERSION values match the VAR_DATA_TYPE, and add fix for VAR_SI_CONVERSION +; calculation rules e.g. if VAR_UNITS=cm m-3 then VAR_SI_CONVERSION=0;0.01;m-2 - Version 1.1 +; 20061012: Procedure simplified, with all portions of the code related to the VAR_UNITS and +; VAR_SI_CONVERSION calculations moved to the Var_Units_Test routine; Common variable +; definition WIDGET_WIN added - Version 2.0 +; 20100205: Add RETURN command after all STOP_WITH_ERROR calls, which allows program to return to the +; calling program if the reterr argument is included in the idlcr8hdf call - Version 3.09 +; 20120313: Add option to check for Metadata Entries that are only different to the TAV entry +; because of case sensitivity and/or use of incorrect apostrophe ("`" instead of "'") +; - Version 4.0b8 +; 20140226: For AFFILIATION, if there is no match, do additional check of the acronym only and, +; if present, correct the value - Version 4.0b19 +; 20150811: Add infoerr array. For these Global Attributes the program will no longer stop, but +; will generate an ERROR message and continue if there is a problem with any of the +; entries - Version 4.0b29 +; 20161230: Add COUNTRY and AFFILIATION to infoerr array and allow for this when creating infotxt +; message - Version 4.0b41 +; 20171121: Fix error caused by incorrectly referring to the variable infoerr as inforerr +; - Version 4.0b43 +; 20191205: Account for DATA_SOURCE being able to have 2 or 3 sub-values, where the third sub- +; value is a VERSION_NAME - Version 4.0b53 +; 20220805: Ensure the 3-digit instrument ID is numeric (each character is now checked in +; IS_A_NUMBER_HDF) - Version 4.0b60 +; +; Inputs: dentry - the Metadata entry to be checked (everything after the '=') +; cpos - a pattern used by StrSplit on DEntry to split it into its component parts e.g. ';' or '_' +; nel - the number of sub-values in the set of tab_arr entries being checked (ta1 only) +; ta1 - the set of tab_arr values used to check against the Metadata contents +; ta2 - a second set of tab_arr values used for DATA_SOURCE checks, o/w set to [''] +; taname - a combination of the Metadata Attribute name and Table Field/Label used for the +; error messages +; aname - the Metadata attribute name to be used for the error message +; ti - the index value of the ta2 values to be checked. Used for DATA_SOURCE checks only +; +; Output: ti - the index value(s) of tab_arr which match the Metadata value (used in Check_Metadata +; to do further checks on the ORIGINATOR values) +; +; Called by: CHECK_METADATA +; +; Subroutines Called: STOP_WITH_ERROR (if error state detected) +; Possible Conditions for STOP_WITH_ERROR call (plus [line number] where called): +; 1. Incorrect number of (sub-)values for a particular Metadata attribute +; 2. Metadata value does not match any values in the Table Attribute Values file + +COMMON TABLEDATA +COMMON WIDGET_WIN + +proname='Check_Metadata/Extract_And_Test procedures: ' & lu=-1L +errtxt2=STRARR(2) +errtxt2[0]='Number of (sub-)values for this attribute should be ' +errtxt2[1]='Doesn''t match any Table Attribute Values under FIELD: ' + +infoerr=['COUNTRY','AFFILIATION','FILE_ACCESS','FILE_META_VERSION','DATA_LOCATION', $ + 'DATA_DISCIPLINE','DATA_GROUP'] ;do not stop the program for errors in these attributes + +;Check number of Metadata entry components is as expected +achk=STRSPLIT(dentry,cpos,/Extract,COUNT=n_achk) +oki=WHERE(n_achk EQ nel,okcnt) ;Note: DATA_SOURCE can have 2 or 3 sub-values (nel=[2,3] from 4.0b53) +IF okcnt EQ 0 THEN BEGIN + STOP_WITH_ERROR,o3[3]+proname+taname+'='+dentry+': ',errtxt2[0]+STRTRIM(nel,2)+'.',lu + RETURN +ENDIF + +;allow fix to be made to the affiliation if the acronym is correct but institute name is wrong +n_ta1=N_ELEMENTS(ta1) +IF (STRPOS(taname,'AFFILIATION') NE -1) AND (cpos NE '_') THEN BEGIN + affchk=1 & acroval=STRTRIM(achk[1],2) & t_acrovals=STRARR(n_ta1) +ENDIF ELSE affchk=0 + +;check Metadata entry with relevant TAV entry or entries +IF cpos EQ '_' THEN BEGIN ;DATA_SOURCE entry so need to treat components separately + achk=STRCOMPRESS(achk,/Remove_All) & nel=1 + achkh=' '+achk[1] + ;strip the last three characters (instrument ID) off achk(1) + achk[1]=STRMID(achk[1],0,STRLEN(achk[1])-3) + ;Check that the instrument ID is valid + instidchk=STRMID(achkh,STRLEN(achkh)-3,3) + IF ~IS_A_NUMBER_HDF(instidchk,2) THEN BEGIN + infotxt='3 DATA_SOURCE 3-digit instrument identifier missing or invalid' + INFOTXT_OUTPUT,infotxt + ENDIF +ENDIF ELSE achk=STRCOMPRESS(dentry,/Remove_All) +FOR j=0,n_ta1-1 DO BEGIN + ;extract valid portion of the entries (depending on ATTRIBUTE name) + tchk=STRSPLIT(ta1[j],';',/Extract) + ta1[j]=STRCOMPRESS(tchk[0],/Remove_All) + IF nel GT 1 THEN $ + FOR k=1,nel-1 DO ta1[j]=ta1[j]+';'+STRCOMPRESS(tchk[k],/Remove_All) + IF affchk EQ 1 THEN t_acrovals[j]=tchk[1] +ENDFOR +IF ta2[0] NE '' THEN BEGIN + FOR j=0,N_ELEMENTS(ta2)-1 DO BEGIN + ;extract valid portion of the entries (depending on ATTRIBUTE name) + tchk=STRSPLIT(ta2[j],';',/Extract) + ta2[j]=STRCOMPRESS(tchk[ti],/Remove_All) + ENDFOR +ENDIF +IF cpos EQ '_' THEN BEGIN + gi=WHERE(ta1 EQ achk[0],gcnt) + IF gcnt NE 0 THEN gi=WHERE(ta2 EQ achk[1],gcnt) +ENDIF ELSE BEGIN ;Check for Case Sensitive or incorrect apostrophe problem + gi=WHERE(ta1 EQ achk,gcnt) + IF gcnt EQ 0 THEN BEGIN + ;Check for Case Sensitivity and/or incorrect apostrophe usage or correct affiliation acronym only + ;Will be fixed in CHECK_METADATA + gi=WHERE(STRLOWCASE(ta1) EQ STRLOWCASE(achk),gcnt) + IF gcnt NE 0 THEN dentry=ta1[gi[0]] ELSE BEGIN + ;Check for "`" instead of "'" in metadata entry + IF STRPOS(achk,'`') NE -1 THEN BEGIN + achkh=achk + WHILE STRPOS(achkh,'`') NE -1 DO STRPUT,achkh,'''',STRPOS(achkh,'`') + gi=WHERE(STRLOWCASE(ta1) EQ STRLOWCASE(achkh),gcnt) + IF gcnt NE 0 THEN dentry=ta1[gi[0]] + ENDIF + ;If AFFILIATION check to see if the acronym only is valid + IF affchk EQ 1 THEN BEGIN + gi=WHERE(STRLOWCASE(acroval) EQ STRLOWCASE(t_acrovals),gcnt) + IF gcnt NE 0 THEN dentry=ta1[gi[0]] + ENDIF + ENDELSE + ENDIF +ENDELSE + +IF gcnt EQ 0 THEN BEGIN + ei=-1 + FOR i=0,N_ELEMENTS(infoerr)-1 DO IF STRPOS(taname,infoerr[i]) NE -1 THEN ei=i + + IF ei NE -1 THEN BEGIN ;Log message instead of STOP_WITH_ERROR + IF ei LE 1 THEN metalabel=STRMID(taname,STRPOS(taname,'/')+1) ELSE metalabel=infoerr[ei] + infotxt='3 '+metalabel+'='+aname+' doesn''t match any ' + infotxt=infotxt+'Table Attribute Values under '+infoerr[ei]+' field' + INFOTXT_OUTPUT,infotxt + ENDIF ELSE BEGIN + STOP_WITH_ERROR,o3[3]+proname+aname+': ',errtxt2[1]+taname,lu & RETURN + ENDELSE +ENDIF +ti=gi ;ti returned to the Check_MetaData procedure to check AFFILIATION, ADDRESS and EMAIL values + +END ;procedure Extract_and_Test + + + +PRO check_metadata +;Procedure to check the validity of Metadata values by checking them against the corresponding +;entries in the Table Attribute Values File +; ---------- +;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org +; +; History: +; 20050802: Original IDLCR8HDF Routine - Version 1.0 +; 20050909: 'rd' flag included to convert VAR_SI_CONVERSION values to floating point if the +; corresponding VAR_DATA_TYPE is real or double; Section included to check for +; repeated SI units in the calculated VAR_SI_CONVERSION - Version 1.1 +; 20061012: Section added to do simple checks on Metadata ORIGINATOR values if the information +; is not otherwise available in the TAV file; Warning messages written to output +; windows and/or log file depending on the options chosen by the user; Section relating +; to checks on VAR_UNITS and VAR_SI_CONVERSION moved to the VAR_UNITS_TEST routine; +; Common variable definition WIDGET_WIN added - Version 2.0 +; 20080302: Sections included to handle checks when an Envisat table.dat file is used, including; +; ensuring metadata labels DATA_TYPE and VAR_DATA_TYPE are not both found when using the +; Strpos procedure; Handling different table.dat format of ORIGINATOR attributes; using +; DATA_SOURCE_02 instead of AFFILIATION for the DATA_SOURCE checks. Section which puts +; TAV file VAR_UNITS and UNIT_PREFIX values into arrays for the VAR_UNITS_TEST call is +; no longer needed and is removed; Change made so that Free Attribute Metadata values +; which include semi-colons do not have white space removed (i.e. the semi-colons are +; not defined as separating sub-values) - Version 3.0 +; 20091203: Account for the situation when a VAR_NAME does not include a Variable Mode, but still +; includes a Variable Descriptor ('__' will be present in the name) - Version 3.08 +; 20100205: Add RETURN command after all STOP_WITH_ERROR calls, which allows program to return to the +; calling program if the reterr argument is included in the idlcr8hdf call - Version 3.09 +; 20101120: Do check for MJD2000 VAR_UNIT, and change to MJD2K; Check for LONG datatype (now refers to +; LONG64) and change to INTEGER (now means LONG32) for TAV checks - Version 4.0b0 +; 20121015: Change VAR_SI_CONVERSION information/error message so will also report if the +; VAR_SI_CONVERSION label does not have an accompanying value - Version 4.0b13 +; 20141110: Add check for DATETIME where VAR_SI_CONVERSION=0.0;86400.0;s, but VAR_UNITS is not MJD2K. +; Will change VAR_UNITS to MJD2K - Version 4.0b24 +; 20150127: Stop removing leading and trailing spaces from variable attribute values (when there is +; only 1 value) to allow for possibility of VAR_FILL_VALUEs being an empty string +; - Version 4.0b25 +; 20150811: Remove STOP_WITH_ERROR message if FILE_META_VERSION doesn't have 2 values. This check is +; now done in the READ_METADATA routine - Version 4.0b29 +; 20151012: Remove checks on white space in Metadata values. This check is now done in the +; READ_METADATA routine - Version 4.0b31 +; 20161130: Allow the program to continue when an error is generated after doing VAR_UNITS and +; VAR_SI_CONVERSION checks (previously called STOP_WITH_ERROR) - Version 4.0b40 +; 20161230: Modify checks on ORIGINATOR values as this section removed from the TAV file from +; 04R025. Add tests for COUNTRY in XX_ADDRESS, and check for '@' in XX_EMAIL - +; Version 4.0b41 +; 20170331: Allow for COUNTRY value in XX_ADDRESS to be all uppercase or have initial capitalization +; (allow for special cases e.g. Cote d'Ivoire; words such as the, and, of also not +; capitalized) - Version 4.0b42 +; 20191205: Add check for optional VERSION_NAME sub-value of DATA_SOURCE. If present then call +; GEOMS_RULE_CHANGES to do checks on the value - Version 4.0b53 +; 20240912: Allow for VAR_SI_CONVERSION to have two possible values depending on the ordering of units +; that have the same power value e.g. m-1 sr-1 or sr-1 m-1 - Version 4.0b64 +; +; Inputs: meta_arr - a string array containing the Global and Variable Attributes +; tab_arr - a string array containing the TAV or table.dat file attributes + +; Output: meta_arr - some meta_arr values may be corrected or calculated in this routine. White space +; between metadata sub-values is also removed. +; +; Called by: IDLCR8HDF +; +; Subroutines Called: EXTRACT_AND_TEST +; VAR_UNITS_TEST +; GEOMS_RULE_CHANGES +; STOP_WITH_ERROR (if error state detected); INFOTXT_OUTPUT +; Possible Conditions for STOP_WITH_ERROR call (plus [line number] where called): +; 1. An attribute value is expected but is absent or invalid +; 2. Incorrect number of (sub-)values for a particular Metadata attribute +; 3. NAME and AFFILIATION values don't match (only applies if reading in the full AVDC +; TAV file) +; 4. AFFILIATION (AVDC) or DATA_SOURCE_02 (Envisat) field not found in the TAV file +; 5. There is no match between a given VAR_UNIT, and the list of valid units and prefixes +; present in the TAV file +; 6. VAR_UNITS value is not valid +; +; Information Conditions (when the program is able to make changes): +; 1. ORIGINATOR values for ADDRESS or EMAIL replaced with those from the TAV file +; 2. VAR_SI_CONVERSION values replaced with those calculated by the program + +COMMON TABLEDATA +COMMON METADATA +COMMON DATA +COMMON WIDGET_WIN + +;Possible error messages for this procedure +proname='Check_Metadata procedure: ' & lu=-1L +errtxt=STRARR(4) +errtxt[0]='No or Invalid value for this Attribute: ' +errtxt[1]='Number of (sub-)values for this Attribute should be ' ;changed to information message +errtxt[2]='AFFILIATION value doesn''t match NAME value: ' +errtxt[3]=' field not found in the input Table Attribute Values file' + +;originator values to be checked +orig_attr=['PI_','DO_','DS_','NAME','AFFILIATION','ADDRESS','EMAIL'] +countrysp1=['Congo, The Democratic Republic of the','Cote d''Ivoire'] ;special cases for initial capitalization of the COUNTRY value +countrysp2=['the','and','of'] ;special cases for initial capitalization of the COUNTRY value (individual words) + +;initialize variables for VAR_UNITS/VAR_SI_CONVERSION checks +firstvu=0 & tchk=[''] & rd=1 & writeonce=0 +xval=[2,2,3,1] ;expected number of sub-values for the four originator attributes +new_name=[''] ;Entry which will contain any ORIGINATOR name values not present in the TAV file + +FOR i=0,N_ELEMENTS(meta_arr)-1 DO BEGIN + res=STRSPLIT(meta_arr[i],'=',/Extract,COUNT=nres) + IF res[0] EQ 'VAR_NAME' THEN vnval=meta_arr[i] + FOR j=0,2 DO BEGIN + FOR k=3,6 DO BEGIN + ;need to change ORIGINATOR NAME ATTRIBUTE names to match TAV File + IF res[0] EQ orig_attr[j]+orig_attr[k] THEN BEGIN + res[0]='ORIGINATOR' & jh=j & kh=k + ENDIF + ENDFOR + ENDFOR + ;Test whether metadata label is also a TAV field + ;First test checks for exact match between metadata label and TAV field + ;(This avoids possible problem using STRPOS (e.g. DATA_TYPE won't also pick up VAR_DATA_TYPE)) + mi=WHERE(tab_arr[*,0] EQ res[0],mcnt) + ;Back-up test in the event that label has more than one TAV field (e.g. DATA_VARIABLES) + IF mcnt EQ 0 THEN mi=WHERE(STRPOS(tab_arr[*,0],res[0]) EQ 0,mcnt) ;was NE -1 + + IF res[0] EQ 'ORIGINATOR' THEN BEGIN + ;Do checks on _NAME, _AFFILIATION, _ADDRESS, and _EMAIL sub-values + resx=STRSPLIT(meta_arr[i],'=;',/Extract) + nel=N_ELEMENTS(resx)-1 + + IF (kh EQ 4) AND (nel EQ xval[1]) THEN BEGIN ;check on AFFILIATION value + resy=STRSPLIT(meta_arr[i],'=',/Extract) + mci=WHERE(tab_arr[*,0] EQ 'AFFILIATION',mccnt) + IF mccnt NE 0 THEN BEGIN + ta1=tab_arr[mci[0],2:tab_arr[mci[0],1]+1] ;subset of relevant FIELD ENTRIES + taname=tab_arr[mci[0],0]+'/'+resy[0] ;name combination for error message + dentry=resy[1] & ti=-1 + EXTRACT_AND_TEST,dentry,';',xval[1],ta1,[''],taname,resy[1],ti + IF STRLEN(rerr[0]) GT 2 THEN RETURN + IF dentry NE resy[1] THEN BEGIN + ;Case and/or apostrophe differences between Metadata entry and TAV entry + achk2=STRSPLIT(tab_arr[mci[0],ti[0]+2],';',/Extract) + achk=STRTRIM(achk2[0],2) + FOR m=1,xval[1]-1 DO achk=achk+';'+STRTRIM(achk2[m],2) + IF qa_yes THEN itxt=' should be ' ELSE itxt=' replaced with ' + infotxt='2 '+meta_arr[i]+itxt+resy[0]+'='+achk+' based on the TAV entry' + INFOTXT_OUTPUT,infotxt + meta_arr[i]=resy[0]+'='+achk + ENDIF + ENDIF + ENDIF + IF (kh EQ 5) AND (nel EQ xval[2]) THEN BEGIN ;check on COUNTRY value + mci=WHERE(tab_arr[*,0] EQ 'COUNTRY',mccnt) + IF mccnt NE 0 THEN BEGIN + ;check the COUNTRY value against the TAV values (note: can be all uppercase or initial capitalization) + aname=res[1] & ti=-1 + ta1=tab_arr[mci[0],2:tab_arr[mci[0],1]+1] ;subset of relevant FIELD ENTRIES + taname=tab_arr[mci[0],0]+'/'+orig_attr[jh]+'ADDRESS' ;name combination for error message + EXTRACT_AND_TEST,STRUPCASE(resx[nel]),';',1,ta1,[''],taname,aname,ti + IF STRLEN(rerr[0]) GT 2 THEN RETURN + ;Test for upper case or initial capitalization + IF resx[nel] NE STRUPCASE(resx[nel]) THEN BEGIN + counterr=0 ;error code to detect problem with initial capitalization + sci=WHERE(STRUPCASE(resx[nel]) EQ STRUPCASE(countrysp1),spcnt) + IF spcnt NE 0 THEN BEGIN ;Test special case countries e.g. Cote d'Ivoire + IF resx[nel] NE countrysp1[sci[0]] THEN counterr=1 + ENDIF ELSE BEGIN ;Test individual words in the country value + sepcount=STRSPLIT(resx[nel],' ,',COUNT=spcnt,/EXTRACT) + FOR j=0,spcnt-1 DO BEGIN + slen=STRLEN(sepcount[j]) + sci=WHERE(STRUPCASE(sepcount[j]) EQ STRUPCASE(countrysp2),sp2cnt) + IF sp2cnt NE 0 THEN test=countrysp2[sci[0]] $ ;special case lowercase word e.g. the, and, of + ELSE IF slen EQ 1 THEN test=STRUPCASE(sepcount[j]) $ ;only one letter in the word so make uppercase + ELSE test=STRUPCASE(STRMID(sepcount[j],0,1))+STRLOWCASE(STRMID(sepcount[j],1)) ;capitalize the first letter + IF sepcount[j] NE test THEN counterr=1 + ENDFOR + ENDELSE + IF counterr EQ 1 THEN BEGIN + uccountry=STRUPCASE(resx[nel]) + IF qa_yes THEN itxt=' should be ' ELSE itxt=' replaced with ' + infotxt='2 COUNTRY value for '+meta_arr[i]+itxt+uccountry + INFOTXT_OUTPUT,infotxt + meta_arr[i]=STRMID(meta_arr[i],0,STRPOS(meta_arr[i],';',/REVERSE_SEARCH)+1)+uccountry + ENDIF + ENDIF + ENDIF + ENDIF + IF (kh EQ 6) AND (nel EQ xval[3]) THEN BEGIN ;check @ present in the e-mail value + amppos=STRPOS(res[1],'@') + IF (amppos LE 0) OR (amppos EQ STRLEN(res[1])-1) THEN BEGIN + infotxt='3 Invalid format for '+orig_attr[jh]+'EMAIL value' + INFOTXT_OUTPUT,infotxt + ENDIF + ENDIF + IF (nel NE xval[kh-3]) OR (STRMID(meta_arr[i],STRLEN(meta_arr[i])-1,1) EQ ';') THEN BEGIN + IF xval[kh-3] GT 1 THEN itxt='sub-' ELSE itxt='' + infotxt='3 Number of '+itxt+'values for attribute '+meta_arr[i]+' should be '+STRTRIM(xval[kh-3],2) + INFOTXT_OUTPUT,infotxt + ENDIF + mcnt=0 + ENDIF + + IF mcnt NE 0 THEN BEGIN ;check Metadata value(s) against TAV file entries + IF (nres NE 2) AND (rd NE 0) THEN BEGIN + STOP_WITH_ERROR,o3[3]+proname,errtxt[0]+meta_arr[i],lu & RETURN + ENDIF ELSE IF res[0] EQ 'DATA_SOURCE' THEN BEGIN + ;need to test against 'DATA_SOURCE' and 'AFFILIATION' (AVDC) or 'DATA_SOURCE_02' + ;(original Envisat) entries + ta1=tab_arr[mi[0],2:tab_arr[mi[0],1]+1] ;DATA_SOURCE ENTRIES + IF tab_type EQ 0 THEN BEGIN + atxt='AFFILIATION' & atx='' & ti=1 + ENDIF ELSE BEGIN + atxt='DATA_SOURCE_02' & atx='_01' & ti=0 + ENDELSE + ai=WHERE(STRPOS(tab_arr[*,0],atxt) NE -1,acnt) + IF acnt EQ 0 THEN BEGIN + STOP_WITH_ERROR,o3[3]+proname,atxt+errtxt[3],lu & RETURN + ENDIF + ta2=tab_arr[ai[0],2:tab_arr[ai[0],1]+1] ;AFFILIATION/DATA_SOURCE_02 ENTRIES + taname=res[0]+atx+'/'+tab_arr[ai[0],0] ;name combination for error message + n_sv=[2,3] ;possible number of DATA_SOURCE sub-values + EXTRACT_AND_TEST,res[1],'_',n_sv,ta1,ta2,taname,res[1],ti + IF STRLEN(rerr[0]) GT 2 THEN RETURN + + achk=STRSPLIT(res[1],'_',/EXTRACT,COUNT=n_achk) + IF n_achk EQ 3 THEN BEGIN + ;Check DATA_VERSION_NAME if there are 3 sub-values in DATA_SOURCE (introduced v4.0b53) + GEOMS_RULE_CHANGES,11,achk[2],meta_arr + ENDIF + + ENDIF ELSE IF res[0] EQ 'DATA_VARIABLES' THEN BEGIN + ;make into 2-D array and separate out the components of the VAR_NAMES + achk=STRSPLIT(res[1],';',/Extract,COUNT=nel) + achk2=STRARR(mcnt,nel) + FOR j=0,nel-1 DO BEGIN + dv=STRSPLIT(achk[j],'_',/Extract,Count=n_dv) + IF n_dv GT mcnt THEN BEGIN ;invalid VAR_NAME composition + STOP_WITH_ERROR,o3[3]+proname,errtxt[0]+res[0]+'='+achk[j],lu & RETURN + ENDIF + achk2[0:N_ELEMENTS(dv)-1,j]=dv + ENDFOR + FOR j=0,nel-1 DO BEGIN + dv=WHERE(achk2[*,j] NE '',dvcnt) + FOR k=0,mcnt-1 DO BEGIN + IF achk2[k,j] NE '' THEN BEGIN + taname=tab_arr[mi[k],0] + ta1=REFORM(tab_arr[mi[k],2:tab_arr[mi[k],1]+1]) ;subset of TAV DATA_VARIABLES entries + IF (k NE 0) AND (dvcnt EQ 2) THEN BEGIN + ;need to check both variable mode and descriptor options + FOR m=k+1,mcnt-1 DO BEGIN + taname=taname+'/'+tab_arr[mi[m],0] + ta1=[ta1,REFORM(tab_arr[mi[m],2:tab_arr[mi[m],1]+1])] + ENDFOR + ENDIF + EXTRACT_AND_TEST,achk2[k,j],';',1,ta1,[''],taname,achk[j],0 + IF STRLEN(rerr[0]) GT 2 THEN RETURN + ENDIF + ENDFOR + ENDFOR + ENDIF ELSE IF res[0] EQ 'FILE_ACCESS' THEN BEGIN + ;need to check each Metadata entry individually + achk=STRSPLIT(res[1],';',/Extract) + nel=N_ELEMENTS(achk) + ta1=tab_arr[mi[0],2:tab_arr[mi[0],1]+1] ;FILE_ACCESS entries + FOR j=0,nel-1 DO BEGIN + EXTRACT_AND_TEST,achk[j],';',1,ta1,[''],res[0],achk[j],0 + IF STRLEN(rerr[0]) GT 2 THEN RETURN + ENDFOR + ENDIF ELSE IF res[0] EQ 'FILE_META_VERSION' THEN BEGIN + ;only need to look at the second value + achk=STRSPLIT(res[1],';',/Extract) + IF N_ELEMENTS(achk) NE 2 THEN achk=[achk,'x','x'] ;ensure there are at least 2 values + achk=[STRTRIM(achk[1],2)] + ta1=tab_arr[mi[0],2:tab_arr[mi[0],1]+1] ;FILE_META_VERSION entries + EXTRACT_AND_TEST,achk[0],';',1,ta1,[''],res[0],achk[0],0 + IF STRLEN(rerr[0]) GT 2 THEN RETURN + ENDIF ELSE IF res[0] EQ 'VAR_UNITS' THEN BEGIN + ;VAR_UNITS and VAR_SI_CONVERSION entries (not needed for STRING data type) + IF rd NE 0 THEN BEGIN + IF STRUPCASE(res[1]) EQ 'MJD2000' THEN BEGIN ;change to MJD2K + in1=meta_arr[i] & in2=res[1] + GEOMS_RULE_CHANGES,3,in1,in2,writeonce + meta_arr[i]=in1 & res[1]=in2 + writeonce=1 + ENDIF + errstate='' & datetimechk=STRPOS(STRUPCASE(vnval),'DATETIME') NE -1 + VAR_UNITS_TEST,res[1],rd,tab_type,tchk,errstate + IF errstate NE '' THEN BEGIN ;VAR_UNIT value not valid + ;STOP_WITH_ERROR,o3[3]+proname+STRMID(errstate,0,STRPOS(errstate,':')+2), $ + ; STRMID(errstate,STRPOS(errstate,':')+2),lu + ;RETURN + IF ~datetimechk THEN itxt='. VAR_SI_CONVERSION value not checked' ELSE itxt='' + infotxt='3 '+errstate+itxt + INFOTXT_OUTPUT,infotxt + ENDIF + ;Find VAR_SI_CONVERSION attribute + k=1 + WHILE STRMID(meta_arr[i+k],0,17) NE 'VAR_SI_CONVERSION' DO k++ + achk=STRSPLIT(meta_arr[i+k],'=',/Extract) ;VAR_SI_CONVERSION values + IF N_ELEMENTS(achk) EQ 1 THEN achk=[achk,' '] + IF datetimechk THEN BEGIN + ;Check if VAR_SI_CONVERSION values are 0.0;86400.0;s regardless of VAR_UNITS. If so, then don't change + achk1=STRSPLIT(achk[1],' ;',/EXTRACT) + achk1=[achk1,'','',''] ;Ensure there are at least 3 values extracted + IF (is_a_number_hdf(achk1[0])) AND (is_a_number_hdf(achk1[1])) THEN BEGIN + IF (FIX(achk1[0]) EQ 0) AND (LONG(achk1[1]) EQ 86400L) AND $ + (STRLOWCASE(achk1[2]) EQ 's') THEN tchk[*]='0.0;86400.0;s' + ENDIF ELSE IF errstate NE '' THEN tchk[*]='0.0;86400.0;s' + ENDIF + ;check calculated VAR_SI_CONVERSION value(s) against existing value in the metadata + IF (errstate EQ '') OR (datetimechk) THEN BEGIN + vscgi=WHERE(achk[1] EQ tchk,vscgcnt) + IF vscgcnt EQ 0 THEN BEGIN + IF qa_yes THEN itxt=' should be ' ELSE itxt=' replaced with ' + infotxt='2 '+meta_arr[i+k]+itxt+'VAR_SI_CONVERSION='+tchk[0]+' for '+vnval + INFOTXT_OUTPUT, infotxt + meta_arr[i+k]=achk[0]+'='+tchk[0] + ENDIF ELSE meta_arr[i+k]=achk[0]+'='+tchk[vscgi[0]] + ENDIF + ENDIF + ENDIF ELSE BEGIN ;perform checks on the remaining ATTRIBUTE_NAMES + achk=STRSPLIT(res[1],';',/Extract) + achk=STRTRIM(achk,2) + nel=N_ELEMENTS(achk) & ta2=[''] + IF res[0] EQ 'VAR_DATA_TYPE' THEN BEGIN + ;to convert VAR_SI_CONVERSION values to floating point if necessary + CASE 1 OF + (STRUPCASE(res[1]) EQ 'REAL') OR (STRUPCASE(res[1]) EQ 'DOUBLE'): rd=-1 + STRUPCASE(res[1]) EQ 'STRING': rd=0 + ELSE: rd=1 + ENDCASE + IF STRUPCASE(achk[0]) EQ 'LONG' THEN achk[0]='INTEGER' + ;Changed to INTEGER for EXTRACT_AND_TEST only - actual change occurs in SET_UP_STRUCTURE + ENDIF + IF (nel NE mcnt) OR (STRMID(meta_arr[i],STRLEN(meta_arr[i])-1,1) EQ ';') THEN BEGIN + IF mcnt GT 1 THEN itxt='sub-' ELSE itxt='' + infotxt='3 Number of '+itxt+'values for attribute '+meta_arr[i]+' should be '+STRTRIM(mcnt,2) + INFOTXT_OUTPUT,infotxt + ;STOP_WITH_ERROR,o3[3]+proname+meta_arr[i]+': ',errtxt[1]+STRTRIM(mcnt,2)+'.',lu + ;RETURN + ENDIF + nel=1 + FOR j=0,mcnt-1 DO BEGIN + ta1=tab_arr[mi[j],2:tab_arr[mi[j],1]+1] ;subset of relevant ENTRIES + dentry=achk[j] & ti=mi[j] + EXTRACT_AND_TEST,dentry,';',nel,ta1,ta2,tab_arr[mi[j],0],achk[j],ti + IF STRLEN(rerr[0]) GT 2 THEN RETURN + IF (mcnt EQ 1) AND (dentry NE achk[j]) THEN BEGIN + ;Case and/or apostrophe differences between Metadata entry and TAV entry + achk2=STRSPLIT(tab_arr[mi[j],ti[0]+2],';',/Extract) + achk=STRTRIM(achk2[0],2) + IF qa_yes THEN itxt=' should be ' ELSE itxt=' replaced with ' + infotxt='2 '+meta_arr[i]+itxt+res[0]+'='+achk+' based on the TAV entry' + INFOTXT_OUTPUT,infotxt + meta_arr[i]=res[0]+'='+achk + ENDIF + IF j EQ 0 THEN ta2[0]='' + ENDFOR + ENDELSE + ENDIF +ENDFOR + +END ;Procedure Check_Metadata + + + +PRO extract_data, sds, inf, vc, dtest, ndl, infowrite +;Procedure to extract the set of data corresponding to a particular Variable Attribute +; ---------- +;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org +; +; History: +; 20050802: Introduced to IDLCR8HDF, originally part of the READ_DATA procedure - Version 1.0 +; 20050909: After reading in a data segment according to the VAR_SIZE values, check the next line to +; ensure it is not a further data point (i.e. there are more data points than specified by +; VAR_SIZE); If sds is a structure, and there is only one data point then return dtest as +; an array rather than a scalar - Version 1.1 +; 20061012: The portion of READ_DATA dealing with extracting a specific data segment renamed +; EXTRACT_DATA - Version 2.0 +; 20080302: The sets of data values in a file can now be in any order, rather than the same order as +; that given by the DATA_VARIABLES attribute; dtest used as an input flag to indicate +; whether the procedure call is from SET_UP_STRUCTURE [-1] or READ_DATA [0]. If from +; SET_UP_STRUCTURE then only the number of data lines (ndl) is determined by the routine; +; an error is generated in the case where the same VAR_NAME is repeated in the data file; +; Error messages clarified when an incorrect number of data values are found in the +; input data file - Version 3.0 +; 20100205: Add RETURN command after all STOP_WITH_ERROR calls, which allows program to return to the +; calling program if the reterr argument is included in the idlcr8hdf call - Version 3.09 +; 20130116: Account for empty string data values in a dataset - previously '' values ignored in an +; input data file - Version 4.0b15 +; 20150227: Do not automatically run STRTRIM on the dataset when it is read in to allow checks on +; string datasets - Version 4.0b26 +; 20150811: Add infowrite parameter so that if DATETIME type dataset is in ISO8601 format then the +; information/error message will only be written once instead of for each value +; - Version 4.0b29 +; 20171130: Remove ds from STOP_WITH_ERROR parameters - Version 4.0b44 +; +; Inputs: sds - Either a structure containing the Variable Attributes and Data, or the input +; data file +; inf - flag identifying sds type where, 0=structure; 1=data file +; vc - the index value of the vn array holding the VAR_NAME being searched for in the data +; file, or the index value of the sds structure which holds the data +; dtest - a single string array to show which procedure called this routine, either +; SET_UP_STRUCTURE [-1], or READ_DATA [0] +; infowrite - optional input to stop ISO8601 to MJD2K information message being written multiple +; times (when called by READ_DATA only) +; +; Outputs: dtest - a string array of size ndl that holds the data values +; ndl - The total number of data lines i.e. the product of the VAR_SIZE values +; +; Called by: SET_UP_STRUCTURE; READ_DATA +; +; Subroutines Called: STOP_WITH_ERROR (if error state detected) +; Possible Conditions for STOP_WITH_ERROR call (plus [line number] where called): +; 1. EOF encountered when trying to find the VAR_NAME or while attempting to read in data +; [2146,2220,2221] +; 2. Invalid data point found in the dataset [2193] +; 3. Two instances of the same VAR_NAME found in the input data file [2204] +; 4. Too many data values present in the dataset or the next line in the file is invalid [2211] +; 5. Number of data values does not match product of VAR_SIZE [2237] +; 6. ISO8601 datetime format not valid [2253] + +COMMON DATA +COMMON WIDGET_WIN + +;possible error messages for this procedure +errtxt=STRARR(10) +errtxt[0]='EOF encountered but not expected' +errtxt[1]=' when trying to find VAR_NAME in ' +errtxt[2]=' when trying to determine VAR_SIZE for VAR_NAME=' +errtxt[3]=' - Number of data lines should be ' +errtxt[4]='Invalid data point found in the dataset at line ' +errtxt[5]='Two instances of this VAR_NAME present in the Data File' +errtxt[6]='Too many data values present in the dataset or invalid data line entry following the dataset' +errtxt[7]='Number of data values does not match product of VAR_SIZE: ' +errtxt[8]='ISO8601 format not valid: ' +errtxt[9]='Unable to determine correct number of data values due to VAR_SIZE calculation error' +proname='Extract_Data procedure: ' + +ndl=1L ;initialize data line count +vsi=WHERE(vserror EQ vn[vc],vcnt) ;check whether there is a VAR_SIZE calculation error for this dataset +sdata=ndm(vc,8) EQ 6 ;Check for String data type + +IF inf EQ 1 THEN BEGIN ;Find the first data value for the requested VAR_NAME + ON_IOERROR,TypeConversionError + dum='' & vntest=vn[vc] + ;Add check in case VAR_NAME has been changed from original input + IF vnchange[vc] NE '' THEN vntest=vnchange[vc] + OPENR,lu,sds,/GET_LUN + IF NOT EOF(lu) THEN BEGIN ;read through data file until the correct Variable Name is found + REPEAT READF,lu,dum UNTIL (EOF(lu)) OR (STRTRIM(STRUPCASE(dum),2) EQ STRUPCASE(vntest)) OR $ + (STRTRIM(STRUPCASE(dum),2) EQ STRUPCASE(vn[vc])) + ENDIF + IF STRTRIM(STRUPCASE(dum),2) EQ STRUPCASE(vn[vc]) THEN vntest=vn[vc] + IF EOF(lu) THEN BEGIN + STOP_WITH_ERROR,o3[3]+proname+vn[vc],errtxt[0]+errtxt[1]+sds,lu & RETURN + ENDIF + + ;Find first data point + dum='!' + valid=-1 ;set to test for successful line read + WHILE (STRMID(dum,0,1) EQ '!') OR (STRMID(dum,0,1) EQ '#') OR ((~sdata) AND (strtrim(dum,2) EQ '')) DO BEGIN + READF,lu,dum ;& dum=STRTRIM(dum,2) + ENDWHILE + valid=1 ;i.e. got to here without prematurely reaching the eof so first data point found OK + + IF dtest[0] EQ '-1' THEN BEGIN ;need to determine the total number of data lines only (ndl) + IF NOT EOF(lu) THEN BEGIN + ;read through until eof or comment line or another VAR_NAME is reached + REPEAT BEGIN + READF,lu,dum + dum=STRTRIM(STRUPCASE(dum),2) + bi=WHERE(dum EQ STRUPCASE(vn)) + IF (STRMID(dum,0,1) NE '!') AND (STRMID(dum,0,1) NE '#') AND $ + ((dum NE '') OR ((sdata) AND (dum EQ ''))) AND (bi[0] EQ -1) THEN incnt=1L $ + ELSE incnt=0L + ndl=ndl+incnt ;presume it is a valid data line so add to total + ENDREP UNTIL (incnt EQ 0L) OR (EOF(lu)) + ENDIF + ENDIF ELSE BEGIN ;return dtest and ndl + di=WHERE(ndm[vc,0:7] NE 0L,ndim) + ;multiply together the array dimensions to determine expected total number of datalines + FOR i=0,ndim-1 DO ndl=ndl*ndm[vc,di[i]] + ;attempt to read any remaining data values + IF ndl GT 1L THEN BEGIN + valid=0 ;set to test for successful block read + dtest=STRARR(ndl-1) & READF,lu,dtest + valid=1 ;i.e. got to here without prematurely reaching the EOF so block read was OK + dtest=[dum,dtest] ;add first value to dtest + ENDIF ELSE dtest=[dum] + dtestup=STRUPCASE(STRTRIM(dtest,2)) ;& dtest=STRTRIM(dtest,2) + ;Test to see if any of the 'data' read in is a VAR_NAME - if so bcnt will NE 0 (and will generate error message) + bcnt=0 & i=0 + WHILE (bcnt EQ 0) AND (i LE nvn-1) DO BEGIN + bi=WHERE(dtestup EQ STRUPCASE(vn[i]),bcnt) + IF bcnt EQ 0 THEN i=i+1 + ENDWHILE + ;Test to see if any of the 'data' read in is not a valid data point - if so bcnt will NE 0 + IF bcnt EQ 0 THEN $ + bi=WHERE((STRMID(dtestup,0,1) EQ '!') OR (STRMID(dtestup,0,1) EQ '#') OR $ + ((dtestup EQ '') AND (~sdata)) OR (dtestup EQ STRUPCASE(vntest)),bcnt) + + IF bcnt NE 0 THEN BEGIN ;Non-valid data point found in the dataset + STOP_WITH_ERROR,o3[3]+proname+vn[vc]+': ',errtxt[4]+STRTRIM(bi[0]+1,2)+errtxt[3]+ $ + STRTRIM(ndl,2),lu + RETURN + ENDIF + + ;Read next line if not EOF to test for the correct number of data lines + IF NOT EOF(lu) THEN READF,lu,dum ELSE dum='!' + dum=STRTRIM(STRUPCASE(dum),2) + ;Test to see if the next line read in is another VAR_NAME - if not then bi[0] will equal -1 + bi=WHERE(dum EQ STRUPCASE(vn)) + IF bi[0] EQ vc THEN BEGIN ;Same VAR_NAME found twice in the data file + STOP_WITH_ERROR,o3[3]+proname+vn[vc]+': ',errtxt[5],lu & RETURN + ENDIF + IF (bi[0] EQ -1) AND (dum NE '!') THEN bi=WHERE(dum EQ STRUPCASE(vnchange)) + ;Test to check if the next line read in is another data point - if so bcnt will be set to 1 + IF (STRMID(dum,0,1) NE '!') AND (STRMID(dum,0,1) NE '#') AND $ + ((dum NE '') OR ((sdata) AND (dum EQ ''))) AND (bi[0] EQ -1) THEN $ + bcnt=1 + IF bcnt NE 0 THEN BEGIN ;not enough data lines read in or next line in the file is invalid + IF vcnt NE 0 THEN STOP_WITH_ERROR,o3[3]+proname+'VAR_NAME='+vn[vc]+': ',errtxt[9],lu $ + ELSE $ + STOP_WITH_ERROR,o3[3]+proname+'VAR_NAME='+vn[vc]+': ',errtxt[6]+errtxt[3]+STRTRIM(ndl,2),lu + RETURN + ENDIF + ENDELSE + FREE_LUN,lu + + TypeConversionError: + IF valid LE 0 THEN BEGIN ;EOF encountered but not expected + IF (dtest[0] EQ '-1') AND (valid EQ -1) THEN $ + STOP_WITH_ERROR,o3[3]+proname,errtxt[0]+errtxt[2]+vn[vc],lu $ + ELSE BEGIN + ;Check to see if it is due to a VAR_SIZE calculation error or not + IF vcnt NE 0 THEN STOP_WITH_ERROR,o3[3]+proname+'VAR_NAME='+vn[vc]+': ',errtxt[9],lu $ + ELSE $ + STOP_WITH_ERROR,o3[3]+proname+'VAR_NAME='+vn[vc]+': ',errtxt[0]+errtxt[3]+STRTRIM(ndl,2),lu + ENDELSE + RETURN + ENDIF +ENDIF ELSE BEGIN ;sds is a structure + IF dtest[0] EQ '-1' THEN BEGIN ;determine the total number of data lines only + dtest=*sds[vc].data + ndl=LONG(N_ELEMENTS(dtest)) + dtest=['-1'] + ENDIF ELSE BEGIN ;return dtest and ndl + lu=-1 + di=WHERE(ndm[vc,0:7] NE 0L,ndim) + ;multiply together the array dimensions to determine expected total number of datalines + FOR i=0,ndim-1 DO ndl=ndl*ndm[vc,di[i]] + dtest=*sds[vc].data + ;check structure contains the correct number of elements + IF N_ELEMENTS(dtest) NE ndl THEN BEGIN + STOP_WITH_ERROR,o3[3]+proname+vn[vc]+': ',errtxt[7]+STRTRIM(N_ELEMENTS(dtest),2)+'/'+STRTRIM(ndl,2),lu + RETURN + ENDIF + stest=SIZE(dtest) ;test to see if dtest is an array or scalar + IF stest[0] EQ 0 THEN dtest=[dtest] ELSE dtest=REFORM(dtest,ndl,/Overwrite) ;convert to a 1-D array if necessary + ENDELSE +ENDELSE + +;Note: this conditional will only be satisfied if the routine is called by READ_DATA +;(vu still not assigned VAR_UNITS value if called by SET_UP_STRUCTURE) +IF STRUPCASE(vu[vc]) EQ 'MJD2K' THEN BEGIN + ;Convert time from ISO8601 to MJD2000 format if necessary + FOR i=0L,ndl-1L DO BEGIN + IF STRPOS(STRUPCASE(dtest[i]),'Z') NE -1 THEN BEGIN + mjd2000=JULIAN_DATE(dtest[i],/I,/M) ;return time in MJD2000 format + IF mjd2000 EQ -99999.d THEN BEGIN ;conversion error + STOP_WITH_ERROR,o3[3]+proname+vn[vc]+': ',errtxt[8]+dtest[i],lu & RETURN + ENDIF + dtest[i]=STRING(format='(d18.9)',mjd2000) + IF infowrite EQ 1 THEN BEGIN + IF qa_yes THEN itxt='cannot be in ISO8601 format' ELSE itxt='changed from ISO8601 to MJD2K format' + infotxt='2 Dataset values '+itxt+' for VAR_NAME='+vn[vc] + INFOTXT_OUTPUT,infotxt + infowrite=0 + ENDIF + ENDIF + ENDFOR +ENDIF + +END ;Procedure Extract_Data + + + +PRO set_up_structure, sds, inf +;Procedure to do additional checks on VAR_NAME, VAR_DEPEND, VAR_DATA_TYPE, VAR_UNITS, and +;VAR_FILL_VALUE metadata attributes and set up a structure which will be used to hold the +;data values. +; ---------- +;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org +; +; History: +; 20050802: Introduced to IDLCR8HDF, as Determine_Arr_Sizes. Data originally stored in 4 +; arrays according to the data type of the variable attributes - Version 1.0 +; 20050909: Additional checks performed on the metadata attributes as follows: that the +; VAR_DEPEND variable name(s), themselves, have a VAR_DEPEND value of CONSTANT or +; INDEPENDENT, and matching VAR_SIZE values; that VAR_DATA_TYPE is DOUBLE for +; attributes with VAR_UNITS=MJD2000; that VAR_UNITS=MJD2000 when VAR_NAME=DATETIME +; - Version 1.1 +; 20061012: Procedure renamed Set_Up_Structure. A structure using pointers used to store the +; data instead of arrays. VAR_FILL_VALUE value saved in common array and, if +; VAR_UNITS=MJD2000, VAR_FILL_VALUE converted to its MJD2000 form if it is in +; ISO8601 format. Common variable definition WIDGET_WIN added - Version 2.0 +; 20080302: Can calculate the VAR_SIZE value(s) from the input data or from the VAR_SIZE +; value(s) of the VAR_DEPEND variable name(s), if they are not present in the +; metadata. Section added to account for the new variables, ALTITUDE/ALTITUDE.GPH/ +; PRESSURE.BOUNDARIES; Additional checks added for VIS_PLOT_TYPE, VIS_SCALE_TYPE, +; VIS_SCALE_MIN, and VIS_SCALE_MAX values - Version 3.0 +; 20100205: Add RETURN command after all STOP_WITH_ERROR calls, which allows program to return to the +; calling program if the reterr argument is included in the idlcr8hdf call - Version 3.09 +; 20101120: Account for GEOMS changes between v3.0 and v4.0; Remove checks on VAR_DIMENSION, and the +; VIS attributes; Add new VAR_DATA_TYPEs (Byte and Short); Check for self-referencing +; VAR_DEPEND values (which replace INDEPENDENT in that context); Remove requirement for +; DATETIME to be of datatype DOUBLE - Version 4.0 +; 20140521: Account for new Dimension Ordering rules for *.BOUNDARIES variables - should be +; INDEPENDENT;*[;DATETIME]. In the event that the input metadata specifies a data template, +; then the template VAR_DEPEND ordering will apply (and be checked by the template checker), +; otherwise full checks will be made - Version 4.0b21 +; 20140806: Allow VAR_UNITS=d/D to be changed to MJD2K for DATETIME checks - Version 4.0b22 +; 20150811: Bug fix - initialize the infotxt array if reporting an ISO8601 to MJD2K conversion +; issue - Version 4.0b29 +; 20151109: Check that VAR_SIZE values are in INTEGER format, even though they are +; written to the HDF file as a string - Version 4.0b34 +; 20161116: Bug fix - resvd array value set correctly when checking whether a dataset with +; VAR_DEPEND=CONSTANT only has a single value (was 0 should have been 1) - Version 4.0b39 +; 20161130: Allow program to continue when VAR_UNITS is not set to MJD2K for DATETIME +; and related datasets (previously called STOP_WITH_ERROR in some conditions - +; Version 4.0b40 +; 20180218: Change Unexpected number of attribute values extracted error message so that it calls +; INFOTXT_OUTPUT rather than STOP_WITH_ERROR - Version 4.0b45 +; 20210617: Add section to do VAR_DATA_TYPE checks on the datasets, VAR_VALID_MIN, VAR_VALID_MAX and +; VAR_FILL_VALUE values, to ensure they are all matching - Version 4.0b59 +; 20231218: Change check that an axis variable must be listed in DATA_VARIABLES before any variable +; that depends on it. The new check is that the axis variable is present anywhere in the file. +; Text for errtxt[3] changed, code added to identify axis variables and determine their +; VAR_SIZE - Version 4.0b61 +; 20240112: Remove test that INDEPENDENT can only be a second dimension variable if the variable includes +; .BOUNDARIES in the name e.g. ALTITUDE.BOUNDARIES. This is too limiting e.g. for Aerosol Lidar +; RANGE_INDEPENDENT_NORMALIZATION has VAR_DEPEND=INDEPENDENT;DATETIME - Version 4.0b62 +; +; Inputs: sds - Either a structure containing the Variable Attributes and Data, or the input +; data file +; inf - flag identifying SDS type where, 0=structure; 1=data file +; meta_arr - a string array containing the Global and Variable Attributes +; +; Outputs: meta_arr - some meta_arr values may be corrected or calculated in this routine, including: +; VAR_DIMENSION, VAR_SIZE, and VAR_FILL_VALUE +; nvn - an integer giving the number of Variable Attributes present in the input +; vn - a string array of size nvn containing the VAR_NAME values +; vu - a string array of size nvn containing the VAR_UNIT values +; ndm - a (nvn,9) long array, where ndm(*,0:7) describes the dimensions of each dataset +; (maximum of 8 dimensions allowed), and ndm(*,8) describes the VAR_DATA_TYPE +; ds - the structure that will hold the data +; +; Called by: IDLCR8HDF +; +; Subroutines Called: EXTRACT_DATA (if it is necessary to determine VAR_SIZE from the input data) +; STOP_WITH_ERROR (if error state detected); INFOTXT_OUTOUT +; Possible Conditions for STOP_WITH_ERROR call (plus [line number] where called): +; 1. Program expects to find the VAR_NAME in the metadata in the same order as that listed under +; DATA_VARIABLES +; 2. The VAR_DIMENSION value is greater than 8 +; 3. Number of VAR_SIZE and VAR_DEPEND attribute values is different +; 4. The VAR_DEPEND value is not 'CONSTANT', 'INDEPENDENT' or self-referencing, and doesn't +; match a previous VAR_NAME +; 5. The VAR_DEPEND variable name must, itself, have a VAR_DEPEND value of 'CONSTANT' or +; 'INDEPENDENT' or be self-referencing +; 6. The VAR_DEPEND variable name must have a VAR_SIZE value matching the current VAR_NAME +; 7. VAR_DATA_TYPE not BYTE, SHORT, INTEGER, LONG, REAL, DOUBLE, or STRING +; 8. Unexpected number of ATTRIBUTE values extracted +; 9. VAR_DATA_TYPE should be DOUBLE for VAR_UNITS=MJD2K - No longer a requirement +; 10. VAR_UNITS should be MJD2K for VAR_NAME=DATETIME +; 11. VAR_FILL_VALUE not a valid value - occurs on an unsuccessful call to JULIAN_DATE +; 12. VAR_DEPEND values not valid - occurs when INDEPENDENT or CONSTANT is a VAR_DEPEND value +; when it should not be +; 13. First VAR_SIZE value is not '2' when VAR_NAME=ALTITUDE/ALTITUDE.GPH/PRESSURE.BOUNDARIES +; 14. Axis variable cannot be dependent on another variable +; 15. Axis variable in the vertical dimension already found +; 16. Attribute which is not an axis variable has a self-referencing VAR_DEPEND value +; 17. Type conversion error. Called when trying to convert a string, which is expected to be +; numeric, to a number +; +; Information Conditions (when the program is able to make changes): +; 1. 64-bit LONG Data Type is not currently supported in GEOMS. Will attempt to write data in 32-bit +; Integer type instead +; 2. Dataset/VAR_VALID_MIN/VAR_VALID_MAX/VAR_FILL_VALUE value(s) have data type that does not match +; VAR_DATA_TYPE + +COMMON METADATA +COMMON DATA +COMMON WIDGET_WIN + +;possible error messages for this procedure +errtxt=STRARR(17) & lu=-1L +errtxt[0]='Does not match the equivalent VAR_NAME in DATA_VARIABLES: ' +errtxt[1]='Number of dimensions too high to make HDF file (max. allowed 8)' +errtxt[2]='VAR_DEPEND attribute value missing or does not match the number of VAR_SIZE values' +errtxt[3]='VAR_DEPEND value not CONSTANT,INDEPENDENT nor self-referencing, and doesn''t match any VAR_NAME: ' +errtxt[4]='VAR_DEPEND variable name(s) must have VAR_DEPEND value of CONSTANT or be self_referencing: ' +errtxt[5]='VAR_DEPEND variable name must have matching VAR_SIZE value (= ' +errtxt[6]='Data type not BYTE, SHORT, INTEGER, REAL, DOUBLE, or STRING: ' +errtxt[7]='Unexpected number of ATTRIBUTE values extracted: ' +errtxt[8]='VAR_DATA_TYPE must be DOUBLE for VAR_UNITS=MJD2K: ' +errtxt[9]='VAR_UNITS must be MJD2K for ' ;no longer used (infotxt message instead) +errtxt[10]='VAR_FILL_VALUE not a valid value: ' +errtxt[11]=' not valid' +errtxt[12]='Second VAR_SIZE value should be ''2'' for this VAR_NAME' +errtxt[13]='Axis variable cannot be dependent on another variable: ' +errtxt[14]=' is already the axis variable in the vertical dimension' +errtxt[15]='is not an axis variable so VAR_DEPEND value cannot be self-referencing' +errtxt[16]='Type conversion error. Entry is not a valid number: ' +proname='Set_Up_Structure procedure: ' + +lc=0 & vc=0 & writeonce=0 & bounderror=0 +allowable_data_types=['BYTE','SHORT','INTEGER','LONG64','REAL','DOUBLE','STRING'] +idl_data_type=[1,2,3,14,4,5,7] ;IDL type identifiers matching allowable_data_types +num_atts=['VAR_VALID_MIN','VAR_VALID_MAX','VAR_FILL_VALUE'] +nna=N_ELEMENTS(num_atts) + +ON_IOERROR,TypeConversionError + +;Do a search for a DATA_TEMPLATE value. If not present will do all .BOUNDARIES checks, otherwise +;these checks will be done by the Template QA program - interim until .BOUNDARIES dimension ordering +;is consistent for all templates (FTIR retains original convention as of 20140521) +dtfound=0 ;default to template not present +dti=WHERE(STRMID(meta_arr,0,13) EQ 'DATA_TEMPLATE',dtcnt) +IF dtcnt NE 0 THEN BEGIN ;check there is a value + res=STRSPLIT(meta_arr[dti[0]],' =;',/Extract,COUNT=rescnt) + IF rescnt EQ 2 THEN dtfound=1 ;valid DATA_TEMPLATE value found +ENDIF + +;Determine a set of possible Axis Variables to be tested (i.e. all VAR_DEPEND values that aren't CONSTANT or INDEPENDENT) +vdi=WHERE(STRMID(meta_arr,0,10) EQ 'VAR_DEPEND',vdcnt) +vardeptest=[''] & vardepndim=INTARR(vdcnt) & vardepvals=STRARR(8,vdcnt) +IF vdcnt NE 0 THEN BEGIN + FOR i=0,vdcnt-1 DO BEGIN + res=STRSPLIT(meta_arr[vdi[i]],' =;',/Extract,COUNT=rescnt) + vardepndim[i]=rescnt-1 + IF rescnt GT 1 THEN BEGIN ;VAR_DEPEND values found + FOR j=1,rescnt-1 DO BEGIN + resuc=STRUPCASE(res[j]) & vardepvals[j-1,i]=res[j] + ni=WHERE(resuc EQ STRUPCASE(vardeptest),ncnt) + IF (ncnt EQ 0) AND (resuc NE 'INDEPENDENT') AND (resuc NE 'CONSTANT') THEN $ + vardeptest=[vardeptest,res[j]] ;Add VAR_DEPEND value to list of axis-variables to be checked + ENDFOR + ENDIF + ENDFOR +ENDIF +;Remove '' from vardeptest +gi=WHERE(vardeptest NE '',n_axv) +IF n_axv NE 0 THEN BEGIN + vardeptest=vardeptest[gi] & vardepndl=lonarr(n_axv) + vni=WHERE(STRMID(meta_arr,0,8) EQ 'VAR_NAME',vncnt) + ndm=LONARR(vncnt,9) & vu=STRARR(vncnt) ;just needed, at thu stage as dummy inputs to EXTRACT_DATA + IF vncnt NE 0 THEN BEGIN + vn=STRARR(vncnt) & vserror=vn ;vserror needed for dummy input to EXTRACT_DATA + FOR i=0,vncnt-1 DO BEGIN + res=STRSPLIT(meta_arr[vni[i]],' =',/Extract,COUNT=rescnt) + IF rescnt EQ 2 THEN vn[i]=res[1] + ENDFOR + multidim=0B + FOR i=0,n_axv-1 DO BEGIN + vcx=WHERE(vardeptest[i] EQ vn,vcxcnt) + IF vcxcnt NE 0 THEN ndimx=vardepndim[vcx[0]] ELSE ndimx=0 + IF (vcxcnt NE 0) AND (ndimx LE 1) THEN BEGIN + dtest=['-1'] ;-1 identifies to the EXTRACT_DATA routine that ndl is all that is required + EXTRACT_DATA,SDS,inf,vcx,dtest,ndl + vardepndl(i)=ndl + ENDIF ELSE IF ndimx GT 1 THEN multidim=1B ;axis variable has more than one dependency e.g. VAR_DEPEND=DATETIME;ALTITUDE for VAR_NAME=ALTITUDE + ENDFOR + IF multidim THEN BEGIN + FOR i=0,n_axv-1 DO BEGIN + vcx=WHERE(vardeptest[i] EQ vn,vcxcnt) + IF vcxcnt NE 0 THEN ndimx=vardepndim[vcx[0]] ELSE ndimx=0 + IF (vcxcnt NE 0) AND (ndimx GT 1) THEN BEGIN + dtest=['-1'] ;-1 identifies to the EXTRACT_DATA routine that ndl is all that is required + EXTRACT_DATA,SDS,inf,vcx,dtest,ndl + ;Determine actual number of dataset values + FOR j=0,ndimx-1 DO BEGIN + IF vardepvals[j,vcx[0]] NE vn[vcx[0]] THEN BEGIN + di=WHERE(vardepvals[j,vcx[0]] EQ vardeptest,dcnt) + IF dcnt NE 0 THEN ndl=ndl/vardepndl[di[0]] + ENDIF + ENDFOR + vardepndl(i)=ndl + ENDIF + ENDFOR + ENDIF + ENDIF +ENDIF + +;At this point have got an array of size n_axv containing the axis variable names (vardeptest) and their VAR_SIZE (vardepndl) + +notlong=0B ;Boolean to check whether VAR_SIZE values are of integer data type +WHILE lc LT N_ELEMENTS(meta_arr) DO BEGIN + valid=0 ;set to test for successful string conversion to a number + IF STRMID(meta_arr[lc],0,14) EQ 'DATA_VARIABLES' THEN BEGIN ;determine number of variables reported + lcdv=lc + vn=STRSPLIT(meta_arr[lc],' =;',/Extract) ;string containing reported variables + nvn=N_ELEMENTS(vn)-1 ;total number of variables reported + vn=vn[1:nvn] ;remove the DATA_VARIABLES label + vu=STRARR(nvn) ;holding the VAR_UNITS values + vfvd=DBLARR(nna,nvn) ;holding array for floating point num_atts array values + vfvl=LON64ARR(nna,nvn) ;holding array for integer num_atts array values + ndm=LONARR(nvn,9) ;to contain the dimensions and data type for each dataset + vserror=STRARR(nvn) ;to hold variable names for datasets where the VAR_SIZE values cannot be determined + ENDIF + + IF STRMID(meta_arr[lc],0,8) EQ 'VAR_NAME' THEN BEGIN + vname=meta_arr[lc] ;used in case of error + lcz=lc ;VAR_NAME index value + res=STRSPLIT(meta_arr[lc],' =',/Extract) + IF N_ELEMENTS(res) NE 2 THEN res=[res[0],'-1'] + IF res[1] NE vn[vc] THEN BEGIN + STOP_WITH_ERROR,o3[3]+proname+vname+': ',errtxt[0]+vn[vc],lu & RETURN + ;Does not match the equivalent VAR_NAME in DATA_VARIABLES + ENDIF + + REPEAT lc=lc+1 UNTIL STRMID(meta_arr[lc],0,8) EQ 'VAR_SIZE' + holdvs=meta_arr[lc] & lcx=lc ;do checks after determining VAR_DATA_TYPE + REPEAT lc=lc+1 UNTIL STRMID(meta_arr[lc],0,10) EQ 'VAR_DEPEND' + holdvd=meta_arr[lc] & lcy=lc ;do checks after determining VAR_DATA_TYPE + REPEAT lc=lc+1 UNTIL STRMID(meta_arr[lc],0,13) EQ 'VAR_DATA_TYPE' + res=STRSPLIT(STRUPCASE(meta_arr[lc]),' =;',/Extract) + IF N_ELEMENTS(res) EQ 2 THEN gi=WHERE(res[1] EQ allowable_data_types,gcnt) $ ;Check VAR_DATA_TYPE is valid + ELSE gcnt=0 + IF gcnt NE 1 THEN BEGIN + STOP_WITH_ERROR,o3[3]+proname+vname+': ',errtxt[6]+meta_arr[lc],lu & RETURN + ;Data type not BYTE, SHORT, INTEGER, LONG64, REAL, DOUBLE, or STRING + ENDIF + IF gi[0] EQ 3 THEN BEGIN + IF writeonce EQ 0 THEN BEGIN + writeonce=1 + infotxt='2 64-bit LONG Data Type is not currently supported|. ' + infotxt=infotxt+'Will attempt to write data in 32-bit INTEGER Data Type' + INFOTXT_OUTPUT,infotxt + ENDIF + gi[0]=2 & meta_arr[lc]='VAR_DATA_TYPE=INTEGER' + ENDIF + ndm[vc,8]=gi[0] ;index number representing the data type + + ;Do VAR_SIZE and VAR_DEPEND checks + resvs=STRSPLIT(holdvs,' =;',/Extract,COUNT=nvs) ;VAR_SIZE entries + resvd=STRSPLIT(holdvd,' =;',/Extract,COUNT=nd) ;VAR_DEPEND entries + nvs=nvs-1 & nd=nd-1 ;Number of Dimensions based on number of VAR_SIZE/VAR_DEPEND values + vsadded=0 + + IF (nvs EQ 0) AND (nd NE 0) THEN BEGIN + ;Determine resvs values from vardeptest and vardepndl + FOR k=1,nd DO BEGIN + mi=WHERE(resvd[k] EQ vardeptest,mcnt) + IF mcnt NE 0 THEN BEGIN + resvs=[resvs,strtrim(vardepndl[mi[0]],2)] + nvs++ + IF k NE nd THEN sctxt=';' ELSE sctxt='' + holdvs=holdvs+strtrim(vardepndl[mi[0]],2)+sctxt + vsadded=1 + ENDIF + ENDFOR + meta_arr[lcx]=holdvs + ENDIF + IF nvs EQ 0 THEN ndmok=0 ELSE ndmok=1 + ;If there are no VAR_SIZE value entries, need to determine from Data file or structure or + ;from the VAR_SIZE values of the VAR_DEPEND dependencies + IF ((nvs NE nd) AND (ndmok EQ 1)) OR (nd EQ 0) THEN BEGIN + STOP_WITH_ERROR,o3[3]+proname+vname+': ',errtxt[2],lu & RETURN + ;VAR_DEPEND attribute value missing or does not match the number of VAR_SIZE values + ENDIF ELSE IF nd GT 8 THEN BEGIN + STOP_WITH_ERROR,o3[3]+proname+vname+': ',errtxt[1],lu & RETURN + ;Number of dimensions too high to make HDF file (max. allowed 8) + ENDIF ELSE IF ndmok EQ 1 THEN BEGIN + ndm[vc,0:nd-1]=LONG(resvs[1:nvs]) ;save VAR_SIZE values in ndm array + + ;Check that original input values are of integer data type + FOR k=1,nvs DO BEGIN + IF STRTRIM(resvs[k],2) NE STRTRIM(LONG(resvs[k]),2) THEN BEGIN + resvs[k]=STRTRIM(LONG(resvs[k]),2) + notlong=1B + ENDIF + ENDFOR + IF notlong THEN BEGIN + FOR k=1,nvs DO IF k EQ 1 THEN vshold=resvs[k] ELSE vshold=vshold+';'+resvs[k] + meta_arr[lcx]='VAR_SIZE='+vshold + holdvs=meta_arr[lcx] + ENDIF + ENDIF + + vshold=LONARR(nd) ;to hold VAR_SIZE of the dependencies + + ;Do checks on VAR_DEPEND values + ci=WHERE((STRUPCASE(resvd) EQ 'INDEPENDENT') OR (STRUPCASE(resvd) EQ 'CONSTANT'),ccnt) + IF ccnt NE 0 THEN resvd[ci]=STRUPCASE(resvd[ci]) + ;Rules for .BOUNDARY VAR_NAME + IF STRPOS(vn[vc],'.BOUNDARIES') NE -1 THEN BEGIN + test1=0 & test2=0 + IF ((nd EQ 2) OR (nd EQ 3)) AND (dtfound EQ 0) THEN BEGIN + ;First VAR_DEPEND value must be INDEPENDENT + test1=resvd[1] NE 'INDEPENDENT' + ;Second VAR_DEPEND value must be present in the Variable Name e.g. ALTITUDE for ALTITUDE.BOUNDARIES + secvdsplt=STRSPLIT(resvd[2],'.',/Extract) + test2=STRPOS(vn[vc],secvdsplt[0]) EQ -1 + ENDIF ELSE IF (nd LE 1) OR (nd GE 4) THEN test2=1 ;incorrect number of VAR_DEPEND values + IF nd EQ 3 THEN IF resvd[3] NE 'DATETIME' THEN test1=1 ;Third VAR_DEPEND value must be DATETIME + IF (test1) OR (test2) THEN BEGIN + IF test1 EQ 1 THEN itxt=' in IDL/Fortran Dimension Ordering' ELSE itxt='' + infotxt='3 '+holdvd+' not valid for '+vname+itxt + INFOTXT_OUTPUT,infotxt + bounderror=1 + ;STOP_WITH_ERROR,o3[3]+proname+vname+': ',holdvd+errtxt[11]+' for the given VAR_NAME'+itxt,lu + ;VAR_DEPEND value(s) not valid for the given VAR_NAME + ;RETURN + ENDIF + ENDIF + + FOR i=1,nd DO BEGIN + IF i EQ 1 THEN BEGIN + ;Check for and Apply GEOMS rule changes for INDEPENDENT and axis variables + vdval=resvd[i] & vnval=vn[vc] + GEOMS_RULE_CHANGES,2,vardeptest,vnval,vdval,holdvd + IF vdval NE resvd[i] THEN BEGIN ;First VAR_DEPEND value has been changed + ;Rebuild VAR_DEPEND entry + holdvd='VAR_DEPEND='+vdval + IF nd GT 1 THEN FOR j=2,nd DO holdvd=holdvd+';'+resvd[j] + ENDIF + IF STRMID(holdvd,0,1) EQ 'E' THEN BEGIN ;error found while attempting to identify axis variables + CASE 1 OF + holdvd EQ 'E1': STOP_WITH_ERROR,o3[3]+proname+vname+': ',errtxt[13]+vdval,lu + holdvd EQ 'E3': STOP_WITH_ERROR,o3[3]+proname+vname+' ',errtxt[15],lu + ENDCASE + RETURN + ENDIF + resvd[i]=vdval + meta_arr[lcy]=holdvd + ENDIF + test1=(resvd[i] EQ 'CONSTANT') AND ((i GT 1) OR (nd GE 2)) ;CONSTANT can be the only VAR_DEPEND + test2=(resvd[i] EQ vn[vc]) AND ((i GT 1) OR (nd GT 2)) ;e.g. ALTITUDE;DATETIME are possible dependencies for ALTITUDE + test3=0B ;(resvd[i] EQ 'INDEPENDENT') AND ((i GT 1) OR (nd GE 2)) AND (STRPOS(vn[vc],'.BOUNDARIES') EQ -1) ;remove test as too specific + test4=(resvd[i] NE 'CONSTANT') AND (resvd[i] NE 'INDEPENDENT') AND (resvd[i] NE vn[vc]) + ;Check for CONSTANT,INDEPENDENT or self-referencing and more than one VAR_DEPEND value + IF (test1) OR (test2) OR (test3) THEN BEGIN + STOP_WITH_ERROR,o3[3]+proname+vname+': ',holdvd+errtxt[11],lu & RETURN + ;VAR_DEPEND value(s) not valid + ENDIF + ;Check for valid dependencies, and hold VAR_SIZE values of the dependencies if required + ;Section modified 20231218 to allow axis variable to be after the variable that depends on it + CASE 1 OF + (vc EQ 0) AND (nvn GE 2): gi=WHERE(resvd[i] EQ vn[1:nvn-1],gcnt) + (vc EQ nvn-1) AND (nvn GE 2): gi=WHERE(resvd[i] EQ vn[0:nvn-2],gcnt) + nvn GE 3: BEGIN + gi=WHERE(resvd[i] EQ vn[0:vc-1],gcnt) + IF gcnt EQ 0 THEN BEGIN + gi=WHERE(resvd[i] EQ vn[vc+1:nvn-1],gcnt) + IF gcnt NE 0 THEN gi[0]=gi[0]+vc+1 + ENDIF + END + ELSE: gcnt=0 + ENDCASE + ;Original check + ;IF vc GE 1 THEN gi=WHERE(resvd[i] EQ vn[0:vc-1],gcnt) ELSE gcnt=0 + IF (gcnt EQ 0) AND (test4) THEN BEGIN + STOP_WITH_ERROR,o3[3]+proname+vname+': ',errtxt[3]+resvd[i],lu & RETURN + ;VAR_DEPEND value not CONSTANT,INDEPENDENT nor Self-referencing, and doesn't match any VAR_NAME + ENDIF + IF gcnt NE 0 THEN BEGIN ;do remaining checks on VAR_DEPEND values + errfound=0 + gi=WHERE(meta_arr EQ 'VAR_NAME='+resvd[i]) + ;check VAR_DEPEND value of the VAR_DEPEND variable name + vi=WHERE(STRPOS(meta_arr[gi[0]:N_ELEMENTS(meta_arr)-1],'VAR_DEPEND') NE -1) + vex=STRSPLIT(STRUPCASE(meta_arr[gi[0]+vi[0]]),' =;',/Extract,COUNT=nvex) + IF nvex NE 2 THEN vex=[vex,''] ;missing VAR_DEPEND value so add dummy value + IF (vex[1] NE 'CONSTANT') AND (vex[1] NE 'INDEPENDENT') AND (vex[1] NE resvd[i]) THEN BEGIN + vert_var=['ALTITUDE','ALTITUDE.GPH','PRESSURE','DEPTH'] + res=WHERE(resvd[i] EQ vert_var,vcnt) ;does VAR_DEPEND variable name reference a vertical axis + res=WHERE(vex[1] EQ vert_var,xcnt) ;does VAR_DEPEND of the VAR_DEPEND variable name reference a vertical axis + IF (vcnt NE 0) AND (xcnt EQ 0) THEN BEGIN ;Vertical axis so should be self-referencing + infotxt='2 '+meta_arr[gi[0]+vi[0]]+' should be self-referencing for axis variable' + infotxt=infotxt+' VAR_NAME='+resvd[i]+'|. VAR_DEPEND value changed in metadata' + INFOTXT_OUTPUT,infotxt + meta_arr[gi[0]+vi[0]]='VAR_DEPEND='+resvd[i] + IF nvex GT 2 THEN FOR j=2,nvex-1 DO meta_arr[gi[0]+vi[0]]=meta_arr[gi[0]+vi[0]]+';'+vex[j] + ENDIF ELSE BEGIN + ;VAR_DEPEND value of the VAR_DEPEND variable name must be CONSTANT or an axis variable + infotxt=STRARR(2) + infotxt[0]='3 '+meta_arr[lcy]+' variable name(s) for '+vname+' must have VAR_DEPEND' + infotxt[1]=' value of CONSTANT or be self-referencing' + INFOTXT_OUTPUT,infotxt + ENDELSE + ENDIF + IF vsadded EQ 0 THEN BEGIN + ;check VAR_SIZE value of the VAR_DEPEND variable name + vi=WHERE(STRPOS(meta_arr[gi[0]:N_ELEMENTS(meta_arr)-1],'VAR_SIZE') NE -1) + vex=STRSPLIT(meta_arr[gi[0]+vi[0]],' =;',/Extract,COUNT=nvex) + + IF (nvex LT 2) OR (nvex GT 3) THEN errfound=1 $ + ELSE IF (ndmok EQ 1) AND (LONG(vex[1]) NE ndm[vc,i-1]) THEN errfound=1 + + IF errfound EQ 1 THEN BEGIN + STOP_WITH_ERROR,o3[3]+proname+vname+': '+'VAR_DEPEND='+resvd[i]+': ', $ + errtxt[5]+STRTRIM(ndm[vc,i-1],2)+'): '+meta_arr[gi[0]+vi[0]],lu + ;VAR_DEPEND variable name must have matching VAR_SIZE value + RETURN + ENDIF ELSE vshold[i-1]=LONG(vex[1]) + ENDIF + ENDIF + ;If i eq 2 and value is INDEPENDENT then check that the corresponding VAR_SIZE value is 2 + ;(if present), o/w generate errors + IF (i EQ 2) AND (STRUPCASE(resvd[i]) EQ 'INDEPENDENT') THEN BEGIN + IF (ndmok EQ 1) AND (ndm[vc,i-1] NE 2L) THEN BEGIN + STOP_WITH_ERROR,o3[3]+proname+vname+': '+holdvs+': ',errtxt[12],lu & RETURN + ;Second VAR_SIZE value should be '2' for this VAR_NAME + ENDIF + vshold[i-1]=2L ;set VAR_SIZE value in the event that it is not part of the Metadata + ENDIF + ENDFOR + + IF (ndmok EQ 0) OR (vsadded EQ 1) THEN BEGIN ;no VAR_SIZE value so need to determine from the data or the vshold array + IF (vshold[0] EQ 0L) AND (vsadded EQ 0) THEN BEGIN + ;VAR_DEPEND is Self-Referencing or CONSTANT so determine VAR_SIZE from the Data file or structure + dtest=['-1'] ;-1 identifies to the EXTRACT_DATA routine that ndl is all that is required + EXTRACT_DATA,SDS,inf,vc,dtest,ndl + IF STRLEN(rerr[0]) GT 2 THEN RETURN + ;Check if any other VAR_SIZE values have already been determined + ;This could happen in the case VAR_DEPEND=ALTITUDE;DATETIME for VAR_NAME=ALTITUDE for e.g. + gi=WHERE(vshold NE 0L,gcnt) + ndltot=' ('+STRTRIM(ndl,2)+')' + IF gcnt NE 0 THEN BEGIN + FOR i=0,gcnt-1 DO ndl=ndl/FLOAT(vshold[gi[i]]) + ENDIF + ;Make up VAR_SIZE string based on available values + IF vshold[0] EQ 0L THEN vsstr=' (VAR_SIZE=x' ELSE vsstr='VAR_SIZE='+STRTRIM(vshold[0],2) + IF nd GT 1 THEN FOR i=1,nd-1 DO $ + IF vshold[i] EQ 0L THEN vsstr=vsstr+';x' ELSE vsstr=vsstr+';'+STRTRIM(vshold[i],2) + vsstr=vsstr+') ' + vshold[0]=LONG(ndl) + bi=WHERE(vshold EQ 0,bcnt) ;Expected VAR_SIZE values are missing if bcnt not equal to 0 + IF (ndl NE vshold[0]) OR (bcnt NE 0) THEN BEGIN + infotxt=STRARR(2) + infotxt[0]='3 Unable to determine missing VAR_SIZE value(s) for '+vname+' based on' + infotxt[1]=' '+holdvd+vsstr+'and the total number of dataset values'+ndltot + INFOTXT_OUTPUT,infotxt + vserror[vc]=vn[vc] + ENDIF + meta_arr[lcx]='VAR_SIZE='+STRTRIM(vshold[0],2) + ndm[vc,0:nd-1]=vshold + IF nd GT 1 THEN FOR i=1,nd-1 DO meta_arr[lcx]=meta_arr[lcx]+';'+STRTRIM(vshold[i],2) + ENDIF ELSE IF vsadded EQ 0 THEN BEGIN ;can determine VAR_SIZE from the vshold array + meta_arr[lcx]='VAR_SIZE=' + FOR i=0,nd-1 DO BEGIN + IF i EQ 0 THEN meta_arr[lcx]=meta_arr[lcx]+STRTRIM(vshold[i],2) $ + ELSE meta_arr[lcx]=meta_arr[lcx]+';'+STRTRIM(vshold[i],2) + ndm[vc,i]=vshold[i] ;EQ array dimensions from VAR_SIZE of the dependencies + ENDFOR + ENDIF + ;Check to see if VAR_SIZE calculation is valid or not based on contents of vserror + vsok=1 + FOR i=1,nd DO BEGIN + ei=WHERE(resvd[i] EQ vserror,ecnt) + IF ecnt NE 0 THEN vsok=0 + ENDFOR + IF (vsok EQ 1) OR (vsadded EQ 1) THEN BEGIN + IF qa_yes THEN qtxt=' not recorded. Calculated as '+meta_arr[lcx] ELSE qtxt=' added: '+meta_arr[lcx] + infotxt='2 VAR_SIZE value(s) for '+vname+qtxt + INFOTXT_OUTPUT,infotxt + ENDIF + ENDIF + + IF (resvd[1] EQ 'CONSTANT') AND (TOTAL(ndm[vc,0:7]) GT 1.) THEN BEGIN + infotxt='3 Dataset '+vn[vc]+' must only have a single value for VAR_DEPEND=CONSTANT: '+meta_arr[lcx] + INFOTXT_OUTPUT,infotxt + ENDIF + + ;Do DATETIME/MJD2000 checks + REPEAT lc=lc+1 UNTIL STRMID(meta_arr[lc],0,9) EQ 'VAR_UNITS' + resv=STRSPLIT(meta_arr[lc],' =',/Extract,Count=rcnt) + IF rcnt EQ 1 THEN resv=[resv,'[MISSING]'] ;add filler for checks + test1=(vn[vc] EQ 'DATETIME') OR (vn[vc] EQ 'DATETIME.START') OR (vn[vc] EQ 'DATETIME.STOP') + IF (test1) AND (STRUPCASE(resv[1]) NE 'MJD2K') THEN BEGIN + lch=lc ;hold setting for VAR_UNITS + ;Find VAR_SI_CONVERSION values and test for correct values for MJD2K + REPEAT lch=lch+1 UNTIL STRMID(meta_arr[lch],0,17) EQ 'VAR_SI_CONVERSION' + res=STRSPLIT(meta_arr[lch],' =;',/EXTRACT) + test1=(STRLOWCASE(resv[1]) EQ 's') AND (FIX(res[1]) EQ 0) AND (LONG(res[2]) EQ 86400L) AND $ + (STRLOWCASE(res[3]) EQ 's') + IF (resv[1] EQ '[MISSING]') OR (resv[1] EQ '1') OR (STRLOWCASE(resv[1]) EQ 'd') OR (test1) THEN BEGIN + errid='2 ' & itxt='|. Value changed in Metadata' + ENDIF ELSE BEGIN + errid='3 ' & itxt='' + ENDELSE + infotxt=errid+meta_arr[lc]+' must be VAR_UNITS=MJD2K for '+vname+itxt + INFOTXT_OUTPUT,infotxt + meta_arr[lc]='VAR_UNITS=MJD2K' + resv=['VAR_UNITS','MJD2K'] + ;Change VAR_SI_CONVERSION value also + CASE 1 OF + ndm[vc,8] LE 3: meta_arr[lch]='VAR_SI_CONVERSION=0;86400;s + ndm[vc,8] LE 5: meta_arr[lch]='VAR_SI_CONVERSION=0.0;86400.0;s' + ELSE: meta_arr[lch]='VAR_SI_CONVERSION=' + ENDCASE + ENDIF + vu[vc]=resv[1] ;used to check for MJD2K units when reading in data as well as NCSA units value + IF N_ELEMENTS(resv) GT 2 THEN $ + FOR i=2,N_ELEMENTS(resv)-1 DO vu[vc]=vu[vc]+' '+resv[i] + + ;Do checks on the numeric attributes + FOR i=0,nna-1 DO BEGIN + REPEAT lc=lc+1 UNTIL STRMID(meta_arr[lc],0,STRLEN(num_atts[i])) EQ num_atts[i] + resv=STRSPLIT(meta_arr[lc],' =;',/Extract,COUNT=nresv) + test1=(STRUPCASE(vu[vc]) NE 'MJD2K') OR (num_atts[i] EQ 'VAR_FILL_VALUE') + test2=(nresv NE 2) AND (ndm[vc,8] NE 6) + IF (test1) AND (test2) THEN BEGIN + infotxt=STRARR(2) + infotxt[0]='3 Unexpected number of ATTRIBUTE values extracted for '+vname+':' + infotxt[1]=' '+meta_arr[lc] + INFOTXT_OUTPUT,infotxt + resv=[resv,''] ;ensure at least 2 values + meta_arr[lc]=resv[0]+'='+resv[1] & nresv=2 + ;STOP_WITH_ERROR,o3[3]+proname+vname+': ',errtxt[7]+meta_arr[lc],lu & RETURN + ;Unexpected number of ATTRIBUTE values extracted + ENDIF + IF (ndm[vc,8] NE 6) AND (nresv EQ 2) THEN BEGIN + ;If VAR_UNITS=MJD2000 then check for ISO8601 format and change to MJD2000 if necessary + IF (STRUPCASE(vu[vc]) EQ 'MJD2K') AND (STRPOS(STRUPCASE(resv[1]),'Z') NE -1) THEN BEGIN + mjd2000=JULIAN_DATE(resv[1],/I,/M) ;return time in MJD2000 format + IF mjd2000 EQ -99999.d THEN BEGIN + STOP_WITH_ERROR,o3[3]+proname+vn[vc]+': ',errtxt[10]+meta_arr[lc],lu & RETURN + ENDIF + vfvd[i,vc]=mjd2000 + infotxt=STRARR(2) + meta_arr[lc]=resv[0]+'='+STRTRIM(STRING(format='(d18.9)',mjd2000),2) + IF qa_yes THEN qtxt=' must be in MJD2K format|' ELSE qtxt=' replaced with MJD2K formatted value ' + infotxt[0]='2 '+resv[0]+'='+resv[1]+' for VAR_NAME='+vn[vc] + infotxt[1]=qtxt+meta_arr[lc] + INFOTXT_OUTPUT,infotxt + ENDIF ELSE BEGIN + ;Check that the VAR_VALID_MIN/MAX or VAR_FILL_VALUE is numeric and save to vfvl or vfvd arrays + lcx=lc + IF ndm[vc,8] LE 3 THEN BEGIN ;value is integer type + IF mv_lng[lc] NE 0LL THEN vfvl[i,vc]=mv_lng[lc] ELSE vfvl[i,vc]=LONG64(DOUBLE(resv[1])) + ENDIF ELSE BEGIN ;value is floating point type + IF mv_dbl[lc] NE 0.D THEN vfvd[i,vc]=mv_dbl[lc] ELSE vfvd[i,vc]=DOUBLE(resv[1]) + ENDELSE + ENDELSE + ENDIF + ENDFOR + vc=vc+1 + ENDIF + lc=lc+1 & valid=1 +ENDWHILE + +IF notlong THEN BEGIN ;write out INFORMATION or ERROR to log + IF qa_yes THEN itxt=' must be ' ELSE itxt=' changed to ' + infotxt='2 VAR_SIZE values'+itxt+'integer format' + INFOTXT_OUTPUT,infotxt +ENDIF + +;Save numeric metadata values to common arrays +mv_lng=vfvl & mv_dbl=vfvd + +IF qa_yes THEN BEGIN + ON_IOERROR, NULL + ;Check VAR_VALID_MIN, VAR_VALID_MAX, VAR_FILL_VALUE and Dataset data types match VAR_DATA_TYPE + s_dims=SIZE(sds,/DIMENSIONS) + pchkd=PTR_VALID(sds.data) + pchkv=PTR_VALID(sds.va_v) + pchkl=PTR_VALID(sds.va_l) + + IF s_dims[0] GE 1 THEN BEGIN + attnamechk=['VAR_NAME','VAR_DATA_TYPE','VAR_VALID_MIN','VAR_VALID_MAX','VAR_FILL_VALUE'] + n_anc=N_ELEMENTS(attnamechk) + FOR i=0,s_dims[0]-1 DO BEGIN + vdtx=BYTARR(n_anc)+255B ;VAR_DATA_TYPE for the attnamechk values + vnh='' + FOR j=0,s_dims[1]-1 DO BEGIN + IF (pchkl[i,j]) AND (pchkv[i,j]) THEN BEGIN + res=*sds[i,j].va_l & res=STRTRIM(STRUPCASE(res),2) + ai=WHERE(res EQ attnamechk,acnt) + IF acnt NE 0 THEN BEGIN + attval=*sds[i,j].va_v + IF ai[0] EQ 0 THEN vnh=STRTRIM(STRUPCASE(attval),2) $ + ELSE IF ai[0] EQ 1 THEN BEGIN + vdth=STRTRIM(STRUPCASE(attval),2) + vi=WHERE(vdth EQ allowable_data_types,vcnt) + IF vcnt NE 0 THEN vdtx[1]=idl_data_type[vi[0]] + ENDIF ELSE vdtx[ai[0]]=SIZE(*sds[i,j].va_v,/TYPE) + ENDIF + ENDIF + ENDFOR + IF pchkd[i,0] THEN dvdtx=SIZE(*sds[i,0].data,/TYPE) ELSE dvdtx=255B ;Data type of the dataset + ;PRINT,vnh,dvdtx,vdtx[1:4] + IF vdtx[1] NE 255B THEN BEGIN + IF (dvdtx NE vdtx[1]) AND (dvdtx NE 255B) THEN BEGIN + vi=WHERE(dvdtx EQ idl_data_type,vcnt) + IF vcnt NE 0 THEN svdt=allowable_data_types[vi[0]] ELSE svdt='' + IF svdt EQ '' THEN $ + infotxt='3 '+vnh+' dataset has data type that does not match VAR_DATA_TYPE='+vdth $ + ELSE $ + infotxt='3 '+vnh+' dataset has data type '+svdt+', which does not match VAR_DATA_TYPE='+vdth + INFOTXT_OUTPUT,infotxt + ENDIF + FOR j=2,n_anc-1 DO BEGIN + IF vdtx[j] NE vdtx[1] THEN BEGIN + vi=WHERE(vdtx[j] EQ idl_data_type,vcnt) + IF vcnt NE 0 THEN svdt=allowable_data_types[vi[0]] ELSE svdt='' + IF svdt EQ '' THEN $ + infotxt='3 '+vnh+' '+attnamechk[j]+ $ + ' value has data type that does not match VAR_DATA_TYPE='+vdth $ + ELSE $ + infotxt='3 '+vnh+' '+attnamechk[j]+' value has data type '+svdt+ $ + ', which does not match VAR_DATA_TYPE='+vdth + INFOTXT_OUTPUT,infotxt + ENDIF + ENDFOR + ENDIF + ENDFOR + ENDIF +ENDIF + +;Define the HDF storage structure +ds_set={data: PTR_NEW()} ;SDS data array +ds=REPLICATE(ds_set,nvn) ;Dimension the structure to the size of the SDS datasets + +TypeConversionError: +IF valid EQ 0 THEN BEGIN + STOP_WITH_ERROR,o3[3]+proname+vname+': ',errtxt[16]+meta_arr[lcx],lu & RETURN +ENDIF +END ;Procedure Set_Up_Structure + + + +PRO check_string_datatype, vc, dtest, ndl +;Procedure to check that the data, and Variable Attributes are correctly configured when the +;VAR_DATA_TYPE=STRING, including VAR_UNITS, VAR_VALID_MIN/MAX, and VAR_FILL_VALUE. The data +;values are returned in the string datatype. +; ---------- +;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org +; +; History: +; 20091203: Introduced to IDLCR8HDF routine - Version 3.08 +; 20101120: Account for GEOMS rule changes from v3.0 to v4.0 - Version 4.0b0 +; 20111220: Check dataset entries for non-ISO646-US ASCII Characters - Version 4.0b7 +; 20130116: Allow for a string dataset with maximum string length of 0, i.e. a set +; of empty values - Version 4.0b15 +; 20150127: Use right padding to make strings the correct length (previously defaulted to +; left padding); Save maximum string length of dataset in mv_str array +; - Version 4.0b25 +; 20150217: VAR_FILL_VALUE reverts to being an empty space (made same length as the longest +; dataset string in 4.0b25); improve checks on variable attribute values; Make +; the first dataset entry equal to the maximum string length if writing to H5 +; due to apparent bug when writing datasets which means that the string +; length is determined by the first entry (white space is removed when writing to +; the H5 file) - Version 4.0b26 +; 20220805: Improve Information/Error message when attributes for string datasets are not +; valid - Version 4.0b60 +; +; Inputs: meta_arr - a string array containing the Global and Variable Attributes +; vc - the index value of the vn array holding the VARIABLE_NAME being searched for in the data +; file, or the index value of the sds structure which holds the data +; dtest - the set of data values to be tested +; ndl - The total number of data values i.e. the product of the VAR_SIZE values +; +; Outputs: meta_arr - in the event that the values being tested are changed, the array will be updated +; dtest - correctly formatted dataset +; +; Called by: READ_DATA +; +; Subroutines Called: INFOTXT_OUTPUT +; Information Conditions (when the program is able to make changes): +; 1. The Metadata value is not valid for VAR_DATA_TYPE=STRING [2645] + +COMMON METADATA +COMMON DATA +COMMON WIDGET_WIN + +vi=WHERE(meta_arr EQ 'VAR_NAME='+vn[vc]) +dtestx=STRTRIM(dtest,0) ;remove trailing blanks only, for testing that string datasets are left justified +dtest=STRTRIM(dtest,2) + +;Check for non ISO646-US ASCII Characters +GEOMS_RULE_CHANGES,10,dtest,vn[vc] + +IF ~ARRAY_EQUAL(dtest,dtestx) THEN BEGIN ;format of the string dataset has been changed + IF qa_yes THEN $ + infotxt='2 Dataset '+vn[vc]+' incorrectly formatted (text must be left-justified)' $ + ELSE infotxt='2 Dataset '+vn[vc]+' reformatted so that text is left-justified' + INFOTXT_OUTPUT, infotxt +ENDIF + +;Determine maximum string length of the dataset +mxdatalen=0L ;default value of string length +testdatalen=MAX(STRLEN(dtest)) +IF testdatalen GT mxdatalen THEN mxdatalen=testdatalen +;Format the first data value to the maximum string length for H5 only (white space removed when writing to the file) +IF (mxdatalen NE 0L) AND (STRPOS(o3[0],'H5') NE -1) THEN BEGIN + ;Done for H5 only because H5S_CREATE_SIMPLE function uses string length of the 1st entry to set length for all entries + vfsx='(A-'+STRTRIM(mxdatalen,2)+')' ;A- does left justified text instead of right justified + dtest[0]=STRING(format=vfsx,dtest[0]) +ENDIF + +label=['VAR_UNITS','VAR_SI_CONVERSION','VAR_VALID_MIN','VAR_VALID_MAX','VAR_FILL_VALUE'] +n_lab=N_ELEMENTS(label) +mxlablen=MAX(STRLEN(label)) ;determine max label string length +metahold=STRMID(meta_arr,0,mxlablen) ;subset of the meta_arr strings, containing first portion of the Metadata + +;Perform checks on the Attributes +FOR i=0,n_lab-1 DO BEGIN + li=WHERE(STRPOS(metahold[vi[0]:N_ELEMENTS(metahold)-1],label[i]) NE -1) + lvi=li[0]+vi[0] + res=STRSPLIT(meta_arr[lvi],'=',/Extract,COUNT=nres) + IF nres EQ 1 THEN res=[res,''] + IF res[1] EQ ' ' THEN res[1]='' + IF (nres GT 2) OR (res[1] NE '') THEN BEGIN + ;print,'"'+res[1]+'"/"'+itxt+'"/'+strtrim(mxdatalen,2) + IF qa_yes THEN itxt='must be [' ELSE itxt='changed to [' + infotxt='2 '+vn[vc]+' '+meta_arr[lvi]+' attribute '+itxt+res[0]+'= ]' + INFOTXT_OUTPUT, infotxt + mv_str[vc]=mxdatalen + ENDIF + meta_arr[lvi]=res[0]+'= ' ;+string(ltxt) + IF res[0] EQ 'VAR_UNITS' THEN vu[vc]=' ' +ENDFOR + +END ;procedure Check_String_Datatype + + + +PRO check_min_max_fill, vc, dtest, ndl +;Procedure to check that the data, VAR_VALID_MIN/MAX, and VAR_FILL_VALUE fit within the given data +;type. That the VAR_FILL_VALUE falls within the GEOMS definition and that the data values are within +;the valid minimum and maximum values (or the fill value). The VAR_VALID_MIN value is less than or +;equal to the VAR_VALID_MAX value. The data values are returned in the data type given by VAR_DATA_TYPE. +; ---------- +;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org +; +; History: +; 20050802: Original IDLCR8HDF routine - Version 1.0 +; 20050909: If VAR_NAME=DATETIME then checks that the first data value is not a fill value, and is +; the lowest value; Change the methods for determining if a given data value falls within +; the range allowed by integer or long data types; Add fix for the case where a dataset +; consists of only fill values - Version 1.1 +; 20051107: Include VIS_SCALE_MIN/MAX values in the checks; Add fix to remove unwanted precision (to +; prevent rounding errors), by converting values to formatted strings then back to numeric +; values - Version 1.11 +; 20061012: Check that all DATETIME values are in chronological order (if more than one value), and +; contain no fill values; ISO8601 DATETIME conversions to MJD2000 format (if necessary) moved +; to the READ_DATA routine; Add additional checks when extracting the VIS_FORMAT values for +; testing; Common variable definition WIDGET_WIN added - Version 2.0 +; 20080302: Fix bug so that unwanted precision from all data and relevant metadata values is removed +; before doing QA checks, to avoid unnecessary error calls - Version 3.0 +; 20100205: Add RETURN command after all STOP_WITH_ERROR calls, which allows program to return to the +; calling program if the reterr argument is included in the idlcr8hdf call - Version 3.09 +; 20101120: Account for GEOMS rule changes v3.0 to v4.0; Remove checks involving VIS_FORMAT; Modify +; method for checking data type limitations - Version 4.0 +; 20120420: Fix CHECK_MATH check so that combined floating point errors are ignored for long integer +; values being tested (error 160) - Version 4.0b9 +; 20120620: Remove CHECK_MATH checks, and just apply Type conversion checks, as it was not possible for +; CHECK_MATH to accurately indicate the problem numeric value - Version 4.0b10 +; 20120829: Fix call to ROUND when the value being rounded is a string (ROUND can only be applied to +; numeric datatypes) - Version 4.0b12 +; 20130327: Add additional check that VAR_VALID_MIN is less than or equal to the VAR_VALID_MAX in the +; event that the dataset only contains fill values - Version 4.0b20 +; 20141110: Check for NaN in the dataset and change to the Fill Value - Version 4.0b24 +; 20170331: Add checks for VAR_VALID_MIN and MAX values for Latitude, Longitude, Zenith, Azimuth and +; Wind direction datasets; check that the maximum dataset value for Azimuth datasets is +; less than 360.0 degrees - Version 4.0b42 +; 20171130: Remove ds from STOP_WITH_ERROR parameters - Version 4.0b44 +; 20180901: Also check that DATETIME.START and DATETIME.STOP values are in chronological order - +; Version 4.0b48 +; 20201211: Fix bug that caused an IDL Math Error (Floating Illegal Operand) when converting large +; floating point values to integers - Version 4.0b58 +; 20220805: Allow program to continue if data type errors in the dataset and attributes are detected - +; Version 4.0b60 +; +; Inputs: meta_arr - a string array containing the Global and Variable Attributes +; vc - the index value of the vn array holding the VARIABLE_NAME being searched for in the data +; file, or the index value of the sds structure which holds the data +; dtest - the set of data values to be tested +; ndl - The total number of data values i.e. the product of the VAR_SIZE values +; +; Outputs: meta_arr - in the event that the VIS_FORMAT value string or the precision of the attribute +; values being tested are changed, the array will be updated +; dtest - dataset returned in correct data type (VAR_FILL_VALUE/VAR_VALID_MIN/VAR_VALID_MAX +; also appended to the start of the array) +; +; Called by: READ_DATA +; +; Subroutines Called: STOP_WITH_ERROR (if error state detected) +; INFOTXT_OUTPUT +; Possible Conditions for STOP_WITH_ERROR call (plus [line number] where called): +; 1. Value is not valid, or does not match VAR_DATA_TYPE [2826] +; 2. A data value falls outside the range given by VAR_VALID_MIN or VAR_VALID_MAX [2846, 2851] +; 3. The VAR_FILL_VALUE is not outside the valid minimum or maximum values - No longer a requirement +; 4. The metadata or data value being tested is outside the data type range. For example, +; if a data value is 40000, but VAR_DATA_TYPE=INTEGER (maximum allowable value of 32767) +; [2786,2793,2800,2807,2813,2819] +; 5. The DATETIME value cannot be a VAR_FILL_VALUE. Because DATETIME is an axis variable +; it should not contain Fill Values [2862] +; 6. The DATETIME values are not in chronological order [2868] +; 7. Type conversion error. The program cannot convert a data value to a valid number [2874] +; +; Information Conditions (when the program is able to make changes): +; 1. Fill value falls within the data range and is defined as a Default value [2836] +; 2. NaN value in the dataset replaced with the fill value + +COMMON METADATA +COMMON DATA +COMMON WIDGET_WIN + +;Error messages for this procedure +ON_IOERROR,TypeConversionError +proname='Check_Min_Max_Fill procedure: ' +errtxt2=STRARR(7) & lu=-1 +errtxt2[0]=' not valid, or does not match VAR_DATA_TYPE' +errtxt2[1]=' data value outside VAR_VALID_' +errtxt2[2]='Fill data value not outside VAR_VALID_MIN or VAR_VALID_MAX: ' +errtxt2[3]=' outside the data type range: ' +errtxt2[4]='DATETIME value cannot be a VAR_FILL_VALUE. ' +errtxt2[5]='DATETIME values not in chronological order' +errtxt2[6]='Type conversion error. Entry is not a valid value' + +vi=WHERE(meta_arr EQ 'VAR_NAME='+vn[vc]) + +;Determine Var_Fill_Value, Var_Valid_Min, Var_Valid_Max, and data values, +;and check that they fall within the range allowed by VAR_DATA_TYPE +label=['VAR_VALID_MIN','VAR_VALID_MAX','VAR_FILL_VALUE','data value'] +n_lab=N_ELEMENTS(label) +mxlablen=MAX(STRLEN(label[0:n_lab-2])) ;determine max label string length +metahold=STRMID(meta_arr,0,mxlablen) ;subset of the meta_arr strings, containing first portion of the Metadata +lvi=LONARR(n_lab-1) ;to hold index values of the VAR_FILL_VALUE/VALID_MIN/VALID_MAX attributes + +CASE ndm[vc,8] OF + 0: vhold=BYTARR(ndl+(n_lab-1L)) + 1: vhold=INTARR(ndl+(n_lab-1L)) + 2: vhold=LONARR(ndl+(n_lab-1L)) + 3: vhold=LON64ARR(ndl+(n_lab-1L)) + 4: vhold=FLTARR(ndl+(n_lab-1L)) + 5: vhold=DBLARR(ndl+(n_lab-1L)) +ENDCASE + +IF SIZE(dtest,/TYPE) EQ 7 THEN convertstr=1 ELSE convertstr=0 ;dtest is in ASCII string format +IF ndm[vc,8] LE 3 then convtolng=1 else convtolng=0 + +nanfound=0 ;boolean setting so information/error message only written once +FOR i=0L,ndl+(n_lab-2L) DO BEGIN + valid=0 ;set-up for possible Type Conversion Error + IF i LE n_lab-2L THEN BEGIN + lin=i ;label index + li=WHERE(STRPOS(metahold[vi[0]:N_ELEMENTS(metahold)-1],label[lin]) NE -1) + lvi[i]=li[0]+vi[0] + res=STRSPLIT(meta_arr[lvi[i]],'=',/Extract) + IF convtolng THEN lv=mv_lng[i,vc] ELSE lv=mv_dbl[i,vc] + tcerr=': '+meta_arr[lvi[i]] ;used for type conversion error + ENDIF ELSE BEGIN ;check individual data values + res=['',''] & lin=n_lab-1L ;label index + tcerr=' in the Data File: '+STRTRIM(dtest[i-lin],2) ;used for type conversion error + IF convertstr THEN BEGIN + IF convtolng THEN lv=ROUND(DOUBLE(dtest[i-lin]),/L64) ELSE lv=DOUBLE(dtest[i-lin]) ;LONG64(DOUBLE(dtest[i-lin])) ELSE lv=DOUBLE(dtest[i-lin]) + ENDIF ELSE lv=dtest[i-lin] + ENDELSE + IF N_ELEMENTS(res) EQ 2 THEN mok=0 ELSE mok=-1 + + IF mok EQ 0 THEN BEGIN + ;test if value is 'NaN'. If so then change to the fill value if already determined + IF (FINITE(lv,/NAN)) AND (i GT 2L) THEN BEGIN ;data value is NaN + lv=vhold[2] + IF nanfound EQ 0 THEN BEGIN + infotxt='2 '+vn[vc]+' dataset contains NaN value(s)|. Replaced with Fill Value(s)' + INFOTXT_OUTPUT,infotxt + nanfound=1 + ENDIF + ENDIF + CASE ndm[vc,8] OF + 0:BEGIN ;BYTE unsigned 8-bit integer (0 to 255) + ;do difference test to account for possible rounding errors + IF lv NE BYTE(lv) THEN BEGIN + infotxt='3 '+vn[vc]+' '+label[lin]+' outside 8-bit unsigned (BYTE) range: '+STRTRIM(lv,2) + INFOTXT_OUTPUT,infotxt + ENDIF ELSE vhold[i]=BYTE(lv) + END + 1:BEGIN ;SHORT signed 16-bit integer (-32,768 to 32,767) + ;do difference test to account for possible rounding errors + IF lv NE FIX(DOUBLE(lv)) THEN BEGIN + infotxt='3 '+vn[vc]+' '+label[lin]+' outside 16-bit signed (SHORT) range: '+STRTRIM(lv,2) + INFOTXT_OUTPUT,infotxt + ENDIF ELSE vhold[i]=FIX(DOUBLE(lv)) + END + 2:BEGIN ;INTEGER signed 32-bit integer (-2.147e+9 to 2.147e+9) + ;do difference test to account for possible rounding errors + IF lv NE LONG(DOUBLE(lv)) THEN BEGIN + infotxt='3 '+vn[vc]+' '+label[lin]+' outside 32-bit signed (INTEGER) range: '+STRTRIM(lv,2) + INFOTXT_OUTPUT,infotxt + ENDIF ELSE vhold[i]=LONG(DOUBLE(lv)) + END + 3:BEGIN ;INTEGER signed 64-bit integer (-9.223e+18 to 9.223e+18) + ;do difference test to account for possible rounding errors + IF ABS(DOUBLE(lv)-DOUBLE(lv)) GE 1. THEN BEGIN + infotxt='3 '+vn[vc]+' '+label[lin]+' outside 64-bit signed (LONG) range: '+STRTRIM(lv,2) + INFOTXT_OUTPUT,infotxt + ENDIF ELSE vhold[i]=LONG64(DOUBLE(lv)) + END + 4:BEGIN + IF NOT FINITE(FLOAT(lv)) THEN BEGIN + infotxt='3 '+vn[vc]+' '+label[lin]+' outside 32-bit floating point (REAL) range: '+STRTRIM(lv,2) + INFOTXT_OUTPUT,infotxt + ENDIF ELSE vhold[i]=FLOAT(lv) + END + 5:BEGIN + IF NOT FINITE(DOUBLE(lv)) THEN BEGIN + infotxt='3 '+vn[vc]+' '+label[lin]+' outside 64-bit floating point (DOUBLE) range: '+STRTRIM(lv,2) + INFOTXT_OUTPUT,infotxt + ENDIF ELSE vhold[i]=DOUBLE(lv) + END + ENDCASE + ENDIF ELSE BEGIN + STOP_WITH_ERROR,o3[3]+proname+vn[vc],label[lin]+errtxt2[0]+tcerr,lu & RETURN + ENDELSE + valid=1 ;type conversion OK if got to here +ENDFOR + +;Check if VAR_FILL_VALUE falls within VAR_VALID_MIN/MAX - if so it is defined +;as a default, rather than missing/error value +mnv=vhold[0] & mxv=vhold[1] & fv=vhold[2] +IF (fv GE mnv) AND (fv LE mxv) THEN BEGIN + infotxt='0 Fill value for '+meta_arr[vi[0]]+' falls within the data' + infotxt=infotxt+' range and is defined as a ''Default'' value' + INFOTXT_OUTPUT,infotxt +ENDIF + +dtest=vhold[n_lab-1L:ndl+(n_lab-2L)] + +;Do checks on VAR_VALID_MIN and MAX values for Latitude, Longitide, Zenith, Azimuth and Wind Direction datasets +IF STRPOS(vn[vc],'LATITUDE') NE -1 THEN BEGIN + IF (mnv LT -90.0) OR (mxv GT 90.0) THEN BEGIN + itxt='-90.0 (South) and +90.0 (North) degrees' + infotxt='3 Valid Minimum and Maximum range values for '+vn[vc]+' are '+itxt + INFOTXT_OUTPUT,infotxt + ENDIF +ENDIF ELSE IF STRPOS(vn[vc],'LONGITUDE') NE -1 THEN BEGIN + IF (mnv LT -180.0) OR (mxv GT 180.0) THEN BEGIN + itxt='-180.0 (West) and +180.0 (East) degrees' + infotxt='3 Valid Minimum and Maximum range values for '+vn[vc]+' are '+itxt + INFOTXT_OUTPUT,infotxt + ENDIF +ENDIF ELSE IF (STRPOS(vn[vc],'ZENITH') NE -1) AND (STRPOS(vn[vc],'ANGLE') NE -1) THEN BEGIN + IF (mnv LT 0.0) OR (mxv GT 180.0) THEN BEGIN + itxt='0.0 and 180.0 degrees' + infotxt='3 Valid Minimum and Maximum range values for '+vn[vc]+' are '+itxt + INFOTXT_OUTPUT,infotxt + ENDIF +ENDIF ELSE IF (STRPOS(vn[vc],'AZIMUTH') NE -1) AND (STRPOS(vn[vc],'ANGLE') NE -1) THEN BEGIN + IF (mnv LT 0.0) OR (mxv GT 360.0) THEN BEGIN + itxt='0.0 (North) and 360.0 degrees (East = 90 degrees and so on)' + infotxt='3 Valid Minimum and Maximum range values for '+vn[vc]+' are '+itxt + INFOTXT_OUTPUT,infotxt + ENDIF +ENDIF ELSE IF STRPOS(vn[vc],'WIND.DIRECTION') NE -1 THEN BEGIN + IF (mnv NE 0.0) OR (mxv NE 360.0) THEN BEGIN + itxt='0.0 (Calm) and 360.0 degrees (use WMO definition)' + infotxt='3 Valid Minimum and Maximum range values for '+vn[vc]+' are '+itxt + INFOTXT_OUTPUT,infotxt + ENDIF +ENDIF + +;determine whether minimum and maximum data values are within VAR_VALID_MIN/MAX +nfvi=WHERE(dtest NE fv,nfvcnt) +IF nfvcnt NE 0 THEN BEGIN ;i.e. there are non-fill values in the data + minv=MIN(dtest(nfvi),minvi,MAX=maxv,SUBSCRIPT_MAX=maxvi) + IF minv LT mnv THEN BEGIN ;Minimum data value is less than VAR_VALID_MIN + IF ndm[vc,8] EQ 0 THEN itxt=STRTRIM(FIX(dtest[nfvi[minvi]]),2) $ + ELSE itxt=STRTRIM(dtest[nfvi[minvi]],2) + infotxt='3 Minimum data value of '+itxt+' is outside '+meta_arr[lvi[0]] + infotxt=infotxt+' for dataset '+vn[vc] + INFOTXT_OUTPUT,infotxt + ;STOP_WITH_ERROR,o3[3]+proname+vn[vc]+': ','Minimum'+errtxt2[1]+ $ + ; 'MIN: '+STRTRIM(dtest[nfvi[minvi]],2)+' ('+meta_arr[lvi[0]]+').',lu,ds + ;RETURN + ENDIF + IF maxv GT mxv THEN BEGIN ;Maximum data value is greater than VAR_VALID_MAX + IF ndm[vc,8] EQ 0 THEN itxt=STRTRIM(FIX(dtest[nfvi[maxvi]]),2) $ + ELSE itxt=STRTRIM(dtest[nfvi[maxvi]],2) + infotxt='3 Maximum data value of '+itxt+' is outside '+meta_arr[lvi[1]] + infotxt=infotxt+' for dataset '+vn[vc] + INFOTXT_OUTPUT,infotxt + ;STOP_WITH_ERROR,o3[3]+proname+vn[vc]+': ','Maximum'+errtxt2[1]+ $ + ; 'MAX: '+STRTRIM(dtest[nfvi[maxvi]],2)+' ('+meta_arr[lvi[1]]+').',lu,ds + ;RETURN + ENDIF ELSE IF (STRPOS(vn[vc],'AZIMUTH') NE -1) AND (STRPOS(vn[vc],'ANGLE') NE -1) THEN BEGIN + ;also check whether maximum dataset value = 360.0 degrees + IF LONG(maxv) EQ 360L THEN BEGIN + infotxt='3 Maximum data value of 360.0 degrees is an invalid value for dataset '+vn[vc]+' (North = 0.0)' + INFOTXT_OUTPUT,infotxt + ENDIF + ENDIF +ENDIF ELSE BEGIN ;dataset consists of fill values so check that VAR_VALID_MIN LE VAR_VALID_MAX + IF mnv GT mxv THEN BEGIN + infotxt='3 VAR_VALID_MIN value is greater than the VAR_VALID_MAX value for dataset '+vn[vc] + INFOTXT_OUTPUT,infotxt + ENDIF +ENDELSE + +;Do Checks with DATETIME attributes +IF STRPOS(vn[vc],'DATETIME') EQ 0 THEN BEGIN ;i.e. DATETIME, DATETIME.START or DATETIME.STOP + ;Does DATETIME contain any fill values? + di=WHERE(dtest EQ fv,dcnt) + IF (vn[vc] EQ 'DATETIME') AND (dcnt NE 0) THEN BEGIN + ;'DTFVOK' = DATETIME fill value is OK + IF (o3[5] EQ 'DTFVOK') AND (dcnt NE ndl) THEN itxt='1 ' ELSE itxt='3 ' + infotxt=itxt+'DATETIME contains fill value(s)' + INFOTXT_OUTPUT,infotxt + ;STOP_WITH_ERROR,o3[3]+proname,errtxt2[4]+STRTRIM(dtest[di[0]],2),lu,ds & RETURN + ENDIF + di=WHERE(dtest NE fv,dcnt) + IF dcnt GT 1 THEN BEGIN + ;Are DATETIME values in chronological order? + dsort=SORT(dtest[di]) + IF ARRAY_EQUAL(dtest[di],dtest[di[dsort]]) EQ 0 THEN BEGIN + IF o3[5] EQ 'DTFVOK' THEN itxt='1 ' ELSE itxt='3 ' + infotxt=itxt+vn[vc]+' values are not in chronological order' + INFOTXT_OUTPUT,infotxt + ;STOP_WITH_ERROR,o3[3]+proname,errtxt2[5],lu,ds & RETURN + ENDIF + ENDIF +ENDIF + +TypeConversionError: +IF valid EQ 0 THEN STOP_WITH_ERROR,o3[3]+proname+'VAR_NAME='+vn[vc]+': ',errtxt2[6]+tcerr,lu + +END ;procedure Check_Min_Max_Fill + + + +PRO read_data, sds, inf +;Procedure to perform checks on the data file or structure. It looks for missing or invalid +;VAR_VALID_MIN/MAX attribute values for datasets which have VAR_UNITS=MJD2K (including +;start and stop times), and determines these values according to values in the data. It +;also adds a comment to VAR_NOTES if averaging kernel data is present, to indicate the +;proper array order (if this option is chosen on program start-up). +; ---------- +;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org +; +; History: +; 20050802: Original IDLCR8HDF routine - Version 1.0 +; 20050909: Check that the input data file is not empty; test for the correct number of +; datalines in the dataset being searched (e.g. there is not an additional data +; line in the input file); Bug fix to save dtest as an array when there is only +; one data value, and the input data is in a structure - Version 1.1 +; 20061012: Portion of routine originally extracting a dataset from the file or structure +; moved to the EXTRACT_DATA routine; Code added to determine missing or invalid +; minimum or maximum metadata values for DATETIME, or START/STOP.TIME attributes; +; Code added to append a comment to VAR_NOTES if averaging kernel data is present, +; to indicate proper array order (if /AVK option selected); Checked data written +; to the ds structure instead of a series of arrays set-up according to data type; +; Common variable definition WIDGET_WIN added - Version 2.0 +; 20080302: dtest initialized for the EXTRACT_DATA call - Version 3.0 +; 20100205: Add test to allow for RETURN to the calling program after STOP_WITH_ERROR calls +; made by the external routines used by this this procedure - Version 3.09 +; 20101120: Account for new GEOMS rules regarding DATETIME.START/STOP and MJD2K; Do check for +; averaging kernel vector instead of matrix, in which case do not add AVK comment, +; if /AVK option chosen - Version 4.0 +; 20150127: Create mv_str array to hold maximum string length of dataset - Version 4.0b25 +; 20150217: Add check for mv_str so it is only created if input is via files; Change AVK +; comment so it refers to the initial altitude level rather than lowest altitude +; level - Version 4.0b26 +; 20150409: Strip trailing and leading spaces of VAR_VALID_MAX and VAR_VALID_MIN values +; when doing checks for missing values for DATETIME datasets - Version 4.0b28 +; 20150811: Initialize infowrite value for call to EXTRACT_DATA - Version 4.0b29 +; 20150924: Add checks on ALTITUDE/PRESSURE ordering plus ordering of corresponding BOUNDARY +; datasets (only if it is an axis variable). Also perform checks on variables that +; should be grouped (e.g. LATITUDE and LONGITUDE) - Version 4.0b30 +; 20151012: Add ALTITUDE.GPH and DEPTH to the vertical variables to be checked. Check for FTIR +; template version before doing some of the .BOUNDARIES checks - Version 4.0b31 +; 20151109: Allow for the possibility of missing variable attributes (i.e. VAR_NOTES) when +; checking for variable attribute values based on the attr_arr_data array +; ordering; Checks that VAR_NOTES label is present if the /AVK option is selected, +; if not then create log message; Do check on *.BOUNDARIES INDEPENDENT dimension +; regardless of whether the related vertical variable is an axis variable or not +; - Version 4.0b34 +; 20170331: Update comment appended to VAR_NOTES to clarify AVK dimension ordering +; -Version 4.0b42 +; 20171121: Change check identifying FTIR templates with incorrect .BOUNDARIES ordering (FTIR +; changed to FTIR-0 to avoid conflict with other FTIR based templates); Change conditions +; for identifying invalid wind speed or direction values - Version 4.0b43 +; 20201020: Add check for negative random uncertainty values (standard or relative only) +; - Version 4.0b56 +; +; Inputs: sds - Either a structure containing the Variable Attributes and Data, or the input +; data file +; inf - flag identifying SDS type where, 0=structure; 1=data file +; meta_arr - a string array containing the Global and Variable Attributes +; +; Outputs: meta_arr - completes missing or invalid metadata values for attributes with +; VAR_UNITS=MJD2K, as required, and also adds comments to VAR_NOTES +; for averaging kernel data to indicate proper array order, if this +; option is chosen +; ds - checked datasets are written to the ds structure with correct dimensions and +; data type +; +; Called by: IDLCR8HDF +; +; Subroutines Called: EXTRACT_DATA +; CHECK_MIN_MAX_FILL + +COMMON METADATA +COMMON DATA +COMMON WIDGET_WIN + +;Go through variable names and check for DATETIME, DATETIME.START, or DATETIME.STOP +;values and determine representative minimum and maximum datetime values +mint=90000.D & maxt=-90000.D ;initialize min and max datetimes +valfound=0 ;count variable that increments when DATETIME and related datasets are found +errorfound=1 ;variable to indicate error in DATETIME checks +infowrite=0 ;do not write ISO8601 to MJD2K information message in EXTRACT_DATA routine (will write on last EXTRACT_DATA call) +;set-up holding array to hold number of bytes in string VAR_FILL_VALUE (note already created if input is via session memory) +IF mv_str[0] EQ -1L THEN mv_str=LONARR(nvn) +vchk=['VAR_VALID_MIN','VAR_VALID_MAX'] +nvchk=N_ELEMENTS(vchk) & mmi=INTARR(nvchk) +FOR i=0,nvchk-1 DO BEGIN + mi=WHERE(attr_arr_data EQ vchk[i]) & mmi[i]=mi[0] +ENDFOR +vavk=INTARR(nvn) ;holding array for AVK attribute (used below) + +;Determine number of DATETIME values, and set arrays for DATETIME checks +dtfv=DBLARR(3)-mint +dti=WHERE(vn EQ 'DATETIME',dtcnt) +IF dtcnt NE 0 THEN BEGIN + dtest=['0'] ;initializing dtest array for EXTRACT_DATA call + EXTRACT_DATA,sds,inf,dti[0],dtest,ndl,infowrite ;return the dataset + IF STRLEN(rerr[0]) GT 2 THEN RETURN + dtchk=N_ELEMENTS(dtest) + dtvals=DBLARR(3,dtchk)-mint + errorfound=0 +ENDIF ELSE BEGIN + dti=WHERE(vn EQ 'DATETIME.START',dtcnt) + dtsi=WHERE(vn EQ 'DATETIME.STOP',dtscnt) + IF (dtcnt NE 0) AND (dtscnt NE 0) THEN BEGIN + dtest=['0'] ;initializing dtest array for EXTRACT_DATA call + EXTRACT_DATA,sds,inf,dti[0],dtest,ndl,infowrite ;return the dataset + IF STRLEN(rerr[0]) GT 2 THEN RETURN + dtchk=N_ELEMENTS(dtest) + dtest=['0'] ;initializing dtest array for EXTRACT_DATA call + EXTRACT_DATA,sds,inf,dtsi[0],dtest,ndl,infowrite ;return the dataset + IF STRLEN(rerr[0]) GT 2 THEN RETURN + IF dtchk EQ N_ELEMENTS(dtest) THEN BEGIN + dtvals=DBLARR(3,dtchk)-mint + errorfound=0 + ENDIF + ENDIF +ENDELSE + +FOR vc=0,nvn-1 DO BEGIN + vnspl=STRSPLIT(vn[vc],'_',/Extract) + vnspl=[vnspl,'x','x'] ;ensures vnspl has a minimum of 3 values + IF (vnspl[1] EQ 'AVK') OR (vnspl[2] EQ 'AVK') THEN vavk[vc]=1 ;identifies a dataset containing AVK data + IF ((vn[vc] EQ 'DATETIME') OR (vn[vc] EQ 'DATETIME.START') OR $ + (vn[vc] EQ 'DATETIME.STOP')) THEN BEGIN + dtest=['0'] ;initializing dtest array for EXTRACT_DATA call + EXTRACT_DATA,sds,inf,vc,dtest,ndl,infowrite ;return the dataset + IF STRLEN(rerr[0]) GT 2 THEN RETURN + IF errorfound EQ 0 THEN BEGIN + dtnel=N_ELEMENTS(dtest) + CASE 1 OF + vn[vc] EQ 'DATETIME.START': BEGIN + IF dtnel EQ dtchk THEN dtvals[0,*]=DOUBLE(dtest) $ + ELSE IF dtnel EQ 1 THEN dtvals[0,0]=DOUBLE(dtest) $ + ELSE errorfound=1 + dtfv[0]=mv_dbl[2,vc] + END + vn[vc] EQ 'DATETIME': BEGIN + dtvals[1,*]=DOUBLE(dtest) & dtfv[1]=mv_dbl[2,vc] + END + ELSE: BEGIN + IF dtnel EQ dtchk THEN dtvals[2,*]=DOUBLE(dtest) $ + ELSE IF dtnel EQ 1 THEN dtvals[2,dtchk-1]=DOUBLE(dtest) $ + ELSE errorfound=1 + dtfv[2]=mv_dbl[2,vc] + END + ENDCASE + ENDIF + gi=WHERE(DOUBLE(dtest) NE mv_dbl[2,vc],gcnt) ;edit out fill values + IF gcnt NE 0 THEN BEGIN + minth=MIN(DOUBLE(dtest[gi]),MAX=maxth) + IF minth LT mint THEN mint=minth + IF maxth GT maxt THEN maxt=maxth + ;IF vn[vc] EQ 'DATETIME.START' THEN mint=minth $ + ;ELSE IF vn[vc] EQ 'DATETIME.STOP' THEN maxt=maxth + ENDIF + valfound=valfound+1 + ENDIF +ENDFOR + +IF (errorfound EQ 0) AND (valfound GT 1) THEN BEGIN + ;Make all missing values equal to fill values + FOR i=0,2 DO BEGIN + mi=WHERE(dtvals[i,*] EQ -90000.D,mcnt) + IF mcnt NE 0 THEN dtvals[i,mi]=dtfv[i] + ENDFOR + ;Check that DATETIME.START LE DATETIME LE DATETIME.STOP + i=0L + WHILE (errorfound EQ 0) AND (i LE dtchk-1L) DO BEGIN + test1=(dtvals[0,i] NE dtfv[0]) AND (dtvals[1,i] NE dtfv[1]) + test2=(dtvals[1,i] NE dtfv[1]) AND (dtvals[2,i] NE dtfv[2]) + test3=(dtvals[0,i] NE dtfv[0]) AND (dtvals[2,i] NE dtfv[2]) + IF (test1) AND (dtvals[0,i] GT dtvals[1,i]) THEN errorfound=1 + IF (test2) AND (dtvals[1,i] GT dtvals[2,i]) THEN errorfound=2 + IF (test3) AND (dtvals[0,i] GT dtvals[2,i]) THEN errorfound=3 + i=i+1L + ENDWHILE + IF errorfound NE 0 THEN BEGIN + v0=STRTRIM(STRING(format='(d18.9)',dtvals[0,i-1L]),2) + v1=STRTRIM(STRING(format='(d18.9)',dtvals[1,i-1L]),2) + v2=STRTRIM(STRING(format='(d18.9)',dtvals[2,i-1L]),2) + CASE 1 OF + errorfound EQ 1: itxt=['DATETIME.START='+v0,'DATETIME='+v1] + errorfound EQ 2: itxt=['DATETIME='+v1,'DATETIME.STOP='+v2] + ELSE: itxt=['DATETIME.START='+v0,'DATETIME.STOP='+v2] + ENDCASE + infotxt='3 '+itxt[0]+' is greater than '+itxt[1] + INFOTXT_OUTPUT,infotxt + ENDIF +ENDIF + +valfound=0 ;Boolean to indicate whether missing VAR_VALID_MIN/MAX values have been added +;Add minimum and maximum values to Metadata as required +di=WHERE(STRUPCASE(vu) EQ 'MJD2K',dcnt) +IF dcnt NE 0 THEN BEGIN + FOR vc=0,dcnt-1 DO BEGIN + mi=WHERE(meta_arr EQ 'VAR_NAME='+vn[di[vc]]) + FOR i=0,nvchk-1 DO BEGIN + IF STRMID(meta_arr[mi[0]+mmi[i]],0,STRLEN(vchk[i])) NE vchk[i] THEN acti=mmi[i]-1L ELSE acti=mmi[i] ;depends if VAR_NOTES is present or not + res=STRSPLIT(STRTRIM(meta_arr[mi[0]+acti],2),'=',/Extract,COUNT=nres) + IF nres EQ 1 THEN BEGIN ;no value so add Min or Max value to this attribute + IF i MOD 2 EQ 0 THEN BEGIN + IF mint NE 90000.d THEN BEGIN + meta_arr[mi[0]+acti]=vchk[i]+'='+STRTRIM(STRING(format='(d18.9)',mint),2) + valfound=1 + ENDIF + mv_dbl[i,di[vc]]=mint + ENDIF ELSE BEGIN + IF maxt NE -90000.d THEN BEGIN + meta_arr[mi[0]+acti]=vchk[i]+'='+STRTRIM(STRING(format='(d18.9)',maxt),2) + valfound=1 + ENDIF + mv_dbl[i,di[vc]]=maxt + ENDELSE + IF valfound EQ 1 THEN BEGIN + infotxt=STRARR(2) + infotxt[0]='2 Missing '+vchk[i]+' value for '+meta_arr[mi[0]]+'| added based on available' + infotxt[1]=' DATETIME[.START][.STOP] values: '+meta_arr[mi[0]+acti] + INFOTXT_OUTPUT,infotxt + ENDIF + ENDIF + ENDFOR + ENDFOR +ENDIF + +IF o3[1] EQ 'AVK' THEN BEGIN ;append sentence to VAR_NOTES in the AVK attribute + di=WHERE(vavk EQ 1,dcnt) + vnc=STRARR(nvn) ;holding array for comment + IF dcnt NE 0 THEN BEGIN + FOR vc=0,dcnt-1 DO BEGIN + mi=WHERE(meta_arr EQ 'VAR_NAME='+vn[di[vc]]) + ;check to see if AVK is a matrix or vector (no need for comment if it us a vector) + add_comm=0 ;default is for no comment + xi=WHERE(attr_arr_data EQ 'VAR_DEPEND') + IF STRMID(meta_arr[mi[0]+xi[0]],0,10) NE 'VAR_DEPEND' THEN acti=xi[0]-1L ELSE acti=xi[0] ;depends if VAR_NOTES is present or not + res=STRSPLIT(meta_arr[mi[0]+acti],'=; ',/EXTRACT,COUNT=nres) + res=STRUPCASE(res) + IF nres GE 3 THEN BEGIN + FOR i=1,nres-1 DO BEGIN + ri=WHERE(res[i] EQ res,rcnt) ;i.e. repeated dependencies indicate an AVK matrix + IF rcnt GE 2 THEN add_comm=1 + ENDFOR + ENDIF + IF add_comm THEN BEGIN + IF acti NE xi[0] THEN BEGIN ;No VAR_NOTES attribute present so generate error + infotxt='3 VAR_NOTES attribute not present under '+meta_arr[mi[0]]+' so unable to append Averaging Kernel information' + INFOTXT_OUTPUT,infotxt + ENDIF ELSE BEGIN + ;find relevant VAR_NOTES attribute label + xi=WHERE(attr_arr_data EQ 'VAR_NOTES') + eqpos=STRPOS(meta_arr[mi[0]+xi[0]],'=') + vnc[di[vc]]=STRTRIM(STRMID(meta_arr[mi[0]+xi[0]],eqpos+1),2) ;existing VAR_NOTES comment, if any + ;if comment exists, check to see if it ends with a period, if not add one + IF vnc[di[vc]] NE '' THEN BEGIN + IF STRMID(vnc[di[vc]],STRLEN(vnc[di[vc]])-1,1) NE '.' THEN $ + vnc[di[vc]]=vnc[di[vc]]+'. ' ELSE vnc[di[vc]]=vnc[di[vc]]+' ' + ENDIF + ;determine the number of dimensions + ni=WHERE(ndm[di[vc],0:7] NE 0,ndim) + IF ndim GT 2 THEN vncx='for the first measurement are:' ELSE vncx='are:' + ;append comment + vnc[di[vc]]=vnc[di[vc]]+'First three values of the initial averaging kernel '+vncx + ;append meta_arr index to comment + vnc[di[vc]]=STRTRIM(mi[0]+xi[0],2)+'_'+vnc[di[vc]] + ENDELSE + ENDIF + ENDFOR + ENDIF +ENDIF + +;If required do checks on the vertical dimension plus correspondng .BOUNDARIES +;Only do checks if variable is an axis variable - so must be in ascending or descending order +bchks=['ALTITUDE','PRESSURE','ALTITUDE.GPH','DEPTH'] +sdata=ndm(vc,8) EQ 6 + +;Do check for DATA_TEMPLATE and do not do all the .BOUNDARIES checks for FTIR 001 or 002 templates +ti=WHERE(STRMID(meta_arr,0,13) EQ 'DATA_TEMPLATE',tcnt) +ftirchk=0 ;default is that it is not an FTIR-001 or 002 measurement +IF tcnt EQ 1 THEN BEGIN + res=STRSPLIT(meta_arr[ti[0]],'=',/EXTRACT,COUNT=nres) + IF nres EQ 2 THEN BEGIN + IF (STRPOS(res[1],'FTIR-0') NE -1) AND ((STRPOS(res[1],'001') NE -1) OR (STRPOS(res[1],'002') NE -1)) THEN ftirchk=1 + ENDIF +ENDIF + +FOR vc=0,N_ELEMENTS(bchks)-1 DO BEGIN + bci=WHERE(vn EQ bchks[vc],bccnt) & bbci=WHERE(vn EQ bchks[vc]+'.BOUNDARIES',bbccnt) + IF bccnt EQ 1 THEN BEGIN ;ALTITUDE or PRESSURE present so extract VAR_DEPEND and VAR_SIZE values + mi=WHERE(meta_arr EQ 'VAR_NAME='+vn[bci[0]]) + xi=WHERE(attr_arr_data EQ 'VAR_DEPEND') + IF STRMID(meta_arr[mi[0]+xi[0]],0,10) NE 'VAR_DEPEND' THEN acti=xi[0]-1L ELSE acti=xi[0] ;depends if VAR_NOTES is present or not + res=STRSPLIT(meta_arr[mi[0]+acti],'=; ',/EXTRACT,COUNT=nres) + vdvals=STRUPCASE(res[1:nres-1]) + di=WHERE(ndm[bci[0],0:7] NE 0L,ndim) + vsvals=ndm[bci[0],di] + bi=WHERE(vdvals EQ bchks[vc],bcnt) ;determine if the variable of interest is also self-referencing + nbi=WHERE(vdvals NE bchks[vc],nbcnt) ;determine if there are any other dependencies e.g. DATETIME + sdata=ndm[bci[0],8] EQ 6 ;test for string datatype + writeonce=[0,0] ;initialize array for writing information/error messages + IF (bcnt EQ 1) AND (ndim LE 2) AND (~sdata) THEN BEGIN ;it is self-referencing and there are 2 or less dependencies so do the checks + ascending=-1 ;set default value - will change to 0 for descending order and 1 for ascending order + writeonce=[1,1] ;ensure log text is only written once + EXTRACT_DATA,sds,inf,bci[0],dtest,ndl,infowrite + IF STRLEN(rerr[0]) GT 2 THEN RETURN + bvals=DOUBLE(REFORM(dtest,ndm[bci[0],di])) ;put into array order and make numeric + IF nbcnt EQ 1 THEN n_sec=ndm[bci[0],nbi[0]] ELSE n_sec=1L ;Number of datasets to be read through + IF bi[0] NE 0 THEN bvals=TRANSPOSE(bvals) ;make ALTITUDE or PRESSURE array the first index + FOR i=0L,n_sec-1L DO BEGIN ;e.g. number of DATETIME values or single loop if only one dimension + bvtest=bvals[*,i] ;set of PRESSURES or ALTITUDES to be tested + asc=-1 + IF ARRAY_EQUAL(bvtest,bvtest[SORT(bvtest)]) THEN asc=1 $ ;ascending order + ELSE IF ARRAY_EQUAL(bvtest,bvtest[REVERSE(SORT(bvtest))]) THEN asc=0 ;descending order + IF ascending EQ -1 THEN ascending=asc + IF (asc EQ -1) OR (ascending NE asc) THEN BEGIN ;altitudes/pressures not in any order + IF (ascending NE -1) AND (writeonce[1] EQ 1) THEN BEGIN ;more than one dimension and error in one or more of the sets + IF ascending EQ 1 THEN itxt='increasing' ELSE itxt='decreasing' + infotxt='3 '+bchks[vc]+' values not in '+itxt+' order for every '+vdvals[nbi[0]]+' for '+meta_arr[mi[0]] + INFOTXT_OUTPUT,infotxt + writeonce[1]=0 + ENDIF ELSE IF writeonce[0] EQ 1 THEN BEGIN + infotxt='3 '+bchks[vc]+' values not monotonically increasing or decreasing for '+meta_arr[mi[0]] + INFOTXT_OUTPUT,infotxt + writeonce[0]=0 + ENDIF + ENDIF + ENDFOR + ENDIF + + IF bbccnt EQ 1 THEN BEGIN ;*.BOUNDARIES present so check that INDEPENDENT index has VAR_SIZE=2 + mi=WHERE(meta_arr EQ 'VAR_NAME='+vn[bbci[0]]) + xi=WHERE(attr_arr_data EQ 'VAR_DEPEND') + IF STRMID(meta_arr[mi[0]+xi[0]],0,10) NE 'VAR_DEPEND' THEN acti=xi[0]-1L ELSE acti=xi[0] ;depends if VAR_NOTES is present or not + res=STRSPLIT(meta_arr[mi[0]+acti],'=; ',/EXTRACT,COUNT=nres) + vdvals=STRUPCASE(res[1:nres-1]) + di=WHERE(ndm[bbci[0],0:7] NE 0L,ndim) + vsvals=ndm[bbci[0],di] + bi=WHERE(vdvals EQ bchks[vc],bcnt) ;determine index of the variable of interest is also self-referencing + IF bcnt EQ 1 THEN n_alt=ndm[bbci[0],bi[0]] + ibi=WHERE(vdvals EQ 'INDEPENDENT',ibcnt) + IF ibcnt EQ 1 THEN BEGIN + n_ind=ndm[bbci[0],ibi[0]] + IF n_ind NE 2 THEN BEGIN + infotxt='3 INDEPENDENT index must have VAR_SIZE=2 for '+meta_arr[mi[0]] + INFOTXT_OUTPUT,infotxt + ENDIF + ENDIF + ENDIF + + oksofar=(writeonce[0] EQ 1) AND (writeonce[1] EQ 1) + IF (bbccnt EQ 1) AND (oksofar) THEN BEGIN ;Also need to check that xx.BOUNDARIES is in the same order (ascending or descending) + ;Note: Only do checks if checks on axis variable were all OK + nbi=WHERE((vdvals NE bchks[vc]) AND (vdvals NE 'INDEPENDENT'),nbcnt) ;determine if there are any other dependencies e.g. DATETIME + IF nbcnt EQ 1 THEN n_sec=ndm[bbci[0],nbi[0]] ELSE n_sec=1 + sdata=(ndm[bbci[0],8] EQ 6) ;test for string datatype + IF (bcnt EQ 1) AND ((ndim EQ 2) OR (ndim EQ 3)) AND (n_ind EQ 2) AND (~sdata) THEN BEGIN + ;it is dependent on ALTITUDE/PRESSURE and there are 2 or less other dependencies so do the checks + EXTRACT_DATA,sds,inf,bbci[0],dtest,ndl,infowrite + IF STRLEN(rerr[0]) GT 2 THEN RETURN + bvals=DOUBLE(REFORM(dtest,ndm[bbci[0],di])) ;put into array order and make numeric + + ;Determine the actual ordering of the array + ;Option 1 = 2-D e.g. ALTITUDE;INDEPENDENT or INDEPENDENT;ALTITUDE + ;Option 2 = 3-D with axis variable as first or last index and INDEPENDENT is the middle index e.g. ALT;IND;DAT or DAT;IND;ALT + ;Option 3 = 3-D with axis variable as first or last index and INDEPENDENT is the first or last index e.g. ALT;DAT;IND or IND;DAT;ALT + ;Option 4 = 3-D with INDEPENDENT variable as first or last index and ALTITUDE is the middle index e.g. IND;ALT;DAT or DAT;ALT;IND + IF (bcnt EQ 1) AND (ibcnt EQ 1) AND (ndim EQ 2) THEN BEGIN + option=1 + IF bi[0] NE 0 THEN bvals=TRANSPOSE(bvals) ;make ALTITUDE or PRESSURE array the first index + ENDIF ELSE IF (bcnt EQ 1) AND (ibcnt EQ 1) AND (ndim EQ 3) THEN BEGIN + IF ((bi[0] EQ 0) OR (bi[0] EQ 2)) AND (ibi[0] EQ 1) THEN BEGIN + option=2 + IF bi[0] EQ 2 THEN bvals=TRANSPOSE(bvals) ;make ALTITUDE or PRESSURE array the first index + ENDIF ELSE IF ((bi[0] EQ 0) OR (bi[0] EQ 2)) AND (nbi[0] EQ 1) THEN BEGIN + option=3 + IF bi[0] EQ 2 THEN bvals=TRANSPOSE(bvals) ;make ALTITUDE or PRESSURE array the first index + ENDIF ELSE BEGIN + option=4 + IF ibi[0] EQ 2 THEN bvals=TRANSPOSE(bvals) ;make INDEPENDENT array the first index (ALTITUDE or PRESSURE is the 2nd index) + ENDELSE + ENDIF ELSE option=0 + + IF option NE 0 THEN BEGIN + writeonceb=[1,1] + FOR i=0L,n_sec-1L DO BEGIN + FOR j=0,n_ind-1 DO BEGIN ;check that order of the boundary sets matches the axis variable ordering + CASE option of ;set of PRESSURES or ALTITUDES to be tested + 1: bvtest=bvals[*,j] + 2: bvtest=bvals[*,j,i] + 3: bvtest=bvals[*,i,j] + 4: bvtest=bvals[j,*,i] + ENDCASE + asc=-1 + IF ARRAY_EQUAL(bvtest,bvtest[SORT(bvtest)]) THEN asc=1 $ ;ascending order + ELSE IF ARRAY_EQUAL(bvtest,bvtest[REVERSE(SORT(bvtest))]) THEN asc=0 ;descending order + + IF (ascending NE asc) AND (writeonceb[0] EQ 1) THEN BEGIN + ;order of the boundaries dataset does not match axis dataset + IF ascending EQ 1 THEN itxt='increasing' ELSE itxt='decreasing' + infotxt='3 '+bchks[vc]+'.BOUNDARIES order does not match '+bchks[vc]+' '+itxt+' ordering for '+meta_arr[mi[0]] + INFOTXT_OUTPUT,infotxt + writeonceb[0]=0 + ENDIF + ENDFOR + ENDFOR + ;Check that the boundary ordering is also the same as the axis variable for every ALTITUDE/PRESSURE + FOR i=0L,n_sec-1L DO BEGIN + FOR j=0L,n_alt-1L DO BEGIN + CASE option OF ;set of boundary pairs to be tested (INDEPENDENT index) + 1: bvtest=bvals[j,*] + 2: bvtest=bvals[j,*,i] + 3: bvtest=bvals[j,i,*] + 4: bvtest=bvals[*,j,i] + ENDCASE + asc=-1 + IF ARRAY_EQUAL(bvtest,bvtest[SORT(bvtest)]) THEN asc=1 $ ;ascending order + ELSE IF ARRAY_EQUAL(bvtest,bvtest[REVERSE(SORT(bvtest))]) THEN asc=0 ;descending order + + IF (ascending NE asc) AND (writeonceb[1] EQ 1) AND (ftirchk EQ 0) THEN BEGIN + ;order of the boundaries values does not match axis dataset + IF ascending EQ 1 THEN itxt='increasing' ELSE itxt='decreasing' + infotxt='3 Boundary index order does not match '+bchks[vc]+' '+itxt+' ordering for '+meta_arr[mi[0]] + INFOTXT_OUTPUT,infotxt + writeonceb[1]=0 + ENDIF + ENDFOR + ENDFOR + ENDIF + ENDIF + ENDIF + ENDIF +ENDFOR + +;Test that random uncertainty values are positive +posun=['UNCERTAINTY.RANDOM.STANDARD','UNCERTAINTY.RANDOM.STANDARD.RELATIVE'] +n_p=N_ELEMENTS(posun) +vnx=vn +;vnx consists of only the last sub-name of vn +FOR i=0,nvn-1 DO $ + IF STRPOS(vnx[i],'_') NE -1 THEN vnx[i]=STRMID(vnx[i],STRPOS(vnx[i],'_',/REVERSE_SEARCH)+1) +FOR i=0,n_p-1 DO BEGIN ;number of values to check + pi=WHERE(vnx EQ posun[i],pcnt) + IF pcnt NE 0 THEN BEGIN + FOR j=0,pcnt-1 DO BEGIN ;test for negative data values + sdata=(ndm[pi[j],8] EQ 6) ;test for string datatype + IF ~sdata THEN BEGIN + EXTRACT_DATA,sds,inf,pi[j],dtest,ndl,infowrite + IF STRLEN(rerr[0]) GT 2 THEN RETURN + + CASE ndm[pi[j],8] OF ;identifies the VAR_DATA_TYPE + 0:BEGIN + fv=BYTE(mv_lng[2,pi[j]]) & urd=BYTE(dtest) & zero=0 + END + 1:BEGIN + fv=FIX(mv_lng[2,pi[j]]) & urd=FIX(dtest) & zero=0 + END + 2:BEGIN + fv=LONG(mv_lng[2,pi[j]]) & urd=LONG(dtest) & zero=0L + END + 3:BEGIN + fv=mv_lng[2,pi[j]] & urd=LONG64(dtest) & zero=0LL + END + 4:BEGIN + fv=FLOAT(mv_dbl[2,pi[j]]) & urd=FLOAT(dtest) & zero=0. + END + 5:BEGIN + fv=mv_dbl[2,pi[j]] & urd=DOUBLE(dtest) & zero=0.d + END + ENDCASE + ni=WHERE((urd LT zero) AND (urd NE fv),ncnt) + IF ncnt NE 0 THEN BEGIN + infotxt='3 '+vn[pi[j]]+' values cannot be negative' + INFOTXT_OUTPUT,infotxt + ENDIF + ENDIF + ENDFOR + ENDIF +ENDFOR + +;Test 'grouped' datasets, i.e. datasets that must both be in the file if one of them is present +;Note that there might be more than one variable in a particular group e.g. LATITUDE and LATITUDE.INSTRUMENT +grouped=[['LATITUDE','LONGITUDE'],['WIND.DIRECTION','WIND.SPEED']] +g_dim=SIZE(grouped,/DIMENSIONS) +FOR i=0,g_dim(1)-1 DO BEGIN ;number of grouped pairs + p1i=WHERE(STRPOS(STRMID(vn,0,STRLEN(grouped[0,i])),grouped[0,i]) NE -1,p1cnt) + p2i=WHERE(STRPOS(STRMID(vn,0,STRLEN(grouped[1,i])),grouped[1,i]) NE -1,p2cnt) + IF p1cnt NE 0 THEN BEGIN + FOR j=0,p1cnt-1 DO BEGIN ;test all variations + extrapart=STRMID(vn[p1i[j]],STRLEN(grouped[0,i])) & extrapart=STRTRIM(extrapart,2) + mi=WHERE(vn EQ grouped[1,i]+extrapart,mcnt) + IF mcnt EQ 0 THEN BEGIN ;matching variable name not found + infotxt='3 VAR_NAME='+grouped[1,i]+extrapart+' must also be present together with VAR_NAME='+vn[p1i[j]] + INFOTXT_OUTPUT,infotxt + ENDIF ELSE BEGIN + ;Do tests on grouped values + ;1. VAR_SIZE values must be the same. If this is the case then for wind direction and wind speed, + ;2. If wind direction EQ 0 then corresponding wind speed must be 0 + ;3. If wind direction NE 0 then corresponding wind speed can't be 0 + IF ~ARRAY_EQUAL(ndm[p1i[j],0:7],ndm[mi[0],0:7]) THEN BEGIN + infotxt='3 VAR_SIZE values for VAR_NAME='+vn[p1i[j]]+' must be the same as VAR_SIZE values for VAR_NAME='+vn[mi[0]] + INFOTXT_OUTPUT,infotxt + ENDIF ELSE BEGIN ;arrays match up so do wind direction/speed checks + sdata=(ndm[p1i[j],8] EQ 6) OR (ndm[mi[0],8] EQ 6) ;test for string datatype + IF (grouped[0,i] EQ 'WIND.DIRECTION') AND (~sdata) THEN BEGIN + EXTRACT_DATA,sds,inf,p1i[j],dtest,ndl,infowrite + IF STRLEN(rerr[0]) GT 2 THEN RETURN + wdd=DOUBLE(dtest) + EXTRACT_DATA,sds,inf,mi[0],dtest,ndl,infowrite + IF STRLEN(rerr[0]) GT 2 THEN RETURN + wsd=DOUBLE(dtest) + ni=WHERE(wdd EQ 0.d,ncnt) + IF ncnt NE 0 THEN BEGIN + si=WHERE(wsd[ni] GT 0.d,scnt) + IF scnt NE 0 THEN BEGIN ;Wind direction = 0.0 but wind speed NE 0.0 + infotxt='3 '+vn[mi[0]]+' values must be 0.0 for corresponding '+vn[p1i[j]]+' 0.0 values' + INFOTXT_OUTPUT,infotxt + ENDIF + ENDIF + ni=WHERE((wdd GT 0.d) AND (wdd LE 360.d),ncnt) + IF ncnt NE 0 THEN BEGIN + si=WHERE(wsd[ni] EQ 0.d,scnt) + IF scnt NE 0 THEN BEGIN ;Wind direction NE 0.0 but wind speed = 0.0 + infotxt='3 '+vn[p1i[j]]+' values must be 0.0 for corresponding '+vn[mi[0]]+' 0.0 values' + INFOTXT_OUTPUT,infotxt + ENDIF + ENDIF + ENDIF + ENDELSE + ENDELSE + ENDFOR + ENDIF + IF p2cnt NE 0 THEN BEGIN ;now do the reverse check + FOR j=0,p2cnt-1 DO BEGIN ;test all variations + extrapart=STRMID(vn[p2i[j]],STRLEN(grouped[1,i])) & extrapart=STRTRIM(extrapart,2) + mi=WHERE(vn EQ grouped[0,i]+extrapart,mcnt) + IF mcnt EQ 0 THEN BEGIN ;matching variable name not found + infotxt='3 VAR_NAME='+grouped[0,i]+extrapart+' must also be present together with VAR_NAME='+vn[p2i[j]] + INFOTXT_OUTPUT,infotxt + ENDIF + ENDFOR + ENDIF +ENDFOR + +FOR vc=0,nvn-1 DO BEGIN + dtest=['0'] ;initializing dtest array for EXTRACT_DATA call + infowrite=1 ;write information message in EXTRACT_DATA routine if required + ;Extract the dataset + EXTRACT_DATA,sds,inf,vc,dtest,ndl,infowrite + IF STRLEN(rerr[0]) GT 2 THEN RETURN + ;perform checks on the attribute and data values + IF ndm[vc,8] EQ 6 THEN CHECK_STRING_DATATYPE,vc,dtest $ ;Test for String Datatype + ELSE BEGIN + CHECK_MIN_MAX_FILL,vc,dtest,ndl ;Test for Numeric Datatype + IF STRLEN(rerr[0]) GT 2 THEN RETURN + ENDELSE + + ;if AVK dataset extracted then add comment to VAR_NOTES if required + IF o3[1] EQ 'AVK' THEN BEGIN + IF (vnc[vc] NE '') AND (ndm[vc,0] GE 3) THEN BEGIN + res=STRSPLIT(vnc[vc],'_',/Extract) & mi=FIX(res[0]) + meta_arr[mi]='VAR_NOTES='+res[1] + FOR i=0,2 DO meta_arr[mi]=meta_arr[mi]+' '+STRTRIM(dtest[i],2) + ENDIF + ENDIF + + ;Copy data to the data structure with correct dimensions and data type + arrchk=SIZE(dtest) ;put it into array form if it is a scalar + IF arrchk[0] EQ 0 THEN dtest=[dtest] + gi=WHERE(ndm[vc,0:7] NE 0) + vs=TRANSPOSE(ndm[vc,gi]) + dtest=REFORM(dtest,vs,/Overwrite) + ds[vc].data=PTR_NEW(dtest) +ENDFOR + +END ;Procedure Read_Data + + + +PRO find_hdf_filename, hdffilename +;Procedure to do the following: +;1. Compare the DATA_START_DATE and DATA_STOP_DATE with the first and last entries under +; DATETIME. If necessary create/change DATA_START_DATE and DATA_STOP_DATE to match the +; DATETIME entries, and put in ISO8601 format. +;2. Compare filename created by the program from the DATASET_ATTRIBUTES with that under the +; FILE_NAME entry (if present). If necessary create/change the FILE_NAME entry to match the +; created filename +;3. Create/change the FILE_GENERATION_DATE and ensure it is in ISO8601 format. +; ---------- +;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org +; +; History: +; 20050802: Original IDLCR8HDF routine - Version 1.0 +; 20050909: Remove requirement for a 'v' to be at the start of the DATA_FILE_VERSION +; (e.g. v1.0 or 1.0 acceptable); Change the format that DATA_START_DATE and +; FILE_GENERATION_DATE values are saved in the HDF file from MJD2000 to +; ISO8601 - Version 1.1 +; 20051107: Move VIS_SCALE_MIN/MAX checks, for correct format of DATETIME values, to the +; CHECK_MIN_MAX_FILL routine - Version 1.11 +; 20061012: Move the FILE_GENERATION_DATE checks to the main loop which determines the file +; name to facilitate the future option of including the file generation date in the +; file name (this option is not currently implemented by the AVDC); Common variable +; definition WIDGET_WIN added - Version 2.0 +; 20080302: Do search for either DATA_LEVEL or DATA_TYPE, depending on whether an AVDC style +; TAV file or original Envisat table.dat file has been read in; Common variable +; definition TABLEDATA added (tab_type used to differentiate between the databases) +; - Version 3.0 +; 20090610: Allow for the Global Variable DATA_STOP_DATE and calculate the value if required +; - Version 3.07 +; 20100205: Add RETURN command after all STOP_WITH_ERROR calls, which allows program to return to the +; calling program if the reterr argument is included in the idlcr8hdf call - Version 3.09 +; 20101120: Account for new GEOMS changes to filename composition - Version 4.0 +; 20151104: Check that ISO8601 format time is all uppercase - Version 4.0b33 +; 20151109: Check for seconds value of 60 in ISO8601 datetime value - Version 4.0b34 +; 20151215: Fix bug if DATE_TIME_START or DATE_TIME_STOP value is missing - Version 4.0b36 +; 20161130: Allow the program to continue running if an ISO8601 error is found (previously +; called STOP_WITH_ERROR) - Version 4.0b40 +; 20171130: Remove ds from STOP_WITH_ERROR parameters; Change some error and information text +; statements - Version 4.0b44 +; 20180314: Fix bug when checking DATA_START|STOP_DATE values. Criteria for difference with +; MJD2K values change to GT 0.99d/86400.d instead of GE 1.0d/86400.d - +; Version 4.0b46 +; 20190807: Allow .nc and .nc4 filename extensions when doing QA on a file that has been read +; into session memory using the HDF5 routines (idlcr8ascii set up to use H5 routines +; to read netCDF4 files as well as HDF5) - Version 4.0b51 +; 20190824: Fix bug in 4.0b51 that caused the file name to be written to the DATA_FILE_VERSION +; attribute - Version 4.0b52 +; 20231218: Always recalculate FILE_GENERATION_DATE if creating a GEOMS file so that it aligns +; with the FILE_META_VERSION - Version 4.0b61 +; 20241029: Add extra resolution when checking for discrepancies between the first and last +; DATETIME[.START][.STOP] values and DATA_[START|STOP]_DATE (was 0.99d/86400.d and +; now 0.9999d/86400.d). This should be equivalent to the NDACC QA criteria, which +; checks against a floored and ceiling second value - Version 4.0b65 +; +; Inputs: meta_arr - a string array containing the Global and Variable Attributes +; hfdfilename - a string holding the extension of the eventual HDF filename, either +; '.hdf' for HDF4 or '.h5' for HDF5 or '.nc' for netCDF3/4 +; +; Outputs: meta_arr - DATA_START_DATE, FILE_GENERATION_DATE and FILE_NAME values will be +; (re)written to the array based on values computed by the routine +; hdffilename - a string holding the full HDF file name determined by the routine +; +; Called by: IDLCR8HDF +; +; Subroutines Called: STOP_WITH_ERROR (if error state detected); INFOTXT_OUTPUT +; Possible Conditions for STOP_WITH_ERROR call (plus [line number] where called): +; 1. An incorrect number of sub-values found in meta_arr for DATA_DISCIPLINE, +; DATA_SOURCE, DATA_LOCATION, or DATA_FILE_VERSION [3129] +; 2. Error encountered when converting ISO8601 datetime values to MJD2000 format [3138,3179] +; 3. Type conversion error encountered when a datetime value is expected to be in +; MJD2000 format, but is not numeric [3216] +; +; Information Conditions (when the program is able to make changes): +; 1. DATA_START_DATE or DATA_STOP_DATE changed to match relevant DATETIME entries [3167] +; 2. Filename determined from DATASETS attributes [3208] + +COMMON TABLEDATA +COMMON METADATA +COMMON DATA +COMMON WIDGET_WIN + +;Possible error messages for this procedure +ON_IOERROR,TypeConversionError +errtxt=STRARR(4) & lu=-1L +proname='Find_HDF_Filename procedure: ' +errtxt[0]='Number of sub-values under DATA_' +errtxt[1]=' should be ' +errtxt[2]=' ISO8601 format not valid: ' +errtxt[3]='Type conversion error. MJD2K entry is not valid: ' + +fni=WHERE(attr_arr_glob EQ 'FILE_NAME') +res=STRSPLIT(meta_arr[fni[0]],' =',/Extract) +IF N_ELEMENTS(res) EQ 1 THEN res=[res,'-1'] +fn_val=res[1] + +IF (qa_yes) AND (hdffilename EQ '.h5') AND (fn_val NE '-1') THEN BEGIN + ;netCDF4 files read in using H5 routines so .h5 extension is assumed. Allow files to + ;also have .nc or .nc4 extensions for files being QA'd + hext=['.h5','.nc','.nc4'] + fn_ext=STRMID(fn_val,STRPOS(fn_val,'.',/REVERSE_SEARCH)) + ei=WHERE(fn_ext EQ hext,ecnt) + IF ecnt NE 0 THEN hdffilename=hext[ei[0]] +ENDIF + +fhold='' & iso='' & mjd2000=0.d +dtmjd=0.d & dtiso='' & itxt=' ' +dt=['DISCIPLINE','SOURCE','LOCATION','START_DATE','STOP_DATE','FILE_GENERATION_DATE','FILE_VERSION'] +valid=0 ;test for Type conversion errors +FOR i=0,N_ELEMENTS(dt)-1 DO BEGIN + IF i EQ 0 THEN nei=4 ELSE nei=2 + IF i EQ 5 THEN ai=WHERE(attr_arr_glob EQ dt[i]) $ + ELSE ai=WHERE(attr_arr_glob EQ 'DATA_'+dt[i]) + + res=STRSPLIT(meta_arr[ai[0]],' =;',/Extract,COUNT=cres) + IF (cres NE nei) AND ((i LE 2) OR (i GE 6)) THEN BEGIN + ;infotxt='3 '+errtxt[0]+dt[i]+errtxt[1]+nes + ;INFOTXT_OUTPUT,infotxt + STOP_WITH_ERROR,o3[3]+proname+meta_arr[ai[0]]+': ',errtxt[0]+dt[i]+errtxt[1]+STRTRIM(nei-1,2),lu + RETURN + ENDIF + + IF i EQ 6 THEN BEGIN ;Do DATA_FILE_VERSION checks + dfvv=res[1] + GEOMS_RULE_CHANGES,4,dfvv + res[1]=dfvv & meta_arr[ai[0]]='DATA_FILE_VERSION='+dfvv + ENDIF + + IF (i EQ 3) OR (i EQ 4) THEN BEGIN ;calculate the ISO or MJD2000 value as required + ;Find lowest or highest DATETIME values from Variable Attributes + IF i EQ 3 THEN BEGIN + di=WHERE(vn EQ 'DATETIME.START',dcnt) + itxt= ' first ' & etxt='DATETIME.START ' + ENDIF ELSE BEGIN + di=WHERE(vn EQ 'DATETIME.STOP',dcnt) + itxt= ' last ' & etxt='DATETIME.STOP ' + ENDELSE + IF dcnt NE 0 THEN BEGIN + darr=*ds[di[0]].data ;extract the DATETIME values + ;remove any fill values + fi=WHERE(darr NE mv_dbl[2,di[0]],dcnt) + ENDIF + IF dcnt EQ 0 THEN BEGIN + di=WHERE(vn EQ 'DATETIME',dcnt) & etxt='DATETIME ' + IF dcnt NE 0 THEN BEGIN + darr=*ds[di[0]].data ;extract the DATETIME values + ;remove any fill values + fi=WHERE(darr NE mv_dbl[2,di[0]],dcnt) + ENDIF + ENDIF + IF dcnt NE 0 THEN BEGIN + darr=darr[fi] + IF N_ELEMENTS(darr) EQ 1 THEN itxt=' ' + IF i EQ 3 THEN dtmjd=MIN(darr) ELSE dtmjd=MAX(darr) + dtiso=JDF_2_DATETIME(dtmjd,/M,/S) ;returns datetime in ISO8601 format + dtisochk=0B ;boolean to check for 60 as seconds value + ;Extract DATA_START/STOP_DATE from the Global Attributes, if present + IF N_ELEMENTS(res) EQ nei THEN BEGIN ;a date/time value is present + IF STRPOS(STRUPCASE(res[1]),'Z') NE -1 THEN BEGIN + IF (STRPOS(res[1],'Z') EQ -1) OR (STRPOS(res[1],'T') EQ -1) THEN BEGIN ;ISO8601 string must be in upper case + IF qa_yes then qtxt=' must be ' ELSE qtxt=' made ' + infotxt='2 '+res[0]+' entry'+qtxt+'uppercase for ISO8601 compliance' + INFOTXT_OUTPUT,infotxt + ENDIF + res[1]=STRUPCASE(res[1]) + mjd2000=JULIAN_DATE(res[1],/I,/M) ;return ISO8601 time in MJD2000 format + IF mjd2000 EQ -99999.d THEN BEGIN ;conversion error + infotxt='3 '+res[0]+errtxt[2]+res[1] + INFOTXT_OUTPUT,infotxt + ;STOP_WITH_ERROR,o3[3]+proname,res[0]+errtxt[2]+res[1],lu,ds & RETURN + ENDIF + dtisochk=STRMID(res[1],13,2) EQ '60' ;existing time in ISO8601 format + ENDIF ELSE BEGIN + mjd2000=DOUBLE(res[1]) + iso=JDF_2_DATETIME(mjd2000,/M,/S) ;return MJD2000 time in ISO8601 format + res[1]=iso + IF qa_yes THEN qtxt=' is not in ' ELSE qtxt= ' converted to ' + infotxt='2 '+meta_arr[ai[0]]+qtxt+'ISO8601 format' + INFOTXT_OUTPUT,infotxt + ENDELSE + res1=res[1] + ENDIF ELSE BEGIN + mjd2000=-99999.0D & res1='-99999.0' + ENDELSE + ;Check that DATA_START_DATE matches the first value under DATETIME.START or DATETIME + ;and DATA_STOP_DATE matches the last value under DATETIME.STOP or DATETIME (if present) + IF (ABS(mjd2000-dtmjd) GT 0.9999D/86400.D) OR ((dtisochk) AND (dtiso NE res1)) THEN BEGIN + IF N_ELEMENTS(res) EQ nei THEN BEGIN + IF qa_yes THEN qtxt=' must match' ELSE qtxt=' changed to match' + infotxt='2 Date in DATA_'+dt[i]+qtxt+itxt+etxt+'entry: '+res[1]+' -> '+dtiso + ENDIF ELSE BEGIN + IF qa_yes THEN qtxt='should be '+dtiso ELSE qtxt=dtiso+' added' + infotxt='2 Missing DATA_'+dt[i]+' value '+qtxt+' based on'+itxt+etxt+'entry' + ENDELSE + INFOTXT_OUTPUT, infotxt + res=[res[0],dtiso] ;new ISO time for filename + ENDIF + meta_arr[ai[0]]=res[0]+'='+res[1] ;rewrite (correct) value in meta_arr in ISO8601 format + ENDIF ELSE IF N_ELEMENTS(res) EQ 1 THEN res=[res,'[MISSING]'] + ENDIF + IF i EQ 5 THEN BEGIN + ;check format of FILE_GENERATION_DATE value and create/change if necessary + IF ~qa_yes THEN BEGIN + ;Not doing QA so calculate new FILE_GENERATION_DATE of file creation so it aligns with FILE_META_VERSION (added 20231218) + mjd2000=SYSTIME(/Julian,/UTC)-2451544.5D + mjds=JDF_2_DATETIME(mjd2000,/M,/S) ;returns datetime in ISO8601 format + meta_arr[ai[0]]=res[0]+'='+mjds ;write date in ISO8601 format + infotxt='0 '+res[0]+' updated with current system time' + INFOTXT_OUTPUT,infotxt + IF cres EQ 1 THEN res=[res,mjds] ELSE res[1]=mjds + ENDIF ELSE BEGIN + IF N_ELEMENTS(res) EQ 1 THEN res=[res,'-1'] + IF STRPOS(STRUPCASE(res[1]),'Z') NE -1 THEN BEGIN + IF (STRPOS(res[1],'Z') EQ -1) OR (STRPOS(res[1],'T') EQ -1) THEN BEGIN ;ISO8601 string must be in upper case + IF qa_yes THEN qtxt=' must be ' ELSE qtxt=' made ' + infotxt='2 '+res[0]+' entry'+qtxt+'uppercase for ISO8601 compliance' + INFOTXT_OUTPUT,infotxt + ENDIF + mjd2000=JULIAN_DATE(res[1],/I,/M) ;return time in MJD2000 format + IF mjd2000 EQ -99999.d THEN res=[res[0],'-2'] $ ;conversion error + ELSE IF STRMID(res[1],13,2) EQ '60' THEN res=[res[0],'-3'] $ + ELSE meta_arr[ai[0]]=res[0]+'='+STRTRIM(STRUPCASE(res[1]),2) ;rewrite date in ISO8601 format + ENDIF + IF STRPOS(STRUPCASE(res[1]),'Z') EQ -1 THEN BEGIN + ;FILE_GENERATION_DATE not present or not in ISO8601 format so recalculate + mjd2000=SYSTIME(/Julian,/UTC)-2451544.5D + mjds=JDF_2_DATETIME(mjd2000,/M,/S) ;returns datetime in ISO8601 format + meta_arr[ai[0]]=res[0]+'='+mjds ;write date in ISO8601 format + IF res[1] EQ '-1' THEN itxt='not present' $ + ELSE IF res[1] EQ '-3' THEN itxt='must have seconds value between 0 and 59' $ + ELSE itxt='not in ISO8601 format' + infotxt='2 '+res[0]+' value '+itxt+'|. Recalculated using the system time' + INFOTXT_OUTPUT,infotxt + res[1]=mjds + ENDIF + ENDELSE + ;Test to see if DATA_STOP_DATE is greater than FILE_GENERATION_DATE + IF dtmjd GT mjd2000 THEN BEGIN + infotxt='3 Calculated DATA_STOP_DATE='+dtiso+' is later than '+meta_arr[ai[0]] + INFOTXT_OUTPUT,infotxt + ENDIF + ENDIF + IF i NE 5 THEN BEGIN + ;i.e. don't include file generation date in the filename + IF fhold EQ '' THEN fhold=fhold+STRLOWCASE(res[nei-1]) $ + ELSE fhold=fhold+'_'+STRLOWCASE(res[nei-1]) + ENDIF +ENDFOR +hdffilename=fhold+hdffilename + +;check to see if the resulting filename is the same as that under FILE_NAME +IF hdffilename NE fn_val THEN BEGIN + IF fn_val NE '-1' THEN BEGIN + infotxt=STRARR(3) + infotxt[0]='2 FILE_NAME entry under File Attributes does not match the filename' + infotxt[0]=infotxt[0]+' determined from the Global Attributes' + infotxt[1]=' '+fn_val+' -> '+hdffilename+'|' + infotxt[2]=' Filename determined from Global Attributes used' + ENDIF ELSE BEGIN + IF qa_yes THEN qtxt='should be '+hdffilename ELSE qtxt=hdffilename+' added' + infotxt=STRARR(2) + infotxt[0]='2 Missing FILE_NAME value '+qtxt + infotxt[1]=' based on Global Attribute values' + ENDELSE + INFOTXT_OUTPUT, infotxt + meta_arr[fni[0]]='FILE_NAME='+hdffilename ;change FILE_NAME entry +ENDIF +valid=1 ;Type conversions performed OK + +TypeConversionError: +IF valid EQ 0 THEN BEGIN + infotxt='3 '+errtxt[3]+res[1] + INFOTXT_OUTPUT, infotxt + ;STOP_WITH_ERROR,o3[3]+proname+res[0]+': ',errtxt[3]+res[1],lu & RETURN +ENDIF + +END ;Procedure Find_HDF_Filename + + + +PRO makean, hdf_fn, obj_id, obj_type, label, value +;Procedure to read the contents of a file given as an attribute value and write +;it directly to the HDF Scientific Dataset or to the annotation file description +;section (depending on its size). Currently set up to always write the contents +;of the file to HDF_SD as the text file size limit is also 4096 bytes. +; ---------- +;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org +; +; History: +; 20050802: Original IDLCR8HDF routine - Version 1.0 +; +; Inputs: hdf_fn - The file handle identifier for the HDF file +; obj_id - The Scientific Dataset (SD) identifier of the HDF file or a newly +; created dataset +; obj_type - a string identifying the type of annotation (set to 'FILE') +; label - a string containing the Attribute label from the metadata +; value - a string containing the Attribute value from the metadata +; +; Output: Nil +; +; Called by: AVDC_HDF_WRITE +; +; Subroutines Called: None + +nchar=4096 ;strlen maximum for writing directly to SD (instead of AN) + +;Extract name of file +pos1=STRPOS(value,'"') +pos2=STRPOS(value,'"',/REVERSE_SEARCH) +data_notes_file=STRMID(value,pos1+1,pos2-pos1-1) +OPENR,lu,data_notes_file,/GET_LUN + +;read each line and count number of characters in file +dum='' & text_in_file='' +i=0 +WHILE NOT EOF(lu) DO BEGIN + READF,lu,dum + text_in_file=text_in_file+dum+STRING(10B) ;+STRING(13B) + i=i+1 +ENDWHILE +FREE_LUN,lu +n1=STRLEN(text_in_file) + +;The text-buffer contains n1 characters which will be written either +;to the annotation file description section or directly to the HDF file as +;the value of the current attribute +IF n1 LE nchar THEN HDF_SD_ATTRSET,obj_id,label,text_in_file,n1,/DFNT_CHAR $ +ELSE BEGIN + ;open the HDF output file for AN write operations + ;Initialize the HDF AN interface for the specified file + an_id=HDF_AN_START(hdf_fn) + + IF obj_type EQ 'FILE' THEN BEGIN + ;Get id for the file label annotation + an_labl_id=HDF_AN_CREATEF(an_id,2) + ;Get id for the file description annotation + an_desc_id=HDF_AN_CREATEF(an_id,3) + ENDIF ELSE BEGIN + ;Get id for the data object label annotation + an_labl_id=HDF_AN_CREATE(an_id,DFTAG_NDG,HDF_SD_IDTOREF(obj_id),0) + ;Get id for the data object description annoatation + an_desc_id=HDF_AN_CREATE(an_id,DFTAG_NDG,HDF_SD_IDTOREF(obj_id),1) + ENDELSE + + ;Write the label annotation + status=HDF_AN_WRITEANN(an_labl_id,label) + ;Terminate access to the label annotation + HDF_AN_ENDACCESS,an_labl_id + + ;Write the description annotation + status=HDF_AN_WRITEANN(an_desc_id,text_in_file) + ;Terminate access to the description annotation + HDF_AN_ENDACCESS,an_desc_id + + ;Terminate access to the AN interface + HDF_AN_END,an_id +ENDELSE + +END ;Procedure MakeAN + + + +PRO avdc_hdf5_write, hdffilename, natts, av, geoms_output +;IDL subroutine to write AVDC-type global attributes and datasets to the Root +;Group in an HDF5 file. +;########################################################################## +;Note: In versions of IDL earlier than 6.2, this procedure may not compile +;and the HDF5 write option will not be available. +;########################################################################## +; ---------- +;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org +; +; History: +; 20061012: Introduced to IDLCR8HDF - Version 2.0 +; 20080302: Removed portion of the code which created the DIMENSIONLIST attribute +; containing the object reference pointers to the variables referred to +; in the VAR_DEPEND values - Version 3.0 +; 20150127: For string datasets, change the VAR_FILL_VALUE to an empty string that +; is the same length as the individual strings - Version 4.0b25 +; 20150217: Change rule from 4.0b25 so the VAR_FILL_VALUE is an empty string; For +; string datasets do a STRTRIM of the dataset when writing to the file, as +; the first dataset entry may have had whitespace added so that the +; maximum dataset length is correctly determined - Version 4.0b26 +; 20181221: Allow compression and shuffle options when creating HDF5 files +; - Version 4.0b49 +; +; Inputs: hdffilename - a string holding the name of the HDF file being created +; and written to +; natts - an integer giving the number of Variable Attributes in each +; dataset (i.e. the number of elements in attr_arr_data) +; av - a string array of size (nvn,natts+7) containing the attribute and +; NCSA values for each dataset +; ds - structure containing the data +; meta_arr - a string array containing the Global and Variable Attributes +; vn - a string array containing the VAR_NAME values +; ndm - a long array describing the dimensions of each dataset and the +; VAR_DATA_TYPE +; +; Outputs: An HDF5 file with the name hdffilename +; +; Called by: AVDC_HDF_WRITE +; +; Subroutines Called: None + +COMMON METADATA +COMMON DATA + +;Check for compression setting +comp_fac=0 +IF STRLEN(geoms_output) EQ 4 THEN BEGIN + val_chk=STRMID(geoms_output,3,1) + IF IS_A_NUMBER_HDF(val_chk) THEN comp_fac=FIX(val_chk) +ENDIF + +;Create an HDF5 file +hdf_file_id=H5F_CREATE(hdffilename) +H5F_CLOSE, hdf_file_id + +;open the HDF5 file for writing. +hdf_file_id=H5F_OPEN(hdffilename,/WRITE) +;The H5G_OPEN function opens an existing group within an HDF5 file +sd_id=H5G_OPEN(hdf_file_id,'/') + +;Write the Global Attributes to file +;Section 4.1 (Bojkov et al., 2002): Originator attributes +;Section 4.2 (Bojkov et al., 2002): Dataset attributes +;Section 4.3 (Bojkov et al., 2002): File attributes +FOR i=0,N_ELEMENTS(attr_arr_glob)-1 DO BEGIN + ;separate out the Meta Attribute entry + eqpos=STRPOS(meta_arr[i],'=') + IF eqpos EQ STRLEN(meta_arr[i])-1 THEN attribute_value=' ' $ + ELSE attribute_value=STRMID(meta_arr[i],eqpos+1) + ;get attribute type and space, needed to create the attribute + atype_id=H5T_IDL_CREATE(attribute_value) + aspace_id=H5S_CREATE_SCALAR() + ;create attribute in the output file + aset_id=H5A_CREATE(sd_id,attr_arr_glob[i],atype_id,aspace_id) + ;write attribute to dataset + H5A_WRITE,aset_id,attribute_value + ;close all open identifiers + H5A_CLOSE,aset_id + H5S_CLOSE,aspace_id + H5T_CLOSE,atype_id +ENDFOR + +;Write the Datasets (SDS) to file +;Section 5.1 (Bojkov et al, 2002): Variable description attributes. +al=STRARR(natts) +al[0:natts-1]=attr_arr_data +FOR i=0,nvn-1 DO BEGIN + CASE ndm[i,8] OF ;identifies the VAR_DATA_TYPE + 0:BEGIN + valid_range=[BYTE(mv_lng[0,i]),BYTE(mv_lng[1,i])] + fill_value=BYTE(mv_lng[2,i]) + END + 1:BEGIN + valid_range=[FIX(mv_lng[0,i]),FIX(mv_lng[1,i])] + fill_value=FIX(mv_lng[2,i]) + END + 2:BEGIN + valid_range=[LONG(mv_lng[0,i]),LONG(mv_lng[1,i])] + fill_value=LONG(mv_lng[2,i]) + END + 3:BEGIN + valid_range=[mv_lng[0,i],mv_lng[1,i]] + fill_value=mv_lng[2,i] + END + 4:BEGIN + valid_range=[FLOAT(mv_dbl[0,i]),FLOAT(mv_dbl[1,i])] + fill_value=FLOAT(mv_dbl[2,i]) + END + 5:BEGIN + valid_range=[mv_dbl[0,i],mv_dbl[1,i]] + fill_value=mv_dbl[2,i] + END + 6:BEGIN + valid_range=[' ',' '] + fill_value=' ' ;STRING(format='(A-'+STRTRIM(mv_str[i],2)+')','') ;bytarr(mv_str[i]) + END + ENDCASE + + ;Make up data array for writing to HDF5 file + data=*ds[i].data + ;may have lost array VAR_SIZE information if the last value is '1' e.g. 41,1 + gi=WHERE(ndm[i,0:7] NE 0L) & vs=TRANSPOSE(ndm[i,gi]) ;transpose to get array values in a vector + ;get dataset type and space, needed to create the dataset + dtype_id=H5T_IDL_CREATE(data) + dspace_id=H5S_CREATE_SIMPLE(vs) + ;create dataset in the output file + IF comp_fac EQ 0 THEN dset_id=H5D_CREATE(sd_id,vn[i],dtype_id,dspace_id) $ + ELSE BEGIN + ;Perform compression and shuffling of the dataset + dset_id=H5D_CREATE(sd_id,vn[i],dtype_id,dspace_id,CHUNK_DIMENSIONS=vs,GZIP=comp_fac,/SHUFFLE) + ENDELSE + ;write data to dataset - for string datasets remove unwanted white space + IF ndm[i,8] EQ 6 THEN H5D_WRITE,dset_id,STRTRIM(data,2) ELSE H5D_WRITE,dset_id,data + + ;write out variable attributes + ninc=0 & j=0 + mi=WHERE(meta_arr EQ attr_arr_data[0]+'='+vn[i]) + WHILE j LE natts-1 DO BEGIN + ma=mi[0]+ninc + ;Extract Attribute Label + eqpos=STRPOS(meta_arr[ma],'=') + res=STRMID(meta_arr[ma],0,eqpos) + WHILE res[0] NE al[j] DO j=j+1 ;in case of missing optional variable attributes + CASE 1 OF + ;attr_arr_data[j] EQ 'VAR_SIZE': athold=vs + attr_arr_data[j] EQ 'VAR_VALID_MIN': athold=valid_range[0] + attr_arr_data[j] EQ 'VAR_VALID_MAX': athold=valid_range[1] + attr_arr_data[j] EQ 'VAR_FILL_VALUE': athold=fill_value + ELSE: athold=av[i,j] + ENDCASE + ;get attribute type and space, needed to create the attribute + atype_id=H5T_IDL_CREATE(athold) + IF N_ELEMENTS(athold) EQ 1 THEN aspace_id=H5S_CREATE_SCALAR() $ + ELSE aspace_id=H5S_CREATE_SIMPLE(N_ELEMENTS(athold)) + ;create attribute in the output file + aset_id=H5A_CREATE(dset_id,al[j],atype_id,aspace_id) + ;write attribute to dataset + H5A_WRITE,aset_id,athold + ;close all open attribute identifiers + H5A_CLOSE,aset_id + H5S_CLOSE,aspace_id + H5T_CLOSE,atype_id + ninc=ninc+1 & j=j+1 + ENDWHILE + ;close all open dataset identifiers + H5D_CLOSE,dset_id + H5S_CLOSE,dspace_id + H5T_CLOSE,dtype_id +ENDFOR + +;The H5G_CLOSE procedure closes the specified group and releases resources used by it. +H5G_CLOSE,sd_id +;The H5F_CLOSE procedure closes the HDF file associated with the given file handle. +H5F_CLOSE,hdf_file_id + +END ;Procedure AVDC_HDF5_Write + + + +PRO avdc_nc_write, hdffilename, natts, av, var_depend, var_size +;IDL subroutine to write AVDC-type global attributes and datasets to a netCDF file. +; ---------- +;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org +; +; History: +; 20111120: Introduced to IDLCR8HDF - Version 4.0 +; 20130116: Account for string datasets which contain only empty string values (make +; string length = 1) - Version 4.0b15 +; 20150127: For string datasets, change the VAR_FILL_VALUE to an empty string that +; is the same length as the individual strings - Version 4.0b25 +; 20150217: Change rule from 4.0b25 so the VAR_FILL_VALUE is an empty string +; - Version 4.0b26 +; 20161230: Assign a dimension ID to STRING datasets with VAR_DEPEND=CONSTANT so that +; the string length is correctly accounted for (previously only wrote the +; first character of the string) - Version 4.0b41 +; +; Inputs: hdffilename - a string holding the name of the netCDF file being created +; and written to +; natts - an integer giving the number of Variable Attributes in each +; dataset (i.e. the number of elements in attr_arr_data) +; av - a string array of size (nvn,natts+7) containing the attribute and +; NCSA values for each dataset +; var_depend - a string array of size (nvn) holding the VAR_DEPEND attribute +; values, used to assign DIM information +; var_size - a string array of size (nvn) holding the VAR_SIZE attribute +; values, used to assign DIM information +; ds - structure containing the data +; meta_arr - a string array containing the Global and Variable Attributes +; vn - a string array containing the VAR_NAME values +; ndm - a long array describing the dimensions of each dataset and the +; VAR_DATA_TYPE +; +; Outputs: A netCDF file with the name hdffilename +; +; Called by: AVDC_HDF_WRITE +; +; Subroutines Called: None + +COMMON METADATA +COMMON DATA + +;Create a netCDF file +df_file_id=NCDF_CREATE(hdffilename,/CLOBBER) + +;Write the Global Attributes to file +;Section 4.1 (Bojkov et al., 2002): Originator attributes +;Section 4.2 (Bojkov et al., 2002): Dataset attributes +;Section 4.3 (Bojkov et al., 2002): File attributes +FOR i=0,N_ELEMENTS(attr_arr_glob)-1 DO BEGIN + ;separate out the Meta Attribute entry + eqpos=STRPOS(meta_arr[i],'=') + IF eqpos EQ STRLEN(meta_arr[i])-1 THEN attribute_value=' ' $ + ELSE attribute_value=STRMID(meta_arr[i],eqpos+1) + ;get attribute type and space, needed to create the attribute + NCDF_ATTPUT,df_file_id,/GLOBAL,attr_arr_glob[i],attribute_value +ENDFOR + +;Write the Datasets (SDS) to file +;Section 5.1 (Bojkov et al, 2002): Variable description attributes. +al=STRARR(natts) +al[0:natts-1]=attr_arr_data +vdh=['CONSTANT','INDEPENDENT'] +dim_id_list=LONARR(2)-999L +constfound=0 + +FOR i=0,nvn-1 DO BEGIN + + ;Make up data array for writing to netCDF file + data=*ds[i].data + ;may have lost array VAR_SIZE information if the last value is '1' e.g. 41,1 + gi=WHERE(ndm[i,0:7] NE 0L) & vs=TRANSPOSE(ndm[i,gi]) ;transpose to get array values in a vector + + ;Determine Dimension IDs for the Variable + vdl=STRSPLIT(var_depend[i],' ;',/EXTRACT,COUNT=vdcount) + vsl=STRSPLIT(var_size[i],' ;',/EXTRACT) & vsl=LONG(vsl) + IF ndm[i,8] EQ 6 THEN BEGIN ;need to include string length dimension variable + vdcount++ + IF MAX(STRLEN(data)) EQ 0 THEN mxstrlen=1 ELSE mxstrlen=MAX(STRLEN(data)) + vdl=['STRLEN',vdl] & vsl=[mxstrlen,vsl] + ENDIF + dims=LONARR(vdcount)-999L & const=0 + FOR j=0,vdcount-1 DO BEGIN + ndi=WHERE(STRUPCASE(vdl[j]) EQ vdh,ndc) + IF vdl[j] EQ 'STRLEN' THEN BEGIN + ;dataset is of type string so add string length dimension variable to vdh array + dim_id=NCDF_DIMDEF(df_file_id,vn[i]+'_STRLEN',vsl[j]) + vdh=[vdh,STRUPCASE(vn[i])+'_STRLEN'] ;add dimension variable to vdh array + dim_id_list=[dim_id_list,dim_id] + dims[j]=dim_id + ENDIF ELSE IF ndc EQ 0 THEN BEGIN ;new dimension found for file + vdh=[vdh,STRUPCASE(vdl[j])] ;add dimension variable to vdh array + dim_id=NCDF_DIMDEF(df_file_id,vdl[j],vsl[j]) + dim_id_list=[dim_id_list,dim_id] + dims[j]=dim_id + ENDIF ELSE BEGIN ;dimension already has ID or is CONSTANT or INDEPENDENT + IF ndi[0] EQ 0 THEN BEGIN + ;CONSTANT so no dimension is assigned + IF constfound EQ 0 THEN BEGIN + dim_id=NCDF_DIMDEF(df_file_id,'CONSTANT',1) + dim_id_list[0]=dim_id ;assign a dim_id for CONSTANT - needed for STRING datasets + constfound=1 + ENDIF + const=1 + dims[j]=dim_id_list[0] + ENDIF ELSE IF ndi[0] EQ 1 THEN BEGIN + ;INDEPENDENT or BOUNDARIES so assign INDEPENDENT/BOUNDARIES_%vs as dimension name + IF (j EQ 1) AND (STRPOS(vn[i],'.BOUNDARIES') NE -1) THEN itxt='BOUNDARIES_' $ + ELSE itxt='INDEPENDENT_' + dim_id=NCDF_DIMDEF(df_file_id,itxt+STRTRIM(vsl[j],2),vsl[j]) + vdh=[vdh,itxt+STRTRIM(vsl[j],2)] ;add dimension variable to vdh array + dim_id_list=[dim_id_list,dim_id] + dims[j]=dim_id + ENDIF ELSE dims[j]=dim_id_list[ndi[0]] + ENDELSE + ENDFOR + + ;If multi-dimensional reverse dimension IDs as the dataset is automatically transposed + IF N_ELEMENTS(vs) GT 1 THEN dims=REVERSE(dims) + + CASE ndm[i,8] OF ;identifies the VAR_DATA_TYPE + 0:BEGIN + valid_range=[BYTE(mv_lng[0,i]),BYTE(mv_lng[1,i])] + fill_value=BYTE(mv_lng[2,i]) + IF const THEN var_id=NCDF_VARDEF(df_file_id,vn[i],/BYTE) $ + ELSE var_id=NCDF_VARDEF(df_file_id,vn[i],dims,/BYTE) + END + 1:BEGIN + valid_range=[FIX(mv_lng[0,i]),FIX(mv_lng[1,i])] + fill_value=FIX(mv_lng[2,i]) + IF const THEN var_id=NCDF_VARDEF(df_file_id,vn[i],/SHORT) $ + ELSE var_id=NCDF_VARDEF(df_file_id,vn[i],dims,/SHORT) + END + 2:BEGIN + valid_range=[LONG(mv_lng[0,i]),LONG(mv_lng[1,i])] + fill_value=LONG(mv_lng[2,i]) + IF const THEN var_id=NCDF_VARDEF(df_file_id,vn[i],/LONG) $ + ELSE var_id=NCDF_VARDEF(df_file_id,vn[i],dims,/LONG) + END + 3:BEGIN + valid_range=[mv_lng[0,i],mv_lng[1,i]] + fill_value=mv_lng[2,i] + IF const THEN var_id=NCDF_VARDEF(df_file_id,vn[i],/LONG) $ + ELSE var_id=NCDF_VARDEF(df_file_id,vn[i],dims,/LONG) + END + 4:BEGIN + valid_range=[FLOAT(mv_dbl[0,i]),FLOAT(mv_dbl[1,i])] + fill_value=FLOAT(mv_dbl[2,i]) + IF const THEN var_id=NCDF_VARDEF(df_file_id,vn[i],/FLOAT) $ + ELSE var_id=NCDF_VARDEF(df_file_id,vn[i],dims,/FLOAT) + END + 5:BEGIN + valid_range=[mv_dbl[0,i],mv_dbl[1,i]] + fill_value=mv_dbl[2,i] + IF const THEN var_id=NCDF_VARDEF(df_file_id,vn[i],/DOUBLE) $ + ELSE var_id=NCDF_VARDEF(df_file_id,vn[i],dims,/DOUBLE) + END + 6:BEGIN + valid_range=[' ',' '] + fill_value=' ' ;bytarr(mv_str[i]) ;STRING(format='(A-'+STRTRIM(mv_str[i],2)+')','') + var_id=NCDF_VARDEF(df_file_id,vn[i],dims,/CHAR) + END + ENDCASE + + ;write out variable attributes + ninc=0 & j=0 + mi=WHERE(meta_arr EQ attr_arr_data[0]+'='+vn[i]) + WHILE j LE natts-1 DO BEGIN + ma=mi[0]+ninc + ;Extract Attribute Label + eqpos=STRPOS(meta_arr[ma],'=') + res=STRMID(meta_arr[ma],0,eqpos) + WHILE res[0] NE al[j] DO j=j+1 ;in case of missing optional variable attributes + CASE 1 OF + attr_arr_data[j] EQ 'VAR_VALID_MIN': athold=valid_range[0] + attr_arr_data[j] EQ 'VAR_VALID_MAX': athold=valid_range[1] + attr_arr_data[j] EQ 'VAR_FILL_VALUE': athold=fill_value + ELSE: athold=av[i,j] + ENDCASE + NCDF_ATTPUT,df_file_id,var_id,attr_arr_data[j],athold + ninc=ninc+1 & j=j+1 + ENDWHILE + + IF ndm[i,8] NE 6 THEN BEGIN + NCDF_ATTPUT,df_file_id,var_id,'units',vu[i] + NCDF_ATTPUT,df_file_id,var_id,'valid_range',valid_range + NCDF_ATTPUT,df_file_id,var_id,'_Fillvalue',fill_value + ENDIF + + NCDF_CONTROL,df_file_id,/ENDEF ;Enter Data Mode + NCDF_VARPUT,df_file_id,var_id,data ;Write Data to file + NCDF_CONTROL,df_file_id,/REDEF ;Enter Define Mode +ENDFOR + +NCDF_CLOSE,df_file_id + +END ;Procedure AVDC_NC_Write + + + +PRO avdc_hdf_write, hdffilename, geoms_output +;IDL subroutine to write AVDC-type global attributes and datasets to a Scientific +;Dataset (SDS) in HDF4 (Bojkov et al.,2004) or HDF5. This routine creates the HDF4 +;file if requested, and calls the AVDC_HDF5_WRITE routine to create the HDF5 file if +;that option is chosen. +; ---------- +;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org +; +; History: +; 20050802: Originally split into two routines to separately write the Global +; attributes (AVDC_HDF_GLOBAL_ATTRIBUTES) and the Variable attributes +; and data (AVDC_HDF_SDS_VARIABLES) to an HDF4 file - Version 1.0 +; 20061012: Two routines combined into the AVDC_HDF_WRITE routine; Call to the +; AVDC_HDF5_WRITE routine if HDF5 file option is chosen; HDF4 file not +; closed between writing the global and variable attributes to file; +; Data now written to file from the ds structure instead of from arrays +; made according to VAR_DATA_TYPE; the av array made to hold attribute +; values to write to the HDF file, instead of writing the values to +; file directly from the meta_arr array (to avoid duplication of code +; in AVDC_HDF5_WRITE) - Version 2.0 +; 20130116: Account for string datasets which contain only empty string values (make +; string length = 1) - Version 4.0b15 +; 20150127: For string datasets, change the VAR_FILL_VALUE to an empty string that +; is the same length as the individual strings - Version 4.0b25 +; 20150217: Change rule from 4.0b25 so the VAR_FILL_VALUE is an empty string +; - Version 4.0b26 +; +; Inputs: hdffilename - a string holding the name of the HDF file being created +; and written to +; ds - structure containing the data +; meta_arr - a string array containing the Global and Variable Attributes +; vn - a string array containing the VAR_NAME values +; ndm - a long array describing the dimensions of each dataset and the +; VAR_DATA_TYPE +; +; Outputs: An HDF4 file with the name hdffilename +; +; Called by: IDLCR8HDF +; +; Subroutines Called: MAKEAN +; AVDC_HDF5_WRITE (if HDF5 option is chosen) +; INFOTXT_OUTPUT +; Information Conditions (when the program is able to make changes): +; 1. HDF4.2R1 or earlier library and SDS name has greater than 63-characters +; - in which case the SDS name will be truncated [3573] + +COMMON METADATA +COMMON DATA + +nodimset=1 ;added from version 3.06 to stop dimension information being written to the HDF4 file + ;while still retaining original code + +;Determine HDF format (ftype is either 'hdf', 'h5' or 'nc') +ftype=STRMID(hdffilename,STRPOS(hdffilename,'.',/Reverse_Search)+1) + +;List of dim. attributes to be assigned to the SDS dimensions. +da_list=['VAR_NAME','VAR_UNITS'] +da_att=INDGEN(N_ELEMENTS(da_list),/String) & natts=N_ELEMENTS(attr_arr_data) +av=STRARR(nvn,natts) ;array containing attribute and NCSA values for each dataset +var_depend=STRARR(nvn) ;holding array for the VAR_DEPEND attributes, used to assign DIM information +var_size=STRARR(nvn) ;holding array for the VAR_SIZE attributes, used to assign netCDF DIM information + +;Write all Dataset Attribute values to an array +FOR i=0,nvn-1 DO BEGIN ;nvn EQ number of variable names + mi=WHERE(meta_arr EQ attr_arr_data[0]+'='+vn[i]) + ninc=0 & j=0 + WHILE j LE natts-1 DO BEGIN + ma=mi[0]+ninc + ;separate out the Meta entry into two components + eqpos=STRPOS(meta_arr[ma],'=') + res=STRMID(meta_arr[ma],0,eqpos) + WHILE res NE attr_arr_data[j] DO j=j+1 ;in case optional variable attributes are not included + IF eqpos EQ STRLEN(meta_arr[ma])-1 THEN av[i,j]=' ' $ + ELSE av[i,j]=STRMID(meta_arr[ma],eqpos+1) + ;Reverse Values for VAR_DEPEND and VAR_SIZE to match order values will be saved in HDF + IF (res EQ 'VAR_DEPEND') OR (res EQ 'VAR_SIZE') THEN BEGIN + atval=STRSPLIT(av[i,j],' ;',/EXTRACT,COUNT=avcnt) + IF avcnt GT 1 THEN BEGIN + atval=REVERSE(atval) + FOR k=0,avcnt-1 DO IF k EQ 0 THEN ahold=atval[k] ELSE ahold=ahold+';'+atval[k] + av[i,j]=ahold + ENDIF + IF res EQ 'VAR_DEPEND' THEN var_depend[i]=av[i,j] ELSE var_size[i]=av[i,j] + ENDIF + ninc=ninc+1 & j=j+1 + ENDWHILE +ENDFOR + +IF ftype EQ 'hdf' THEN BEGIN + + ;Determine HDF library used by this program + HDF_LIB_INFO,MAJOR=mj,MINOR=mn,RELEASE=rl + hdf4lib=(mj*100)+(mn*10)+rl + hdf4txt='HDF'+STRTRIM(mj,2)+'.'+STRTRIM(mn,2)+'R'+STRTRIM(rl,2) + + ;Create and open the HDF4 file for writing + hdf_file_id=HDF_OPEN(hdffilename,/ALL) + ;The HDF_SD_START function opens or creates an HDF4 file and initializes the SD interface. + sd_id=HDF_SD_START(hdffilename,/RDWR) + + ;Write the Global Attributes to file + ;Section 4.1 (Bojkov et al., 2002): Originator attributes + ;Section 4.2 (Bojkov et al., 2002): Dataset attributes + ;Section 4.3 (Bojkov et al., 2002): File attributes + FOR i=0,N_ELEMENTS(attr_arr_glob)-1 DO BEGIN + ;separate out the Meta Attribute entry + eqpos=STRPOS(meta_arr[i],'=') + IF eqpos EQ STRLEN(meta_arr[i])-1 THEN attribute_value=' ' $ + ELSE attribute_value=STRMID(meta_arr[i],eqpos+1) + ;check for Free text attribute, and filename entry + res=STRMID(meta_arr[i],0,eqpos) + fai=WHERE(res EQ attr_free,facnt) + IF (facnt NE 0) AND (STRMID(STRUPCASE(attribute_value),0,6) EQ 'FILE("') THEN $ + MAKEAN,hdf_file_id,sd_id,'FILE',res,attribute_value $ + ELSE HDF_SD_ATTRSET,sd_id,attr_arr_glob[i],attribute_value + ENDFOR + + ;Write the Datasets (SDS) to file + ;Section 5.1 (Bojkov et al, 2002): Variable description attributes. + FOR i=0,nvn-1 DO BEGIN ;nvn EQ number of variable names + + ;If hdf4 library version is less than 4.2R2 then check length of VAR_NAME + IF hdf4lib LT 422 THEN BEGIN + IF STRLEN(vn[i]) GT 63 THEN BEGIN + infotxt=STRARR(4) + infotxt[0]='2 '+hdf4txt+' truncates the dataset name if its' + infotxt[0]=infotxt[0]+' length is greater than 63-characters.' + infotxt[1]=' If possible update the HDF4 library to HDF4.2R2 or newer| ' + infotxt[1]=infotxt[1]+'(refer to program documentation for procedure to do this). + infotxt[2]=' The Archive Data Center may also be set up to update the file' + infotxt[2]=infotxt[2]+' on submission.' + infotxt[3]=' VAR_NAME='+vn[i] + INFOTXT_OUTPUT, infotxt + ENDIF + ENDIF + + ;create data array from structure and create and define dataset for an HDF4 file + data=*ds[i].data + ;may have lost array VAR_SIZE information if the last value is '1' e.g. 41,1 + gi=WHERE(ndm[i,0:7] NE 0L) & vs=TRANSPOSE(ndm[i,gi]) + type=HDF_IDL2HDFTYPE(SIZE(data,/Type)) + IF ndm[i,8] EQ 6 THEN BEGIN + mxstrlen=MAX(STRLEN(data)) ;determine maximum stength length + IF mxstrlen EQ 0 THEN mxstrlen=1 + sds_id_1=HDF_SD_CREATE(sd_id,vn[i],[mxstrlen,vs],HDF_TYPE=type) + ENDIF ELSE sds_id_1=HDF_SD_CREATE(sd_id,vn[i],vs,HDF_TYPE=type) + + mi=WHERE(meta_arr EQ attr_arr_data[0]+'='+vn[i]) + CASE ndm[i,8] OF ;identifies the VAR_DATA_TYPE + 0:BEGIN + valid_range=[BYTE(mv_lng[0,i]),BYTE(mv_lng[1,i])] + fill_value=BYTE(mv_lng[2,i]) + END + 1:BEGIN + valid_range=[FIX(mv_lng[0,i]),FIX(mv_lng[1,i])] + fill_value=FIX(mv_lng[2,i]) + END + 2:BEGIN + valid_range=[LONG(mv_lng[0,i]),LONG(mv_lng[1,i])] + fill_value=LONG(mv_lng[2,i]) + END + 3:BEGIN + valid_range=[mv_lng[0,i],mv_lng[1,i]] + fill_value=mv_lng[2,i] + END + 4:BEGIN + valid_range=[FLOAT(mv_dbl[0,i]),FLOAT(mv_dbl[1,i])] + fill_value=FLOAT(mv_dbl[2,i]) + END + 5:BEGIN + valid_range=[mv_dbl[0,i],mv_dbl[1,i]] + fill_value=mv_dbl[2,i] + END + 6:BEGIN + valid_range=[' ',' '] + fill_value=' ' ;bytarr(mv_str[i]) ;STRING(format='(A-'+STRTRIM(mv_str[i],2)+')','') + ;Note: this seems to give the same result as using 0B and saving it as type string in the HDF_SD_ATTRSET call + END + ENDCASE + ninc=0 & j=0 + WHILE j LE natts-1 DO BEGIN + ma=mi[0]+ninc + ;Extract Attribute Label + eqpos=STRPOS(meta_arr[ma],'=') + res=STRMID(meta_arr[ma],0,eqpos) + WHILE res[0] NE attr_arr_data[j] DO j=j+1 + CASE 1 OF + ;attr_arr_data[j] EQ 'VAR_SIZE': HDF_SD_ATTRSET,sds_id_1,attr_arr_data[j],vs + attr_arr_data[j] EQ 'VAR_VALID_MIN': HDF_SD_ATTRSET,sds_id_1,attr_arr_data[j],valid_range[0] + attr_arr_data[j] EQ 'VAR_VALID_MAX': HDF_SD_ATTRSET,sds_id_1,attr_arr_data[j],valid_range[1] + attr_arr_data[j] EQ 'VAR_FILL_VALUE': HDF_SD_ATTRSET,sds_id_1,attr_arr_data[j],fill_value + ELSE: HDF_SD_ATTRSET,sds_id_1,attr_arr_data[j],av[i,j] + ENDCASE + ninc=ninc+1 & j=j+1 + ENDWHILE + ;Set information about the dataset (note no pre-defined attributes for String Datatype) + IF ndm[i,8] NE 6 THEN $ + HDF_SD_SETINFO,sds_id_1,UNIT=vu[i],RANGE=valid_range,FILL=fill_value + + IF nodimset EQ 0 THEN BEGIN + ;Assign the VAR_DEPEND dimension(s) attribute information + vd_out=STRSPLIT(var_depend[i],' ;',/Extract) ;Read the variable dependencies + vdim=N_ELEMENTS(vd_out) + vd_out=STRTRIM(vd_out,2) + ;Start search of information + FOR j=0,vdim-1 DO BEGIN + IF (STRUPCASE(vd_out[j]) NE 'CONSTANT') AND (STRUPCASE(vd_out[j]) NE 'INDEPENDENT') THEN BEGIN + ;Return the index of the var_depend array SDS dataset + index=HDF_SD_NAMETOINDEX(sd_id,vd_out[j]) + ;Access the dataset + sds_id_tmp=HDF_SD_SELECT(sd_id,index) + FOR k=0,N_ELEMENTS(da_list)-1 DO BEGIN + ;Find the dataset attribute in da_list + dindex=HDF_SD_ATTRFIND(sds_id_tmp,da_list[k]) + ;Read attribute info and assign to da_att + HDF_SD_ATTRINFO,sds_id_tmp,dindex,DATA=att_data + da_att[k]=att_data + ENDFOR + ;End access of SDS search + HDF_SD_ENDACCESS,sds_id_tmp + + ;Assign dimension to the current SDS + dim_id=HDF_SD_DIMGETID(sds_id_1,j) + HDF_SD_DIMSET,dim_id,/BW_INCOMP,NAME=da_att[0],UNIT=da_att[1] + ENDIF + ENDFOR + ENDIF + + ;Write data to HDF4 file + HDF_SD_ADDDATA,sds_id_1,data + HDF_SD_ENDACCESS,sds_id_1 + ENDFOR + + ;The HDF_SD_END function closes the SD interface to an HDF4 file. + HDF_SD_END,sd_id + ;The HDF_CLOSE procedure closes the HDF4 file associated with the given file handle. + HDF_CLOSE,hdf_file_id +ENDIF ELSE IF ftype EQ 'h5' THEN AVDC_HDF5_WRITE,hdffilename,natts,av,geoms_output $ +ELSE AVDC_NC_WRITE,hdffilename,natts,av,var_depend,var_size + +END ;Procedure AVDC_HDF_Write + + + +PRO idlcr8hdf, ga, sds, tav, odir, reterr, H5=o1, AVK=o2, LOG=o4, POPUP=o5, QA=o6, NOHDF=o7, NC=o8, DATETIME=o9, $ + C1=o11, C2=o12, C3=o13, C4=o14, C5=o15, C6=o16, C7=o17, C8=o18, C9=o19 +;Main IDL program to create HDF4 or HDF5 format files for submission to the AVDC, NDACC, or +;NILU (ESA Envisat) databases. +; +;Program documentation, idlcr8hdf-v4.0_Readme.pdf, available from http://avdc.gsfc.nasa.gov. +; +;Program sub-version 4.0b65 (20241029) +; ---------- +;Written by Ian Boyd for the EVDC/AVDC - iboyd@bryanscientific.org +; +; History: +; 20050802: Original Release - Version 1.0 +; 20050909: No change to routine - Version 1.1 +; 20051107: No change to routine - Version 1.11 +; 20061012: Make the code suitable for running on a licensed version of IDL (using the .pro +; and .sav versions of the code) and on IDL Virtual Machine (using the .sav version +; of the code); Change command line keyword options, remove the /Help (window now +; opened if there are no command line parameters) and /File options (now automatically +; identifies whether input is from session memory or files), and add the /H5 (write +; HDF5), /AVK (for Averaging Kernel Datasets add sentence to VAR_NOTES indicating +; averaging kernel order), /Log (append input/output information to a log file), and +; /Popup options (append input/output, error and warning information to a pop-up +; display window); If input is from files, the program can now handle multiple data +; files (either as a string array or as a file spec), together with a Metadata template +; file and the TAV file; Input/output information as well as errors and warnings +; included in the IDL DE output log window and/or to a log file; The user has the +; option of continuing to run the program with file inputs from the 'Introduction' +; window; Common variable definition WIDGET_WIN added - Version 2.0 +; 20080302: Remove HELP,/TRACEBACK call, which identified how the program was called. This was used +; to stop output to the IDLDE output window in the event that IDL VM was used, but it is +; not required as IDL VM ignores these print calls; Add DIALOG_BOX to show completion +; of the program if program inputs are via the INTRO box, and logging window option +; isn't requested - Version 3.0 +; 20100205: Add optional reterr parameter which allows program to return to a calling program with +; an error message, rather than stop - Version 3.09 +; 20101120: Adopt GEOMS metadata standard; New structure format required when input is by session +; memory; New arrays created to hold numeric metadata values (mv_lng and mv_dbl) in +; either LONG64 or DOUBLE format (changed to their actual format before writing to file); +; Add QA keyword, to perform QA function on HDF file read into session memory (does not +; create an HDF file) - Version 4.0 +; 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 and /NC keywords in DEMO mode - Version 4.0b14 +; 20140327: Add check and log message for VAR_SIZE values not being of type string - Version 4.0b20 +; 20150127: When input is by structure, stop removing leading and trailing spaces from variable +; attribute values when creating metafile array - Version 4.0b25 +; 20150217: Initialize mv_str if input is via files - Version 4.0b26 +; 20150302: Fix Bug which incorrectly set mv_str value if input is via structure - Version 4.0b27 +; 20151012: Create qa_yes common variable to streamline checks on whether program called in QA mode +; or not - Version 4.0b31 +; 20160725: Fix bug so that numeric metadata sub-values read in from a structure will be written to +; the metafile array OK - Version 4.0b38 +; 20161130: Fix issue when a metadata value is not numeric or a string e.g. a structure when the +; inputs are in the form of a structure - Version 4.0b40 +; 20180218: Ensure variable attribute value (vav) is only a single variable when calling +; PRE_DEFINED_ATT_CHECKS - Version 4.0b45 +; 20181221: Allow compression and shuffle options when creating HDF5 files - Version 4.0b49 +; +; Inputs: DIALOG PROMPTS WITH IDL VIRTUAL MACHINE (File input only) +; Metadata template file - The program will fill in missing spaces based on data file and +; TAV inputs +; Data file(s) - the user has the option of picking multiple data files +; tav - the current Table Attribute Values (TAV) file +; outdir - the directory which will hold the HDF file +; +; COMMAND LINE PARAMETER OPTION AVAILABLE WITH FULL IDL VERSION +; ga - either a string array containing the Global Attributes, or a Metadata template file +; (as above) +; sds - either a heap structure using pointers containing the Data (DS.Data) and the Variable +; Attributes (DS.VA_L and DS.VA_V) for a single file, or a string array or file spec of +; file(s) containing the data (as above) +; tav - the current Table Attribute Values (TAV) file +; outdir - the directory to which any HDF and log files will be written +; 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. +; +; OPTIONS +; H5 - output will be as an HDF5 file instead of the standard HDF4 +; NC - output will be as a netCDF3 file instead of the standard HDF4 +; AVK - to add a sentence to VAR_NOTES indicating array order for Averaging Kernel +; datasets +; Log - to append input/output information as well as warnings and errors to a log file +; named idlcr8hdf.log, or idlcr8qa.log if QA option chosen +; Popup - to append input/output information as well as warnings and errors to a pop-up +; display window +; QA - to perform QA function on an HDF file passed to the program using the session +; memory option. The logfile is automatically generated and will be called +; idlcr8qa.log instead of idlcr8hdf.log +; NoHDF - an HDF file will not be created, but the logfile idlcr8hdf.log will automatically +; be generated +; Cn - to enable compression and shuffling of HDF5 files only (ignored for netCDF3 and HDF4) +; +; Output: An HDF file formatted to GEOMS standards. Keyword options can also result +; in an output log file (idlcr8hdf.log), or a pop-up box showing log output. If the +; reterr string input parameter is included then reterr will return an error message to the +; calling program, if encountered. +; +; Called by: Main Routine +; +; Subroutines Called: INTRO (if program called without command line parameters) +; READ_TABLEFILE +; READ_METADATA +; CHECK_METADATA +; SET_UP_STRUCTURE +; READ_DATA +; FIND_HDF_FILENAME +; AVDC_HDF_WRITE +; STOP_WITH_ERROR (if error state detected) +; INFOTXT_OUTPUT (if information conditions detected) +; Possible Conditions for STOP_WITH_ERROR call: +; 1. If Metadata, Data, or Table Attribute Values file(s), or Output Directory +; selection is not valid +; 2. If input is via session memory and the array holding the global attributes +; is not of type string, or is not one dimensional +; 3. If input is via session memory and the input heap structure is not valid +; 4. If input is via session memory and the array sizes of the heap structures +; do not match +; 5. If the H5 option is chosen and the IDL version does not support HDF5 write +; routines (less than v6.2) +; 6. If the NC option is chosen and IDL was called in IDL DEMO mode +; +; Information Conditions (when the program is able to make changes): +; 1. /POPUP keyword cannot be used together with the 'reterr' argument +; 2. Argument 'reterr' must be a scalar variable of type string +; 3. /LOG keyword cannot be used in IDL DEMO mode + +COMMON TABLEDATA +COMMON METADATA +COMMON DATA +COMMON WIDGET_WIN + +;Possible error message for this procedure +proname='IDLcr8HDF procedure: ' & lu=-1L +errtxt=STRARR(6) +errtxt[0]=' selection not valid' +errtxt[1]='Global Attributes Array is not of type string, or is not one dimensional' +errtxt[2]='structure is not valid (' +errtxt[3]='Array sizes of the heap structure do not match: ' +errtxt[4]=' does not support HDF5 Write Routines' +errtxt[5]='IDLcr8HDF requires session memory input to perform the QA function' + +demomode=LMGR(/DEMO) ;Boolean to check for IDL being run in demo mode (disable /LOG option) +IF N_PARAMS() GE 2 THEN intype=SIZE(sds,/TYPE) $ ;Is SDS a structure (8) or a string (7)? +ELSE IF demomode THEN intype=-3 $ +ELSE intype=-1 +IF (intype NE 7) AND (intype NE 8) AND (intype NE -1) AND (intype NE -3) THEN intype=-2 +;Command line parameter neither string nor structure +dux=[-1,-1,1] ;initialize flags for output log to IDLDE Output Window and/or to file +;dux[0] default allows output to the IDLDE output window (ignored by IDL VM) +;dux[1] is the program assigned file unit for sending log output to file +;dux[2] is the step between dux[0] and dux[1] +lineno=0L ;count variable to put curser at end of text pop-up window +rerr=['NA','NA'] ;initialize return error string + +IF intype LT 0 THEN BEGIN ;either no input parameters or invalid second parameter + o3=['0','0','0','0','0','0'] + lvals=['None','1','2','3','4','5','6','7','8','9'] ;compression level for HDF5 option + INTRO,intype ;Open Intro Box and determine HDF output format (HDF4 or HDF5) + IF o3[0] EQ '0' THEN BEGIN + STOP_WITH_ERROR,'','',lu & RETURN + ENDIF + IF o3[3] EQ 'Pop' THEN o3[3]='' ELSE o3[3]='D_' +ENDIF ELSE BEGIN ;Set options (HDF4, HDF5, netCDF, AVK, LOGFILE, POP-UP, QA, NOHDF) + IF KEYWORD_SET(o1) THEN BEGIN + o3=['H5','0','0','D_','0','0'] + CASE 1 OF + KEYWORD_SET(o11): o3[0]='H5_1' + KEYWORD_SET(o12): o3[0]='H5_2' + KEYWORD_SET(o13): o3[0]='H5_3' + KEYWORD_SET(o14): o3[0]='H5_4' + KEYWORD_SET(o15): o3[0]='H5_5' + KEYWORD_SET(o16): o3[0]='H5_6' + KEYWORD_SET(o17): o3[0]='H5_7' + KEYWORD_SET(o18): o3[0]='H5_8' + KEYWORD_SET(o19): o3[0]='H5_9' + ELSE: + ENDCASE + ;PRINT,o3 + ENDIF ELSE IF KEYWORD_SET(o8) THEN o3=['NC','0','0','D_','0','0'] $ + ELSE o3=['H4','0','0','D_','0','0'] + IF KEYWORD_SET(o2) THEN o3[1]='AVK' ;AVK option + IF KEYWORD_SET(o4) THEN o3[2]='idlcr8hdf.log' ;Log option + IF KEYWORD_SET(o5) THEN o3[3]='' ;POP-UP option + IF KEYWORD_SET(o6) THEN o3[2]='idlcr8qa.log' ;QA option + IF KEYWORD_SET(o7) THEN o3[4]='NOHDF' ;NoHDF option + IF KEYWORD_SET(o9) THEN o3[5]='DTFVOK' ;DATETIME Fill Value OK +ENDELSE +IF o3[0] EQ 'NC' THEN dftxt='netCDF' ELSE dftxt='HDF' + +;If reterr included allows error output to return to a calling program +n_it=5 +infotxt=STRARR(2,n_it) +IF N_PARAMS() GE 5 THEN BEGIN + ;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_' + infotxt[0,0]='0 /POPUP keyword cannot be used together with the ''reterr'' argument.' + infotxt[1,0]=' The request for a POPUP window has been ignored.' + ENDIF + ENDIF ELSE BEGIN + ;reterr input included but is of the incorrect type, or parameter cannot be returned + ;to the calling program + infotxt[0,1]='0 Argument ''reterr'' must be of type string.' ;a returnable variable of type string.' + infotxt[1,1]=' idlcr8hdf will stop normally if an error is encountered.' + ENDELSE +ENDIF + +;Do check for /LOG and /NC keywords in IDL DEMO Mode and if necessary disable or stop the program +IF demomode THEN BEGIN + IF o3[2] EQ 'idlcr8hdf.log' THEN BEGIN + o3[2]='0' + infotxt[0,2]='0 /LOG keyword cannot be used in IDL DEMO mode and has been ignored.' + ENDIF + IF o3[0] EQ 'NC' THEN BEGIN + o3[4]='NOHDF' + infotxt[0,3]='3 NetCDF file create feature is disabled in IDL DEMO mode. ' + infotxt[1,3]=' To generate files please use the free IDL Virtual Machine or a licenced version of IDL.' + ENDIF +ENDIF + +qa_yes=STRPOS(o3[2],'idlcr8qa.log') NE -1 ;Set Boolean for doing QA checks + +IF (KEYWORD_SET(o1)) AND (FLOAT(!Version.Release) LT 6.2) THEN BEGIN ;No HDF5 library for IDL6.1 or less + STOP_WITH_ERROR,'D_'+proname,'IDL Version '+!Version.Release+errtxt[4],lu + IF STRLEN(rerr[0]) GT 2 THEN reterr=rerr[0] & RETURN +ENDIF + +;Check TAV file and Output Directory inputs +tablefile='' & outdir='' +CD,CURRENT=path ;identify current working directory +IF N_PARAMS() GE 3 THEN BEGIN ;check TAV file + IF (SIZE(tav,/N_ELEMENTS) EQ 1) AND (SIZE(tav,/TYPE) EQ 7) THEN BEGIN + file_exist=FILE_TEST(tav,/READ) + IF (file_exist EQ 1) AND (tav NE '') THEN tablefile=tav + ENDIF +ENDIF +IF N_PARAMS() GE 4 THEN BEGIN ;check output directory + IF (SIZE(odir,/N_ELEMENTS) EQ 1) AND (SIZE(odir,/TYPE) EQ 7) THEN BEGIN + file_exist=FILE_TEST(odir,/DIRECTORY) + IF (file_exist EQ 1) AND (odir NE '') THEN BEGIN + outdir=FILE_SEARCH(odir,/FULLY_QUALIFY_PATH,/MARK_DIRECTORY) + path=outdir + ENDIF ELSE IF intype NE 8 THEN outdir=STRUPCASE(STRTRIM(odir,2)) + ENDIF +ENDIF + +IF (intype LT 0) OR (intype EQ 7) THEN BEGIN ;Input via files + IF KEYWORD_SET(o6) THEN BEGIN + ;session memory inputs required for QA option + STOP_WITH_ERROR,'D_'+proname,errtxt[5],lu + IF STRLEN(rerr[0]) GT 2 THEN reterr=rerr[0] & RETURN + ENDIF + metafile='' & inf=1 & mv_str=[-1L] + IF N_PARAMS() GE 2 THEN BEGIN + ;Check that input files exist. If not then user will be prompted for new inputs + IF SIZE(ga,/N_ELEMENTS) EQ 1 THEN BEGIN + file_exist=FILE_TEST(ga,/READ) + IF (file_exist EQ 1) AND (ga NE '') THEN $ + metafile=FILE_DIRNAME(ga,/MARK_DIRECTORY)+FILE_BASENAME(ga) + ENDIF + arorfs=SIZE(sds,/DIMENSIONS) ;Is datafile input Filespec or String Array? + IF arorfs[0] EQ 0 THEN BEGIN ;input is filespec (scalar) + IF sds NE '' THEN sds=FINDFILE(sds) + isize=SIZE(sds,/DIMENSIONS) + IF isize[0] EQ 0 THEN sds=[''] ;no files found matching filespec + ENDIF + IF N_ELEMENTS(arorfs) GT 1 THEN sds=REFORM(sds,N_ELEMENTS(sds),/OVERWRITE) ;Convert to 1-D array + file_exist=FILE_TEST(sds,/READ) + gi=WHERE(file_exist EQ 1,nfile) + IF nfile NE 0 THEN BEGIN + sds=sds[gi] & sds=FILE_DIRNAME(sds,/MARK_DIRECTORY)+FILE_BASENAME(sds) + ENDIF ELSE sds=[''] ;contains all valid datafiles + ENDIF ELSE sds=[''] + IF metafile EQ '' THEN BEGIN + metafile=DIALOG_PICKFILE(Filter=['*.meta','meta*.txt','*.txt'], PATH=path, $ + /MUST_EXIST,Title='Select Metadata Template File') + IF metafile EQ '' THEN BEGIN + STOP_WITH_ERROR,'D_'+proname,'Metadata file'+errtxt[0],lu + IF STRLEN(rerr[0]) GT 2 THEN reterr=rerr[0] & RETURN + ENDIF + path=FILE_DIRNAME(metafile,/MARK_DIRECTORY) + END + IF sds[0] EQ '' THEN BEGIN + datafile=DIALOG_PICKFILE(Filter=['*.data','*.dat','*.txt'], PATH=path, $ + /MUST_EXIST,/Multiple_Files,Title='Select Data File(s)') + gi=WHERE(datafile NE '',nfile) + IF nfile EQ 0 THEN BEGIN + STOP_WITH_ERROR,'D_'+proname,'Data file(s)'+errtxt[0],lu + IF STRLEN(rerr[0]) GT 2 THEN reterr=rerr[0] & RETURN + ENDIF ELSE BEGIN + datafile=datafile[gi] & dsort=SORT(datafile) & sds=datafile[dsort] + path=FILE_DIRNAME(sds[0],/MARK_DIRECTORY) + ENDELSE + ENDIF + IF outdir EQ 'M' THEN outdir=FILE_DIRNAME(metafile,/MARK_DIRECTORY) $ + ELSE IF outdir EQ 'D' THEN outdir=FILE_DIRNAME(sds[0],/MARK_DIRECTORY) +ENDIF ELSE BEGIN ;SDS is a data structure, so do initial checks on parameters + ;check GA array + inf=0 & nfile=1 + as=SIZE(ga) & n_ga=N_ELEMENTS(ga) + IF (as[0] NE 1) OR (as[2] NE 7) THEN BEGIN + STOP_WITH_ERROR,'D_'+proname,errtxt[1],lu + IF STRLEN(rerr[0]) GT 2 THEN reterr=rerr[0] & RETURN + ENDIF + + 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) + it5=[''] ;array holding infotxt[*,5] error messages + + ;check pointer array (heap structure) + IF N_TAGS(sds) EQ 0 THEN BEGIN + STOP_WITH_ERROR,'D_'+proname,'Input heap '+errtxt[2]+'expecting VA_L, VA_V, and DATA tags).',lu + IF STRLEN(rerr[0]) GT 2 THEN reterr=rerr[0] & RETURN + ENDIF + tagnames=STRUPCASE(TAG_NAMES(sds)) + si=WHERE((tagnames EQ 'VA_L') OR (tagnames EQ 'VA_V') OR (tagnames EQ 'DATA'),scnt) + IF scnt NE 3 THEN BEGIN + STOP_WITH_ERROR,'D_'+proname,'Input heap '+errtxt[2]+'missing VA_L, VA_V, and/or DATA tags).',lu + IF STRLEN(rerr[0]) GT 2 THEN reterr=rerr[0] & RETURN + ENDIF + ;Check for number and size of dimensions, and invalid pointer arguments for the data + FOR i=0,2 DO BEGIN + CASE 1 OF + i EQ 0: BEGIN + pchk=PTR_VALID(sds.data) & sdstxt='SDS.DATA ' + END + i EQ 1: BEGIN + pchk=PTR_VALID(sds.va_v) & sdstxt='SDS.VA_V ' + pchkv=pchk + END + ELSE: BEGIN + pchk=PTR_VALID(sds.va_l) & sdstxt='SDS.VA_L ' + END + ENDCASE + ;Check that the SDS structure has two dimensions + s_ndim=SIZE(pchk,/N_DIMENSIONS) & s_dim=SIZE(pchk,/DIMENSIONS) + IF s_ndim NE 2 THEN BEGIN + STOP_WITH_ERROR,'D_'+proname,sdstxt+errtxt[2]+'two dimensions expected).',lu + IF STRLEN(rerr[0]) GT 2 THEN reterr=rerr[0] & RETURN + ENDIF ELSE IF i EQ 0 THEN BEGIN + pi=WHERE(pchk[*,0] NE 1,pcnt) + IF pcnt NE 0 THEN BEGIN + STOP_WITH_ERROR,'D_'+proname,sdstxt+errtxt[2]+'null pointer arguments).',lu + IF STRLEN(rerr[0]) GT 2 THEN reterr=rerr[0] & RETURN + ENDIF + ENDIF ELSE BEGIN + IF i EQ 2 THEN test=s_dim[1] NE s_dim0[1] ELSE test=0 + IF (s_dim[0] NE s_dim0[0]) OR (test) THEN BEGIN + dimv='['+STRTRIM(s_dim0[0],2)+','+STRTRIM(sdim0[1],2)+']/['+ $ + STRTRIM(s_dim[0],2)+','+STRTRIM(sdim[1],2)+'].' + STOP_WITH_ERROR,'D_'+proname,errtxt[3]+dimv,lu + IF STRLEN(rerr[0]) GT 2 THEN reterr=rerr[0] & RETURN + ENDIF + ENDELSE + s_dim0=s_dim + ENDFOR + + ;create Metafile by combining global and variable attributes, also make + ;accompanying arrays to hold any numeric metadata values + vi=WHERE(pchk EQ 1,vatot) ;total number of Variable Attribute labels + metafile=STRARR(n_ga+vatot) & mv_lng=LON64ARR(n_ga+vatot) & mv_dbl=DBLARR(n_ga+vatot) + pcnt=LONG(n_ga) & mv_str=LONARR(vatot) + metafile[0:pcnt-1L]=ga + FOR i=0,s_dim[0]-1 DO BEGIN ;s_dim[0] is the number of datasets + dset=*sds[i,0].data & dtype=SIZE(dset,/TYPE) + FOR j=0,s_dim[1]-1 DO BEGIN ;s_dim[1] is the number of attributes + IF pchk[i,j] EQ 1 THEN BEGIN + IF pchkv[i,j] EQ 0 THEN metafile[pcnt]=*sds[i,j].va_l+'=' $ + ELSE BEGIN + vavt=SIZE(*sds[i,j].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 + vav=*sds[i,j].va_v + n_vav=N_ELEMENTS(vav) + FOR k=0,n_vav-1 DO BEGIN + IF k EQ 0 THEN BEGIN + IF vavt EQ 1 THEN metafile[pcnt]=*sds[i,j].va_l+'='+STRING(FIX(vav[k])) $ + ELSE IF vavt EQ 7 THEN metafile[pcnt]=*sds[i,j].va_l+'='+STRING(vav[k]) $ ;save all values as a string representation + ELSE metafile[pcnt]=*sds[i,j].va_l+'='+STRTRIM(vav[k],2) + ENDIF ELSE BEGIN + IF vavt EQ 1 THEN metafile[pcnt]=metafile[pcnt]+';'+STRING(FIX(vav[k])) $ + ELSE IF vavt EQ 7 THEN metafile[pcnt]=metafile[pcnt]+';'+STRING(vav[k]) $ + ELSE metafile[pcnt]=metafile[pcnt]+';'+STRTRIM(vav[k],2) + ENDELSE + ENDFOR + IF vavt NE 7 THEN BEGIN ;i.e. if the metadata value is not of STRING datatype + + ;Do check for VAR_SIZE value written as a numeric type instead of string + vs_chk=*sds[i,j].va_l + IF (STRUPCASE(vs_chk) EQ 'VAR_SIZE') AND (infotxt[0,4] EQ '') THEN BEGIN + ;need to generate infotxt message + IF qa_yes THEN itxt=' must be of ' ELSE itxt=' changed to ' + infotxt[0,4]='2 VAR_SIZE attribute value(s)'+itxt+'type STRING' + ENDIF + + IF n_vav EQ 1 THEN BEGIN ;save numeric values to the appropriate numeric array (LONG64 or DOUBLE) + CASE 1 OF + ((vavt GE 1) AND (vavt LE 3)) OR ((vavt GE 12) AND (vavt LE 14)): mv_lng[pcnt]=vav + (vavt GE 4) AND (vavt LE 5): mv_dbl[pcnt]=vav + ELSE: + ENDCASE + ENDIF + ENDIF + ENDIF ELSE BEGIN ;invalid data type for the attribute label + IF qa_yes THEN itxt='' ELSE itxt=' and will be ignored' + val=*sds[i,j].va_l & val=STRTRIM(val,2) + bli=WHERE(val EQ badlabel,blcnt) + IF blcnt EQ 0 THEN BEGIN + badlabel=[badlabel,val] + IF it5[0] EQ '' THEN it5[0]='2 Value for Attribute Label '+val+' is an invalid Data Type'+itxt $ + ELSE it5=[it5,'2 Value for Attribute Label '+val+' is an invalid Data Type'+itxt] + ENDIF + ENDELSE + ENDELSE + pcnt=pcnt+1L + ENDIF + ENDFOR + ENDFOR +ENDELSE + +;If necessary open DIALOG_BOXES for the TAV File and Output Directory Inputs +IF tablefile EQ '' THEN BEGIN + tablefile=DIALOG_PICKFILE(Filter=['table*.dat','*.dat'], PATH=path, $ + /MUST_EXIST,Title='Select Table Attribute Values File') + IF tablefile EQ '' THEN BEGIN + STOP_WITH_ERROR,'D_'+proname,'Table Attribute Values file'+errtxt[0],lu + IF STRLEN(rerr[0]) GT 2 THEN reterr=rerr[0] & RETURN + ENDIF + path=FILE_DIRNAME(tablefile,/MARK_DIRECTORY) +ENDIF +IF outdir EQ '' THEN BEGIN + outdir=DIALOG_PICKFILE(/DIRECTORY,Title='Select Directory for Output '+dftxt+' file(s)', PATH=path) + IF outdir EQ '' THEN BEGIN + STOP_WITH_ERROR,'D_'+proname,dftxt+' file(s) output directory'+errtxt[0],lu + IF STRLEN(rerr[0]) GT 2 THEN reterr=rerr[0] & RETURN + ENDIF +ENDIF + +;Set dux values according to output options +IF o3[2] NE '0' THEN BEGIN ;open idlcr8hdf/idlcr8qa.log + o3[2]=outdir+o3[2] + res=FILE_TEST(o3[2],/WRITE) + IF res EQ 0 THEN OPENW,du,o3[2],/GET_LUN $ + ELSE BEGIN + OPENW,du,o3[2],/Append,/GET_LUN & FOR i=0,1 DO PRINTF,du,'' + ENDELSE + dux[1]=du & dux[2]=dux[1]-dux[0] +ENDIF +FOR i=dux[0],dux[1],dux[2] DO BEGIN + IF i EQ -1 THEN BEGIN + PRINT,dftxt+' File Input/Output Log - Program Started on '+SYSTIME(0) & PRINT,'' + ENDIF ELSE IF (i EQ dux[0]) OR ((i EQ dux[1]) AND (STRPOS(o3[2],'idlcr8hdf.log') NE -1)) THEN BEGIN + PRINTF,i,dftxt+' File Input/Output Log - Program Started on '+SYSTIME(0) + PRINTF,i,'' + ENDIF +ENDFOR + +IF o3[3] EQ '' THEN BEGIN + ;Set-up output file display widget and log file + base=WIDGET_BASE(Title=dftxt+' File Input/Output Log',Units=2,yoffset=3,xoffset=3,Tlb_Frame_Attr=1,/COLUMN) ;,Tab_Mode=1) + wtxt=WIDGET_TEXT(base,/Scroll,xsize=100,ysize=12) + base2=WIDGET_BASE(base) + b1=WIDGET_BUTTON(base2,value='Finish',uvalue='DONE',frame=3,Sensitive=0) ;Accelerator='Return') + WIDGET_CONTROL,wtxt,/REALIZE + WIDGET_CONTROL,base,/REALIZE + WIDGET_CONTROL,wtxt,set_value='',/APPEND + WIDGET_CONTROL,wtxt,set_value='Input TAV File: '+tablefile,/APPEND +ENDIF + +;Call the procedure to read the TAV file (returns tab_arr) +FOR i=dux[0],dux[1],dux[2] DO $ + IF i EQ -1 THEN PRINT,'Input TAV File: '+tablefile ELSE PRINTF,i,'Input TAV File: '+tablefile +READ_TABLEFILE,tablefile +IF STRLEN(rerr[0]) GT 2 THEN BEGIN + reterr=rerr[0] & RETURN +ENDIF + +;Check (and write out) any earlier infotxt statements +FOR i=0,3 DO BEGIN + IF infotxt[0,i] NE '' THEN BEGIN + IF i EQ 2 THEN INFOTXT_OUTPUT,infotxt[0,i] ELSE INFOTXT_OUTPUT,infotxt[*,i] + ENDIF +ENDFOR + +FOR ndf=0,nfile-1 DO BEGIN + IF o3[3] EQ '' THEN BEGIN + lineno=lineno+3L + WIDGET_CONTROL,wtxt,set_value='',/Append + ENDIF + FOR i=dux[0],dux[1],dux[2] DO IF i EQ -1 THEN PRINT,'' ELSE PRINTF,i,'' + IF inf EQ 1 THEN BEGIN + IF o3[3] EQ '' THEN BEGIN + lineno=lineno+2L + WIDGET_CONTROL,wtxt,set_value='Input Metadata File: '+metafile,/Append + WIDGET_CONTROL,wtxt,set_value='Input Data File: '+sds[ndf],/Append + ENDIF + FOR i=dux[0],dux[1],dux[2] DO BEGIN + IF i EQ -1 THEN BEGIN + PRINT,'Input Metadata File: '+metafile & PRINT,'Input Data File: '+sds[ndf] + ENDIF ELSE BEGIN + PRINTF,i,'Input Metadata File: '+metafile + PRINTF,i,'Input Data File: '+sds[ndf] + ENDELSE + ENDFOR + ENDIF ELSE BEGIN + IF o3[3] EQ '' THEN $ + WIDGET_CONTROL,wtxt,set_value='Reading input Global Attribute array and Data Structure',/Append + FOR i=dux[0],dux[1],dux[2] DO BEGIN + IF i EQ -1 THEN PRINT,'Reading input Global Attribute array and Data Structure' $ + ELSE IF (i EQ dux[0]) OR ((i EQ dux[1]) AND (STRPOS(o3[2],'idlcr8hdf.log') NE -1)) THEN $ + PRINTF,i,'Reading input Global Attribute array and Data Structure' + ENDFOR + ;check for numeric VAR_SIZE value message + IF infotxt[0,4] NE '' THEN INFOTXT_OUTPUT,infotxt[0,4] + + ;check for invalid variable attribute value data type + IF it5[0] NE '' THEN BEGIN + FOR i=0,N_ELEMENTS(it5)-1 DO INFOTXT_OUTPUT,it5[i] + ENDIF + + ;Do stage 1 of HDF Pre-Defined attributes check (checking units,valid_range,_FillValue only) + ncsa=['x','x','units','_fillvalue','valid_range','valid_range'] + var_atts=['VAR_NAME','VAR_DATA_TYPE','VAR_UNITS','VAR_FILL_VALUE','VAR_VALID_MIN','VAR_VALID_MAX'] + FOR i=0,s_dim[0]-1 DO BEGIN ;s_dim[0] is the number of datasets + vdtv='' & vnx='' & verror=INTARR(2) + FOR k=0,N_ELEMENTS(var_atts)-1 DO BEGIN + vav='' & nav='' + FOR j=0,s_dim[1]-1 DO BEGIN ;s_dim[1] is the number of attributes + IF (pchk[i,j] EQ 1) AND (pchkv[i,j] EQ 1) THEN BEGIN + CASE 1 OF + STRUPCASE(STRTRIM(*sds[i,j].va_l,2)) EQ var_atts[k]: vav=*sds[i,j].va_v + STRLOWCASE(STRTRIM(*sds[i,j].va_l,2)) EQ ncsa[k]: nav=*sds[i,j].va_v + ELSE: + ENDCASE + ENDIF + ENDFOR + IF k EQ 0 THEN vnx=STRTRIM(vav[0],2) $ ;VAR_NAME + ELSE IF k EQ 1 THEN vdtv=STRUPCASE(STRTRIM(vav[0],2)) $ ;VAR_DATA_TYPE + ELSE IF k GE 4 THEN BEGIN ;separate out min and max valid_range values + IF ((SIZE(nav[0],/TYPE) EQ 7) AND (STRTRIM(nav[0],2) NE '')) OR (SIZE(nav[0],/TYPE) NE 7) THEN BEGIN + IF k EQ 4 THEN nav=nav[0] $ + ELSE IF N_ELEMENTS(nav) GT 1 THEN nav=nav[1] + ENDIF + vav=vav[0] + ENDIF ELSE BEGIN + vav=vav[0] & nav=nav[0] + ENDELSE + IF k GE 2 THEN PRE_DEFINED_ATT_CHECKS,k-2,nav,vav,vnx,vdtv,verror + ENDFOR + ENDFOR + ENDELSE + + ;Call the procedure to read the metadata (returns meta_arr) + READ_METADATA,metafile,inf + IF STRLEN(rerr[0]) GT 2 THEN BEGIN + reterr=rerr[0] & RETURN + ENDIF + ;Call Procedure to check inputs in meta_arr with those from the TAV file + CHECK_METADATA + IF STRLEN(rerr[0]) GT 2 THEN BEGIN + reterr=rerr[0] & RETURN + ENDIF + ;Call the procedure to set up the data structure and assign arrays for VAR_NAME and VAR_UNITS + IF inf EQ 0 THEN SET_UP_STRUCTURE,sds,inf ELSE SET_UP_STRUCTURE,sds[ndf],inf + IF STRLEN(rerr[0]) GT 2 THEN BEGIN + reterr=rerr[0] & RETURN + ENDIF ;Call the procedure to read the data + IF inf EQ 0 THEN READ_DATA,sds,inf ELSE READ_DATA,sds[ndf],inf + IF STRLEN(rerr[0]) GT 2 THEN BEGIN + reterr=rerr[0] & RETURN + ENDIF + ;Call the Procedure to construct the HDF output filename based on input metadata. + IF STRPOS(o3[0],'H5') NE -1 THEN hdffilename='.h5' $ + ELSE IF o3[0] EQ 'NC' THEN hdffilename='.nc' $ + ELSE hdffilename='.hdf' + FIND_HDF_FILENAME,hdffilename + IF STRLEN(rerr[0]) GT 2 THEN BEGIN + reterr=rerr[0] & RETURN + ENDIF + + ;Create HDF file if the program is not performing QA and NOHDF keyword not selected + IF (~qa_yes) AND (o3[4] EQ '0') THEN BEGIN + ;Check to see if file exists - if so delete (o/w will append to existing file) + IF FILE_TEST(hdffilename) EQ 1 THEN FILE_DELETE,hdffilename + + ;Call the procedure to write the Global Attributes, Variable Attributes and Data + ;to an HDF4 or HDF5 file + geoms_output=o3[0] + AVDC_HDF_WRITE,hdffilename,geoms_output + FILE_MOVE, hdffilename, outdir+hdffilename,/Allow_Same,/Overwrite + hdffilename=outdir+hdffilename + + IF o3[3] EQ '' THEN $ + WIDGET_CONTROL,wtxt,set_value=hdffilename+' successfully created!',/Append,Set_text_top_line=lineno + FOR i=dux[0],dux[1],dux[2] DO BEGIN + IF i EQ -1 THEN PRINT,hdffilename+' successfully created!' $ + ELSE PRINTF,i,hdffilename+' successfully created!' + ENDFOR + ENDIF + ;Free up memory by destroying the heap variables pointed at by its pointer arguments + PTR_FREE,ds.data +ENDFOR + +IF qa_yes THEN endtxt=dftxt+' QA function ' $ +ELSE IF o3[4] EQ '0' THEN endtxt=dftxt+' file creation ' $ +ELSE endtxt='GEOMS file testing ' + +FOR i=dux[0],dux[1],dux[2] DO BEGIN + IF i EQ -1 THEN BEGIN + PRINT,'' & PRINT,endtxt+'completed - Program Ended on '+SYSTIME(0) + ENDIF ELSE IF (i EQ dux[0]) OR ((i EQ dux[1]) AND (STRPOS(o3[2],'idlcr8hdf.log') NE -1)) THEN BEGIN + PRINTF,i,'' & PRINTF,i,endtxt+'completed - Program Ended on '+SYSTIME(0) + ENDIF +ENDFOR +IF dux[1] NE dux[0] THEN FREE_LUN,dux[1] + +ri=WHERE(rerr EQ '',rcnt) +IF (rcnt EQ N_ELEMENTS(rerr)) AND (~qa_yes) AND (o3[4] EQ '0') THEN BEGIN + IF nfile EQ 1 THEN reterr=hdffilename+' successfully created' $ + ELSE reterr=dftxt+' files successfully created' +ENDIF ELSE IF (rerr[1] NE 'NA') AND (rerr[1] NE '') THEN reterr=rerr[1] + +IF o3[3] EQ '' THEN BEGIN + WIDGET_CONTROL,b1,Sensitive=1,/Input_Focus + WIDGET_CONTROL,wtxt,set_value='',/Append,Set_text_top_line=lineno+2L + WIDGET_CONTROL,wtxt,set_value=endtxt+'completed - hit <Finish> to close program',/Append + XMANAGER,'idlcr8hdf',base +ENDIF ELSE IF intype LT 0 THEN BEGIN ;Create Finish Dialog Box + res=DIALOG_MESSAGE(endtxt+'completed successfully!',/Information,Title='EVDC IDLcr8HDF') +ENDIF + +END ;Procedure IDLcr8HDF