;+ ; IO_SDFITS is the base class for spectral line and continuum sdfits classes. All the GENERAL functionality for reading, writing, navigating sdfits files, and for; translating their info to data containers is placed in this class. See UML for all IO Classes, or IO_SDFITS UML for just the line and continuum sdfits classes. ; ; @field file_path The path in which index and sdfits files are to be found ; @field fits_files An array of pointers to objects, one for each sdfits file ; @field index Object which manages the index file ; @field index_synced Flag which signals wether index and fits files are in sync ; @field update_expanded_files Flag determining wether index files are updated to keep in sink with an expanded fits file ; @field one_file Flag for determining if this object is locked into working with just one file ; @field Observer should not be here? ; @field tcal_table should not be here? ; @field backend should not be here? ; @field debug Flag that determines the verbosity of object ; ; ; @file_comments ; IO_SDFITS is the base class for spectral line and continuum sdfits classes. All the general functionality for reading, writing, navigating sdfits files, and for; translating their info to data containers is placed in this class. See UML for all IO Classes, or IO_SDFITS UML for just the line and continuum sdfits classes. ; ; @inherits io ; ; @version $Id: io_sdfits__define.pro,v 1.49 2005/06/02 17:10:43 bgarwood Exp $ ;- PRO io_sdfits__define compile_opt idl2, hidden io = { io_sdfits, inherits io, $ file_path:string(replicate(32B,256)), $ fits_files:ptr_new(), $ index:obj_new(), $ index_synced:0L, $ update_expanded_files:0L, $ one_file:0L, $ observer:string(replicate(32B,256)), $ tcal_table:string(replicate(32B,256)), $ backend:string(replicate(32B,256)), $ online:0L, $ debug:0L $ } END ;+ ; Class Constructor. Initialize the object ; ; @uses IO::init ; ; @private ;- FUNCTION IO_SDFITS::init compile_opt idl2 r = self->IO::init() return, r END ;+ ; Class cleanup on deletion: cleans up index object and sdfits objects ; ; @private ;- PRO IO_SDFITS::cleanup compile_opt idl2 if (obj_valid(self.index) eq 1) then obj_destroy, self.index self->free_fits_objs END ;+ ; The class can be made to verbosly describe what its doing ;- PRO IO_SDFITS::set_debug_on compile_opt idl2 self.debug = 1 if obj_valid(self.index) then self.index->set_debug_on self->set_debug_for_fits_objs, 1 END ;+ ; The class can be made to be quite ;- PRO IO_SDFITS::set_debug_off compile_opt idl2 self.debug = 0 if obj_valid(self.index) then self.index->set_debug_off self->set_debug_for_fits_objs, 0 END ;+ ; This method looks into the given directory and attempts to load any existing ; index file. If the file does not exist, all sdfits files in this directory ; are loaded using the add_file method, and a new index is created. For a complete ; description, see the flowchart. ; ; @uses add_file ; ; @param dir {in}{type=string} The path in which all sdfits files and possibly ; the index file are to be found ; ; @examples ;
; path = '/users/me/my_project' ; io = obj_new('io_sdfits_line') ; io->set_project, path ;;- PRO IO_SDFITS::set_project, dir, _EXTRA=ex compile_opt idl2 if (self.one_file ne 0) then message, "this object is commited to using only one file" if (self->file_exists(dir) eq 0) then begin message, 'Cannot set project; directory does not exist' return endif ; parse the contents of the extra keywords new_index = 0 if n_elements(ex) ne 0 then begin tags = tag_names(ex) for i=0,n_elements(tags)-1 do begin if tags[i] eq "NEW_INDEX" then begin if ex.(i) eq 1 then new_index = 1 endif endfor endif ; set location of all sdfits files self->set_file_path, dir file_paths = file_search(dir+'/*.fits') if (self.debug eq 1) then print, file_paths files = file_basename(file_paths) ; create the name of the index file based off the parts = strsplit(dir, "/", /extract) index_file_name = parts[n_elements(parts)-1]+".index" self.index->set_file_name, index_file_name ; place the index file in this same dir full_index_file_name = self.index->get_full_file_name() if self.debug then print, "index_file_name: "+index_file_name ; read existing index file, or create a new one ;if keyword_set(new_index) then begin if new_index then begin ; create a new index wether it exists or not for i=0,n_elements(files)-1 do begin if i eq 0 then begin self->add_file,files[i],/new_index endif else begin self->add_file,files[i] endelse endfor endif else begin if (self->file_exists(full_index_file_name) eq 0) then begin ; index doesn't exist, so create one using all files in path for i=0,n_elements(files)-1 do begin self->add_file,files[i] endfor endif else begin ; index exists use it self.index->read_file, file_name=file_name, ver_status ; could not read index; version number too old if ver_status eq 0 then begin message, 'cannot load index; version number too old. Creating new index.',/info self.index->reset for i=0,n_elements(files)-1 do begin if i eq 0 then begin self->add_file,files[i],/new_index endif else begin self->add_file,files[i] endelse endfor endif ; if the index info makes no sense, dont use it if (self.index->check_index_with_reality() eq 0) then begin message, 'cannot load pre-existing index; creating new index',/info self.index->reset for i=0,n_elements(files)-1 do begin if i eq 0 then begin self->add_file,files[i],/new_index endif else begin self->add_file,files[i] endelse endfor return endif ; create fits objs for files listed in index, if need be self->conform_fits_objs_to_index self.index_synced = 1 ; add the additional files that might not be in this index already index_files = self->get_index_files() for i=0,n_elements(files)-1 do begin cnt=0 c = where(files[i] eq index_files,cnt) if (cnt eq 0) then begin ; this file isn't in the index file - add it self->add_file,files[i] endif endfor endelse ; if index exists endelse ; if new_index keyword set END ;+ ; This method can be used to lock the io object into working with only one ; sdfits file. An index file is automatically created (overwrites pre-existing one). ; @uses add_file ; @param file_name {in}{type=string} The file name of the sdfits file (no path) ; ; @keyword file_path {in}{optinal}{type=string} Where to find the sdfits file ; @keyword index_name {in}{optinal}{type=string}{default='file_name.index'} What to call the new index file ; ; @examples ;
; io = obj_new('io_sdfits_cntm') ; io->set_file, 'TREG_04_01.dcr.raw.fits' ;;- PRO IO_SDFITS::set_file, file_name, file_path=file_path, index_name=index_name, online=online, _EXTRA=ex compile_opt idl2 if (self.one_file ne 0) then message, "this object is commited to using only one file" ; online mode supercedes all other keywords if keyword_set(online) then begin self->set_online, file_name return endif ; discard all other fits objects self->free_fits_objs ; see if file path is inlcuded seperately if keyword_set(file_path) then begin self->set_file_path, file_path file_base=file_name endif else begin ; see if file path is inlcuded in file name or not if (strpos(file_name,'/') ne -1) then begin self->set_file_path, file_dirname(file_name) file_base = file_basename(file_name) endif else begin file_base = file_name endelse endelse ; index file name == to file, or keyword? if keyword_set(index_name) then begin index_file=index_name endif else begin parts=strsplit(file_base,'.',/extract) index_file = strjoin(parts[0:n_elements(parts)-2],'.')+'.index' endelse self->set_index_file_name, index_file ; add file, loading or creating a new index according to keyword self->add_file, file_base, _EXTRA=ex ; mark this io object as dedicated to one file self.one_file = 1 END ;+ ; This method is the main interface to the sdfits io classes. It is used in turn ; by set_project and set_file. Most of the logic for keeping the index file in ; sync with the fits files is coded in this method. For a complete description, ; see the flowchart. ; ; @param file_name {in}{type=string} Name of sdfits file to add (no path) ; ; @keyword new_index {in}{optinal}{type=boolean}{default=0} Forces the creation ; of a new index file (overwrites pre-existing index) ; ; @examples ;
; path = '/users/me/my_project' ; io = obj_new('io_sdfits_line') ; io->set_file_path, path ; io->add_file, 'TREG_O1_04.acs.raw.fits' ;;- PRO IO_SDFITS::add_file, file_name, new_index=new_index, index_name=index_name compile_opt idl2 if (self.one_file ne 0) then message, "this object is commited to using only one file" if keyword_set(new_index) then make_new_index=1 else make_new_index=0 if keyword_set(index_name) then self.index->set_file_name, index_name ; check file existence if (self->file_exists(self->get_full_file_name(file_name)) eq 0 ) then begin message, "Cannot add file that does not exist: "+file_name return endif ; check fits object existence fits_obj = self->get_fits(file_name) if (obj_valid(fits_obj) eq 0) then begin ; create an interface object for this file' self->add_fits_obj, file_name fits_obj = self->get_fits(file_name) endif else begin ; nothing will be done; not really an error message, 'this file has already been added to this object' return endelse ; index must be brought in line with new fits file self.index_synced = 0 ; force a new index file? if make_new_index then begin ; starting from scratch, we're done self->create_index_file return endif ; is there an index file loaded? if (self.index->is_file_loaded() eq 0) then begin if self.debug then print, 'index not loaded' ; try to load up the named index file; file exists? if(self.index->file_exists() eq 0) then begin if self.debug then print, 'index does not exist' ; index file does not exist, create it, we're done self->create_index_file return endif else begin if self.debug then print, 'index exists, reading' ; index file exists, load it self.index->read_file, ver_status ; if version keyword is wrong, this index file could not be read. if ver_status eq 0 then begin print, "Index file is out of date ... updating to current version number." self.index->reset self->create_index_file endif ; index file match reality? a fits file with larger extension is allowable expanded = 0 index_matches = self.index->check_index_with_reality(expanded,/verbose) if (index_matches eq 0) then begin ; something doesn't agree between the index file and reality message, 'Cannot use this index file ... Creating new index file', /info self.index->reset self->create_index_file return endif endelse ; if file exists endif ; if file loaded ; check this file is in index files = self->get_index_files(/unique) ; is base file name in list of files in index? count = 0 ind = where(fits_obj->get_file_name() eq files, count) if (count eq 0) then begin if self.debug then print, 'fits file not in index, updating index with it.' ; file is not in index, update the index file, we're done self->update_index_with_fits_file, fits_obj return endif else begin ; file is in index, does index info match with fits info? expanded = 0 file_info_matches = self.index->check_file_properties(fits_obj->get_file_name(),expanded,/verbose) if file_info_matches then begin if self.debug then print, 'fits file in index and properties match.' ; all extensions have same length, we're done self.index_synced = 1 return endif else begin if self.debug then print, 'fits file in index but properties do not match.' if expanded then begin ; fits extension too big, updating? if self.update_expanded_files then begin print, 'update the damn files' endif else begin ; no -error message, 'cannot use this index file; file has been expanded since index creation.' return endelse endif else begin message, 'cannot use this index file; file properties do not match index.' return endelse ; if file has been expanded since index creation endelse ; if file properties match index endelse ; if file found in index END ;+ ; Creates a new index file using current info (file_path, observer, etc.) for index header. ; ; @private ;- PRO IO_SDFITS::init_index compile_opt idl2 self.index->set_file_name, self.index->get_file_name() self.index->new_file, self.observer, self.backend, self.tcal_table, self.file_path END ;+ ; This method will read an index file, check that the index agrees with ; the sdfits files on disk, and create fits objects for the files listed ; in its index. ; ; @param file_name {in}{type=string} Index file name (no path) ; ; @keyword file_path {in}{optinal}{type=string} Where to find index file and sdfits files ; ; @examples ;
; path = '/users/me/my_project' ; io = obj_new('io_sdfits_line') ; io->load_index, 'my_index', file_path='/users/me/my_project' ;;- PRO IO_SDFITS::load_index, file_name, file_path=file_path compile_opt idl2 if keyword_set(file_path) then self->set_file_path, file_path ; load the index info self.index->read_file, file_name=file_name, ver_status ; if the version number is old, then we can't even read it if ver_status eq 0 then begin message, "Cannot load pre-existing index: old version number." return endif ; if the index info makes no sense, dont use it if (self.index->check_index_with_reality() eq 0) then begin message, 'Cannot load pre-existing index: does not match files' return endif ; create fits objs for files listed in index, if need be self->conform_fits_objs_to_index ; we're good to go self.index_synced = 1 END ;+ ; Calls index file's read_file method ; @param file_name {in}{type=string} full path name to the index file ; @uses INDEX_FILE::read_file ; @private ;- PRO IO_SDFITS::read_index, file_name compile_opt idl2 self.index->read_file, file_name=file_name END ;+ ; Creates a fits object for the file name passed to it, and adds it to the list of fits objects ; @param file_name {in}{type=string} full path name of the sdfits file to add ; @uses IO_SDFITS::get_full_file_name ; @uses IO_SDFITS::get_new_fits_obj ; @private ;- PRO IO_SDFITS::add_fits_obj, file_name, _EXTRA=ex ; get the full path full_file_name = self->get_full_file_name(file_name) ; check to see if this fits object already exists if (self->find_fits(full_file_name) ne -1) then begin message, "Cannot add fits file, fits object exists already for fits file: "+full_file_name return endif new_fits = self->get_new_fits_obj(full_file_name, _EXTRA=ex) ; pass on the debug state if self.debug then new_fits->set_debug_on if (obj_valid(new_fits) eq 0) then message, "Could not create valid sdfits object for file: "+full_file_name if ptr_valid(self.fits_files) then begin *self.fits_files = [*self.fits_files, new_fits] endif else begin self.fits_files = ptr_new([new_fits]) endelse END ;+ ; From an array of full path names to sdfits files, create an array of sdfits objects ; @private ;- PRO IO_SDFITS::add_fits_objs, file_names compile_opt idl2 for i=0,n_elements(file_names)-1 do begin self->add_fits_obj, file_names[i] endfor END ;+ ; Creates a new index file, populating it with information from all ; the fits object ; @param index_file_name {in}{type=string} full path name to index file ; @uses SDFITS::get_rows ; @uses FITS::get_extension_header_value ; @uses IO_SDFITS::init_index ; @uses IO_SDFITS::update_index_with_fits_file ; @private ;- PRO IO_SDFITS::create_index_file, index_file_name compile_opt idl2 print, "About to create Index..." fits_files = *self.fits_files ; HACK HACK HACK ; use the first fits object to init index with common info fits_obj = fits_files[0] ;rows = fits_obj->get_rows(ext=ext,row_nums=[0]) ;self.observer = rows[0].observer self.observer = 'unknown' self.backend = fits_obj->get_extension_header_value('BACKEND') self.tcal_table = 'unknown' self->init_index ; use fits objects to populate index file for i = 0, (n_elements(fits_files)-1) do begin fits_obj = fits_files[i] self->update_index_with_fits_file, fits_obj endfor self.index_synced = 1 print, "Index file created." END ;+ ; Checks to make sure index file that is loaded agrees with this object, ; and will create new sdfits objects if necessary to conform. ; @uses INDEX_FILE::get_column_values ; @uses INDEX_FILE::get_header_value ; @uses IO_SDFITS::file_exists ; @uses INDEX_FILE::search_for_row_info ; @uses IO_SDFITS::group_row_info ; @uses FITS_OPEN ; @uses FITS_CLOSE ; @uses IO_SDFITS::free_group_row_info ; @uses IO_SDFITS::conform_fits_objs ; @uses IO_SDFITS::set_file_path ; @private ;- FUNCTION IO_SDFITS::conform_to_index compile_opt idl2 print, 'conforming to index' ; check that files in index all exist files = self.index->get_column_values("file",/unique) file_path = self.index->get_header_value("file_path") for i = 0, n_elements(files)-1 do begin if (file_path ne '') then begin file_name = file_path + '/' + files[i] endif else begin file_name = files[i] endelse if (self->file_exists(file_name) eq 0) then begin message, 'Cannot confirm index: file does not exist: '+file_name return, 0 endif if (i eq 0) then file_names=[file_name] else file_names=[file_names,file_name] endfor ; check the extensions, get all rows row_infos = self.index->search_for_row_info() ; group all rows in index file by filename and extension row_groups = self->group_row_info(row_infos) ; check the extensions for i = 0, n_elements(row_groups)-1 do begin if (file_path ne '') then begin file_name = file_path + '/' + row_groups[i].file endif else begin file_name = row_groups[i].file endelse FITS_OPEN, file_name,fcb num_extensions = fcb.nextend FITS_CLOSE,fcb if (row_groups[i].extension gt num_extensions) then begin message, 'Cannot confirm: file does not contain extension: '+string(row_groups[i].extension) return, 0 endif endfor self->free_group_row_info, row_groups ; check that we have the fits objects for these files self->conform_fits_objs, file_names ; we can set our member variables according to the index file self->set_file_path, file_path ; return success return, 1 END ;+ ; Called after loading an index, gets all files in index and calls conform_fits_objs ; @uses IO_SDFITS::get_index_files ; @uses IO_SDFITS::conform_fits_objs ; @private ;- PRO IO_SDFITS::conform_fits_objs_to_index file_names = self->get_index_files() if (self.file_path ne '') then begin file_names = self.file_path +'/'+file_names endif self->conform_fits_objs, file_names END ;+ ; Checks that the list of file names passed to it are all represented by fits objects. ; If not, the fits objects are created from scratch. ; @param file_names {in}{type=array} array of full path names to sdfits files ; @uses IO_SDFITS::get_fits ; @uses IO_SDFITS::add_fits_objs ; @private ;- PRO IO_SDFITS::conform_fits_objs, file_names compile_opt idl2 missing_fits = 0 for i = 0, n_elements(file_names)-1 do begin fits = self->get_fits(file_names[i]) if (obj_valid(fits) eq 0) then missing_fits = 1 endfor ; if we are missing a fits object, we can recover from this if (missing_fits eq 1) then begin ; recreate ALL fits objects if self.debug then print, "creating new fits objects for all files:" if self.debug then print, file_names ptr_free, self.fits_files self->add_fits_objs, file_names endif return END ;+ ; Takes in a file name string and returns the full path, with prepended path if needed. ; If the object has no file path, then the file name is returned unaltered. If ; the object DOES have a file path, then the passed in file name is checked for backslashes. ; If there is no backslash, the objects file path is prepended to the file name and passed back. ; @param file_name {in}{type=string} a file name, can be full path or not. ; @returns Either the original passed in file name, or the full path file name. ; @private ;- FUNCTION IO_SDFITS::get_full_file_name, file_name compile_opt idl2 ; if there is no file path, do nothing if (self.file_path eq '') then begin full_file_name = file_name endif else begin ; if there is a file path, see if the file_name already has one if (strpos(file_name,'/') eq -1) then begin ; we must add the file path full_file_name = self.file_path + '/' + file_name endif else begin ; the name already has some kind of path in it full_file_name = file_name endelse endelse return, full_file_name END ;+ ; Sets the path where index file and all sdfits files are to be found ; ; @param file_path {in}{type=string} Path where index file and all sdfits files are to be found. ; ; @examples ;
; path = '/users/me/my_project' ; io = obj_new('io_sdfits_line') ; io->set_file_path, path ;;- PRO IO_SDFITS::set_file_path, file_path compile_opt idl2 self.file_path = file_path self.index->set_file_path, file_path END ;+ ; Gets the path where index file and all sdfits files are to be found ; ; @returns {type=string} Path where index file and all sdfits files are to be found. ; ; @examples ;
; path = '/users/me/my_project' ; io = obj_new('io_sdfits_line') ; io->set_project, path ; print, io->get_file_path() ; '/users/me/my_project' ;;- FUNCTION IO_SDFITS::get_file_path compile_opt idl2 return, self.file_path END ;+ ; Checks to see if this object has any sdfits files connected to it. ; @returns 0 - data is not loaded; 1 - data is loaded. ;- FUNCTION IO_SDFITS::is_data_loaded compile_opt idl2 return, self.index_synced END ;+ ; Sets the file name of the index file. ;- PRO IO_SDFITS::set_index_file_name, file_name compile_opt idl2 self.index->set_file_name, file_name END ;+ ; Retrieves the file name of the index file. ; @keyword full {in}{optional}{type=boolean} wether to return the full path name of the index file or not ; @returns The file name of the index file ;- FUNCTION IO_SDFITS::get_index_file_name, full=full compile_opt idl2 if keyword_set(full) then begin file_name = self.index->get_full_file_name() endif else begin file_name = self.index->get_file_name() endelse return, file_name END ;+ ; Given the full path name of an sdfits file, returns the object ; that represents that file ; @param file_name {in}{type=string} full path name to an sdfits file ; @uses IO_SDFITS::get_full_file_name ; @returns The index for the sdfits object to represent that represents this file, or -1 if object not found ; @private ;- FUNCTION IO_SDFITS::find_fits, file_name compile_opt idl2 full_file_name = self->get_full_file_name(file_name) if self.debug then print, "searching for: "+full_file_name +" in list: " if (ptr_valid(self.fits_files) eq 0) then return, -1 fits_files = *self.fits_files fits_found = 0 i = 0 while (fits_found eq 0) and (i lt n_elements(*self.fits_files)) do begin fits_obj = fits_files[i] if self.debug then print, "fits obj #"+string(i)+" "+fits_obj->get_full_file_name() if (full_file_name eq fits_obj->get_full_file_name()) then fits_found = 1 else i = i + 1 endwhile if self.debug then print, "fits found: "+string(fits_found) if (fits_found eq 1) then return, i else return, -1 END ;+ ; @param file_name {in}{type=string} full path name to an sdfits file ; @uses IO_SDFITS::find_fits ; @returns The the sdfits object to represent that represents this file, or -1 if object not found ; @private ;- FUNCTION IO_SDFITS::get_fits, file_name compile_opt idl2 index = self->find_fits(file_name) if (index eq -1) then begin return, -1 endif else begin fits_files = *self.fits_files return, fits_files[index] endelse END ;+ ; Retrieves the number of extensions for the given sdfits file ; @param file_name {in}{type=string} full file name of the sdfits file in question ; @returns number of extensions for file_name ; @private ;- FUNCTION IO_SDFITS::get_number_extensions, file_name compile_opt idl2 fits = self->get_fits(file_name) if obj_valid(fits) then return, fits->get_number_extensions() else return, -1 END ;+ ; Retrieves the extension type for the given sdfits file and extension ; @param ext_num {in}{type=long} extension (1-based) ; @param file_name {in}{type=string} full file name of the sdfits file in question ; @returns extension type for file_name and extension ; @private ;- FUNCTION IO_SDFITS::get_extension_type, ext_num, file_name compile_opt idl2 fits = self->get_fits(file_name) if obj_valid(fits) then return, fits->get_extension_type(ext_num) else return, -1 END ;+ ; Retrieves the extension name for the given sdfits file and extension ; @param ext_num {in}{type=long} extension (1-based) ; @param file_name {in}{type=string} full file name of the sdfits file in question ; @returns extension name for file_name and extension number ; @private ;- FUNCTION IO_SDFITS::get_extension_name, ext_num, file_name compile_opt idl2 fits = self->get_fits(file_name) if obj_valid(fits) then return, fits->get_extension_name(ext_num) else return, -1 END ;+ ; Retrieves the number of rows for the given sdfits file and extension ; @param ext_num {in}{type=long} extension (1-based) ; @param file_name {in}{type=string} full file name of the sdfits file in question ; @returns number of rows for file_name and extension ; @private ;- FUNCTION IO_SDFITS::get_ext_num_rows, ext_num, file_name compile_opt idl2 fits = self->get_fits(file_name) if obj_valid(fits) then return, fits->get_ext_num_rows(ext_num) else return, -1 END ;+ ; Prints out rows from the index file used by object. For exact search parameters ; to enter, see LINE_INDEX::search_index or ; CNTM_INDEX::search_index ; ; @param start {in}{optional}{type=long} where to start the range to list ; @param finish {in}{optional}{type=long} where to stop the range to list ; @keyword sortcol {in}{optional}{type=string} what index column name to order listwith ; @keyword verbose {in}{optinal}{type=boolean}{default=0} Print out ALL information? ; @keyword user {in}{optional}{type=boolean} print out columns specified using set_user_columns? Takes precedence over verbose keyword ; @keyword columns {in}{optional}{type=string array} array of column names to print out upon list command. Takes precedence over user and verbose keywords. ; ;- PRO IO_SDFITS::list, start, finish, sortcol=sortcol, _EXTRA=ex, verbose=verbose, user=user, columns=columns compile_opt idl2 if self.index->validate_search_keywords(ex) eq 0 then begin message, "Error with search keywords, cannot perform list", /info return endif if n_elements(columns) ne 0 then begin if self.index->validate_column_names(columns) eq 0 then begin message, "Error with column names, cannot perform list", /info return endif endif ; if we're online, read in the latest index rows into memory if self.online then self->update ; find the rows in the index file that meat our search criteria results = self.index->search_index(start, finish, _EXTRA=ex) if (size(results,/dim) eq 0) then begin if results eq -1 then return endif ; order the search results if n_elements(sortcol) ne 0 then begin results = self->sort_search_results(results,sortcol) endif ; what style to list in? columns provided by user takes precedence over ; columns set for object, takes precedence over verbose or quiet listing if n_elements(columns) ne 0 then begin self.index->list, rows=results, columns=columns endif else begin if keyword_set(user) then begin self.index->list_user, rows=results endif else begin self.index->list, rows=results, verbose=verbose, _EXTRA=ex endelse endelse END ;+ ; Returns indicies of rows in index file that match search. For exact search parameters ; to enter, see LINE_INDEX::search_index or ; CNTM_INDEX::search_index ; @uses INDEX_FILE::search_index ; @returns Long array of indicies of rows in index file that match search ;- FUNCTION IO_SDFITS::get_index, _EXTRA=ex compile_opt idl2 ; if we're online, read in the latest index rows into memory if self.online then self->update if self.index->validate_search_keywords(ex) eq 0 then begin message, "Error with search keywords, cannot perform search", /info return, -1 endif ind = self.index->search_index(_EXTRA=ex) if (size(ind,/dim) eq 0) then begin if (ind eq 0) then return, -1 endif return, ind END ;+ ; Returns a structure that contains info about the scan number given, such ; as scan number, procedure name, number of integrations, ifs, etc.. ; @param scan_number {in}{type=long} scan number information is queried for ; @uses INDEX_FILE::get_scan_info ; @returns Structure containing info on scan ;- FUNCTION IO_SDFITS::get_scan_info, scan_number compile_opt idl2 ; if we're online, read in the latest index rows into memory if self.online then self->update scan_info = self.index->get_scan_info( scan_number ) return, scan_info END ;+ ; Prints out the header section of the index file used by this object ; @uses INDEX_FILE::list_header ;- PRO IO_SDFITS::list_index_header compile_opt idl2 self.index->list_header END ;+ ; Returns the values contained in the index file column used by this object ; @param column_name {in}{required}{type=string} name of the column to query ; @uses INDEX_FILE::get_column_values ; @returns the values found in the index column name specified. ;- FUNCTION IO_SDFITS::get_index_values, column_name, _EXTRA=ex compile_opt idl2 ; if we're online, read in the latest index rows into memory if self.online then self->update if self.index->validate_search_keywords(ex) eq 0 then begin message, "Error with search keywords, cannot perform search", /info return, -1 endif return, self.index->get_column_values(column_name,_EXTRA=ex) END ;+ ; Returns the unique file names (no path) contained in the index file used by this object ; @uses INDEX_FILE::get_column_values ; @returns The unique file names (no path) contained in the index file used by this object ;- FUNCTION IO_SDFITS::get_index_files, _EXTRA=ex, full=full compile_opt idl2 ; if we're online, read in the latest index rows into memory if self.online then self->update if keyword_set(full) then begin f = self.index->get_column_values("file",/unique) path = self->get_file_path() files = strarr(n_elements(f)) for i =0, n_elements(f) - 1 do begin files[i] = path + '/' + f[i] endfor endif else begin files = self.index->get_column_values("file",/unique) endelse return, files END ;+ ; Returns the unique project names (no path) contained in the index file used by this object ; @uses INDEX_FILE::get_column_values ; @returns The unique project names (no path) contained in the index file used by this object ;- FUNCTION IO_SDFITS::get_index_projects, _EXTRA=ex compile_opt idl2 ; if we're online, read in the latest index rows into memory if self.online then self->update return, self.index->get_column_values("project",/unique) END ;+ ; Returns the unique source names (no path) contained in the index file used by this object ; @uses INDEX_FILE::get_column_values ; @returns The unique source names (no path) contained in the index file used by this object ;- FUNCTION IO_SDFITS::get_index_sources, _EXTRA=ex compile_opt idl2 ; if we're online, read in the latest index rows into memory if self.online then self->update return, self.index->get_column_values("source",/unique) END ;+ ; Returns the unique procedure names (no path) contained in the index file used by this object ; @uses INDEX_FILE::get_column_values ; @returns The unique procedure names (no path) contained in the index file used by this object ;- FUNCTION IO_SDFITS::get_index_procedures, _EXTRA=ex compile_opt idl2 ; if we're online, read in the latest index rows into memory if self.online then self->update return, self.index->get_column_values("procedure",/unique) END ;+ ; Returns the unique scan names (no path) contained in the index file used by this object ; @uses INDEX_FILE::get_column_values ; @returns The unique scan names (no path) contained in the index file used by this object ;- FUNCTION IO_SDFITS::get_index_scans, _EXTRA=ex compile_opt idl2 ; if we're online, read in the latest index rows into memory if self.online then self->update return, self.index->get_column_values("SCAN",/unique) END ;+ ; Groups rows from the index table and sorts them by file-extension, since if we read these rows ; we want to do that efficiently (one file-extension at a time). ; @param row_info {in}{type=array} array of structures that mirror rows in the index file ; @returns an array of structures, each struct representing index file rows grouped by file-extension. ; @private ;- FUNCTION IO_SDFITS::group_row_info, row_info compile_opt idl2 ; get all files files = row_info.file unique_files = files[uniq(files[sort(files)])] group = {sdfits_row_group} for i = 0, (n_elements(unique_files)-1) do begin file_locals = row_info[ where(row_info.file eq unique_files[i]) ] exts = file_locals.extension unique_exts = exts[uniq(exts[sort(exts)])] for j = 0, (n_elements(unique_exts)-1) do begin file_ext_locals = file_locals[ where(file_locals.extension eq unique_exts[j]) ] ; collapse the array into one struct group.file = file_ext_locals[0].file group.extension = file_ext_locals[0].extension group.rows = ptr_new(file_ext_locals.row_num) group.integrations = ptr_new(file_ext_locals.integration) group.if_numbers = ptr_new(file_ext_locals.if_number) if (i eq 0) and (j eq 0) then groups = [group] else groups = [groups,group] endfor endfor return, groups END ;+ ; Deprecated function: given a listing of rows from the same file-extension, this uses ; an sdfits objects to simply return those rows from the file. ; @param row_group {in}{type=struct} struct that specifies location of rows to be read from a file-extension ; @uses IO_SDFITS::get_fits ; @uses SDFITS::get_sdfits_rows ; @returns array of structs mirroring the rows in the sdfits file specified to read in. ; @private ;- FUNCTION IO_SDFITS::get_sdfits_rows, row_group compile_opt idl2 rows_ptr = row_group.rows ext = row_group.extension rows = *rows_ptr fits = self->get_fits(row_group.file) if obj_valid(fits) then begin if self.debug then print, fits->get_file_name() if self.debug then print, fits->get_full_file_name() sdfits_rows = fits->get_sdfits_rows(ext=ext, row_nums=rows) endif else begin message, 'no fits object for: '+row_group.file return, -1 endelse return, sdfits_rows END ;+ ; Given a listing of rows from the same file-extension, this uses ; an sdfits object to simply return those rows from the file and evaluate what ; columns are missing and what are the keywords for the extension ; @param row_group {in}{type=struct} struct that specifies location of rows to be read from a file-extension ; @param missing {out}{type=array} array of column names that were expected in extension, but are missing ; @param virtuals {out}{type=struct} struct that contains keywords that were found in the extension header ; @uses IO_SDFITS::get_fits ; @uses SDFITS::get_and_eval_rows ; @returns array of structs mirroring the rows in the sdfits file specified to read in. ; @private ;- FUNCTION IO_SDFITS::get_and_eval_rows, row_group, missing, virtuals rows_ptr = row_group.rows ext = row_group.extension rows = *rows_ptr fits = self->get_fits(row_group.file) if obj_valid(fits) then begin sdfits_rows = fits->get_and_eval_rows(missing, virtuals, rows, ext=ext) endif else begin message, 'no fits object for: '+row_group.file return, -1 endelse return, sdfits_rows END ;+ ; Method for attempting to extract a value from an sdfits row. If the row contains the ; tag name requested, that value is passed back. If that tag name actually specifies a ; keyword in the extension-header, and NOT a column, then that value is returned. Finally, ; if the tag name mathes one of the expected column names that were not found in this ; extension, the default value is returned. ; @param row {in}{type=struct} structure that mirrors a row in an sdfits file ; @param tag_name {in}{type=string} name of the value that we want to retrieve ; @param virtuals {in}{type=struct} struct giving the keyword-values found in the file-extension ; @param names {in}{type=struct} struct contiaining pointers to the names of columns in the row, missing columns, and tag names in the virtuals struct ; @param default_value {in} value to be returned if the tag_name is of a missing column ; @returns either the value of row.tag_name, virtauls.tag_name, or default_value ; @private ;- FUNCTION IO_SDFITS::get_row_value, row, tag_name, virtuals, names, default_value compile_opt idl2 ; look for the tag name inside each member of 'names' i = where(tag_name eq *names.row) if (i ne -1) then begin ; its in the sdfits row value = row.(i) endif else begin ; see if there are virtual cols to check if (size(*names.virtuals,/dim) ne 0) then begin i = where(tag_name eq *names.virtuals) endif else begin i = -1 endelse if (i ne -1) then begin ; its a keyword in ext header (virtual) value = virtuals.(i) endif else begin ; see if there are missing cols to check if (size(*names.missing,/dim) ne 0) then begin i = where(tag_name eq *names.missing) endif else begin i = -1 endelse if (i ne -1) then begin ; its a missing column from sdfits row value = default_value ;missing.(i) endif else begin ; use the default value again ;print, 'tag_name: '+tag_name+' not found in row, missing, or virtuals' value = default_value endelse endelse endelse return, value END ;+ ; Translates the polarization from the sdfits integer value into a char based value ; @returns Char representation of polarization ; @private ;- FUNCTION IO_SDFITS::format_sdfits_polarization, pol compile_opt idl2 case pol of 1: value = 'I' 2: value = 'Q' 3: value = 'U' 4: value = 'V' -1: value = 'RR' -2: value = 'LL' -3: value = 'RL' -4: value = 'LR' -5: value = 'XX' -6: value = 'YY' -7: value = 'XY' -8: value = 'YX' else: value = '?' endcase return, value END ;+ ; Translates the sdfits string for frequency type to the gbtidl representation ; ; CTYPE1 has two parts: ; ; First 4 characters are the type of quantity on that axis. Possible ; values are: ; ; FREQ == frequency. The axis is linear in frequency. ; VELO == velocity. The axis is linear in velocity. ; FELO == The axis is linear in frequency, but the quantities describing ; it are given in velocity units in the optical convention. ; ; These all date from a AIPS memo by Eric Griesen from November of 1983. ; The WCS paper III finally gets it right and eventually we'll have to ; update SDFITS to do that as Arecibo apparently has done. SDFITS, ; though, is still stuck in 1983. ; ; The second 4 characters describe the reference frame. These come ; from M&C, which uses these codes: ; OBS - observed (sky frequencies) ; GEO - geocentric ; BAR - barycentric ; HEL - heliocentric ; GAL - galactic ; LSR - Local Standard of Rest (equivalent to Kinematic LSR or LSRK) ; LSD - Dynamic Local Standard of Rest ; LGR - Local group ; CMB - Cosmic Microwave Background ; ; There's a "-" as the first character in this second set of 4. ; ; For reasons I can't remember, we decided that OBS should be translated ; to TOPO ; but everything else was okay as is. ; ; The sdfits tool at the moment always sets CTYPE1=FREQ-OBS, but of course ; other ; SDFITS files might have other combinations. ; ; For the moment, ensure that the first 4 characters are FREQ, and set ; frequency_type ; to the characters after the "-", translating "OBS" to "TOPO". ; ; Eventually, we'll want to translate VELO and FELO to true FREQ axes in the ; data container, but I think you should just reject those for now and ; come back to it ; later. ; @private ;- FUNCTION IO_SDFITS::format_sdfits_freq_type, freq_type compile_opt idl2 freq_type = strtrim(freq_type,2) first_half = strmid(freq_type,0,4) ; first part must be FREQ or its an error if (first_half eq 'FREQ' ) then begin ; ignore any dashes in the second half first_char = strmid(freq_type,4,1) if (first_char eq '-') then begin second_half = strmid(freq_type,5,3) endif else begin second_half = strmid(freq_type,4,4) endelse ; translate some second half values if (second_half eq 'OBS') then second_half = 'TOPO' value = second_half endif else begin value = 'unknown' endelse return, value END ;+ ; Deprecated ; @private ;- FUNCTION IO_SDFITS::format_sdfits_procedure, obsmode compile_opt idl2 obsmodes = strsplit(obsmode,":",/extract) return, obsmodes[0] END ;+ ; Translates the sdfits value for obsmode into seperate procedure, switch state and switch signal values ; @param obsmode {in}{type=string} the obsmode column from sdfits; takes form 'proc:swstate:swtchsig' ; @param proc {out}{type=string} procedure ; @param swstate {out}{type=string} switch state ; @param swtchsig {out}{type=string} switch signal ; @private ;- PRO IO_SDFITS::parse_sdfits_obsmode, obsmode, proc, swstate, swtchsig parts = strsplit(obsmode,":",/extract) if (n_elements(parts) ne 3) then begin proc = 'unknown' swstate = 'unknown' swtchsig = 'unknown' endif else begin proc = parts[0] swstate = parts[1] swtchsig = parts[2] endelse END ;+ ; Incomplete: Translates the sdfits values for longitutde and latitude type into a coordinate mode. ; @param long_type {in}{type=string} type of longitude coordinate ; @param lat_type {in}{type=string} type of latitude coordinate ; @returns coordinate mode ; @private ;- FUNCTION IO_SDFITS::coord_mode_from_types, long_type, lat_type compile_opt idl2 ; default value value = 'OTHER' long_type = strtrim(strupcase(long_type),2) lat_type = strtrim(strupcase(lat_type),2) if (long_type eq 'RA') and (lat_type eq 'DEC') then begin value = 'RADEC' endif if (long_type eq 'GLON') and (lat_type eq 'GLAT') then begin value = 'GALACTIC' endif if (long_type eq 'HA') and (lat_type eq 'DEC') then begin value = 'HADEC' endif if (long_type eq 'AZ') and (lat_type eq 'EL') then begin value = 'AZEL' endif if (long_type eq 'OLON') and (lat_type eq 'OLAT') then begin value = 'OTHER' endif return, value END ;+ ; Translates sdfits cal string value into 1 or 0 ; @param cal {in}{type=string} sdfits sig value ; @returns 1,0 ; @private ;- FUNCTION IO_SDFITS::translate_cal, cal compile_opt idl2 ; cal default is 0 if (cal eq 'T') then return, 1 else return, 0 END ;+ ; Translates sdfits sig string value into 1 or 0 ; @param sig {in}{type=string} sdfits sig value ; @returns 1,0 ; @private ;- FUNCTION IO_SDFITS::translate_sig, sig compile_opt idl2 ; sig default is 1 if (sig eq 'T') or (sig eq 'U') then return, 1 else return, 0 END ;+ ; Releases the memory for all the current sdfits objects for this object ; @private ;- PRO IO_SDFITS::free_fits_objs compile_opt idl2 if (ptr_valid(self.fits_files)) then begin fits_files = *self.fits_files for i = 0,(n_elements(fits_files)-1) do begin if obj_valid(fits_files[i]) then obj_destroy,fits_files[i] endfor ptr_free,self.fits_files endif END ;+ ; Sets debug flag for all fits objs ; @private ;- PRO IO_SDFITS::set_debug_for_fits_objs, debug compile_opt idl2 if (ptr_valid(self.fits_files)) then begin fits_files = *self.fits_files for i = 0,(n_elements(fits_files)-1) do begin if debug then begin if obj_valid(fits_files[i]) then fits_files[i]->set_debug_on endif else begin if obj_valid(fits_files[i]) then fits_files[i]->set_debug_off endelse endfor ptr_free,self.fits_files endif END ;+ ; Diagnostic function to determine if index file indicies are unique (as they should be) ; @returns 0 - bad, 1 - good ;- FUNCTION IO_SDFITS::are_index_file_indicies_unique compile_opt idl2 return, self.index->are_index_file_indicies_unique() END ;+ ; Sets the object to print rows using the interactive 'more' format ;- PRO IO_SDFITS::set_more_format_on compile_opt idl2, hidden self.index->set_more_format_on END ;+ ; Sets the object NOT to print rows using the interactive 'more' format ;- PRO IO_SDFITS::set_more_format_off compile_opt idl2, hidden self.index->set_more_format_off END ;+ ; Prints the available columns from the rows section for list; ; these are also the valid search keywords ;- PRO IO_SDFITS::list_available_columns compile_opt idl2, hidden self.index->list_available_columns END ;+ ; Sets what columns should be used for user listing ; @param columns {in}{required}{type=string array} array of columns to print on list command ;- PRO IO_SDFITS::set_user_columns, columns compile_opt idl2, hidden self.index->set_user_columns, columns END ;+ ; Prints the columns currently selected for the user specified listing ;- PRO IO_SDFITS::list_user_columns compile_opt idl2, hidden self.index->list_user_columns END ;+ ; Retrieves number of records the object is connected to - or how many rows ; currently in the index file ; @returns number of records this object is connected to ;- FUNCTION IO_SDFITS::get_num_index_rows compile_opt idl2, hidden return, self.index->get_num_index_rows() END ;+ ; ; Given an integer array representing some rows in the index, ; sorts this array by a given column name in the index file ; ; @param results {in}{required}{type=array} integer array representing some rows in the index file ; @param column {in}{required}{type=string} must be uniquely identify a column in the index file ; ;- FUNCTION IO_SDFITS::sort_search_results, results, column compile_opt idl2, hidden column = strtrim(strupcase(column),2) ; make sure this is a valid column name if not self.index->validate_column_names(column) then begin message, "Cannot sort search results; column name invalid: "+column, /info return, results endif ; sort the results - first get the values for the column in question values = self.index->get_column_values( column, subset=results) sorted_values = values[sort(values)] sorted_results = make_array(n_elements(values),/long) used = -1 if self.debug then print, "values: ", values ; reorder the results according to the sorted values array for i=0,n_elements(values)-1 do begin cnt = 0 ind = where(sorted_values[i] eq values, cnt) if self.debug then print, "sorted_values[i]: ", sorted_values[i] if self.debug then print, "are found at: ", ind if cnt gt 1 then begin ; this sorted value is not unique, have we used any of these values yet? if n_elements(used) eq 1 then if used eq -1 then first=1 else first=0 if first then begin ; no, just use the first value for the sorted result sorted_results[i] = results[ind[0]] used = [ind[0]] if self.debug then print, "first used: ", used endif else begin ; we have used some of these values already ; use just the first one that hasn't been placed in the sorted result if self.debug then print, "used: ", used next_not_used = ind[n_elements(used)] if self.debug then print, "next_not_used: ", next_not_used sorted_results[i] = results[next_not_used] used = [used,next_not_used] ; was this the last one? if n_elements(used) eq n_elements(ind) then used = -1 endelse endif else begin ; this sorted value is unique, we can place it in the sorted results sorted_results[i] = results[ind] used = -1 endelse endfor return, sorted_results END