Commit a97eb9cd1dedfc14b1da5e585a874686db38f229

Authored by Jürgen Knödlseder
1 parent c4ca5ceb

Implement FITS extension handling in GSkyMap load() and save() methods.

ChangeLog
... ... @@ -3,6 +3,7 @@
3 3 * Version 1.1.0 released
4 4 =========================
5 5  
  6 + Handle FITS extensions in GSkyMap load() and save() methods (#1695)
6 7 Restrict Monte Carlo simulations in GModelSpatialDiffuseConst to simulation cone (#1674)
7 8 Use default GTime in GModelSpectral eval() and eval_gradients() methods (#1672)
8 9 Optimise GModelSpatialDiffuseMap::set_mc_cone() method (#1673)
... ...
... ... @@ -151,7 +151,11 @@ None
151 151  
152 152 12. Sky module
153 153 --------------
154   -None
  154 +The GSkyMap load() and save() methods now support the handling of FITS
  155 +extension names. Users can now specify with which extension name a sky
  156 +map is written to a FITS file, and from which extension name it should
  157 +be loaded. This allows to have multiple sky maps in the same file, and
  158 +to mix also HEALPix and WCS maps in the same file (#1695).
155 159  
156 160  
157 161 13. Support module
... ...
include/GSkyMap.hpp
1 1 /***************************************************************************
2 2 * GSkyMap.hpp - Sky map class *
3 3 * ----------------------------------------------------------------------- *
4   - * copyright (C) 2010-2015 by Juergen Knoedlseder *
  4 + * copyright (C) 2010-2016 by Juergen Knoedlseder *
5 5 * ----------------------------------------------------------------------- *
6 6 * *
7 7 * This program is free software: you can redistribute it and/or modify *
... ... @@ -33,20 +33,12 @@
33 33 #include "GSkyDir.hpp"
34 34 #include "GSkyPixel.hpp"
35 35 #include "GSkyProjection.hpp"
36   -/*
37   -#include "GFits.hpp"
38   -#include "GFitsTable.hpp"
39   -#include "GFitsBinTable.hpp"
40   -#include "GFitsImage.hpp"
41   -#include "GFitsImageDouble.hpp"
42   -*/
43   -//#include "GMatrix.hpp"
44   -//#include "GVector.hpp"
45 36 #include "GBilinear.hpp"
46 37  
47 38 /* __ Forward declarations _______________________________________________ */
48 39 class GFilename;
49 40 class GFits;
  41 +class GFitsHDU;
50 42 class GFitsTable;
51 43 class GFitsBinTable;
52 44 class GFitsImage;
... ... @@ -186,6 +178,8 @@ private:
186 178 const GSkyDir& dir3) const;
187 179 double solidangle(const GSkyDir& dir1, const GSkyDir& dir2,
188 180 const GSkyDir& dir3, const GSkyDir& dir4) const;
  181 + bool is_healpix(const GFitsHDU& hdu) const;
  182 + bool is_wcs(const GFitsHDU& hdu) const;
189 183  
190 184 // Private data area
191 185 int m_num_pixels; //!< Number of pixels (used for pixel allocation)
... ...
src/sky/GSkyMap.cpp
... ... @@ -1746,56 +1746,85 @@ void GSkyMap::stack_maps(void)
1746 1746 ***************************************************************************/
1747 1747 void GSkyMap::load(const GFilename& filename)
1748 1748 {
1749   - // Free memory and initialise members
1750   - free_members();
1751   - init_members();
1752   -
1753   - // Open FITS file
1754   - GFits fits(filename);
1755   -
1756   - // Get number of HDUs
1757   - int num = fits.size();
  1749 + // Clear sky map
  1750 + clear();
1758 1751  
1759 1752 // Initialize load flag
1760   - bool loaded = false;
  1753 + bool loaded(false);
1761 1754  
1762   - // First search for HEALPix extension. We can skip the first extension
1763   - // since this is always an image and a HEALPix map is stored in a
1764   - // binary table
1765   - for (int extno = 1; extno < num; ++extno) {
1766   -
1767   - // Get reference to HDU
1768   - const GFitsHDU& hdu = *fits.at(extno);
  1755 + // Open FITS file
  1756 + GFits fits(filename);
1769 1757  
1770   - // If PIXTYPE keyword equals "HEALPIX" then load map
1771   - if (hdu.has_card("PIXTYPE") && hdu.string("PIXTYPE") == "HEALPIX") {
  1758 + // If an extension name is specified then first try loading that
  1759 + // extension
  1760 + if (filename.has_extname()) {
  1761 + const GFitsHDU& hdu = *fits.at(filename.extname());
  1762 + if (is_healpix(hdu)) {
1772 1763 read_healpix(static_cast<const GFitsTable&>(hdu));
1773 1764 loaded = true;
1774   - break;
1775 1765 }
  1766 + else if (is_wcs(hdu)) {
  1767 + read_wcs(static_cast<const GFitsImage&>(hdu));
  1768 + loaded = true;
  1769 + }
  1770 + }
1776 1771  
1777   - } // endfor: looped over HDUs
  1772 + // ... otherwise is an extension number is specified then try loading
  1773 + // the corresponding HDU
  1774 + else if (filename.has_extno()) {
  1775 + const GFitsHDU& hdu = *fits.at(filename.extno());
  1776 + if (is_healpix(hdu)) {
  1777 + read_healpix(static_cast<const GFitsTable&>(hdu));
  1778 + loaded = true;
  1779 + }
  1780 + else if (is_wcs(hdu)) {
  1781 + read_wcs(static_cast<const GFitsImage&>(hdu));
  1782 + loaded = true;
  1783 + }
  1784 + }
1778 1785  
1779   - // If we have not found a HEALPIX map then search now for image.
1780   - // Skip empty images
  1786 + // If no map has yet been loaded then scan the file for an appropriate
  1787 + // HDU and read sky map from the first suitable HDU
1781 1788 if (!loaded) {
1782   - for (int extno = 0; extno < num; ++extno) {
1783 1789  
1784   - // Get referene to HDU
  1790 + // Get number of HDUs
  1791 + int num = fits.size();
  1792 +
  1793 + // First search for HEALPix extension. We can skip the first
  1794 + // extension since this is always an image and a HEALPix map
  1795 + // is stored in a binary table
  1796 + for (int extno = 1; extno < num; ++extno) {
  1797 +
  1798 + // Get reference to HDU
1785 1799 const GFitsHDU& hdu = *fits.at(extno);
1786 1800  
1787   - // Skip if extension is not an image
1788   - if (extno > 0) {
1789   - if (hdu.string("XTENSION") != "IMAGE")
1790   - continue;
  1801 + // If HDU is HEALPix then read data
  1802 + if (is_healpix(hdu)) {
  1803 + read_healpix(static_cast<const GFitsTable&>(hdu));
  1804 + loaded = true;
  1805 + break;
1791 1806 }
1792 1807  
1793   - // Load WCS map
1794   - read_wcs(static_cast<const GFitsImage&>(hdu));
1795   - break;
1796   -
1797 1808 } // endfor: looped over HDUs
1798   - } // endif: no HEALPix map found
  1809 +
  1810 + // If we have not found a HEALPIX map then search now for an
  1811 + // image.
  1812 + if (!loaded) {
  1813 + for (int extno = 0; extno < num; ++extno) {
  1814 +
  1815 + // Get referene to HDU
  1816 + const GFitsHDU& hdu = *fits.at(extno);
  1817 +
  1818 + // If HDU is WCS then read data
  1819 + if (is_wcs(hdu)) {
  1820 + read_wcs(static_cast<const GFitsImage&>(hdu));
  1821 + loaded = true;
  1822 + break;
  1823 + }
  1824 +
  1825 + } // endfor: looped over HDUs
  1826 + } // endif: no sky map yet loaded
  1827 + } // endif: no sky map yet loaded
1799 1828  
1800 1829 // Close FITS file
1801 1830 fits.close();
... ... @@ -1806,12 +1835,14 @@ void GSkyMap::load(const GFilename&amp; filename)
1806 1835  
1807 1836  
1808 1837 /***********************************************************************//**
1809   - * @brief Save skymap into FITS file.
  1838 + * @brief Save sky map into FITS file
1810 1839 *
1811 1840 * @param[in] filename FITS file name.
1812   - * @param[in] clobber Overwrite existing file? (true=yes)
  1841 + * @param[in] clobber Overwrite existing file? (default: false)
1813 1842 *
1814   - * The method does nothing if the skymap holds no valid WCS.
  1843 + * Saves the sky map into a FITS file. If the file exists already the method
  1844 + * will append the sky map to the FITS file in case that the @p clobber
  1845 + * parameter is set to true.
1815 1846 ***************************************************************************/
1816 1847 void GSkyMap::save(const GFilename& filename, bool clobber) const
1817 1848 {
... ... @@ -1831,15 +1862,28 @@ void GSkyMap::save(const GFilename&amp; filename, bool clobber) const
1831 1862 hdu = create_wcs_hdu();
1832 1863 }
1833 1864  
1834   - // Create FITS file and save it to disk
  1865 + // If we have a valid HDU then save it now to the FITS file
1835 1866 if (hdu != NULL) {
1836   - GFits fits;
  1867 +
  1868 + // Set extension name
  1869 + if (filename.has_extname()) {
  1870 + hdu->extname(filename.extname());
  1871 + }
  1872 +
  1873 + // Open or create FITS file (without extension name since the
  1874 + // requested extension may not yet exist in the file)
  1875 + GFits fits(filename.url(), true);
  1876 +
  1877 + // Append sky map to FITS file
1837 1878 fits.append(*hdu);
1838   - fits.saveto(filename, clobber);
1839   - }
1840 1879  
1841   - // Delete HDU
1842   - if (hdu != NULL) delete hdu;
  1880 + // Save FITS file
  1881 + fits.save(clobber);
  1882 +
  1883 + // Delete HDU
  1884 + delete hdu;
  1885 +
  1886 + } // endif: HDU was valid
1843 1887  
1844 1888 } // endif: we had data to save
1845 1889  
... ... @@ -2674,6 +2718,51 @@ double GSkyMap::solidangle(const GSkyDir&amp; dir1, const GSkyDir&amp; dir2,
2674 2718 }
2675 2719  
2676 2720  
  2721 +/***********************************************************************//**
  2722 + * @brief Check if HDU contains HEALPix data
  2723 + *
  2724 + * @param[in] hdu FITS Header Data Unit.
  2725 + * @return True is HDU contains HEALPix data.
  2726 + ***************************************************************************/
  2727 +bool GSkyMap::is_healpix(const GFitsHDU& hdu) const
  2728 +{
  2729 + // Initialise flag
  2730 + bool flag(false);
  2731 +
  2732 + // If PIXTYPE keyword equals "HEALPIX" then signal that we have
  2733 + // HEALPix data
  2734 + if ((hdu.exttype() != GFitsHDU::HT_IMAGE) &&
  2735 + (hdu.has_card("PIXTYPE")) &&
  2736 + (hdu.string("PIXTYPE") == "HEALPIX")) {
  2737 + flag = true;
  2738 + }
  2739 +
  2740 + // Return flag
  2741 + return (flag);
  2742 +}
  2743 +
  2744 +
  2745 +/***********************************************************************//**
  2746 + * @brief Check if HDU contains WCS data
  2747 + *
  2748 + * @param[in] hdu FITS Header Data Unit.
  2749 + * @return True is HDU contains WCS data.
  2750 + ***************************************************************************/
  2751 +bool GSkyMap::is_wcs(const GFitsHDU& hdu) const
  2752 +{
  2753 + // Initialise flag
  2754 + bool flag(false);
  2755 +
  2756 + // If extension is an image thn signal that we have WCS data
  2757 + if (hdu.exttype() == GFitsHDU::HT_IMAGE) {
  2758 + flag = true;
  2759 + }
  2760 +
  2761 + // Return flag
  2762 + return (flag);
  2763 +}
  2764 +
  2765 +
2677 2766 /*==========================================================================
2678 2767 = =
2679 2768 = Friends =
... ...
test/test_GSky.cpp
1 1 /***************************************************************************
2 2 * test_GSky.cpp - Test sky module *
3 3 * ----------------------------------------------------------------------- *
4   - * copyright (C) 2010-2015 by Juergen Knoedlseder *
  4 + * copyright (C) 2010-2016 by Juergen Knoedlseder *
5 5 * ----------------------------------------------------------------------- *
6 6 * *
7 7 * This program is free software: you can redistribute it and/or modify *
... ... @@ -60,6 +60,7 @@ void TestGSky::set(void){
60 60 append(static_cast<pfunction>(&TestGSky::test_GSkyMap_wcs_construct),"Test WCS GSkyMap constructors");
61 61 append(static_cast<pfunction>(&TestGSky::test_GSkyMap_wcs_io),"Test WCS GSkyMap I/O");
62 62 append(static_cast<pfunction>(&TestGSky::test_GSkyMap),"Test GSkyMap");
  63 + append(static_cast<pfunction>(&TestGSky::test_GSkyMap_io),"Test GSkyMap I/O");
63 64 append(static_cast<pfunction>(&TestGSky::test_GSkyRegions_io),"Test GSkyRegions");
64 65 append(static_cast<pfunction>(&TestGSky::test_GSkyRegionCircle_construct),"Test GSkyRegionCircle constructors");
65 66 append(static_cast<pfunction>(&TestGSky::test_GSkyRegionCircle_logic),"Test GSkyRegionCircle logic");
... ... @@ -1032,6 +1033,68 @@ void TestGSky::test_GSkyMap(void)
1032 1033  
1033 1034  
1034 1035 /***************************************************************************
  1036 + * @brief GSkyMap
  1037 + *
  1038 + * This method tests the GSkyMap class input and output. Specifically, the
  1039 + * test verifies that saving of a HEALPix and WCS map in the same FITS file
  1040 + * is possible, and that upon opening the correct extension is selected.
  1041 + ***************************************************************************/
  1042 +void TestGSky::test_GSkyMap_io(void)
  1043 +{
  1044 + // Set filename
  1045 + GFilename filename("test_skymap.fits");
  1046 + filename.remove();
  1047 +
  1048 + // Create HEALPix and WCS maps and save them to FITS file
  1049 + GSkyMap healpix1("GAL", 4, "RING", 1);
  1050 + GSkyMap healpix2("GAL", 2, "RING", 1);
  1051 + GSkyMap wcs1("CAR", "GAL", 0.0, 0.0, -1.0, 1.0, 10, 10);
  1052 + GSkyMap wcs2("CAR", "GAL", 0.0, 0.0, -1.0, 1.0, 5, 5);
  1053 + for (int i = 0; i < healpix1.npix(); ++i) {
  1054 + healpix1(i) = double(i+1);
  1055 + }
  1056 + for (int i = 0; i < healpix2.npix(); ++i) {
  1057 + healpix2(i) = 3.0*double(i+1);
  1058 + }
  1059 + for (int i = 0; i < wcs1.npix(); ++i) {
  1060 + wcs1(i) = double(i+1);
  1061 + }
  1062 + for (int i = 0; i < wcs2.npix(); ++i) {
  1063 + wcs2(i) = 3.0*double(i+1);
  1064 + }
  1065 + healpix1.save(filename+"[HEALPIX1]", true);
  1066 + wcs1.save(filename+"[WCS1]", true);
  1067 + healpix2.save(filename+"[HEALPIX2]", true);
  1068 + wcs2.save(filename+"[WCS2]", true);
  1069 +
  1070 + // Load HEALPix map and check content
  1071 + GSkyMap map1(filename+"[HEALPIX1]");
  1072 + test_value(map1.npix(), healpix1.npix(), "Check number of HEALPix pixels");
  1073 + int failures1(0);
  1074 + for (int i = 0; i < map1.npix(); ++i) {
  1075 + if (std::abs(map1(i)-double(i+1)) > 1.0e-6) {
  1076 + failures1++;
  1077 + }
  1078 + }
  1079 + test_value(failures1, 0, "Check number of invalid HEALPix pixel values");
  1080 +
  1081 + // Load WCS map and check content
  1082 + GSkyMap map2(filename+"[WCS1]");
  1083 + test_value(map2.npix(), wcs1.npix(), "Check number of WCS pixels");
  1084 + int failures2(0);
  1085 + for (int i = 0; i < map2.npix(); ++i) {
  1086 + if (std::abs(map2(i)-double(i+1)) > 1.0e-6) {
  1087 + failures2++;
  1088 + }
  1089 + }
  1090 + test_value(failures2, 0, "Check number of invalid WCS pixel values");
  1091 +
  1092 + // Exit test
  1093 + return;
  1094 +}
  1095 +
  1096 +
  1097 +/***************************************************************************
1035 1098 * @brief GSkyRegionCircle_construct
1036 1099 ***************************************************************************/
1037 1100 void TestGSky::test_GSkyRegionCircle_construct(void)
... ...
test/test_GSky.hpp
1 1 /***************************************************************************
2 2 * test_GSky.hpp - Test sky module *
3 3 * ----------------------------------------------------------------------- *
4   - * copyright (C) 2012-2014 by Jean-Baptiste Cayrou *
  4 + * copyright (C) 2012-2016 by Jean-Baptiste Cayrou *
5 5 * ----------------------------------------------------------------------- *
6 6 * *
7 7 * This program is free software: you can redistribute it and/or modify *
... ... @@ -48,6 +48,7 @@ public:
48 48 void test_GSkyMap_wcs_construct(void);
49 49 void test_GSkyMap_wcs_io(void);
50 50 void test_GSkyMap(void);
  51 + void test_GSkyMap_io(void);
51 52 void test_GSkyRegions_io(void);
52 53 void test_GSkyRegionCircle_construct(void);
53 54 void test_GSkyRegionCircle_logic(void);
... ...