/*************************************************************************** * GFitsTable.cpp - FITS table base class * * ----------------------------------------------------------------------- * * copyright (C) 2008-2012 by Juergen Knoedlseder * * ----------------------------------------------------------------------- * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * * * ***************************************************************************/ /** * @file GFitsTable.cpp * @brief FITS table abstract base class implementation * @author Juergen Knoedlseder */ /* __ Includes ___________________________________________________________ */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include "GException.hpp" #include "GTools.hpp" #include "GFitsCfitsio.hpp" #include "GFits.hpp" #include "GFitsTable.hpp" #include "GFitsTableBitCol.hpp" #include "GFitsTableByteCol.hpp" #include "GFitsTableBoolCol.hpp" #include "GFitsTableStringCol.hpp" #include "GFitsTableUShortCol.hpp" #include "GFitsTableShortCol.hpp" #include "GFitsTableULongCol.hpp" #include "GFitsTableLongCol.hpp" #include "GFitsTableLongLongCol.hpp" #include "GFitsTableFloatCol.hpp" #include "GFitsTableDoubleCol.hpp" #include "GFitsTableCFloatCol.hpp" #include "GFitsTableCDoubleCol.hpp" /* __ Method name definitions ____________________________________________ */ #define G_ACCESS1 "GFitsTable::operator[](int&)" #define G_ACCESS2 "GFitsTable::operator[](std::string&)" #define G_APPEND_COLUMN "GFitsTable::append_column(GFitsTableCol*)" #define G_INSERT_COLUMN "GFitsTable::insert_column(int, GFitsTableCol*)" #define G_INSERT_ROWS "GFitsTable::insert_rows(int&, int&)" #define G_REMOVE_ROWS "GFitsTable::remove_rows(int&, int&)" #define G_DATA_OPEN "GFitsTable::data_open(void*)" #define G_DATA_SAVE "GFitsTable::data_save()" #define G_GET_TFORM "GFitsTable::get_tform(int&)" /* __ Macros _____________________________________________________________ */ /* __ Coding definitions _________________________________________________ */ /* __ Debug definitions __________________________________________________ */ //#define G_DEBUG_SAVE //!< Debug data_save() method /*========================================================================== = = = Constructors/destructors = = = ==========================================================================*/ /***********************************************************************//** * @brief Void constructor * * Construct an instance of GFitsTable with zero rows. ***************************************************************************/ GFitsTable::GFitsTable(void) : GFitsHDU() { // Initialise class members for clean destruction init_members(); // Return return; } /***********************************************************************//** * @brief Table constructor * * @param[in] nrows Number of rows in table. * * Construct an instance of GFitsTable with a given number of rows. ***************************************************************************/ GFitsTable::GFitsTable(int nrows) : GFitsHDU() { // Initialise class members for clean destruction init_members(); // Store numnber of rows m_rows = nrows; // Return return; } /***********************************************************************//** * @brief Copy constructor * * @param[in] table Table from which to construct instance. ***************************************************************************/ GFitsTable::GFitsTable(const GFitsTable& table) : GFitsHDU(table) { // Initialise class members for clean destruction init_members(); // Copy members copy_members(table); // Return return; } /***********************************************************************//** * @brief Destructor ***************************************************************************/ GFitsTable::~GFitsTable(void) { // Free members free_members(); // Return return; } /*========================================================================== = = = Operators = = = ==========================================================================*/ /***********************************************************************//** * @brief Assignment operator * * @param[in] table Table. ***************************************************************************/ GFitsTable& GFitsTable::operator=(const GFitsTable& table) { // Execute only if object is not identical if (this != &table) { // Copy base class members this->GFitsHDU::operator=(table); // Free members free_members(); // Initialise private members for clean destruction init_members(); // Copy members copy_members(table); } // endif: object was not identical // Return this object return *this; } /***********************************************************************//** * @brief Returns reference to table column * * @param[in] colnum Column number [0,...,ncols()-1]. * * @exception GException::fits_no_data * No data found in table. * @exception GException::out_of_range * Column number is out of range. ***************************************************************************/ GFitsTableCol& GFitsTable::operator[](const int& colnum) { // If there is no data then throw an exception if (m_columns == NULL) { throw GException::fits_no_data(G_ACCESS1, "No columns in table."); } // Compile option: raise exception if column number is out of range #if defined(G_RANGE_CHECK) if (colnum < 0 || colnum >= m_cols) throw GException::out_of_range(G_ACCESS1, colnum, 0, m_cols-1); #endif // Get column pointer GFitsTableCol* ptr = m_columns[colnum]; if (ptr == NULL) { throw GException::fits_no_data(G_ACCESS1, "No data for this column."); } // Return reference return *ptr; } /***********************************************************************//** * @brief Returns reference to table column (const version) * * @param[in] colnum Column number [0,...,ncols()-1]. * * @exception GException::fits_no_data * No data found in table. * @exception GException::out_of_range * Column number is out of range. ***************************************************************************/ const GFitsTableCol& GFitsTable::operator[](const int& colnum) const { // If there is no data then throw an exception if (m_columns == NULL) { throw GException::fits_no_data(G_ACCESS1, "No columns in table."); } // Compile option: raise exception if column number is out of range #if defined(G_RANGE_CHECK) if (colnum < 0 || colnum >= m_cols) throw GException::out_of_range(G_ACCESS1, colnum, 0, m_cols-1); #endif // Get column pointer const GFitsTableCol* ptr = m_columns[colnum]; if (ptr == NULL) { throw GException::fits_no_data(G_ACCESS1, "No data for this column."); } // Return reference return *ptr; } /***********************************************************************//** * @brief Returns reference to table column * * @param[in] colname Column name. * * @exception GException::fits_no_data * No data found in table. * @exception GException::fits_column_not_found * Column name not found. ***************************************************************************/ GFitsTableCol& GFitsTable::operator[](const std::string& colname) { // If there is no data then throw an exception if (m_columns == NULL) { throw GException::fits_no_data(G_ACCESS2, "No columns in table."); } // Get column pointer GFitsTableCol* ptr = ptr_column(colname); // If column has not been found throw an exception if (ptr == NULL) { throw GException::fits_column_not_found(G_ACCESS2, colname); } // Return reference return *ptr; } /***********************************************************************//** * @brief Returns reference to table column (const version) * * @param[in] colname Column name. * * @exception GException::fits_no_data * No data found in table. * @exception GException::fits_column_not_found * Column name not found. ***************************************************************************/ const GFitsTableCol& GFitsTable::operator[](const std::string& colname) const { // If there is no data then throw an exception if (m_columns == NULL) { throw GException::fits_no_data(G_ACCESS2, "No columns in table."); } // Get column pointer GFitsTableCol* ptr = ptr_column(colname); // If column has not been found throw an exception if (ptr == NULL) { throw GException::fits_column_not_found(G_ACCESS2, colname); } // Return reference return *ptr; } /*========================================================================== = = = Public methods = = = ==========================================================================*/ /***********************************************************************//** * @brief Append column to the table * * @param[in] column Column which should be appended to table. * * A column will be appended at the end of the table. See * GFitsTable::insert_column * for more details on the method. ***************************************************************************/ void GFitsTable::append_column(GFitsTableCol& column) { // Inserting at the end correspond to appending insert_column(m_cols, column); // Return return; } /***********************************************************************//** * @brief Insert column into the table * * @param[in] colnum Column number to be inserted. * @param[in] column Column which should be inserted into the table. * * @exception GException::fits_bad_col_length * The length of the column is incompatible with the number of * rows in the table. * * A column will be inserted at position 'colnum' of the table. If the * position is beyond the end of the table the column will be appended. * Any 'colnum' value smaller than 0 will be set automatically to 0. * * If the table is empty and has 0 rows, the number of rows will be set to * the length of the column. * * The length of the column to be inserted has to be identical to the number * of rows in the table. ***************************************************************************/ void GFitsTable::insert_column(int colnum, GFitsTableCol& column) { // Make sure that 'colnum' is valid. Since we add one column the total // number of columns at return will be m_cols+1, hence the column // index is comprised between [0,m_cols] if (colnum < 0) colnum = 0; if (colnum > m_cols) colnum = m_cols; // If the table is empty and has 0 rows then set the number of rows in // the table to the length of the column if (m_columns == NULL && m_rows == 0) m_rows = column.length(); // Throw exception if the column length is incompatible with number of // rows in the table if (m_rows != column.length()) throw GException::fits_bad_col_length(G_INSERT_COLUMN, column.length(), m_rows); // If no column data exist then allocate them now if (m_columns == NULL) { m_columns = new GFitsTableCol*[1]; m_cols = 1; m_columns[0] = NULL; colnum = 0; // We insert at the first place } // ... otherwise make space to insert column else { // Allocate fresh memory GFitsTableCol** tmp = new GFitsTableCol*[m_cols+1]; // Copy over old column pointers. Leave some space at the position // where we want to insert the column int src; int dst; for (src = 0, dst = 0; dst < m_cols+1; ++dst) { if (dst == colnum) tmp[dst] = NULL; else { tmp[dst] = m_columns[src]; src++; } } // Free old column pointer array delete [] m_columns; // Connect new memory m_columns = tmp; // Increment column counter m_cols++; } // endelse: we made space to insert a column // Copy column by cloning it m_columns[colnum] = column.clone(); // Reset column number since column does not already exist in FITS // file m_columns[colnum]->m_colnum = 0; // Return return; } /***********************************************************************//** * @brief Append rows to the table * * @param[in] nrows Number of rows to be appended. * * This method appends rows to a FITS table. This implies that all columns * will be loaded into memory. ***************************************************************************/ void GFitsTable::append_rows(const int& nrows) { // Set row number for insertion to end of the file int rownum = this->nrows(); // Insert rows insert_rows(rownum, nrows); // Return return; } /***********************************************************************//** * @brief Insert rows into the table * * @param[in] rownum Row after which rows should be inserted (0=first row). * @param[in] nrows Number of rows to be inserted. * * @exception GException::fits_invalid_row * Specified rownum is invalid. * * This method inserts rows into a FITS table. This implies that all columns * will be loaded into memory. ***************************************************************************/ void GFitsTable::insert_rows(const int& rownum, const int& nrows) { // Make sure that rownum is valid if (rownum < 0 || rownum > m_rows) throw GException::fits_invalid_row(G_INSERT_ROWS, rownum, m_rows); // Continue only if there are rows to be inserted if (nrows > 0) { // Insert rows for all columns for (int icol = 0; icol < m_cols; ++icol) { m_columns[icol]->insert(rownum, nrows); } // Increment number of rows in table m_rows += nrows; } // endfor: there were rows to be inserted // Return return; } /***********************************************************************//** * @brief Remove rows from the table * * @param[in] rownum Row after which rows should be removed (0=first row). * @param[in] nrows Number of rows to be removed. * * @exception GException::fits_invalid_row * Specified rownum is invalid. * @exception GException::fits_invalid_nrows * Invalid number of rows specified. * * This method inserts rows into a FITS table. This implies that all columns * will be loaded into memory. ***************************************************************************/ void GFitsTable::remove_rows(const int& rownum, const int& nrows) { // Make sure that rownum is valid if (rownum < 0 || rownum >= m_rows) throw GException::fits_invalid_row(G_REMOVE_ROWS, rownum, m_rows-1); // Make sure that we don't remove beyond the limit if (nrows < 0 || nrows > m_rows-rownum) throw GException::fits_invalid_nrows(G_REMOVE_ROWS, nrows, m_rows-rownum); // Continue only if there are rows to be removed if (nrows > 0) { // Insert rows for all columns for (int icol = 0; icol < m_cols; ++icol) { m_columns[icol]->remove(rownum, nrows); } // Decrement number of rows in table m_rows -= nrows; } // endfor: there were rows to be removed // Return return; } /***********************************************************************//** * @brief Return number of rows in table ***************************************************************************/ int GFitsTable::nrows(void) const { // Return number of rows return m_rows; } /***********************************************************************//** * @brief Return number of columns in table ***************************************************************************/ int GFitsTable::ncols(void) const { // Return number of columns return m_cols; } /***********************************************************************//** * @brief Checks the presence of a column in table * * @param[in] colname Column name. ***************************************************************************/ bool GFitsTable::hascolumn(const std::string& colname) const { // Get pointer in column GFitsTableCol* ptr = ptr_column(colname); // Return state return (ptr != NULL); } /***********************************************************************//** * @brief Print table information ***************************************************************************/ std::string GFitsTable::print(void) const { // Initialise result string std::string result; // Append header result.append("=== GFitsTable ===\n"); // Append HDU information result.append(print_hdu()); // Append table type result.append(parformat("Table type")); switch (m_type) { case GFitsHDU::HT_ASCII_TABLE: result.append("ASCII table\n"); break; case GFitsHDU::HT_BIN_TABLE: result.append("Binary table\n"); break; default: result.append("Unknown\n"); break; } // Append table dimensions result.append(parformat("Number of rows")+str(m_rows)+"\n"); result.append(parformat("Number of columns")+str(m_cols)+"\n"); // Append header information result.append(m_header.print()); // Append table columns if (m_columns != NULL) { for (int i = 0; i < m_cols; ++i) { result.append("\n"); if (m_columns[i] != NULL) result.append(m_columns[i]->print()); else result.append(" Column "+str(i)+" undefined"); } } else result.append(" Table columns undefined"); // Return result return result; } /*========================================================================== = = = Protected methods = = = ==========================================================================*/ /***********************************************************************//** * @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 vdim; std::string sdim = strip_chars(strip_whitespace(&(dim[1])),"()"); if (sdim.length() > 0) { std::vector 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; } /***********************************************************************//** * @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 Close table ***************************************************************************/ void GFitsTable::data_close(void) { // Free members free_members(); // Initialise members init_members(); // Return return; } /***********************************************************************//** * @brief Connect table data to FITS file * * @param[in] vptr FITS file pointer. * * Connects the table columns to the file specified by the FITS file pointer. * This method does nothing if the file pointer in not valid. ***************************************************************************/ void GFitsTable::data_connect(void* vptr) { // Continue only if file pointer is valid if (vptr != NULL) { // Connect all columns if (m_columns != NULL) { for (int i = 0; i < m_cols; ++i) { if (m_columns[i] != NULL) m_columns[i]->connect(vptr); } } } // endif: file pointer was valid // Return return; } /***********************************************************************//** * @brief Returns pointer to column type * * @param[in] colnum Column number for which type is to be returned * * This methods allocates memory for the character string that holds the * column type. The client has to de-allocate this memory after usage. * In case that the column does not exist a NULL pointer is returned. ***************************************************************************/ char* GFitsTable::get_ttype(const int& colnum) const { // Initialise result with NULL pointer char* ptr = NULL; // Get type only if column exists if (m_columns != NULL && colnum >=0 && colnum < m_cols && m_columns[colnum] != NULL) { int size = m_columns[colnum]->m_name.length(); ptr = new char[size+1]; std::strncpy(ptr, m_columns[colnum]->m_name.c_str(), size); ptr[size] = '\0'; } // Return result return ptr; } /***********************************************************************//** * @brief Returns pointer to column format * * @param[in] colnum Column number (starting from 0). * * @exception GException::fits_unknown_tabtype * Table is neither ASCII nor Binary. * * This methods allocates memory for the character string that holds the * column format. The client has to de-allocate this memory after usage. * In case that the column does not exist a NULL pointer is returned. ***************************************************************************/ char* GFitsTable::get_tform(const int& colnum) const { // Initialise result with NULL pointer char* ptr = NULL; // Get type only if column exists if (m_columns != NULL && colnum >=0 && colnum < m_cols && m_columns[colnum] != NULL) { // Get table type specific format int size; switch (m_type) { case GFitsHDU::HT_ASCII_TABLE: size = m_columns[colnum]->ascii_format().length(); ptr = new char[size+1]; std::strncpy(ptr, m_columns[colnum]->ascii_format().c_str(), size); break; case GFitsHDU::HT_BIN_TABLE: size = m_columns[colnum]->binary_format().length(); ptr = new char[size+1]; std::strncpy(ptr, m_columns[colnum]->binary_format().c_str(), size); break; default: throw GException::fits_unknown_tabtype(G_GET_TFORM, m_type); break; } ptr[size] = '\0'; } // Return result return ptr; } /***********************************************************************//** * @brief Returns pointer to column unit * * @param[in] colnum Column number (starting from 0). * * This methods allocates memory for the character string that holds the * column unit. The client has to de-allocate this memory after usage. * In case that the column does not exist a NULL pointer is returned. ***************************************************************************/ char* GFitsTable::get_tunit(const int& colnum) const { // Initialise result with NULL pointer char* ptr = NULL; // Get type only if column exists if (m_columns != NULL && colnum >=0 && colnum < m_cols && m_columns[colnum] != NULL) { int size = m_columns[colnum]->m_unit.length(); ptr = new char[size+1]; std::strncpy(ptr, m_columns[colnum]->m_unit.c_str(), size); ptr[size] = '\0'; } // Return result return ptr; } /*========================================================================== = = = Private methods = = = ==========================================================================*/ /***********************************************************************//** * @brief Initialise class members ***************************************************************************/ void GFitsTable::init_members(void) { // Initialise members m_type = -1; m_rows = 0; m_cols = 0; m_columns = NULL; // Return return; } /***********************************************************************//** * @brief Copy class members * * @param[in] table Table to copy ***************************************************************************/ void GFitsTable::copy_members(const GFitsTable& table) { // Copy attributes m_type = table.m_type; m_rows = table.m_rows; m_cols = table.m_cols; // Copy column definition if (table.m_columns != NULL && m_cols > 0) { m_columns = new GFitsTableCol*[m_cols]; for (int i = 0; i < m_cols; ++i) { if (table.m_columns[i] != NULL) m_columns[i] = table.m_columns[i]->clone(); else m_columns[i] = NULL; } } // Return return; } /***********************************************************************//** * @brief Delete class members * * De-allocates all column pointers ***************************************************************************/ void GFitsTable::free_members(void) { // Free memory if (m_columns != NULL) { for (int i = 0; i < m_cols; ++i) { if (m_columns[i] != NULL) delete m_columns[i]; } delete [] m_columns; } // Mark memory as freed m_columns = NULL; // Return return; } /***********************************************************************//** * @brief Allocates column * * @param[in] typecode cfitsio type code * * Allocates a table column depending on the cfitsio type code. If type code * is not found then return a NULL pointer. ***************************************************************************/ GFitsTableCol* GFitsTable::alloc_column(int typecode) const { // Initialise table column pointer GFitsTableCol* ptr = NULL; // Allocate column switch (typecode) { case __TBIT: ptr = new GFitsTableBitCol; break; case __TBYTE: ptr = new GFitsTableByteCol; break; case __TLOGICAL: ptr = new GFitsTableBoolCol; break; case __TSTRING: ptr = new GFitsTableStringCol; break; case __TUSHORT: ptr = new GFitsTableUShortCol; break; case __TSHORT: ptr = new GFitsTableShortCol; break; case __TULONG: ptr = new GFitsTableULongCol; break; case __TLONG: ptr = new GFitsTableLongCol; break; case __TFLOAT: ptr = new GFitsTableFloatCol; break; case __TLONGLONG: ptr = new GFitsTableLongLongCol; break; case __TDOUBLE: ptr = new GFitsTableDoubleCol; break; case __TCOMPLEX: ptr = new GFitsTableCFloatCol; break; case __TDBLCOMPLEX: ptr = new GFitsTableCDoubleCol; break; default: break; } // Return return ptr; } /***********************************************************************//** * @brief Returns pointer of column with given name * * @param[in] colname Name of column. * * This method returns a pointer on the column with the specified name. It * returns NULL if no column with the given name was found, or if the * column is not allocated. ***************************************************************************/ GFitsTableCol* GFitsTable::ptr_column(const std::string& colname) const { // Initialise pointer GFitsTableCol* ptr = NULL; // If there are columns then search for the specified name if (m_columns != NULL) { for (int i = 0; i < m_cols; ++i) { if (m_columns[i]->name() == colname) { ptr = m_columns[i]; break; } } } // Return pointer return ptr; }