;Main Program Version: idlcr8hdf.pro v4.0b66, 20241126 ; 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.0b66 November 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 ; 20241126: Ensure the number of sub-values present in the file match the expected number by, if ; necessary, adding dummy values to the arttribute entry e.g. For DATA_DISCIPLINE ensure ; there are 3 entries - Version 4.0b66 ; ; 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 IF nel LT mcnt THEN FOR j=nel,mcnt-1 DO achk=[achk,' '] ;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.0b66 (20241126) ; ---------- ;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 ; 20241126: Fix bug to treat BYTE metadata entries the same as other numeric datatypes when ; writing metafile array from session memory input. Previously STRING(FIX(vav[k])) and ; changed to STRTRIM(FIX(vav[k]),2) - Version 4.0b66 ; ; 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+'='+STRTRIM(FIX(vav[k]),2) $ ;Byte - need to treat the same as any other numeric type (20241111) 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]+';'+STRTRIM(FIX(vav[k]),2) $ ;Byte - need to treat the same as any other numeric type (20241111) 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