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,6 +3,7 @@
3 * Version 1.1.0 released 3 * Version 1.1.0 released
4 ========================= 4 =========================
5 5
  6 + Handle FITS extensions in GSkyMap load() and save() methods (#1695)
6 Restrict Monte Carlo simulations in GModelSpatialDiffuseConst to simulation cone (#1674) 7 Restrict Monte Carlo simulations in GModelSpatialDiffuseConst to simulation cone (#1674)
7 Use default GTime in GModelSpectral eval() and eval_gradients() methods (#1672) 8 Use default GTime in GModelSpectral eval() and eval_gradients() methods (#1672)
8 Optimise GModelSpatialDiffuseMap::set_mc_cone() method (#1673) 9 Optimise GModelSpatialDiffuseMap::set_mc_cone() method (#1673)
@@ -151,7 +151,11 @@ None @@ -151,7 +151,11 @@ None
151 151
152 12. Sky module 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 13. Support module 161 13. Support module
include/GSkyMap.hpp
1 /*************************************************************************** 1 /***************************************************************************
2 * GSkyMap.hpp - Sky map class * 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 * This program is free software: you can redistribute it and/or modify * 7 * This program is free software: you can redistribute it and/or modify *
@@ -33,20 +33,12 @@ @@ -33,20 +33,12 @@
33 #include "GSkyDir.hpp" 33 #include "GSkyDir.hpp"
34 #include "GSkyPixel.hpp" 34 #include "GSkyPixel.hpp"
35 #include "GSkyProjection.hpp" 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 #include "GBilinear.hpp" 36 #include "GBilinear.hpp"
46 37
47 /* __ Forward declarations _______________________________________________ */ 38 /* __ Forward declarations _______________________________________________ */
48 class GFilename; 39 class GFilename;
49 class GFits; 40 class GFits;
  41 +class GFitsHDU;
50 class GFitsTable; 42 class GFitsTable;
51 class GFitsBinTable; 43 class GFitsBinTable;
52 class GFitsImage; 44 class GFitsImage;
@@ -186,6 +178,8 @@ private: @@ -186,6 +178,8 @@ private:
186 const GSkyDir& dir3) const; 178 const GSkyDir& dir3) const;
187 double solidangle(const GSkyDir& dir1, const GSkyDir& dir2, 179 double solidangle(const GSkyDir& dir1, const GSkyDir& dir2,
188 const GSkyDir& dir3, const GSkyDir& dir4) const; 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 // Private data area 184 // Private data area
191 int m_num_pixels; //!< Number of pixels (used for pixel allocation) 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,56 +1746,85 @@ void GSkyMap::stack_maps(void)
1746 ***************************************************************************/ 1746 ***************************************************************************/
1747 void GSkyMap::load(const GFilename& filename) 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 // Initialize load flag 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 read_healpix(static_cast<const GFitsTable&>(hdu)); 1763 read_healpix(static_cast<const GFitsTable&>(hdu));
1773 loaded = true; 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 if (!loaded) { 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 const GFitsHDU& hdu = *fits.at(extno); 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 } // endfor: looped over HDUs 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 // Close FITS file 1829 // Close FITS file
1801 fits.close(); 1830 fits.close();
@@ -1806,12 +1835,14 @@ void GSkyMap::load(const GFilename&amp; filename) @@ -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 * @param[in] filename FITS file name. 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 void GSkyMap::save(const GFilename& filename, bool clobber) const 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,15 +1862,28 @@ void GSkyMap::save(const GFilename&amp; filename, bool clobber) const
1831 hdu = create_wcs_hdu(); 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 if (hdu != NULL) { 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 fits.append(*hdu); 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 } // endif: we had data to save 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,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 = Friends = 2768 = Friends =
test/test_GSky.cpp
1 /*************************************************************************** 1 /***************************************************************************
2 * test_GSky.cpp - Test sky module * 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 * This program is free software: you can redistribute it and/or modify * 7 * This program is free software: you can redistribute it and/or modify *
@@ -60,6 +60,7 @@ void TestGSky::set(void){ @@ -60,6 +60,7 @@ void TestGSky::set(void){
60 append(static_cast<pfunction>(&TestGSky::test_GSkyMap_wcs_construct),"Test WCS GSkyMap constructors"); 60 append(static_cast<pfunction>(&TestGSky::test_GSkyMap_wcs_construct),"Test WCS GSkyMap constructors");
61 append(static_cast<pfunction>(&TestGSky::test_GSkyMap_wcs_io),"Test WCS GSkyMap I/O"); 61 append(static_cast<pfunction>(&TestGSky::test_GSkyMap_wcs_io),"Test WCS GSkyMap I/O");
62 append(static_cast<pfunction>(&TestGSky::test_GSkyMap),"Test GSkyMap"); 62 append(static_cast<pfunction>(&TestGSky::test_GSkyMap),"Test GSkyMap");
  63 + append(static_cast<pfunction>(&TestGSky::test_GSkyMap_io),"Test GSkyMap I/O");
63 append(static_cast<pfunction>(&TestGSky::test_GSkyRegions_io),"Test GSkyRegions"); 64 append(static_cast<pfunction>(&TestGSky::test_GSkyRegions_io),"Test GSkyRegions");
64 append(static_cast<pfunction>(&TestGSky::test_GSkyRegionCircle_construct),"Test GSkyRegionCircle constructors"); 65 append(static_cast<pfunction>(&TestGSky::test_GSkyRegionCircle_construct),"Test GSkyRegionCircle constructors");
65 append(static_cast<pfunction>(&TestGSky::test_GSkyRegionCircle_logic),"Test GSkyRegionCircle logic"); 66 append(static_cast<pfunction>(&TestGSky::test_GSkyRegionCircle_logic),"Test GSkyRegionCircle logic");
@@ -1032,6 +1033,68 @@ void TestGSky::test_GSkyMap(void) @@ -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 * @brief GSkyRegionCircle_construct 1098 * @brief GSkyRegionCircle_construct
1036 ***************************************************************************/ 1099 ***************************************************************************/
1037 void TestGSky::test_GSkyRegionCircle_construct(void) 1100 void TestGSky::test_GSkyRegionCircle_construct(void)
test/test_GSky.hpp
1 /*************************************************************************** 1 /***************************************************************************
2 * test_GSky.hpp - Test sky module * 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 * This program is free software: you can redistribute it and/or modify * 7 * This program is free software: you can redistribute it and/or modify *
@@ -48,6 +48,7 @@ public: @@ -48,6 +48,7 @@ public:
48 void test_GSkyMap_wcs_construct(void); 48 void test_GSkyMap_wcs_construct(void);
49 void test_GSkyMap_wcs_io(void); 49 void test_GSkyMap_wcs_io(void);
50 void test_GSkyMap(void); 50 void test_GSkyMap(void);
  51 + void test_GSkyMap_io(void);
51 void test_GSkyRegions_io(void); 52 void test_GSkyRegions_io(void);
52 void test_GSkyRegionCircle_construct(void); 53 void test_GSkyRegionCircle_construct(void);
53 void test_GSkyRegionCircle_logic(void); 54 void test_GSkyRegionCircle_logic(void);