;+ ; 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