/***********************************************************************//** * @brief Connect table column to FITS file * * @param[in] vptr Column file void pointer. * * A table column is connected to a FITS file when the m_fitsfile holds a * FITS file pointer. In that way, the column knows to which file it belongs. ***************************************************************************/ void GFitsTableCol::connect(void* vptr) { // Connect table column by copying the column file pointer FPTR_COPY(m_fitsfile, vptr); // Return return; }
/***********************************************************************//** * @brief Open Image * * @param[in] vptr FITS file void pointer. * * @exception GException::fits_error * FITS error. * * Open FITS image in FITS file. Opening means connecting the FITS file * pointer to the image and reading the image and axes dimensions. ***************************************************************************/ void GFitsImage::open_image(void* vptr) { // Move to HDU gammalib::fits_move_to_hdu(G_OPEN_IMAGE, vptr); // Save the FITS file pointer and the HDU number FPTR_COPY(m_fitsfile, vptr); m_hdunum = FPTR(vptr)->HDUposition; // Get the image dimensions int status = 0; status = __ffgidm(FPTR(m_fitsfile), &m_naxis, &status); if (status != 0) { throw GException::fits_error(G_OPEN_IMAGE, status); } // Reset number of image pixels m_num_pixels = 0; // Get the axes dimensions if (m_naxis > 0) { // Allocate memory for axes dimensions if (m_naxes != NULL) delete [] m_naxes; m_naxes = new long[m_naxis]; // Get the axes dimensions status = __ffgisz(FPTR(m_fitsfile), m_naxis, m_naxes, &status); if (status != 0) { throw GException::fits_error(G_OPEN_IMAGE, status); } // Calculate number of image pixels m_num_pixels = 1; for (int i = 0; i < m_naxis; ++i) { m_num_pixels *= m_naxes[i]; } } // endif: there is an image // Return return; }
/***********************************************************************//** * @brief Copy class members * * @param[in] column Column to be copied. ***************************************************************************/ void GFitsTableCol::copy_members(const GFitsTableCol& column) { // Copy attributes m_name = column.m_name; m_unit = column.m_unit; m_dim = column.m_dim; m_colnum = column.m_colnum; m_type = column.m_type; m_repeat = column.m_repeat; m_width = column.m_width; m_number = column.m_number; m_length = column.m_length; m_variable = column.m_variable; m_varlen = column.m_varlen; m_rowstart = column.m_rowstart; m_size = column.m_size; m_anynul = column.m_anynul; FPTR_COPY(m_fitsfile, column.m_fitsfile); // Return return; }
/***********************************************************************//** * @brief Save table into FITS file * * @exception GException::fits_error * A CFITSIO error occured in this method. * @exception GException::fits_bad_col_length * Table columns have inconsistent lengths. * * This method saves a table into a FITS file. * * In case that no table HDU exists it will be created by appending a new * HDU to the existing file. The definition of this HDU will be based on the * table definition in the class instance. * * The method also verifies the consistency of all table columns. Table * columns need to have identical lengths to be saved into a FITS table. * All columns with a length of zero will be excluded from saving, and if * they exist in the FITS file, they will be removed from the file. * * @todo This method should also update the header. Even easier, this method * should save the header into the file using the m_header.save() * method. * Only this assures coherence between the files !!!! Once this has * been implemented (also in the GFitsImage method) we should delete * the m_header.save() call in GFitsHDU::save. ***************************************************************************/ void GFitsTable::data_save(void) { // Debug definition: Dump method entry #if defined(G_DEBUG_SAVE) std::cout << "GFitsTable::save: entry" << std::endl; #endif // Make sure that column lengths are consistent with table length. // Columns with zero length will not be considered (why?) for (int i = 0; i < m_cols; ++i) { if (m_columns[i] != NULL && m_columns[i]->length() > 0) { if (m_columns[i]->length() != m_rows) { throw GException::fits_bad_col_length(G_DATA_SAVE, m_columns[i]->length(), m_rows); } } } // Move to HDU int status = 0; status = __ffmahd(FPTR(m_fitsfile), m_hdunum+1, NULL, &status); // If HDU does not exist in file then create it now if (status == 107) { // Reset status status = 0; // Initialise number of fields int tfields = 0; // Setup cfitsio column definition arrays char** ttype = NULL; char** tform = NULL; char** tunit = NULL; if (m_cols > 0) { ttype = new char*[m_cols]; tform = new char*[m_cols]; tunit = new char*[m_cols]; for (int i = 0; i < m_cols; ++i) { ttype[i] = NULL; tform[i] = NULL; tunit[i] = NULL; } for (int i = 0; i < m_cols; ++i) { ttype[tfields] = get_ttype(i); tform[tfields] = get_tform(i); tunit[tfields] = get_tunit(i); if (ttype[tfields] != NULL && tform[tfields] != NULL && tunit[tfields] != NULL) tfields++; } } // Create FITS table status = __ffcrtb(FPTR(m_fitsfile), m_type, m_rows, tfields, ttype, tform, tunit, NULL, &status); if (status != 0) throw GException::fits_error(G_DATA_SAVE, status); // De-allocate column definition arrays if (m_cols > 0) { for (int i = 0; i < m_cols; ++i) { if (ttype[i] != NULL) delete [] ttype[i]; if (tform[i] != NULL) delete [] tform[i]; if (tunit[i] != NULL) delete [] tunit[i]; } if (ttype != NULL) delete [] ttype; if (ttype != NULL) delete [] tform; if (ttype != NULL) delete [] tunit; } // Connect all existing columns to FITS table if (m_columns != NULL) { for (int i = 0; i < m_cols; ++i) { if (m_columns[i] != NULL) { FPTR_COPY(m_columns[i]->m_fitsfile, m_fitsfile); m_columns[i]->m_colnum = i+1; } } } // Debug option: Signal table creation #if defined(G_DEBUG_SAVE) std::cout << "GFitsTable::save: created new table" << std::endl; #endif } // ... otherwise we signal a FITS error else if (status != 0) { throw GException::fits_error(G_DATA_SAVE, status); } // Determine number of columns in table int num_cols = 0; status = __ffgncl(FPTR(m_fitsfile), &num_cols, &status); if (status != 0) { throw GException::fits_error(G_DATA_SAVE, status); } // Debug option: Log number of columns in FITS table #if defined(G_DEBUG_SAVE) std::cout << "GFitsTable::save: FITS table contains "; std::cout << num_cols << " columns." << std::endl; #endif // If we have no columns in the table then delete all columns from // FITS table if (m_columns == NULL && num_cols > 0) { for (int i = 0; i < num_cols; ++i) { status = __ffdcol(FPTR(m_fitsfile), i, &status); if (status != 0) { throw GException::fits_error(G_DATA_SAVE, status); } } } // ... otherwise update the FITS table else { // Determine number of rows in table long num_rows = 0; status = __ffgnrw(FPTR(m_fitsfile), &num_rows, &status); if (status != 0) { throw GException::fits_error(G_DATA_SAVE, status); } // Debug option: Log number of rows in FITS table #if defined(G_DEBUG_SAVE) std::cout << "GFitsTable::save: FITS table contains "; std::cout << num_rows << " rows." << std::endl; #endif // If the table length differs from number of rows in the FITS file // then readjust FITS table length. We do this by adding or // deleting rows at the end of the table as we anyways rewrite the // entire table later if (m_rows > num_rows) { // Insert rows at the end of the table long long firstrow = num_rows; long long nrows = m_rows - num_rows; status = __ffirow(FPTR(m_fitsfile), firstrow, nrows, &status); if (status != 0) { throw GException::fits_error(G_DATA_SAVE, status); } // Debug option: Log row adding #if defined(G_DEBUG_SAVE) std::cout << "GFitsTable::save: Added " << nrows; std::cout << " rows to FITS table." << std::endl; #endif } else if (m_rows < num_rows) { // Delete rows at the end of the table long long firstrow = num_rows; long long nrows = num_rows - m_rows; status = __ffdrow(FPTR(m_fitsfile), firstrow, nrows, &status); if (status != 0) { throw GException::fits_error(G_DATA_SAVE, status); } // Debug option: Log row adding #if defined(G_DEBUG_SAVE) std::cout << "GFitsTable::save: Deleted " << nrows; std::cout << " rows from FITS table." << std::endl; #endif } // Update all columns. The 'm_colnum' field specifies where in the // FITS file the column resides. If 'm_colnum=0' then we have a new // column that does not yet exist. In this case we append a new column // to the FITS file. for (int i = 0; i < m_cols; ++i) { // Only consider valid columns if (m_columns[i] != NULL) { // If column has no correspondance than add new column in // FITS table and link column to table. if (m_columns[i]->m_colnum == 0) { // Increment number of columns in FITS file num_cols++; // Append column to FITS file status = __fficol(FPTR(m_fitsfile), num_cols, get_ttype(i), get_tform(i), &status); if (status != 0) { throw GException::fits_error(G_DATA_SAVE, status); } // Connect all column to FITS table by copying over the // FITS file pointer. FPTR_COPY(m_columns[i]->m_fitsfile, m_fitsfile); m_columns[i]->m_colnum = num_cols; } // endif: column appended to FITS file // Now write column into FITS file (only if length is positive) if (m_columns[i]->length() > 0) { m_columns[i]->save(); } } // endif: column was valid } // endfor: looped over all table columns // Delete all unused columns from FITS file. We do this from last to // first so that the column numbers remain valid. Also note that // FITS column counting starts from 1. for (int colnum = num_cols; colnum > 0; --colnum) { // Get column name from FITS file char keyname[10]; char value[80]; sprintf(keyname, "TTYPE%d", colnum); status = __ffgkey(FPTR(m_fitsfile), keyname, value, NULL, &status); if (status != 0) { throw GException::fits_error(G_DATA_SAVE, status); } value[strlen(value)-1] = '\0'; std::string colname = strip_whitespace(&(value[1])); // Check if this column is actually in our list of columns bool used = false; for (int i = 0; i < m_cols; ++i) { if (m_columns[i] != NULL && m_columns[i]->length() > 0 && m_columns[i]->name() == colname) { used = true; break; } } // If column is not used then delete it now from FITS table if (!used) { // Delete column status = __ffdcol(FPTR(m_fitsfile), colnum, &status); if (status != 0) { throw GException::fits_error(G_DATA_SAVE, status); } // Debug option: Log column deletion #if defined(G_DEBUG_SAVE) std::cout << "GFitsTable::save: Deleted obsolete column "; std::cout << value << " from FITS table." << std::endl; #endif } // endif: deleted column } // endfor: Looped over all FITS columns } // endelse: FITS table has been updated // Now update the header for all columns (unit and TDIM information) for (int i = 0; i < m_cols; ++i) { // Only consider valid columns if (m_columns[i] != NULL) { // Get column number int colnum = m_columns[i]->m_colnum; // Update column units if available if (m_columns[i]->unit().length() > 0) { // Build keyname std::string keyname = "TUNIT"+str(colnum); // Update header card card(keyname, m_columns[i]->unit(), "physical unit of field"); } // Write TDIM keyword if available if (m_columns[i]->dim().size() > 0) { // Build keyname std::string keyname = "TDIM"+str(colnum); // Build value string std::string value = "("+str(m_columns[i]->dim()[0]); for (int k = 1; k < m_columns[i]->dim().size(); ++k) { value += ","+str(m_columns[i]->dim()[k]); } value += ")"; // Update header card card(keyname, value, "dimensions of field"); } // endif: wrote TDIM keyword } // endif: column was valid } // endfor: looped over all columns // Debug definition: Dump method exit #if defined(G_DEBUG_SAVE) std::cout << "GFitsTable::save: exit" << std::endl; #endif // Return return; }
/***********************************************************************//** * @brief Open Table * * @param[in] vptr FITS file pointer. * * @exception GException::fits_hdu_not_found * Specified HDU not found in FITS file. * @exception GException::fits_error * A CFITSIO error occured during loading the table. * @exception GException::fits_unknown_coltype * FITS column of unsupported type has been found in the FITS file. * @exception GException::fits_inconsistent_tdim * The TDIM information provided in the header is inconsistent * with the size of the column. * * This method loads a description of the table into memory. Column data are * not loaded at this point to save memory. They will be loaded on request * later. ***************************************************************************/ void GFitsTable::data_open(void* vptr) { // Move to HDU int status = 0; status = __ffmahd(FPTR(vptr), (FPTR(vptr)->HDUposition)+1, NULL, &status); if (status != 0) throw GException::fits_hdu_not_found(G_DATA_OPEN, (FPTR(vptr)->HDUposition)+1, status); // Save FITS file pointer FPTR_COPY(m_fitsfile, vptr); // Determine number of rows in table long nrows = 0; status = __ffgnrw(FPTR(m_fitsfile), &nrows, &status); if (status != 0) throw GException::fits_error(G_DATA_OPEN, status); else m_rows = (int)nrows; // Determine number of columns in table status = __ffgncl(FPTR(m_fitsfile), &m_cols, &status); if (status != 0) throw GException::fits_error(G_DATA_OPEN, status); // Allocate and initialise memory for column pointers. Note that this // initialisation is needed to allow for a clean free_members() call // in case of any exception. if (m_columns != NULL) delete [] m_columns; m_columns = new GFitsTableCol*[m_cols]; for (int i = 0; i < m_cols; ++i) m_columns[i] = NULL; // Get table column information int typecode = 0; long repeat = 0; long width = 0; for (int i = 0; i < m_cols; ++i) { // Get column name char keyname[10]; char value[80]; sprintf(keyname, "TTYPE%d", i+1); status = __ffgkey(FPTR(m_fitsfile), keyname, value, NULL, &status); if (status != 0) throw GException::fits_error(G_DATA_OPEN, status); value[strlen(value)-1] = '\0'; // Get column definition status = __ffgtcl(FPTR(m_fitsfile), i+1, &typecode, &repeat, &width, &status); if (status != 0) throw GException::fits_error(G_DATA_OPEN, status); // Check for unsigned columns unsigned long offset = 0; sprintf(keyname, "TZERO%d", i+1); status = __ffgky(FPTR(m_fitsfile), __TULONG, keyname, &offset, NULL, &status); if (status == 0) { if (typecode == __TSHORT && offset == 32768u) typecode = __TUSHORT; else if (typecode == __TLONG && offset == 2147483648u) typecode = __TULONG; else if (typecode == __TINT && offset == 2147483648u) typecode = __TUINT; else { std::ostringstream message; message << ", but column " << value << " has typecode " << typecode << " and unexpected associated TZERO=" << offset << "."; throw GException::fits_error(G_DATA_OPEN, 0, message.str()); } } else status = 0; // Get column unit (optional, leave blank if not found) char unit[80]; sprintf(keyname, "TUNIT%d", i+1); status = __ffgkey(FPTR(m_fitsfile), keyname, unit, NULL, &status); if (status != 0) { status = 0; unit[0] = '\0'; unit[1] = '\0'; } else unit[strlen(unit)-1] = '\0'; // Get column dimension (optional, leave blank if not found) char dim[80]; sprintf(keyname, "TDIM%d", i+1); status = __ffgkey(FPTR(m_fitsfile), keyname, dim, NULL, &status); if (status != 0) { status = 0; dim[0] = '\0'; dim[1] = '\0'; } else { dim[strlen(dim)-1] = '\0'; } // If found, extract column dimension into vector array of integers std::vector<int> vdim; std::string sdim = strip_chars(strip_whitespace(&(dim[1])),"()"); if (sdim.length() > 0) { std::vector<std::string> elements = split(sdim, ","); for (int k = 0; k < elements.size(); ++k) { vdim.push_back(toint(elements[k])); } } // Allocate column m_columns[i] = alloc_column(typecode); if (m_columns[i] == NULL) { std::ostringstream colname; colname << value; throw GException::fits_unknown_coltype(G_DATA_OPEN, colname.str(), typecode); } // Store column definition m_columns[i]->name(strip_whitespace(&(value[1]))); m_columns[i]->unit(strip_whitespace(&(unit[1]))); m_columns[i]->dim(vdim); m_columns[i]->m_colnum = i+1; m_columns[i]->m_type = typecode; m_columns[i]->m_repeat = repeat; m_columns[i]->m_width = width; m_columns[i]->m_length = m_rows; m_columns[i]->connect(FPTR(m_fitsfile)); // Extract column vector size if (m_columns[i]->m_repeat == 1) { // ASCII tables m_columns[i]->m_number = 1; } else { // Binary tables if (typecode == __TSTRING) { m_columns[i]->m_number = m_columns[i]->m_repeat / m_columns[i]->m_width; } else { m_columns[i]->m_number = m_columns[i]->m_repeat; } } // If TDIM information was set then check its consistency if (!vdim.empty()) { // Compute expectation int num = vdim[0]; for (int k = 1; k < vdim.size(); ++k) { num *= vdim[k]; } // Compare with real size if (num != m_columns[i]->m_number) { throw GException::fits_inconsistent_tdim(G_DATA_OPEN, vdim, m_columns[i]->m_number); } } // endif: Valid TDIM information was found } // endfor: looped over all columns // Return return; }