;;---------------------------------------------------------------------------- ;; Name: TABLEPDS ;; ;; Purpose: To read a PDS ASCII or BINARY TABLE object into IDL structure ;; ;; Calling Sequence: ;; result = tablepds (filename, label, index [, /SILENT]) ;; ;; Input: ;; filename - a scalar string containing the name of the PDS file ;; to read ;; label - string array containing the image header information ;; index - an integer giving the index of label array where the ;; start of the TABLE object ;; ;; Output: ;; result - a table structure constructed from designated record ;; ;; Optional inputs: ;; SILENT - suppresses any messages from the procedure ;; ;; Examples: ;; To read an ASCII or BINARY table file table.lbl into variable ;; 'table': ;; IDL> label = headpds('table.lbl', /SILENT) ;; IDL> table = tablepds('table.lbl', label, 8, /SILENT) ;; ;; External routines: Verify_label, Get_index, Pointpds, Clean, ;; Remove, Extract_keyword, Apply_bit_mask, Objpds ;; ;; Modification history: ;; Written by: John D. Koch [December 1994] (adapted from READFITS by ;; Wayne Landsman) ;; Re-written by: Puneet Khetarpal [30 June, 2005] ;; 12 Feb 08, HJJ: Added VERIFY keyword (VERIFY=0 speeds up table reading). ;; ;; Modifications: To view a complete list of modifications made to this ;; routine, please see changelog.txt file. ;; ;;---------------------------------------------------------------------------- ;- level 2 ------------------------------------------------------------------- ; precondition: the label contains column object definitions, and index ; and endindex specify the start and end of the current table object. ; postcondition: the column object names and indices are extracted ; from the label and returned. function obtain_column_objects, label, index, endindex ; extract all objects from label objects = objpds(label, 'COLUMN') objname = objects.array objind = objects.index objcnt = objects.count ; get position in index array of column objects within table range pos = where(objind ge index and objind le endindex, matches) struct = {name:objname[pos],index:objind[pos],count:matches} return, struct end ; precondition: the column objects have been extracted from label, the start ; and endindices are range for current column object ; postcondition: the keywords for current column object are extracted ; and returned. function obtain_column_keywords, label, start, endind ; extract name keyword name = extract_keyword(label, 'NAME', start, endind, 1) name = remove(name, '"') ; extract data type keyword data_type = extract_keyword(label, 'DATA_TYPE', start, endind, 1) data_type = remove(data_type, '"') ; extract start byte keyword start_byte = extract_keyword(label, 'START_BYTE', start, endind, 1) start_byte = long(start_byte) ; extract items keyword (if present) items = extract_keyword(label, 'ITEMS', start, endind, 0) if (items eq '###~') then begin ; if no items present items = 0L ; set items to 0 item_bytes = 0L ; set item bytes to 0 item_offset = 0L ; set item offset to 0 bytes = extract_keyword(label, 'BYTES', start, endind, 1) bytes = long(bytes) ; extract and store required BYTES total_bytes = bytes ; store total bytes for current object endif else begin ; else items = long(items) ; convert items to long ; extract item offset optional keyword item_offset = extract_keyword(label, 'ITEM_OFFSET', start, endind, 0) item_offset = (item_offset eq '###~') ? 0L : long(item_offset) ; extract item bytes required keyword item_bytes = extract_keyword(label, 'ITEM_BYTES', start, endind, 1) item_bytes = long(item_bytes) ; compute bytes for total items bytes = item_bytes ; compute total bytes for current object total_bytes = (item_offset eq 0) ? items * item_bytes : $ ((items - 1) * item_offset) + item_bytes endelse ; extract bit mask keyword if present bit_mask = extract_keyword(label, 'BIT_MASK', start, endind, 0) bit_mask = (bit_mask eq '###~') ? '0' : bit_mask ; store keywords in structure struct = {name:name, data_type:data_type, start_byte:start_byte, $ items:items, item_offset:item_offset, item_bytes:item_bytes, $ bytes:bytes, bit_mask:bit_mask, total_bytes:total_bytes} return, struct end ; precondition: the column keywords have been extracted, and bytecount holds ; value of the current number of bytes for a table record ; postcondition: the buffer between previous column object and current object ; is computed and returned as a bytarr. function get_column_buffer, colkeys, byte_count ; compute difference between start byte of current column and bytecount diff = colkeys.start_byte - byte_count - 1 ; construct the buffer array if needed buffer = (diff gt 0) ? bytarr(diff) : -1 ; add the difference to the byte count value byte_count += diff return, buffer end ; precondition: the required and optional keywords have been obtained ; for current column object, byte count has been incremented for buffer ; postcondition: an idl element is constructed for current column ; object and returned, and byte count is incremented for current bytes function construct_column_element, keys, colkeys, byte_count ; store data type and bytes and interchange format data_type = colkeys.data_type bytes = colkeys.bytes inter = keys.inter ; get idl type for current element idltype = (inter eq 'ASCII')? 1 : get_idl_type(data_type, bytes, inter) ; construct the simplest element elem = (inter eq 'ASCII') ? make_array(bytes, type = idltype) : $ fix(0, type = idltype) ; check if items > 1 then look for buffers if (colkeys.items gt 1) then begin diff = colkeys.item_offset - colkeys.item_bytes if (diff le 0) then begin ; if no buffer present, replicate items item_struct = make_array(colkeys.items, type = idltype) endif else begin ; else buffer = bytarr(diff) ; construct byte array buffer item_struct = {elem1:elem} ; store in struct for j = 2, colkeys.items do begin ; add offset buffer elem in rest text = clean(string(j), /space) item_struct = create_struct(item_struct, 'item_buffer'+text, $ buffer, 'elem'+text, elem) endfor endelse elem = item_struct ; store item structure in elem endif ; increment byte count with total bytes for current column byte_count += colkeys.total_bytes return, elem end ; precondition: the column element has been created, and so is the ; buffer, the 'number' represents the current column number, and ; structure is the current structure for the table ; postcondition: the column element and the buffer is added to the ; structure function addto_column_structure, number, buffer, colelem, struct ; first construct the tag names for the elements to be stored buf_name = 'buffer' + clean(string(number + 1), /space) col_name = 'column' + clean(string(number + 1), /space) ; determine the idl type of struct struct_type = size(struct, /type) ; if buffer is not -1 then store buffer in the structure if (size(buffer, /n_dimensions) ne 0) then begin if (struct_type eq 2) then begin ; if struct = 0 then construct struct = create_struct(buf_name, buffer, col_name, colelem) endif else begin ; add to previous structure struct = create_struct(struct, buf_name, buffer, col_name, colelem) endelse endif else begin ; if no buffer then if (struct_type eq 2) then begin ; if struct = 0 then construct struct = create_struct(col_name, colelem) endif else begin ; add to previous structure struct = create_struct(struct, col_name, colelem) endelse endelse return, struct end ;- level 1 ------------------------------------------------------------------- ; precondition: the label has been verified, index and endindex are viable ; postcondition: the required and optional keywords are extracted and returned function obtain_table_keywords, label, index, endindex ; extract interchange format keyword inter = extract_keyword(label, 'INTERCHANGE_FORMAT', index, endindex, 1) inter = remove(inter, '"') ; extract rows keyword rows = extract_keyword(label, 'ROWS', index, endindex, 1) ; extract columns keyword columns = extract_keyword(label, 'COLUMNS', index, endindex, 1) ; extract row bytes keyword row_bytes = extract_keyword(label, 'ROW_BYTES', index, endindex, 1) ; extract row prefix bytes keyword row_prefix = extract_keyword(label, 'ROW_PREFIX_BYTES', index, endindex, 0) row_prefix = (row_prefix eq '###~') ? 0 : long(row_prefix) ; extract row suffix bytes keyword row_suffix = extract_keyword(label, 'ROW_SUFFIX_BYTES', index, endindex, 0) row_suffix = (row_suffix eq '###~') ? 0 : long(row_suffix) ; store in structure struct = {inter:inter, rows:long(rows), columns:long(columns), $ row_bytes:long(row_bytes), row_prefix:row_prefix, $ row_suffix:row_suffix} return, struct end ; precondition: the required and optional table keywords have been extracted ; postcondition: the column definitions are obtained and used to construct ; the table structure for idl reading function construct_table_structure, label, index, endindex, keys ; first take care of prefix bytes struct = (keys.row_prefix ne 0) ? {prefix:bytarr(keys.row_prefix)} : 0 ; obtain column objects colobjs = obtain_column_objects(label, index, endindex) col_nam = colobjs.name ; store column object names col_ind = colobjs.index ; store index col_cnt = keys.columns ; store column counts ; initialize byte count for the table byte_count = keys.row_prefix ; initialize to row prefix bytes ; go through each column object and create structure for i = 0L, col_cnt - 1 do begin ; get endindex for current column object col_end = get_index(label, col_ind[i]) ; get column keywords from label colkeys = obtain_column_keywords(label, col_ind[i], col_end) ; get column buffer variable if any required buffer = get_column_buffer(colkeys, byte_count) ; construct the column element from data type colelem = construct_column_element(keys, colkeys, byte_count) ; add the buffer and column element to structure struct = addto_column_structure(i, buffer, colelem, struct) endfor ; take care of suffix bytes if any if (keys.row_suffix eq 0 && keys.inter eq 'ASCII') then begin ; struct = create_struct(struct, 'suffix', bytarr(2)) ; add crlf chars diff = keys.row_bytes - byte_count struct = create_struct(struct, 'suffix', BYTARR(diff)) endif else if (keys.row_suffix ne 0) then begin ; add suffix bytes struct = create_struct(struct, 'suffix', bytarr(keys.row_suffix)) endif ; replicate the structure rows times struct = replicate(struct, keys.rows) return, struct end ; precondition: the label contains data type for the table, index and ; endindex are ranges for the current table object ; postcondition: the architecture of the data is extracted from the label function obtain_table_architecture, label, index, endindex ; extract the first data type keyword data_type = extract_keyword(label, 'DATA_TYPE', index, endindex, 1, 1) ; check if there exists lsb, vax or pc characters in the data type arch = (stregex(data_type, '(LSB)|(VAX)|(PC)', /boolean)) ? 'LSB' : 'MSB' return, arch end ; precondition: the pointer contains the filename and the bytes to be skipped ; for current table, and struct contains the structure to be read from ; the file; arch is the architecture of the system (only used for BINARY) ; postcondition: the data is read from the file function read_table_data, pointer, struct, arch, inter ; error protection on_ioerror, signal ; construct data structure data_read = {flag:1} ; if interchange format of the table is binary, then swap endian if (inter eq 'BINARY') then begin if (arch eq 'MSB') then begin openr, unit, pointer.datafile, /get_lun, /swap_if_little_endian endif else begin openr, unit, pointer.datafile, /get_lun, /swap_if_big_endian endelse endif else begin openr, unit, pointer.datafile, /get_lun endelse ; set the file pointer to current object to be read point_lun, unit, pointer.skip ; read the table object into the structure readu, unit, struct ; close the unit and free it close, unit free_lun, unit ; add to structure data_read = create_struct(data_read, 'struct', struct) ; return return, data_read signal: on_ioerror, null print, "Error: file either corrupted or invalid parameters specified" data_read.flag = 0 return, data_read end ; precondition: the data structure has been read from file, and ; keywords contains keywords for current table ; postcondition: the data is extracted from the structure; sorted and ; converted into proper format. function convert_table_data, struct, keywords, label, index, endindex ; first extract the data from the structure new_struct = {names:strarr(keywords.columns)} ; initialize structure names = tag_names(struct) ; obtain tag names for struct ; determine the positions in the names array which are "COLUMN" pos = where(stregex(names, 'COLUMN', /boolean) eq 1) ; obtain column objects from label colobjs = obtain_column_objects(label, index, endindex) ; create a new structure for k = 0L, keywords.columns - 1 do begin ; for each column tag = names[pos[k]] ; store the tag name column = struct[*].(pos[k]) ; store column data ; extract item elements from current column object if (size(struct.(pos[k]), /type) eq 8) then begin ; if item struct item_names = tag_names(struct.(pos[k])) ; extract tag names ; get position of non buffer items tag from item names arr ipos = where(stregex(item_names, 'ELEM', /boolean) eq 1, match) dim = size(column.(ipos[0]), /dimensions) ; get dim of column itype = size(column.(ipos[0]), /type) ; get type of elem iarr = make_array([dim, match], type = itype) ; initialize arr for d = 0, match - 1 do begin ; for each element in item iarr[*,*,d] = column.(ipos[d]) ; store in item array endfor column = iarr ; store as current column endif ; get endindex for column object col_end = get_index(label, colobjs.index[k]) ; get column keywords for current column colkeys = obtain_column_keywords(label, colobjs.index[k], col_end) ; store column name in structure new_struct.names[k] = colkeys.name ; if ascii table then perform conversion if (keywords.inter eq 'ASCII') then begin column = string(column) ; first convert to string ; obtain idl type for current column idltype = get_idl_type(colkeys.data_type, colkeys.bytes, 'ASCII') ; convert column to idltype column = fix(column, type=idltype) endif else if (colkeys.bit_mask ne '0') then begin ; if binary data and bit mask value is present, then apply column = apply_bit_mask(column, colkeys.bit_mask) endif ; add to the new structure new_struct = create_struct(new_struct, tag, column) ; add to struct endfor return, new_struct end ;- level 0 ------------------------------------------------------------------- function tablepds, filename, label, index, SILENT = silent, VERIFY = verify ; error check on_error, 1 ; check for number of parameters in function call if (n_params() lt 3) then begin message, "Syntax Error: table = tablepds(filename, label, index "+$ "[, /SILENT])" endif silent = keyword_set(silent) verify = keyword_set(verify) ; verify label IF (verify) THEN $ res = verify_label(label, filename) ; check current table object index if (~ stregex(label[index], '(TABLE)|(PALETTE)|(SPECTRUM)|(SERIES)', $ /boolean)) then begin print, "Error: invalid index specified for TABLE object "+string(index) return, 0 endif ; get endindex value for current table object endindex = get_index(label, index) ; obtain all required table object keywords keywords = obtain_table_keywords(label, index, endindex) ; construct table structure struct = construct_table_structure(label, index, endindex, keywords) ; obtain object name from current index name = stregex(label[index], '= +([0-9A-Z_]+)', /extract, /subexpr) ; obtain pointer information pointer = pointpds(label, filename, name[1]) ; if not silent then inform the user if (~ silent) then begin print, "Now reading " + clean(string(keywords.rows), /space) + " by "+$ clean(string(keywords.columns), /space) + " table " endif ; determine architecture arch = obtain_table_architecture(label, index, endindex) ; read data from file data_read = read_table_data(pointer, struct, arch, keywords.inter) if (~ data_read.flag) then return, -1 ; perform converion on data data = convert_table_data(data_read.struct, keywords, label,index,endindex) return, data end