/*************************************************************************** * GFitsHeader.hpp - FITS header cards container class * * ----------------------------------------------------------------------- * * copyright (C) 2008-2013 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 GFitsHeader.cpp * @brief FITS header cards container class implementation * @author Juergen Knoedlseder */ /* __ Includes ___________________________________________________________ */ #ifdef HAVE_CONFIG_H #include #endif #include "GException.hpp" #include "GFitsCfitsio.hpp" #include "GFits.hpp" #include "GFitsHeader.hpp" #include "GTools.hpp" /* __ Method name definitions ____________________________________________ */ #define G_AT1 "GFitsHeaderCard& GFitsHeader::at(int&)" #define G_AT2 "GFitsHeaderCard& GFitsHeader::at(std::string&)" #define G_STRING1 "GFitsHeader::string(int&)" #define G_STRING2 "GFitsHeader::string(std::string&)" #define G_REAL1 "GFitsHeader::real(int&)" #define G_REAL2 "GFitsHeader::real(std::string&)" #define G_INTEGER1 "GFitsHeader::integer(int&)" #define G_INTEGER2 "GFitsHeader::integer(std::string&)" #define G_INSERT1 "GFitsHeader::insert(int&, GFitsHeaderCard&)" #define G_INSERT2 "GFitsHeader::insert(std::string&, GFitsHeaderCard&)" #define G_REMOVE1 "GFitsHeader::remove(int&)" #define G_REMOVE2 "GFitsHeader::remove(std::string&)" #define G_OPEN "GFitsHeader::open(void*)" #define G_SAVE "GFitsHeader::save(void*)" /* __ Macros _____________________________________________________________ */ /* __ Coding definitions _________________________________________________ */ /* __ Debug definitions __________________________________________________ */ /*========================================================================== = = = Constructors/destructors = = = ==========================================================================*/ /***********************************************************************//** * @brief Constructor ***************************************************************************/ GFitsHeader::GFitsHeader(void) { // Initialise class members for clean destruction init_members(); // Return return; } /***********************************************************************//** * @brief Copy constructor * * @param header FITS header. ***************************************************************************/ GFitsHeader::GFitsHeader(const GFitsHeader& header) { // Initialise class members for clean destruction init_members(); // Copy members copy_members(header); // Return return; } /***********************************************************************//** * @brief Destructor ***************************************************************************/ GFitsHeader::~GFitsHeader(void) { // Free members free_members(); // Return return; } /*========================================================================== = = = Operators = = = ==========================================================================*/ /***********************************************************************//** * @brief Assignment operator * * @param header FITS header. * @return FITS header. ***************************************************************************/ GFitsHeader& GFitsHeader::operator=(const GFitsHeader& header) { // Execute only if object is not identical if (this != &header) { // Free members free_members(); // Initialise private members for clean destruction init_members(); // Copy members copy_members(header); } // endif: object was not identical // Return this object return *this; } /*========================================================================== = = = Public methods = = = ==========================================================================*/ /***********************************************************************//** * @brief Clear header ***************************************************************************/ void GFitsHeader::clear(void) { // Free members free_members(); // Initialise members init_members(); // Return return; } /***********************************************************************//** * @brief Clone header * * @return Pointer to deep copy of header. ***************************************************************************/ GFitsHeader* GFitsHeader::clone(void) const { return new GFitsHeader(*this); } /***********************************************************************//** * @brief Return header card * * @param[in] cardno Number of card in header [0,...,size()-1] * @return Header card. * * @exception GException::out_of_range * Card number out of range. ***************************************************************************/ GFitsHeaderCard& GFitsHeader::at(const int& cardno) { // Compile option: raise an exception if cardno is out of range #if defined(G_RANGE_CHECK) if (!contains(cardno)) { throw GException::out_of_range(G_AT1, "Header card number", cardno, size()); } #endif // Return card return (m_cards[cardno]); } /***********************************************************************//** * @brief Return header card (const version) * * @param[in] cardno Number of card in header [0,...,size()-1] * @return Header card. * * @exception GException::out_of_range * Card number out of range. ***************************************************************************/ const GFitsHeaderCard& GFitsHeader::at(const int& cardno) const { // Compile option: raise an exception if cardno is out of range #if defined(G_RANGE_CHECK) if (!contains(cardno)) { throw GException::out_of_range(G_AT1, "Header card number", cardno, size()); } #endif // Return card return (m_cards[cardno]); } /***********************************************************************//** * @brief Return header card * * @param[in] keyname Name of header card * @return Header card. * * @exception GException::invalid_argument * Key name not found. ***************************************************************************/ GFitsHeaderCard& GFitsHeader::at(const std::string& keyname) { // Get card number int cardno = get_index(keyname); // Throw an exception if keyname is not found if (cardno == -1) { std::string msg = "Keyword \""+keyname+"\" not found in FITS header."; throw GException::invalid_argument(G_AT2, msg); } // Return card return (m_cards[cardno]); } /***********************************************************************//** * @brief Return header card (const version) * * @param[in] keyname Name of header card * @return Header card. * * @exception GException::invalid_argument * Key name not found. ***************************************************************************/ const GFitsHeaderCard& GFitsHeader::at(const std::string& keyname) const { // Get card number int cardno = get_index(keyname); // Throw an exception if keyname is not found if (cardno == -1) { std::string msg = "Keyword \""+keyname+"\" not found in FITS header."; throw GException::invalid_argument(G_AT2, msg); } // Return card return (m_cards[cardno]); } /***********************************************************************//** * @brief Return header card value as string value * * @param[in] cardno Header card number [0,...,size()-1]. * @return Header card string value. * * @exception GException::out_of_range * Card number out of range. ***************************************************************************/ std::string GFitsHeader::string(const int& cardno) const { // Compile option: raise an exception if cardno is out of range #if defined(G_RANGE_CHECK) if (!contains(cardno)) { throw GException::out_of_range(G_STRING1, "Header card number", cardno, size()); } #endif // Return card value return (m_cards[cardno].string()); } /***********************************************************************//** * @brief Return header card value as string value * * @param[in] keyname Header card key name. * @return Header card string value. * * @exception GException::invalid_argument * Key name not found. ***************************************************************************/ std::string GFitsHeader::string(const std::string& keyname) const { // Get card number int cardno = get_index(keyname); // Throw an exception if keyname is not found if (cardno == -1) { std::string msg = "Keyword \""+keyname+"\" not found in FITS header."; throw GException::invalid_argument(G_STRING2, msg); } // Return card value return (m_cards[cardno].string()); } /***********************************************************************//** * @brief Return header card value as double precision value * * @param[in] cardno Header card number [0,...,size()-1]. * @return Header card double precision value. * * @exception GException::out_of_range * Card number out of range. ***************************************************************************/ double GFitsHeader::real(const int& cardno) const { // Compile option: raise an exception if cardno is out of range #if defined(G_RANGE_CHECK) if (!contains(cardno)) { throw GException::out_of_range(G_REAL1, "Header card number", cardno, size()); } #endif // Return card value return (m_cards[cardno].real()); } /***********************************************************************//** * @brief Return header card value as double precision value * * @param[in] keyname Header card key name. * @return Header card double precision value. * * @exception GException::invalid_argument * Key name not found. ***************************************************************************/ double GFitsHeader::real(const std::string& keyname) const { // Get card number int cardno = get_index(keyname); // Throw an exception if keyname is not found if (cardno == -1) { std::string msg = "Keyword \""+keyname+"\" not found in FITS header."; throw GException::invalid_argument(G_REAL2, msg); } // Return card value return (m_cards[cardno].real()); } /***********************************************************************//** * @brief Return header card value as integer value * * @param[in] cardno Header card number [0,...,size()-1]. * @return Header card integer value. * * @exception GException::out_of_range * Card number out of range. ***************************************************************************/ int GFitsHeader::integer(const int& cardno) const { // Compile option: raise an exception if cardno is out of range #if defined(G_RANGE_CHECK) if (!contains(cardno)) { throw GException::out_of_range(G_INTEGER1, "Header card number", cardno, size()); } #endif // Return card value return (m_cards[cardno].integer()); } /***********************************************************************//** * @brief Return header card value as integer value * * @param[in] keyname Header card key name. * @return Header card integer value. * * @exception GException::invalid_argument * Key name not found. ***************************************************************************/ int GFitsHeader::integer(const std::string& keyname) const { // Get card number int cardno = get_index(keyname); // Throw an exception if keyname is not found if (cardno == -1) { std::string msg = "Keyword \""+keyname+"\" not found in FITS header."; throw GException::invalid_argument(G_INTEGER2, msg); } // Return card value return (m_cards[cardno].integer()); } /***********************************************************************//** * @brief Append or update header card * * @param[in] card Header card. * @return Reference to appended header card. * * If the keyname of the header @p card does not yet exist in the header * (or if the keyname is COMMENT or HISTORY) then append the header card * to the header. If the keyname exists already, the header card is updated. ***************************************************************************/ GFitsHeaderCard& GFitsHeader::append(const GFitsHeaderCard& card) { // If card keyname is not COMMENT or HISTORY, then check first if // card exists. If yes then update existing card int cardno = -1; if (card.keyname() != "COMMENT" && card.keyname() != "HISTORY") { cardno = get_index(card.keyname()); if (cardno != -1) { m_cards[cardno] = card; } } // If card has not yet been updated then append card to header if (cardno == -1) { m_cards.push_back(card); cardno = size()-1; } // Return reference return (m_cards[cardno]); } /***********************************************************************//** * @brief Insert card into header * * @param[in] cardno Header card number [0,...,size()-1]. * @param[in] card Header card. * @return Reference to inserted header card. * * @exception GException::out_of_range * Card number out of range. * * Inserts a @p card into the header before the card with the specified card * number @p cardno. ***************************************************************************/ GFitsHeaderCard& GFitsHeader::insert(const int& cardno, const GFitsHeaderCard& card) { // Compile option: raise exception if index is out of range #if defined(G_RANGE_CHECK) if (is_empty()) { if (cardno > 0) { throw GException::out_of_range(G_INSERT1, "Header card number", cardno, size()); } } else { if (cardno < 0 || cardno >= size()) { throw GException::out_of_range(G_INSERT1, "Header card number", cardno, size()); } } #endif // Inserts card m_cards.insert(m_cards.begin()+cardno, card); // Return reference return m_cards[cardno]; } /***********************************************************************//** * @brief Insert card into header * * @param[in] keyname Header card key name. * @param[in] card Header card. * @return Reference to inserted header card. * * @exception GException::invalid_argument * Key name not found. * * Inserts a @p card into the header before the card with the specified * @p keyname. ***************************************************************************/ GFitsHeaderCard& GFitsHeader::insert(const std::string& keyname, const GFitsHeaderCard& card) { // Get card number int cardno = get_index(keyname); // Throw an exception if keyname is not found if (cardno == -1) { std::string msg = "Keyword \""+keyname+"\" not found in FITS header."; throw GException::invalid_argument(G_INSERT2, msg); } // Inserts card m_cards.insert(m_cards.begin()+cardno, card); // Return reference return m_cards[cardno]; } /***********************************************************************//** * @brief Remove card from header * * @param[in] cardno Header card number [0,...,size()-1]. * * @exception GException::out_of_range * Card number out of range. * * Remove header card of specified card number from container. ***************************************************************************/ void GFitsHeader::remove(const int& cardno) { // Compile option: raise an exception if cardno is out of range #if defined(G_RANGE_CHECK) if (!contains(cardno)) { throw GException::out_of_range(G_REMOVE1, "Header card number", cardno, size()); } #endif // Erase card from header m_cards.erase(m_cards.begin() + cardno); // Return return; } /***********************************************************************//** * @brief Remove card from header * * @param[in] keyname Header card key name. * * @exception GException::invalid_argument * Key name not found. * * Remove card with specified @p keyname from header. ***************************************************************************/ void GFitsHeader::remove(const std::string& keyname) { // Get card number int cardno = get_index(keyname); // Throw an exception if keyname is not found if (cardno == -1) { std::string msg = "Keyword \""+keyname+"\" not found in FITS header."; throw GException::invalid_argument(G_REMOVE2, msg); } // Erase card from header m_cards.erase(m_cards.begin() + cardno); // Return return; } /***********************************************************************//** * @brief Append header * * @param[in] header FITS header. * * Append all cards from a FITS header to the actual header. ***************************************************************************/ void GFitsHeader::extend(const GFitsHeader& header) { // Do nothing if header is empty if (!header.is_empty()) { // Get size. Note that we extract the size first to avoid an // endless loop that arises when a container is appended to // itself. int num = header.size(); // Reserve enough space reserve(size() + num); // Loop over all card and append them to the header for (int i = 0; i < num; ++i) { m_cards.push_back(header[i]); } } // endif: header was not empty // Return return; } /***********************************************************************//** * @brief Load header from FITS file * * @param[in] vptr FITS file void pointer. * * @exception GException::fits_error * FITS error occured. * * Loads all header cards into memory. Any header cards that existed before * will be dropped. ***************************************************************************/ void GFitsHeader::load(void* vptr) { // Move to HDU gammalib::fits_move_to_hdu(G_OPEN, vptr); // Determine number of cards in header int status = 0; int num_cards = 0; status = __ffghsp(FPTR(vptr), &num_cards, NULL, &status); if (status != 0) { throw GException::fits_error(G_OPEN, status); } // Drop any old cards and reserve space for new cards m_cards.clear(); reserve(num_cards); // Read all cards for (int i = 0; i < num_cards; ++i) { GFitsHeaderCard card; card.read(FPTR(vptr), i+1); m_cards.push_back(card); } // Return return; } /***********************************************************************//** * @brief Save header to FITS file * * @param[in] vptr FITS file void pointer. * * @exception GException::fits_error * FITS error occured. * * Saves all header cards into HDU. This method does not write the following * mandatory keywords to the HDU (those will be written by methods handling * the data of the HDU): * 'SIMPLE' * 'BITPIX' * 'NAXIS', 'NAXIS1', 'NAXIS2', etc. * 'EXTEND' * 'PCOUNT' * 'GCOUNT' ***************************************************************************/ void GFitsHeader::save(void* vptr) const { // Save all cards for (int i = 0; i < size(); ++i) { if (m_cards[i].keyname() != "SIMPLE" && m_cards[i].keyname() != "XTENSION" && m_cards[i].keyname() != "BITPIX" && m_cards[i].keyname() != "EXTEND" && m_cards[i].keyname() != "PCOUNT" && m_cards[i].keyname() != "GCOUNT" && m_cards[i].keyname().find("NAXIS") == std::string::npos) { m_cards[i].write(FPTR(vptr)); } } // Return return; } /***********************************************************************//** * @brief Print FITS header information * * @param[in] chatter Chattiness (defaults to NORMAL). * @return String containing FITS header information. ***************************************************************************/ std::string GFitsHeader::print(const GChatter& chatter) const { // Initialise result string std::string result; // Continue only if chatter is not silent if (chatter != SILENT) { // Append header result.append("=== GFitsHeader ("+gammalib::str(size())+" cards) ==="); // NORMAL: Append cards if (chatter >= NORMAL) { for (int i = 0; i < size(); ++i) { result.append("\n"+m_cards[i].print()); } } } // endif: chatter was not silent // Return result return result; } /*========================================================================== = = = Private methods = = = ==========================================================================*/ /***********************************************************************//** * @brief Initialise class members ***************************************************************************/ void GFitsHeader::init_members(void) { // Initialise members m_cards.clear(); // Return return; } /***********************************************************************//** * @brief Copy class members * * @param[in] header FITS header. ***************************************************************************/ void GFitsHeader::copy_members(const GFitsHeader& header) { // Copy members m_cards = header.m_cards; // Return return; } /***********************************************************************//** * @brief Delete class members ***************************************************************************/ void GFitsHeader::free_members(void) { // Return return; } /***********************************************************************//** * @brief Get index of header card * * @param[in] keyname Header card key name. * @return Index of header card (-1 if @p keyname is not found) * * Returns index of header card based on the @p keyname. If no header card * is found, -1 is returned. ***************************************************************************/ int GFitsHeader::get_index(const std::string& keyname) const { // Initialise index int index = -1; // Search keyname in list for (int i = 0; i < size(); ++i) { if (m_cards[i].keyname() == keyname) { index = i; break; } } // Return index return index; }