/*************************************************************************** * GFits.cpp - FITS file access class * * ----------------------------------------------------------------------- * * copyright (C) 2008-2018 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 GFits.cpp * @brief FITS file access class implementation * @author Juergen Knoedlseder */ /* __ Includes ___________________________________________________________ */ #ifdef HAVE_CONFIG_H #include #endif #include #include "GException.hpp" #include "GTools.hpp" #include "GFitsCfitsio.hpp" #include "GFits.hpp" #include "GFitsImageByte.hpp" #include "GFitsImageSByte.hpp" #include "GFitsImageUShort.hpp" #include "GFitsImageShort.hpp" #include "GFitsImageULong.hpp" #include "GFitsImageLong.hpp" #include "GFitsImageLongLong.hpp" #include "GFitsImageFloat.hpp" #include "GFitsImageDouble.hpp" #include "GFitsAsciiTable.hpp" #include "GFitsBinTable.hpp" #include "GVOClient.hpp" #include "GVOTable.hpp" /* __ Method name definitions ____________________________________________ */ #define G_AT1 "GFits::at(int&)" #define G_AT2 "GFits::at(std::string&)" #define G_IMAGE1 "GFits::image(int&)" #define G_IMAGE2 "GFits::image(std::string&)" #define G_TABLE1 "GFits::table(int&)" #define G_TABLE2 "GFits::table(std::string&)" #define G_SET1 "GFits::set(int&, GFitsHDU&)" #define G_SET2 "GFits::set(std::string&, GFitsHDU&)" #define G_INSERT1 "GFits::insert(const int& extno, GFitsHDU& hdu)" #define G_INSERT2 "GFits::insert(std::string&, GFitsHDU& hdu)" #define G_REMOVE1 "GFits::remove(int&)" #define G_REMOVE2 "GFits::remove(std::string&)" #define G_OPEN "GFits::open(GFilename&, bool&)" #define G_SAVE "GFits::save(bool&)" #define G_SAVETO "GFits::saveto(GFilename&, bool&)" #define G_PUBLISH "GFits::publish(std::string&, std::string&)" #define G_FREE_MEM "GFits::free_members()" #define G_NEW_IMAGE "GFits::new_image()" /* __ Macros _____________________________________________________________ */ /* __ Coding definitions _________________________________________________ */ #define G_DELETE_EMPTY_FITS_FILES //!< Do not write empty FITS files /* __ Debug definitions __________________________________________________ */ /*========================================================================== = = = Constructors/destructors = = = ==========================================================================*/ /***********************************************************************//** * @brief Void constructor * * Constructs empty FITS file. ***************************************************************************/ GFits::GFits(void) { // Initialise class members init_members(); // Return return; } /***********************************************************************//** * @brief FITS file constructor * * @param[in] filename FITS file name. * @param[in] create Create file if it does not exist? (default: false) * * Construct an object by opening a FITS file. If the file does not exist it * will be created if @p create is set to true. ***************************************************************************/ GFits::GFits(const GFilename& filename, const bool& create) { // Initialise class members init_members(); // Open specified FITS file open(filename, create); // Return return; } /***********************************************************************//** * @brief Copy constructor * * @param[in] fits FITS file. ***************************************************************************/ GFits::GFits(const GFits& fits) { // Initialise class members init_members(); // Copy members copy_members(fits); // Return return; } /***********************************************************************//** * @brief Destructor ***************************************************************************/ GFits::~GFits(void) { // Free members free_members(); // Return return; } /*========================================================================== = = = Operators = = = ==========================================================================*/ /***********************************************************************//** * @brief Assignment operator * * @param[in] fits FITS file. * @return FITS file. ***************************************************************************/ GFits& GFits::operator=(const GFits& fits) { // Execute only if object is not identical if (this != &fits) { // Free members free_members(); // Initialise private members for clean destruction init_members(); // Copy members copy_members(fits); } // endif: object was not identical // Return this object return *this; } /*========================================================================== = = = Public methods = = = ==========================================================================*/ /***********************************************************************//** * @brief Clear FITS file ***************************************************************************/ void GFits::clear(void) { // Close file and free all members free_members(); // Initialise members init_members(); // Return return; } /***********************************************************************//** * @brief Clone FITS file * * @return Pointer to deep copy of FITS file. ***************************************************************************/ GFits* GFits::clone(void) const { return new GFits(*this); } /***********************************************************************//** * @brief Get pointer to HDU * * @param[in] extno Extension number [0,...,size()-1]. * @return Pointer to HDU. * * @exception GException::out_of_range * Extension number is out of range. * @exception GException::fits_hdu_not_found * No HDU found for specified extension number. * * Returns a pointer to the HDU with the specified extension number @p extno. * An exception is thrown if the HDU is not valid (i.e. NULL). ***************************************************************************/ GFitsHDU* GFits::at(const int& extno) { // Compile option: raise an exception if index is out of range #if defined(G_RANGE_CHECK) if (extno < 0 || extno >= size()) { throw GException::out_of_range(G_AT1, "Extension number", extno, size()); } #endif // Get HDU pointer GFitsHDU* ptr = m_hdu[extno]; // Throw an error if HDU has not been found if (ptr == NULL) { std::string extname = "extno="+gammalib::str(extno); throw GException::fits_hdu_not_found(G_AT1, extname); } // Return pointer return ptr; } /***********************************************************************//** * @brief Get pointer to HDU (const version) * * @param[in] extno Extension number [0,...,size()-1]. * @return Pointer to HDU. * * @exception GException::out_of_range * Extension number is out of range. * @exception GException::fits_hdu_not_found * No HDU found for specified extension number. * * Returns a pointer to the HDU with the specified extension number @p extno. * An exception is thrown if the HDU is not valid (i.e. NULL). ***************************************************************************/ const GFitsHDU* GFits::at(const int& extno) const { // Compile option: raise an exception if index is out of range #if defined(G_RANGE_CHECK) if (extno < 0 || extno >= size()) { throw GException::out_of_range(G_AT1, "Extension number", extno, size()); } #endif // Get HDU pointer GFitsHDU* ptr = m_hdu[extno]; // Throw an error if HDU has not been found if (ptr == NULL) { std::string extname = "extno="+gammalib::str(extno); throw GException::fits_hdu_not_found(G_AT1, extname); } // Return pointer return ptr; } /***********************************************************************//** * @brief Get pointer to HDU * * @param[in] extname Name of HDU extension. * @return Pointer to HDU. * * @exception GException::fits_hdu_not_found * No HDU with specified name has been found. * * Returns a pointer to the HDU with the specified @p extname. An exception * is thrown if the HDU is not valid (i.e. NULL). ***************************************************************************/ GFitsHDU* GFits::at(const std::string& extname) { // Get extenion number int extno = this->extno(extname); if (extno == -1) { throw GException::fits_hdu_not_found(G_AT2, extname); } // Get HDU pointer GFitsHDU* ptr = m_hdu[extno]; // Return pointer return ptr; } /***********************************************************************//** * @brief Get pointer to HDU (const version) * * @param[in] extname Name of HDU extension. * @return Pointer to HDU. * * @exception GException::fits_hdu_not_found * No HDU with specified name has been found. * * Returns a pointer to the HDU with the specified @p extname. An exception * is thrown if the HDU is not valid (i.e. NULL). ***************************************************************************/ const GFitsHDU* GFits::at(const std::string& extname) const { // Get extenion number int extno = this->extno(extname); if (extno == -1) { throw GException::fits_hdu_not_found(G_AT2, extname); } // Get HDU pointer GFitsHDU* ptr = m_hdu[extno]; // Return pointer return ptr; } /***********************************************************************//** * @brief Get pointer to image HDU * * @param[in] extno Extension number [0,...,size()-1]. * @return Pointer to FITS image. * * @exception GException::fits_hdu_not_image * Requested HDU is not an image. * * Returns a pointer to the image HDU with extension number extno. ***************************************************************************/ GFitsImage* GFits::image(const int& extno) { // Get HDU pointer GFitsImage* ptr = dynamic_cast(at(extno)); // Throw an error if HDU is not an image if (ptr == NULL) { throw GException::fits_hdu_not_image(G_IMAGE1, "(extno="+gammalib::str(extno)+")", at(extno)->exttype()); } // Return pointer return ptr; } /***********************************************************************//** * @brief Get pointer to image HDU (const version) * * @param[in] extno Extension number [0,...,size()-1]. * @return Pointer to FITS image. * * @exception GException::fits_hdu_not_image * Requested HDU is not an image. * * Returns a pointer to the image HDU with extension number extno. ***************************************************************************/ const GFitsImage* GFits::image(const int& extno) const { // Get HDU pointer const GFitsImage* ptr = dynamic_cast(at(extno)); // Throw an error if HDU is not an image if (ptr == NULL) { throw GException::fits_hdu_not_image(G_IMAGE1, "(extno="+gammalib::str(extno)+")", at(extno)->exttype()); } // Return pointer return ptr; } /***********************************************************************//** * @brief Get pointer to image HDU * * @param[in] extname Name of HDU extension. * @return Pointer to FITS image. * * @exception GException::fits_hdu_not_image * Requested HDU is not an image. * * Returns a pointer to the image HDU with extension name extname. ***************************************************************************/ GFitsImage* GFits::image(const std::string& extname) { // Get HDU pointer GFitsImage* ptr = dynamic_cast(at(extname)); // Throw an error if HDU is not an image if (ptr == NULL) { throw GException::fits_hdu_not_image(G_IMAGE2, extname, at(extname)->exttype()); } // Return pointer return ptr; } /***********************************************************************//** * @brief Get pointer to image HDU (const version) * * @param[in] extname Name of HDU extension. * @return Pointer to FITS image. * * @exception GException::fits_hdu_not_image * Requested HDU is not an image. * * Returns a pointer to the image HDU with extension name extname. ***************************************************************************/ const GFitsImage* GFits::image(const std::string& extname) const { // Get HDU pointer const GFitsImage* ptr = dynamic_cast(at(extname)); // Throw an error if HDU is not an image if (ptr == NULL) { throw GException::fits_hdu_not_image(G_IMAGE2, extname, at(extname)->exttype()); } // Return pointer return ptr; } /***********************************************************************//** * @brief Get pointer to table HDU * * @param[in] extno Extension number [0,...,size()-1]. * @return Pointer to FITS table. * * @exception GException::fits_hdu_not_table * Requested HDU is not a table. * * Returns a pointer to the table HDU with extension number extno. ***************************************************************************/ GFitsTable* GFits::table(const int& extno) { // Get HDU pointer GFitsTable* ptr = dynamic_cast(at(extno)); // Throw an error if HDU is not an image if (ptr == NULL) { throw GException::fits_hdu_not_table(G_TABLE1, "(extno="+gammalib::str(extno)+")", at(extno)->exttype()); } // Return pointer return ptr; } /***********************************************************************//** * @brief Get pointer to table HDU (const version) * * @param[in] extno Extension number [0,...,size()-1]. * @return Pointer to FITS table. * * @exception GException::fits_hdu_not_table * Requested HDU is not a table. * * Returns a pointer to the table HDU with extension number extno. ***************************************************************************/ const GFitsTable* GFits::table(const int& extno) const { // Get HDU pointer const GFitsTable* ptr = dynamic_cast(at(extno)); // Throw an error if HDU is not an image if (ptr == NULL) { throw GException::fits_hdu_not_table(G_TABLE1, "(extno="+gammalib::str(extno)+")", at(extno)->exttype()); } // Return pointer return ptr; } /***********************************************************************//** * @brief Get pointer to table HDU * * @param[in] extname Name of HDU extension. * @return Pointer to FITS table. * * @exception GException::fits_hdu_not_table * Requested HDU is not a table. * * Returns a pointer to the table HDU with extension name extname. ***************************************************************************/ GFitsTable* GFits::table(const std::string& extname) { // Get HDU pointer GFitsTable* ptr = dynamic_cast(at(extname)); // Throw an error if HDU is not an image if (ptr == NULL) { throw GException::fits_hdu_not_table(G_TABLE2, extname, at(extname)->exttype()); } // Return pointer return ptr; } /***********************************************************************//** * @brief Get pointer to table HDU * * @param[in] extname Name of HDU extension. * @return Pointer to FITS table. * * @exception GException::fits_hdu_not_table * Requested HDU is not a table. * * Returns a pointer to the table HDU with extension name extname. ***************************************************************************/ const GFitsTable* GFits::table(const std::string& extname) const { // Get HDU pointer const GFitsTable* ptr = dynamic_cast(at(extname)); // Throw an error if HDU is not an image if (ptr == NULL) { throw GException::fits_hdu_not_table(G_TABLE2, extname, at(extname)->exttype()); } // Return pointer return ptr; } /***********************************************************************//** * @brief Set HDU for the specified extension number * * @param[in] extno Extension number [0,...,size()-1]. * @param[in] hdu HDU. * @return Pointer to cloned HDU. * * @exception GException::out_of_range * Extension number out of range. * @exception GException::invalid_argument * Attempt to insert non-image HDU in first slot. ***************************************************************************/ GFitsHDU* GFits::set(const int& extno, const GFitsHDU& hdu) { // Compile option: raise exception if index is out of range #if defined(G_RANGE_CHECK) if (extno < 0 || extno >= size()) { throw GException::out_of_range(G_SET1, "Extension number", extno, size()); } #endif // Throw an exception if a non-image extension should be set in the // first slot if (extno == 0 && hdu.exttype() != GFitsHDU::HT_IMAGE) { std::string msg = "Attempt to set a table extension as the first" " extension of a FITS file.\n" "The first extension of a FITS file must be an" " image, hence use the next slot to set a" " table."; throw GException::invalid_argument(G_SET1, msg); } // Free existing HDU only if it differs from current HDU. This prevents // unintential deallocation of the argument if ((m_hdu[extno] != NULL) && (m_hdu[extno] != &hdu)) { delete m_hdu[extno]; } // Assign new HDU by cloning m_hdu[extno] = hdu.clone(); // If FITS file exists then connect cloned HDU to FITS file if (m_fitsfile != NULL) { __fitsfile fptr = *(FPTR(m_fitsfile)); fptr.HDUposition = extno; m_hdu[extno]->connect(&fptr); } else { m_hdu[extno]->extno(extno); } // Return pointer to HDU return (m_hdu[extno]); } /***********************************************************************//** * @brief Set HDU for the specified extension name * * @param[in] extname Name of HDU extension. * @param[in] hdu HDU. * @return Pointer to cloned HDU. * * @exception GException::invalid_argument * Extension name not found. ***************************************************************************/ GFitsHDU* GFits::set(const std::string& extname, const GFitsHDU& hdu) { // Get extenion number int extno = this->extno(extname); if (extno == -1) { std::string msg = "No extension with name \""+extname+"\" found in " "FITS file. Please specify a valid extension name."; throw GException::invalid_argument(G_SET2, msg); } // Set HDU and return pointer return (set(extno, hdu)); } /***********************************************************************//** * @brief Append HDU to FITS file * * @param[in] hdu HDU. * @return Pointer to appended HDU. * * Append HDU to the next free position in a FITS file. In case that no HDU * exists so far in the FITS file and if the HDU to append is NOT an image, * an empty primary image will be inserted as first HDU in the FITS file. * This guarantees the compatibility with the FITS standard. ***************************************************************************/ GFitsHDU* GFits::append(const GFitsHDU& hdu) { // Debug header #if defined(G_DEBUG) std::cout << "GFits::append("; switch (hdu.exttype()) { case GFitsHDU::HT_IMAGE: std::cout << "GFitsImage"; break; case GFitsHDU::HT_ASCII_TABLE: std::cout << "GFitsAsciiTable"; break; case GFitsHDU::HT_BIN_TABLE: std::cout << "GFitsBinTable"; break; default: std::cout << ""; break; } std::cout << ") (size=" << size() << ") -->" << std::endl; #endif // Determine next free HDU number int n_hdu = size(); // Add primary image if required if (n_hdu == 0 && hdu.exttype() != GFitsHDU::HT_IMAGE) { // Allocate primary image GFitsHDU* primary = new_primary(); // If FITS file exists then connect primary image to FITS file if (m_fitsfile != NULL) { __fitsfile fptr = *(FPTR(m_fitsfile)); fptr.HDUposition = n_hdu; primary->connect(&fptr); } // Debug information #if defined(G_DEBUG) std::cout << "Append primary image to HDU." << std::endl; #endif // Push back primary image m_hdu.push_back(primary); // Increment HDU number n_hdu++; } // Clone FITS HDU GFitsHDU* ptr = hdu.clone(); // Append HDU if it's valid if (ptr != NULL) { // If FITS file exists then connect cloned HDU to FITS file if (m_fitsfile != NULL) { __fitsfile fptr = *(FPTR(m_fitsfile)); fptr.HDUposition = n_hdu; ptr->connect(&fptr); } else { ptr->extno(n_hdu); } // Debug information #if defined(G_DEBUG) std::cout << "Append HDU (extno=" << n_hdu << ")." << std::endl; #endif // Push back HDU m_hdu.push_back(ptr); // Debug trailer #if defined(G_DEBUG) std::cout << "<-- GFits::append" << std::endl; #endif } // endif: HDU was valid // Return return ptr; } /***********************************************************************//** * @brief Set HDU for the specified extension number * * @param[in] extno Extension number [0,...,size()-1]. * @param[in] hdu HDU. * @return Pointer to cloned HDU. * * @exception GException::out_of_range * Extension number out of range. * @exception GException::invalid_argument * Attempt to insert non-image HDU in first slot. ***************************************************************************/ GFitsHDU* GFits::insert(const int& extno, const GFitsHDU& hdu) { // Compile option: raise exception if extension number is out of range #if defined(G_RANGE_CHECK) if (extno < 0 || extno >= size()) { throw GException::out_of_range(G_INSERT1, "Extension number", extno, size()); } #endif // Throw an exception if a non-image extension should be inserted in the // first slot if (extno == 0 && hdu.exttype() != GFitsHDU::HT_IMAGE) { std::string msg = "Attempt to insert a table extension as the first " "extension of a FITS file. The first extension of " "a FITS file must be an image, hence use the next " "slot to insert a table."; throw GException::invalid_argument(G_INSERT1, msg); } // Create deep copy of HDU GFitsHDU* ptr = hdu.clone(); // Inserts deep copy of HDU m_hdu.insert(m_hdu.begin() + extno, ptr); // If FITS file exists then connect cloned HDU to FITS file if (m_fitsfile != NULL) { __fitsfile fptr = *(FPTR(m_fitsfile)); fptr.HDUposition = extno; ptr->connect(&fptr); } else { ptr->extno(extno); } // Update extno for all subsequent HDUs for (int i = extno + 1; i < size(); ++i) { m_hdu[i]->extno(i); } // Return pointer to HDU return ptr; } /***********************************************************************//** * @brief Insert HDU into FITS file * * @param[in] extname Extension name. * @param[in] hdu Extension. * @return Pointer to deep copy of extension @p hdu. * * @exception GException::invalid_argument * Extension name not found. * * Inserts an extension @p hdu into the FITS file before the extension with * the specified @p extname. ***************************************************************************/ GFitsHDU* GFits::insert(const std::string& extname, const GFitsHDU& hdu) { // Get extenion number int extno = this->extno(extname); if (extno == -1) { std::string msg = "No extension with name \""+extname+"\" found in " "FITS file. Please specify a valid extension name."; throw GException::invalid_argument(G_INSERT2, msg); } // Insert HDU and return pointer return (insert(extno, hdu)); } /***********************************************************************//** * @brief Remove HDU from FITS file * * @param[in] extno Extension number [0,...,size()-1]. * * @exception GException::out_of_range * Specified @p extno is out of range. * * @todo Handle HDU update in FITS file. ***************************************************************************/ void GFits::remove(const int& extno) { // Compile option: raise an exception if index is out of range #if defined(G_RANGE_CHECK) if (extno < 0 || extno >= size()) { throw GException::out_of_range(G_REMOVE1, "Extension number", extno, size()); } #endif // Throw an exception if removal would lead to a non-image extension // in the first slot if (extno == 0 && size() > 1 && m_hdu[1]->exttype() != GFitsHDU::HT_IMAGE) { std::string msg = "Attempt to remove primary image from a FITS file " "with a table extension as second extension. The " "removal of the primary image would result in " "having a table as the first extension of the FITS " "file, which is not a valid FITS file."; throw GException::invalid_argument(G_REMOVE1, msg); } // Delete HDU if (m_hdu[extno] != NULL) delete m_hdu[extno]; // Erase HDU from FITS file m_hdu.erase(m_hdu.begin() + extno); // Update extno for all subsequent HDUs for (int i = extno; i < size(); ++i) { m_hdu[i]->extno(i); } // Return return; } /***********************************************************************//** * @brief Remove HDU from FITS file * * @param[in] extname Name of HDU extension. * * @exception GException::invalid_argument * Specified @p extname not found in FITS file. ***************************************************************************/ void GFits::remove(const std::string& extname) { // Get extenion number int extno = this->extno(extname); if (extno == -1) { std::string msg = "No extension with name \""+extname+"\" found in " "FITS file. Please specify a valid extension name."; throw GException::invalid_argument(G_REMOVE2, msg); } // Remove extension remove(extno); // Return return; } /***********************************************************************//** * @brief Append FITS file * * @param[in] fits FITS file. * * Append all extension of @p fits file to FITS file. * * @todo Handle HDU update in FITS file. ***************************************************************************/ void GFits::extend(const GFits& fits) { // Do nothing if FITS file is empty if (!fits.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 = fits.size(); // Reserve enough space reserve(size() + num); // Loop over all HDUs and append pointers to deep copies for (int i = 0; i < num; ++i) { // Clone HDU GFitsHDU* ptr = m_hdu[i]->clone(); // Push back HDU on stack m_hdu.push_back(ptr); // Retrieve extno int extno = m_hdu.size()-1; // If FITS file exists then connect cloned HDU to FITS file if (m_fitsfile != NULL) { __fitsfile fptr = *(FPTR(m_fitsfile)); fptr.HDUposition = extno; ptr->connect(&fptr); } else { ptr->extno(extno); } } // endfor: looped over all HDUs } // endif: FITS file was not empty // Return return; } /***********************************************************************//** * @brief Return extension number for specified extension name * * @param[in] extname Name of HDU extension. * @return Extension number (-1 of not found). * * Returns the extension number for a specified extension name @p extname. If * the extension name if "PRIMARY" an extension number of 0 is returned. * If the extension is not found, -1 is returned. ***************************************************************************/ int GFits::extno(const std::string& extname) const { // Initialise extension number int extno = -1; // Return primary HDU if requested ... if (gammalib::toupper(extname) == "PRIMARY") { if (size() > 0) { extno = 0; } } // ... otherwise search for specified extension else { for (int i = 0; i < size(); ++i) { if (m_hdu[i]->extname() == extname) { extno = i; break; } } } // Return extno return extno; } /***********************************************************************//** * @brief Open or (optionally) create FITS file * * @param[in] filename Name of FITS file to be opened. * @param[in] create Create FITS file if it does not exist (default: false) * * @exception GException::invalid_argument * Class instance contains already an opened FITS file. * Close file before opening a new one using GFits::close(). * @exception GException::fits_open_error * Unable to open the specified file. * @exception GException::fits_hdu_not_found * Requested HDU not found. * @exception GException::fits_error * Unable to determine number of HDUs in the FITS file. * * This method opens all HDUs that are found in the specified FITS file. * If the file does not exist, and if create=true, a new FITS file is created. * For each HDU, a GFitsHDU object is associated to the GFits object. * The HDUs can then be accessed using the hdu(const std::string&) or * hdu(int extno) method. * Any environment variable present in the filename will be expanded. ***************************************************************************/ void GFits::open(const GFilename& filename, const bool& create) { // Don't allow opening if a file is already open if (m_fitsfile != NULL) { std::string msg; if (m_filename == filename) { msg = "FITS file \""+m_filename+"\" has already been " "opened, cannot open it again."; } else { msg = "A FITS file \""+m_filename+"\" has already " "been opened, cannot open another FITS file \""+ filename+"\" before closing the existing one."; } throw GException::invalid_argument(G_OPEN, msg); } // Remove any HDUs m_hdu.clear(); // Initialise FITS file as readwrite and non created m_readwrite = true; m_created = false; // Try opening FITS file with readwrite access int status = 0; status = __ffopen(FHANDLE(m_fitsfile), std::string(filename).c_str(), 1, &status); // If failed then try opening as readonly. // // Possible error codes are: // 104: FILE_NOT_OPENED (could not open the named file) // 107: END_OF_FILE (tried to move past end of file, occurs if extname is invalid) // 112: READONLY_FILE (cannot write to readonly file) if (status == 104 || status == 107 || status == 112) { status = 0; status = __ffopen(FHANDLE(m_fitsfile), std::string(filename).c_str(), 0, &status); m_readwrite = false; } // If failed and if we are allowed to create a new FITS file then create // FITS file now. We pass the URL as filename since we want to create a // fresh file without any extension. We also remove any file before in // case that file exists but is not a FITS file. // // Possible error codes are: // 104: FILE_NOT_OPENED (could not open the named file) // 107: END_OF_FILE (tried to move past end of file, occurs if extname is invalid) if (create && (status == 104 || status == 107)) { filename.remove(); status = 0; status = __ffinit(FHANDLE(m_fitsfile), filename.url().c_str(), &status); m_readwrite = true; m_created = true; } // Throw special exception if status=202 (keyword not found). This error // may occur if the file is opened with an expression if (status == 202) { throw GException::fits_open_error(G_OPEN, filename, status, "Keyword not found when opening file."); } // Throw any other error else if (status != 0) { throw GException::fits_open_error(G_OPEN, filename, status); } // Store FITS file name as GFilename object m_filename = filename; // Determine number of HDUs int num_hdu = 0; status = __ffthdu(FPTR(m_fitsfile), &num_hdu, &status); if (status != 0) { throw GException::fits_error(G_OPEN, status); } // Open and append all HDUs for (int i = 0; i < num_hdu; ++i) { // Move to HDU and get HDU type int type = gammalib::fits_move_to_hdu(G_OPEN, m_fitsfile, i+1); // Perform type dependent HDU allocation GFitsHDU* hdu = NULL; switch (type) { case GFitsHDU::HT_IMAGE: hdu = new_image(); break; case GFitsHDU::HT_ASCII_TABLE: hdu = new GFitsAsciiTable; break; case GFitsHDU::HT_BIN_TABLE: hdu = new GFitsBinTable; break; default: std::string msg = "Unknown HDU type \""+gammalib::str(type)+"\""; throw GException::fits_invalid_type(G_OPEN, msg); break; } // Open HDU hdu->open(FPTR(m_fitsfile), i); // Append HDU m_hdu.push_back(hdu); } // endfor: looped over all HDUs // Return return; } /***********************************************************************//** * @brief Saves FITS file * * @param[in] clobber Overwrite existing FITS file? (default: false). * * @exception GException::fits_file_exist * Attemting to overwrite an existing file without having specified * clobber=true. * @exception GException::fits_file_not_open * FITS file needs to be opened before saving. * * Saves all HDUs of an open FITS file to disk. After saving, the FITS file * remains open. Invoke the close() method if explicit closing is needed. * Note that de-allocation of the GFits object also closes the FITS file. * * In the special case that no first HDU exists an empty primary image is * created. ***************************************************************************/ void GFits::save(const bool& clobber) { // Debug header #if defined(G_DEBUG) std::cout << "GFits::save (size=" << size() << ") -->" << std::endl; #endif // Initialise cfitsio status int status = 0; // If we attempt to save an existing file without overwriting permission // then throw an error if (!m_created && !clobber) { throw GException::fits_file_exist(G_SAVE, m_filename); } // If no FITS file has been opened then throw an error if (m_fitsfile == NULL) { throw GException::fits_file_not_open(G_SAVE, "Either open FITS file before saving or use saveto() method."); } // We have to make sure that all data are online before we can save them. // This needs to be done before the data are deleted from disk. We do // this by cloning all HDUs, as cloning will force all data to get online. for (int i = 0; i < size(); ++i) { GFitsHDU* hdu = m_hdu[i]->clone(); delete m_hdu[i]; m_hdu[i] = hdu; } // Determine number of HDUs in current FITS file on disk int num_hdu = 0; status = __ffthdu(FPTR(m_fitsfile), &num_hdu, &status); if (status != 0) { throw GException::fits_error(G_SAVE, status); } // Delete all HDUs (except of the primary HDU) since we will write them // all freshly for (int i = num_hdu-1; i >= 0; --i) { status = __ffdhdu(FPTR(m_fitsfile), NULL, &status); if (status != 0) { throw GException::fits_error(G_SAVE, status); } } // If no HDUs exist in the FITS object then write an empty primary image if (size() == 0) { status = __ffmahd(FPTR(m_fitsfile), 1, NULL, &status); status = __ffcrim(FPTR(m_fitsfile), 8, 0, NULL, &status); if (status != 0) { throw GException::fits_error(G_SAVE, status); } } // ... otherwise save all HDUs else { for (int i = 0; i < size(); ++i) { m_hdu[i]->extno(i); m_hdu[i]->save(); } } // Flush file to disk status = __ffflus(FPTR(m_fitsfile), &status); if (status != 0) { throw GException::fits_error(G_SAVE, status); } // Signal that file needs not to be created anymore m_created = false; // Debug trailer #if defined(G_DEBUG) std::cout << "<-- GFits::save" << std::endl; #endif // Return return; } /***********************************************************************//** * @brief Saves to specified FITS file * * @param[in] filename Filename. * @param[in] clobber Overwrite existing FITS file? (default: false) * * @exception GException::fits_file_exist * Specified file exists already. Overwriting requires * clobber=true. * * Saves the FITS object into a specific file. ***************************************************************************/ void GFits::saveto(const GFilename& filename, const bool& clobber) { // Debug header #if defined(G_DEBUG) std::cout << "GFits::saveto(\"" << fname << "\", " << clobber << ")" << " (size=" << size() << ") -->" << std::endl; #endif // If overwriting has been specified then remove the file. Otherwise, // throw an exception if the file exists. if (clobber) { filename.remove(); } else if (filename.exists()) { throw GException::fits_file_exist(G_SAVETO, filename); } // Create or open FITS file GFits fits(filename, true); // Append all headers for (int i = 0; i < size(); ++i) { fits.append(*m_hdu[i]); } // Save FITS file fits.save(); // Close FITS file fits.close(); // Debug trailer #if defined(G_DEBUG) std::cout << "<-- GFits::saveto" << std::endl; #endif // Return return; } /***********************************************************************//** * @brief Close FITS file * * Closing detaches a FITS file from the GFits object and returns a clean * empty object. ***************************************************************************/ void GFits::close(void) { // Close file and free all members free_members(); // Initialise members init_members(); // Return return; } /***********************************************************************//** * @brief Publish FITS HDU on VO Hub * * @param[in] extno Extension number. * @param[in] name Optional name of published ressource. * * Publishes FITS HDU with specified @p extno. ***************************************************************************/ void GFits::publish(const int& extno, const std::string& name) const { // If HDU is an image then publish it as a FITS image if (this->at(extno)->exttype() == GFitsHDU::HT_IMAGE) { // Get HDU as an image const GFitsImage* image = this->image(extno); // Save extension name std::string extname = image->extname(); // Optionally set extension name if (!name.empty()) { const_cast(image)->extname(name); } // Create VO Client GVOClient client; // Publish image using VO client client.publish(*image); // Restore extension name const_cast(image)->extname(extname); } // endif: HDU was an image // ... otherwise publish the HDU as a VO table else { // Get HDU as a table const GFitsTable* table = this->table(extno); // Save extension name std::string extname = table->extname(); // Optionally set extension name if (!name.empty()) { const_cast(table)->extname(name); } // Create VO Client GVOClient client; // Generate VO table FITS table GVOTable votable(*table); // Publish VO table using VO client client.publish(votable); // Restore extension name const_cast(table)->extname(extname); } // endelse: published HDU as VO table // Return return; } /***********************************************************************//** * @brief Publish FITS HDU on VO Hub * * @param[in] extname Extension name. * @param[in] name Optional name of published ressource. * * Publishes FITS HDU with specified @p extname. ***************************************************************************/ void GFits::publish(const std::string& extname, const std::string& name) const { // Get extenion number int extno = this->extno(extname); if (extno == -1) { std::string msg = "Extension \""+extname+"\" not found in FITS file. " "Please specify a valid extension name."; throw GException::invalid_argument(G_PUBLISH, msg); } // Publish FITS HDU publish(extno, name); // Return return; } /***********************************************************************//** * @brief Print FITS information * * @param[in] chatter Chattiness. * @return String containing FITS information. ***************************************************************************/ std::string GFits::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("=== GFits ==="); // Append file information result.append("\n"+gammalib::parformat("Filename")); result.append(m_filename); result.append("\n"+gammalib::parformat("History")); if (m_created) { result.append("new file"); } else { result.append("existing file"); } result.append("\n"+gammalib::parformat("Mode")); if (m_readwrite) { result.append("read/write"); } else { result.append("read only"); } result.append("\n"+gammalib::parformat("Number of HDUs")); result.append(gammalib::str(size())); // NORMAL: Append HDUs if (chatter >= NORMAL) { for (int i = 0; i < size(); ++i) { result.append("\n"); result.append(m_hdu[i]->print(gammalib::reduce(chatter))); } } } // endif: chatter was not silent // Return result return result; } /*========================================================================== = = = Private methods = = = ==========================================================================*/ /***********************************************************************//** * @brief Initialise class members ***************************************************************************/ void GFits::init_members(void) { // Initialise GFits members m_hdu.clear(); m_filename.clear(); m_fitsfile = NULL; m_readwrite = true; m_created = true; // Return return; } /***********************************************************************//** * @brief Copy class members * * @param fits Object to be copied * * The method does not copy the FITS file name and FITS file pointer. * This prevents that several copies of the FITS file pointer exist in * different instances of GFits, which would lead to confusion since one * instance could close the file while for another it still would be * opened. The rule ONE INSTANCE - ONE FILE applies. ***************************************************************************/ void GFits::copy_members(const GFits& fits) { // Reset FITS file attributes m_filename.clear(); m_fitsfile = NULL; m_readwrite = true; m_created = true; // Clone HDUs m_hdu.clear(); for (int i = 0; i < fits.m_hdu.size(); ++i) { m_hdu.push_back((fits.m_hdu[i]->clone())); } // Return return; } /***********************************************************************//** * @brief Delete class members * * Closes the FITS file if it had been opened and deallocate all HDUs. * * If the G_DELETE_EMPTY_FITS_FILES option is defined, files without HDUs * or corrupted files will be deleted. This prevents leaving corrupted files * on disk (yet, corrupted files may be generated by another application, * thus this is not 100% safe; better make the code solid against reading * corrupted FITS files). ***************************************************************************/ void GFits::free_members(void) { // If FITS file has been opened then close it now if (m_fitsfile != NULL) { // If file has been created but not yet saved then delete the file // now. We do not worry about the status in this case. if (m_created) { int status = 0; __ffdelt(FPTR(m_fitsfile), &status); } // Compile option: If there are no HDUs then delete the file (don't // worry about error) #if defined(G_DELETE_EMPTY_FITS_FILES) else if (size() == 0) { int status = 0; __ffdelt(FPTR(m_fitsfile), &status); } #endif // ... otherwise close the file else { int status = 0; status = __ffclos(FPTR(m_fitsfile), &status); if (status == 252) { int new_status = 0; __ffdelt(FPTR(m_fitsfile), &new_status); if (new_status != 0) { throw GException::fits_error(G_FREE_MEM, new_status); } } else if (status != 0) { throw GException::fits_error(G_FREE_MEM, status); } } // endelse: there was an open FITS file // Set FITS file pointer to NULL m_fitsfile = NULL; } // endif: FITS file had been opened // Free HDUs for (int i = 0; i < m_hdu.size(); ++i) { if (m_hdu[i] != NULL) { delete m_hdu[i]; m_hdu[i] = NULL; } } // Clear HDUs m_hdu.clear(); // Return return; } /***********************************************************************//** * @brief Allocate new FITS image and return memory pointer * * Depending on the number of bits per pixel, a FITS image is allocated * and the pointer is returned. The following FITS image classes are * handled: * GFitsImageByte (bitpix=8) * GFitsImageShort (bitpix=16) * GFitsImageLong (bitpix=32) * GFitsImageLongLong (bitpix=64) * GFitsImageFloat (bitpix=-32) * GFitsImageDouble (bitpix=-64) * The information about the number of bits per pixels is extracted from * the actual HDU. * * @todo Additional code is needed to detect unsigned integer images. This * code may be insprired by the code used for table columns as the unsigned * information is stored in the BZERO keyword. ***************************************************************************/ GFitsImage* GFits::new_image(void) { // Initialise return value GFitsImage* image = NULL; // Get number of bits per pixel int status = 0; int bitpix = -64; status = __ffgiet(FPTR(m_fitsfile), &bitpix, &status); if (status != 0) { throw GException::fits_error(G_NEW_IMAGE, status); } // Check for unsigned image char keyname[10]; long long bzero; long long bscale; std::sprintf(keyname, "BZERO"); if (__ffgky(FPTR(m_fitsfile), __TLONGLONG, keyname, &bzero, NULL, &status) != 0) { bzero = 0; } std::sprintf(keyname, "BSCALE"); if (__ffgky(FPTR(m_fitsfile), __TLONGLONG, keyname, &bscale, NULL, &status) != 0) { bscale = 0; } if (bitpix == 8 && bzero == -128 && bscale == 1) { bitpix = 10; } else if (bitpix == 16 && bzero == 32768u && bscale == 1) { bitpix = 20; } else if (bitpix == 32 && bzero == 2147483648u && bscale == 1) { bitpix = 40; } // Allocate bitpix dependent image switch (bitpix) { case 8: image = new GFitsImageByte; break; case 10: image = new GFitsImageSByte; break; case 16: image = new GFitsImageShort; break; case 20: image = new GFitsImageUShort; break; case 32: image = new GFitsImageLong; break; case 40: image = new GFitsImageULong; break; case 64: image = new GFitsImageLongLong; break; case -32: image = new GFitsImageFloat; break; case -64: image = new GFitsImageDouble; break; default: throw GException::fits_bad_bitpix(G_NEW_IMAGE, bitpix); break; } // Return image pointer return image; } /***********************************************************************//** * @brief Return minimal primary HDU * * Creates a primary HDU in memory and open it using the GFitsHDU::open() * method. ***************************************************************************/ GFitsImage* GFits::new_primary(void) { // Allocate an empty image GFitsImage* image = new GFitsImageByte; // Create primary image in memory int status = 0; __fitsfile* fptr; status = __ffinit(&fptr, "mem://", &status); status = __ffcrim(fptr, 8, 0, NULL, &status); // Open HDU image->open(fptr,0); // Close FITS file in memory status = __ffclos(fptr, &status); if (status == 252) { status = 0; status = __ffdelt(fptr, &status); } // Initialise FITS file pointer FPTR(image->m_fitsfile)->HDUposition = 0; FPTR(image->m_fitsfile)->Fptr = NULL; // Return image return image; } /*========================================================================== = = = FITS utility functions = = = ==========================================================================*/ /***********************************************************************//** * @brief Move to FITS extension * * @param[in] caller Name of caller. * @param[in] vptr FITS file void pointer. * @param[in] hdunum HDU number (optional) * * @exception GException::fits_error * cfitsio error occured. * * If @p hdunum is >0, moves the FITS file void pointer to the HDU * specified by @p hdunum. Otherwise, the FITS file void pointer is moved * to the HDU specified by the @p HDUposition attribute of the void pointer. ***************************************************************************/ int gammalib::fits_move_to_hdu(const std::string& caller, void* vptr, const int& hdunum) { // Initialise status and HDU type int status = 0; int type = 0; // Set HDU position int position = (hdunum > 0) ? hdunum : (FPTR(vptr)->HDUposition)+1; // Move to HDU status = __ffmahd(FPTR(vptr), position, &type, &status); // Throw exception in case of an error if (status != 0) { std::string msg = "Unable to move FITS file pointer to extension " "number "+gammalib::str(position-1)+"."; throw GException::fits_error(caller, status, msg); } // Return HDU type return type; }