Commit a97eb9cd1dedfc14b1da5e585a874686db38f229
1 parent
c4ca5ceb
Implement FITS extension handling in GSkyMap load() and save() methods.
Showing
6 changed files
with
208 additions
and
56 deletions
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) | ... | ... |
NEWS
| ... | ... | @@ -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& 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& 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& dir1, const GSkyDir& 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); | ... | ... |