Commit 4bc6ad57d11758855774e7cefa66b33ce3f336e3

Authored by knodlseder
1 parent 54522345

implemented WCS loading/saving

include/GSkymap.hpp
... ... @@ -96,7 +96,10 @@ private:
96 96 int xy2pix(const GSkyPixel& pix) const;
97 97 GSkyPixel pix2xy(const int& pix) const;
98 98 void read_healpix(const GFitsHDU* hdu);
  99 + void read_wcs(const GFitsHDU* hdu);
  100 + void alloc_wcs(const GFitsHDU* hdu);
99 101 GFitsHDU* create_healpix_hdu(void);
  102 + GFitsHDU* create_wcs_hdu(void);
100 103  
101 104 // Private data area
102 105 int m_num_pixels; //!< Number of pixels (used for pixel allocation)
... ...
include/GWcs.hpp
... ... @@ -54,11 +54,11 @@ public:
54 54 virtual GWcs& operator= (const GWcs& wcs);
55 55  
56 56 // Pure virtual methods (not implemented)
57   - virtual std::string type(void) const = 0;
  57 + virtual void read(const GFitsHDU* hdu) = 0;
  58 + virtual void write(GFitsHDU* hdu) const = 0;
58 59  
59 60 // Virtual methods
60   - virtual void read(const GFitsHDU* hdu);
61   - virtual void write(GFitsHDU* hdu);
  61 + virtual std::string type(void) const;
62 62 virtual std::string coordsys(void) const;
63 63 virtual void coordsys(const std::string& coordsys);
64 64 virtual double omega(const int& pix) const;
... ... @@ -79,23 +79,32 @@ protected:
79 79 // Typdefs
80 80 typedef void (GWcs::*_wcspf)(GVector* coord) const;
81 81  
82   - // Protected methods
83   - GVector wcs_dir2native(GSkyDir dir) const;
84   - GSkyDir wcs_native2dir(GVector native) const;
85   - void wcs_init(const double& theta0);
86   - GVector wcs_getpole(const double& theta0);
87   - GMatrix wcs_get_rot(void);
88   - void dump_wcs(std::ostream& os) const;
  82 + // Protected methods (providing services to derived classes)
  83 + GVector wcs_dir2native(GSkyDir dir) const;
  84 + GSkyDir wcs_native2dir(GVector native) const;
  85 + void wcs_init(const double& theta0);
  86 + GVector wcs_getpole(const double& theta0);
  87 + GMatrix wcs_get_rot(void);
  88 + void wcs_set(const std::string& coords,
  89 + const double& crval1, const double& crval2,
  90 + const double& crpix1, const double& crpix2,
  91 + const double& cdelt1, const double& cdelt2);
  92 + void wcs_read(const GFitsHDU* hdu);
  93 + void wcs_write(GFitsHDU* hdu) const;
  94 + std::string wcs_crval1(void) const;
  95 + std::string wcs_crval2(void) const;
  96 + void wcs_dump(std::ostream& os) const;
89 97  
90 98 // Protected data area (astr structure)
91   - int m_coordsys; //!< 0=celestial, 1=galactic
92   - int m_reverse; //!< Reverse axes (1=true)
93   - GVector m_crval; //!< Value of reference point
94   - GVector m_crpix; //!< Pixel of reference point (starting from 1)
95   - GVector m_cdelt; //!< Increment at reference point (deg/pixel)
96   - GVector m_npole; //!< Native coordinate of North Pole
97   - GMatrix m_cd; //!< Astrometry parameters (2x2 matrix, deg/pixel)
98   - GVector m_pv2; //!< Projection parameters (up to 21)
  99 + std::string m_type; //!< WCS type
  100 + int m_coordsys; //!< 0=celestial, 1=galactic
  101 + int m_reverse; //!< Reverse axes (1=true)
  102 + GVector m_crval; //!< Value of reference point
  103 + GVector m_crpix; //!< Pixel of reference point (starting from 1)
  104 + GVector m_cdelt; //!< Increment at reference point (deg/pixel)
  105 + GVector m_npole; //!< Native coordinate of North Pole
  106 + GMatrix m_cd; //!< Astrometry parameters (2x2 matrix, deg/pixel)
  107 + GVector m_pv2; //!< Projection parameters (up to 21)
99 108  
100 109 // Derived parameters
101 110 double m_theta0; //!< Native latitude of the fiducial point
... ...
include/GWcsCAR.hpp
... ... @@ -54,7 +54,8 @@ public:
54 54 GWcsCAR& operator= (const GWcsCAR& wcs);
55 55  
56 56 // Implemented pure virtual methods
57   - std::string type(void) const;
  57 + void read(const GFitsHDU* hdu);
  58 + void write(GFitsHDU* hdu) const;
58 59  
59 60 // Overloaded base class methods
60 61 double omega(const GSkyPixel& pix) const;
... ...
include/GWcsHPX.hpp
... ... @@ -51,9 +51,8 @@ public:
51 51 GWcsHPX& operator= (const GWcsHPX& wcs);
52 52  
53 53 // Implemented pure virtual methods
54   - std::string type(void) const;
55   - void read(const GFitsHDU* hdu);
56   - void write(GFitsHDU* hdu) const;
  54 + void read(const GFitsHDU* hdu);
  55 + void write(GFitsHDU* hdu) const;
57 56  
58 57 // Overloaded base class methods
59 58 double omega(const int& pix) const;
... ...
src/sky/GSkymap.cpp
... ... @@ -9,7 +9,6 @@
9 9 * the Free Software Foundation; either version 2 of the License, or *
10 10 * (at your option) any later version. *
11 11 * *
12   - * ----------------------------------------------------------------------- *
13 12 ***************************************************************************/
14 13 /**
15 14 * @file GSkymap.cpp
... ... @@ -29,6 +28,7 @@
29 28 #include "GWcsHPX.hpp"
30 29 #include "GFits.hpp"
31 30 #include "GFitsTableDblCol.hpp"
  31 +#include "GFitsImageDbl.hpp"
32 32  
33 33 /* __ Method name definitions ____________________________________________ */
34 34 #define G_CONSTRUCT_HPX "GSkymap::GSkymap(std::string,std::string,int," \
... ... @@ -46,6 +46,8 @@
46 46 #define G_SET_WCS "GSkymap::set_wcs(std::string,std::string,double,double," \
47 47 "double,double,double,double,GMatrix,GVector)"
48 48 #define G_READ_HEALPIX "GSkymap::read_healpix(GFitsHDU*)"
  49 +#define G_READ_WCS "GSkymap::read_wcs(GFitsHDU*)"
  50 +#define G_ALLOC_WCS "GSkymap::alloc_wcs(GFitsHDU*)"
49 51  
50 52 /* __ Macros _____________________________________________________________ */
51 53  
... ... @@ -53,6 +55,7 @@
53 55  
54 56 /* __ Debug definitions __________________________________________________ */
55 57 //#define G_READ_HEALPIX_DEBUG // Debug read_healpix
  58 +//#define G_READ_WCS_DEBUG // Debug read_wcs
56 59  
57 60 /* __ Prototype __________________________________________________________ */
58 61  
... ... @@ -141,7 +144,7 @@ GSkymap::GSkymap(const std::string&amp; wcs, const std::string&amp; coords,
141 144  
142 145  
143 146 /***********************************************************************//**
144   - * @brief 2D sky map constructor
  147 + * @brief WCS sky map constructor
145 148 *
146 149 * @param[in] wcs World Coordinate System.
147 150 * @param[in] coords Coordinate System (CEL or GAL).
... ... @@ -405,7 +408,7 @@ void GSkymap::load(const std::string&amp; filename)
405 408 // Get pointer to HDU
406 409 hdu = fits.hdu(extno);
407 410  
408   - // Check if PIXTYPE keyword equals "HEALPIX"
  411 + // If PIXTYPE keyword equals "HEALPIX" then load map
409 412 try {
410 413 if (hdu->card("PIXTYPE")->string() == "HEALPIX") {
411 414 read_healpix(hdu);
... ... @@ -422,9 +425,23 @@ void GSkymap::load(const std::string&amp; filename)
422 425 // Skip empty images
423 426 if (loaded == 0) {
424 427 for (int extno = 0; extno < num; ++extno) {
425   - //TODO: Implement non HEALPix loading
426   - }
427   - }
  428 +
  429 + // Get pointer to HDU
  430 + hdu = fits.hdu(extno);
  431 +
  432 + // Skip if extension is not an image
  433 + if (extno > 0) {
  434 + if (hdu->card("XTENSION")->string() != "IMAGE")
  435 + continue;
  436 + }
  437 +
  438 + // Load WCS map
  439 + read_wcs(hdu);
  440 + loaded = 1;
  441 + break;
  442 +
  443 + } // endfor: looped over HDUs
  444 + } // endif: no HEALPix map found
428 445  
429 446 // Close FITS file
430 447 fits.close();
... ... @@ -453,9 +470,8 @@ void GSkymap::save(const std::string&amp; filename, int clobber)
453 470 hdu = create_healpix_hdu();
454 471  
455 472 // Case B: Skymap is not Healpix
456   - else {
457   - //TODO: Implement non HEALPix saving
458   - }
  473 + else
  474 + hdu = create_wcs_hdu();
459 475  
460 476 // Create FITS file and save it to disk
461 477 if (hdu != NULL) {
... ... @@ -500,8 +516,14 @@ void GSkymap::read(const GFitsHDU* hdu)
500 516  
501 517 // ... otherwise try loading as non HEALPix map
502 518 if (loaded == 0) {
503   - //TODO: Implement non HEALPix loading
504   - }
  519 +
  520 + // Load only if HDU contains an image
  521 + if (hdu->card("XTENSION")->string() == "IMAGE") {
  522 + read_wcs(hdu);
  523 + loaded = 1;
  524 + }
  525 +
  526 + } // endif
505 527  
506 528 } // endif: HDU pointer was valid
507 529  
... ... @@ -1068,6 +1090,127 @@ void GSkymap::read_healpix(const GFitsHDU* hdu)
1068 1090  
1069 1091  
1070 1092 /***********************************************************************//**
  1093 + * @brief Read WCS image from FITS HDU
  1094 + *
  1095 + * @param[in] hdu FITS HDU containing the WCS image.
  1096 + *
  1097 + * @exception GException::skymap_bad_image_dim
  1098 + * WCS image has invalid dimension (naxis=2 or 3).
  1099 + ***************************************************************************/
  1100 +void GSkymap::read_wcs(const GFitsHDU* hdu)
  1101 +{
  1102 + // Continue only if HDU is valid
  1103 + if (hdu != NULL) {
  1104 +
  1105 + // Allocate WCS
  1106 + alloc_wcs(hdu);
  1107 +
  1108 + // Read WCS information from FITS header
  1109 + m_wcs->read(hdu);
  1110 +
  1111 + // Load FITS image
  1112 + GFitsImageDbl* image = (GFitsImageDbl*)hdu->data();
  1113 +
  1114 + // Extract map dimension and number of maps from image
  1115 + if (image->naxis() == 2) {
  1116 + m_num_x = image->naxes(0);
  1117 + m_num_y = image->naxes(1);
  1118 + m_num_maps = 1;
  1119 + }
  1120 + else if (image->naxis() >= 3) {
  1121 + m_num_x = image->naxes(0);
  1122 + m_num_y = image->naxes(1);
  1123 + m_num_maps = image->naxes(2);
  1124 + }
  1125 + else
  1126 + throw GException::skymap_bad_image_dim(G_READ_WCS, image->naxis());
  1127 + #if defined(G_READ_WCS_DEBUG)
  1128 + std::cout << "m_num_x=" << m_num_x << std::endl;
  1129 + std::cout << "m_num_y=" << m_num_y << std::endl;
  1130 + std::cout << "m_num_maps=" << m_num_maps << std::endl;
  1131 + #endif
  1132 +
  1133 + // Compute number of pixels
  1134 + m_num_pixels = m_num_x * m_num_y;
  1135 + #if defined(G_READ_WCS_DEBUG)
  1136 + std::cout << "m_num_pixels=" << m_num_pixels << std::endl;
  1137 + #endif
  1138 +
  1139 + // Allocate pixels to hold the map
  1140 + alloc_pixels();
  1141 +
  1142 + // Read image
  1143 + if (image->naxis() == 2) {
  1144 + double* ptr = m_pixels;
  1145 + for (int iy = 0; iy < m_num_y; ++iy) {
  1146 + for (int ix = 0; ix < m_num_x; ++ix, ++ptr)
  1147 + *ptr = (*image)(ix,iy);
  1148 + }
  1149 + }
  1150 + else {
  1151 + double* ptr = m_pixels;
  1152 + for (int iy = 0; iy < m_num_y; ++iy) {
  1153 + for (int ix = 0; ix < m_num_x; ++ix) {
  1154 + for (int imap = 0; imap < m_num_maps; ++imap, ++ptr)
  1155 + *ptr = (*image)(ix,iy,imap);
  1156 + }
  1157 + }
  1158 + }
  1159 +
  1160 + } // endif: HDU was valid
  1161 +
  1162 + // Return
  1163 + return;
  1164 +}
  1165 +
  1166 +
  1167 +/***********************************************************************//**
  1168 + * @brief Allocate WCS class
  1169 + *
  1170 + * @param[in] hdu FITS HDU containing the WCS image.
  1171 + *
  1172 + * @exception GException::fits_key_not_found
  1173 + * Unable to find required FITS header keyword.
  1174 + * @exception GException::skymap_bad_ctype
  1175 + * CTYPE1 and CTYPE2 keywords are incompatible.
  1176 + * @exception GException::wcs_invalid
  1177 + * WCS projection of FITS file not supported by GammaLib.
  1178 + ***************************************************************************/
  1179 +void GSkymap::alloc_wcs(const GFitsHDU* hdu)
  1180 +{
  1181 + // Continue only if HDU is valid
  1182 + if (hdu != NULL) {
  1183 +
  1184 + // Get standard keywords
  1185 + std::string ctype1 = hdu->card("CTYPE1")->string();
  1186 + std::string ctype2 = hdu->card("CTYPE2")->string();
  1187 +
  1188 + // Extract projection type
  1189 + std::string xproj = ctype1.substr(5,3);
  1190 + std::string yproj = ctype2.substr(5,3);
  1191 +
  1192 + // Check that projection type is identical on both axes
  1193 + if (xproj != yproj)
  1194 + throw GException::skymap_bad_ctype(G_ALLOC_WCS,
  1195 + ctype1, ctype2);
  1196 +
  1197 + // Allocate WCS class
  1198 + if (xproj == "CAR") {
  1199 + m_wcs = new GWcsCAR;
  1200 + }
  1201 + else
  1202 + throw GException::wcs_invalid(G_ALLOC_WCS, xproj,
  1203 + "WCS projection found in FITS file"
  1204 + " is currently not supported.");
  1205 +
  1206 + } // endif: HDU was valid
  1207 +
  1208 + // Return
  1209 + return;
  1210 +}
  1211 +
  1212 +
  1213 +/***********************************************************************//**
1071 1214 * @brief Create FITS HDU containing Healpix data
1072 1215 *
1073 1216 * Returns pointer to HDU that contains the Healpix data. Deallocation of the
... ... @@ -1123,6 +1266,71 @@ GFitsHDU* GSkymap::create_healpix_hdu(void)
1123 1266 }
1124 1267  
1125 1268  
  1269 +/***********************************************************************//**
  1270 + * @brief Create FITS HDU containing WCS image
  1271 + *
  1272 + * Returns pointer to HDU that contains the WCS image. Deallocation of the
  1273 + * GFitsHDU object has to be done by the client.
  1274 + ***************************************************************************/
  1275 +GFitsHDU* GSkymap::create_wcs_hdu(void)
  1276 +{
  1277 + // Initialise result to NULL pointer
  1278 + GFitsHDU* hdu = NULL;
  1279 +
  1280 + // Compute size of Healpix data
  1281 + int size = m_num_pixels * m_num_maps;
  1282 +
  1283 + // Continue only if we have pixels
  1284 + if (size > 0) {
  1285 +
  1286 + // Set axis parameters for image construction
  1287 + int naxis = (m_num_maps == 1) ? 2 : 3;
  1288 + int naxes[] = {m_num_x, m_num_y, m_num_maps};
  1289 +
  1290 + // Allocate image
  1291 + GFitsImageDbl image(naxis, naxes);
  1292 +
  1293 + // Store data in image
  1294 + if (naxis == 2) {
  1295 + double* ptr = m_pixels;
  1296 + for (int iy = 0; iy < m_num_y; ++iy) {
  1297 + for (int ix = 0; ix < m_num_x; ++ix, ++ptr)
  1298 + image(ix,iy) = *ptr;
  1299 + }
  1300 + }
  1301 + else {
  1302 + double* ptr = m_pixels;
  1303 + for (int iy = 0; iy < m_num_y; ++iy) {
  1304 + for (int ix = 0; ix < m_num_x; ++ix) {
  1305 + for (int imap = 0; imap < m_num_maps; ++imap, ++ptr)
  1306 + image(ix,iy,imap) = *ptr;
  1307 + }
  1308 + }
  1309 + }
  1310 +
  1311 + // Create HDU that contains WCS map
  1312 + hdu = new GFitsHDU(image);
  1313 +
  1314 + } // endif: there were pixels
  1315 +
  1316 + // ... otherwise create an empty header
  1317 + else
  1318 + hdu = new GFitsHDU;
  1319 +
  1320 + // Set extension name
  1321 + hdu->extname("IMAGE");
  1322 +
  1323 + // If we have WCS information then write into FITS header
  1324 + if (m_wcs != NULL) m_wcs->write(hdu);
  1325 +
  1326 + // Set additional keywords
  1327 + //TODO
  1328 +
  1329 + // Return HDU
  1330 + return hdu;
  1331 +}
  1332 +
  1333 +
1126 1334 /*==========================================================================
1127 1335 = =
1128 1336 = GSkymap friends =
... ...
src/sky/GWcs.cpp
... ... @@ -25,13 +25,17 @@
25 25 #include "GWcs.hpp"
26 26  
27 27 /* __ Method name definitions ____________________________________________ */
28   -#define G_WCS "GWcs::GWcs(std::string,double,double,double,double,double,"\
29   - "double)"
30 28 #define G_COORDSYS_SET "GWcs::coordsys(std::string)"
31 29 #define G_OMEGA1 "GWcs::omega(int)"
32 30 #define G_OMEGA2 "GWcs::omega(GSkyPixel)"
33 31 #define G_XY2DIR "GWcs::xy2dir(GSkyPixel)"
34 32 #define G_DIR2XY "GWcs::dir2xy(GSkyDir)"
  33 +#define G_WCS_SET "GWcs::GWcs(std::string,double,double,double,double,"\
  34 + "double,double)"
  35 +#define G_WCS_READ "GWcs::wcs_read(GFitsHDU*)"
  36 +#define G_WCS_WRITE "GWcs::wcs_write(GFitsHDU*)"
  37 +#define G_WCS_CRVAL1 "GWcs::wcs_crval1()"
  38 +#define G_WCS_CRVAL2 "GWcs::wcs_crval1()"
35 39  
36 40 /* __ Macros _____________________________________________________________ */
37 41  
... ... @@ -67,7 +71,7 @@ GWcs::GWcs(void)
67 71  
68 72  
69 73 /***********************************************************************//**
70   - * @brief Standard 2D sky map constructor
  74 + * @brief Standard WCS sky map constructor
71 75 *
72 76 * @param[in] coords Coordinate system.
73 77 * @param[in] crval1 X value of reference pixel.
... ... @@ -77,7 +81,7 @@ GWcs::GWcs(void)
77 81 * @param[in] cdelt1 Increment in x direction at reference pixel (deg).
78 82 * @param[in] cdelt2 Increment in y direction at reference pixel (deg).
79 83 *
80   - * Construct standard 2D sky map from standard definition parameters. This
  84 + * Construct standard WCS sky map from standard definition parameters. This
81 85 * method
82 86 ***************************************************************************/
83 87 GWcs::GWcs(const std::string& coords,
... ... @@ -89,35 +93,11 @@ GWcs::GWcs(const std::string&amp; coords,
89 93 // Initialise class members
90 94 init_members();
91 95  
92   - //TODO: Check parameters
93   -
94   - // Set coordinate system
95   - coordsys(coords);
96   -
97   - // Set parameters
  96 + // Initialise reverse flag
98 97 m_reverse = 0;
99   - m_crval = GVector(crval1, crval2);
100   - m_crpix = GVector(crpix1, crpix2);
101   - m_cdelt = GVector(cdelt1, cdelt2);
102   - m_refval = m_crval;
103   - m_refpix = m_crpix - GVector(1.0,1.0);
104 98  
105   - // Set CD matrix corresponding to no rotation
106   - m_cd(0,0) = m_cdelt(0);
107   - m_cd(1,0) = 0.0;
108   - m_cd(0,1) = 0.0;
109   - m_cd(1,1) = m_cdelt(1);
110   -
111   - // Compute inverse CD matrix
112   - double delta = m_cd(0,0)*m_cd(1,1) - m_cd(0,1)*m_cd(1,0);
113   - if (delta != 0.0) {
114   - m_invcd(0,0) = m_cd(1,1) / delta;
115   - m_invcd(0,1) = -m_cd(0,1) / delta;
116   - m_invcd(1,0) = -m_cd(1,0) / delta;
117   - m_invcd(1,1) = m_cd(0,0) / delta;
118   - }
119   - else
120   - throw GException::wcs::wcs(G_WCS, "Unable to invert CD matrix.");
  99 + // Set standard parameters
  100 + wcs_set(coords, crval1, crval2, crpix1, crpix2, cdelt1, cdelt2);
121 101  
122 102 // Return
123 103 return;
... ... @@ -194,37 +174,12 @@ GWcs&amp; GWcs::operator= (const GWcs&amp; wcs)
194 174 ==========================================================================*/
195 175  
196 176 /***********************************************************************//**
197   - * @brief Read WCS definiton from FITS header.
198   - *
199   - * @param[in] hdu FITS HDU containing the WCS definition.
  177 + * @brief Return WCS type
200 178 ***************************************************************************/
201   -void GWcs::read(const GFitsHDU* hdu)
  179 +std::string GWcs::type(void) const
202 180 {
203   - // Free memory and initialise members
204   - free_members();
205   - init_members();
206   -
207   - //TODO: Implement WCS definition reading
208   -
209   - // Return
210   - return;
211   -}
212   -
213   -
214   -/***********************************************************************//**
215   - * @brief Write WCS definiton into FITS HDU
216   - *
217   - * @param[in] hdu FITS HDU into which WCS definition will be written.
218   - ***************************************************************************/
219   -void GWcs::write(GFitsHDU* hdu)
220   -{
221   - // Continue only if HDU is valid
222   - if (hdu != NULL) {
223   -
224   - } // endif: HDU was valid
225   -
226   - // Return
227   - return;
  181 + // Return Healix type
  182 + return m_type;
228 183 }
229 184  
230 185  
... ... @@ -753,11 +708,263 @@ GMatrix GWcs::wcs_get_rot(void)
753 708  
754 709  
755 710 /***********************************************************************//**
  711 + * @brief Set standard WCS parameters
  712 + *
  713 + * @param[in] coords Coordinate system.
  714 + * @param[in] crval1 X value of reference pixel.
  715 + * @param[in] crval1 Y value of reference pixel.
  716 + * @param[in] crpix1 X index of reference pixel (first pixel is 1).
  717 + * @param[in] crpix2 Y index of reference pixel (first pixel is 1).
  718 + * @param[in] cdelt1 Increment in x direction at reference pixel (deg).
  719 + * @param[in] cdelt2 Increment in y direction at reference pixel (deg).
  720 + *
  721 + * @exception GException::wcs
  722 + * Unable to invert CD matrix.
  723 + *
  724 + * Note that this method does not initialize the class members.
  725 + ***************************************************************************/
  726 +void GWcs::wcs_set(const std::string& coords,
  727 + const double& crval1, const double& crval2,
  728 + const double& crpix1, const double& crpix2,
  729 + const double& cdelt1, const double& cdelt2)
  730 +
  731 +{
  732 + //TODO: Check parameters
  733 +
  734 + // Set coordinate system
  735 + coordsys(coords);
  736 +
  737 + // Set parameters
  738 + m_crval = GVector(crval1, crval2);
  739 + m_crpix = GVector(crpix1, crpix2);
  740 + m_cdelt = GVector(cdelt1, cdelt2);
  741 + m_refval = m_crval;
  742 + m_refpix = m_crpix - GVector(1.0,1.0);
  743 +
  744 + // Set CD matrix corresponding to no rotation
  745 + m_cd(0,0) = m_cdelt(0);
  746 + m_cd(1,0) = 0.0;
  747 + m_cd(0,1) = 0.0;
  748 + m_cd(1,1) = m_cdelt(1);
  749 +
  750 + // Compute inverse CD matrix
  751 + double delta = m_cd(0,0)*m_cd(1,1) - m_cd(0,1)*m_cd(1,0);
  752 + if (delta != 0.0) {
  753 + m_invcd(0,0) = m_cd(1,1) / delta;
  754 + m_invcd(0,1) = -m_cd(0,1) / delta;
  755 + m_invcd(1,0) = -m_cd(1,0) / delta;
  756 + m_invcd(1,1) = m_cd(0,0) / delta;
  757 + }
  758 + else
  759 + throw GException::wcs::wcs(G_WCS_SET, "Unable to invert CD matrix.");
  760 +
  761 + // Return
  762 + return;
  763 +}
  764 +
  765 +
  766 +/***********************************************************************//**
  767 + * @brief Read WCS definiton from FITS header.
  768 + *
  769 + * @param[in] hdu FITS HDU containing the WCS definition.
  770 + *
  771 + * @exception GException::fits_key_not_found
  772 + * Unable to find required FITS header keyword.
  773 + * @exception GException::wcs_bad_coords
  774 + * Coordinate system is not of valid type.
  775 + *
  776 + * Note that this method does not initialize the class members.
  777 + ***************************************************************************/
  778 +void GWcs::wcs_read(const GFitsHDU* hdu)
  779 +{
  780 + // Continue only if HDU is valid
  781 + if (hdu != NULL) {
  782 +
  783 + // Get standard keywords
  784 + std::string ctype1 = hdu->card("CTYPE1")->string();
  785 + std::string ctype2 = hdu->card("CTYPE2")->string();
  786 + double crval1 = hdu->card("CRVAL1")->real();
  787 + double crval2 = hdu->card("CRVAL2")->real();
  788 + double crpix1 = hdu->card("CRPIX1")->real();
  789 + double crpix2 = hdu->card("CRPIX2")->real();
  790 + double cdelt1 = hdu->card("CDELT1")->real();
  791 + double cdelt2 = hdu->card("CDELT2")->real();
  792 +
  793 + // Determine coordinate system
  794 + std::string coords;
  795 + std::string xcoord = ctype1.substr(0,4);
  796 + std::string ycoord = ctype2.substr(0,4);
  797 + if (xcoord == "RA--" && ycoord == "DEC-") {
  798 + m_reverse = 0;
  799 + coords = "EQU";
  800 + }
  801 + else if (xcoord == "DEC-" && ycoord == "RA--") {
  802 + m_reverse = 1;
  803 + coords = "EQU";
  804 + }
  805 + else if (xcoord == "GLON" && ycoord == "GLAT") {
  806 + m_reverse = 0;
  807 + coords = "GAL";
  808 + }
  809 + else if (xcoord == "GLAT" && ycoord == "GLON") {
  810 + m_reverse = 1;
  811 + coords = "GAL";
  812 + }
  813 + else
  814 + throw GException::wcs_bad_coords(G_WCS_READ, coordsys());
  815 +
  816 + // Set standard parameters
  817 + wcs_set(coords, crval1, crval2, crpix1, crpix2, cdelt1, cdelt2);
  818 +
  819 + // Get projection keywords
  820 + //TODO
  821 +
  822 + } // endif: HDU was valid
  823 +
  824 + // Return
  825 + return;
  826 +}
  827 +
  828 +
  829 +/***********************************************************************//**
  830 + * @brief Write WCS definiton into FITS HDU
  831 + *
  832 + * @param[in] hdu FITS HDU into which WCS definition will be written.
  833 + *
  834 + * @exception GException::wcs_bad_coords
  835 + * Coordinate system is not of valid type.
  836 + ***************************************************************************/
  837 +void GWcs::wcs_write(GFitsHDU* hdu) const
  838 +{
  839 + // Continue only if HDU is valid
  840 + if (hdu != NULL) {
  841 +
  842 + // Set coordinate system dependent strings
  843 + std::string c_equinox;
  844 + std::string c_crval1;
  845 + std::string c_crval2;
  846 + std::string c_cdelt1;
  847 + std::string c_cdelt2;
  848 + std::string c_lonpole;
  849 + std::string c_latpole;
  850 + if (m_coordsys == 0) {
  851 + c_equinox = "Equinox for celestial coordinate system";
  852 + c_crval1 = "[deg] Right Ascension at reference pixel";
  853 + c_crval2 = "[deg] Declination at reference pixel";
  854 + c_cdelt1 = "[deg/pixel] Right Ascension increment at"
  855 + " reference pixel";
  856 + c_cdelt2 = "[deg/pixel] Declination increment at"
  857 + " reference pixel";
  858 + c_lonpole = "[deg] Native longitude of Celestial North Pole";
  859 + c_latpole = "[deg] Native latitude of Celestial North Pole";
  860 + }
  861 + else if (m_coordsys == 1) {
  862 + c_equinox = "Equinox for celestial coordinate system";
  863 + c_crval1 = "[deg] Galactic Longitude at reference pixel";
  864 + c_crval2 = "[deg] Galactic Latitude at reference pixel";
  865 + c_cdelt1 = "[deg/pixel] Galactic Longitude increment at"
  866 + " reference pixel";
  867 + c_cdelt2 = "[deg/pixel] Galactic Latitude increment at"
  868 + " reference pixel";
  869 + c_lonpole = "[deg] Native longitude of Galactic North Pole";
  870 + c_latpole = "[deg] Native latitude of Galactic North Pole";
  871 + }
  872 + else
  873 + throw GException::wcs_bad_coords(G_WCS_WRITE, coordsys());
  874 +
  875 + // Set keywords
  876 + hdu->card("EQUINOX", 2000.0, c_equinox);
  877 + hdu->card("CTYPE1", wcs_crval1(), "Projection Type");
  878 + hdu->card("CTYPE2", wcs_crval2(), "Projection Type");
  879 + hdu->card("CDELT1", m_cdelt(0), c_cdelt1);
  880 + hdu->card("CDELT2", m_cdelt(1), c_cdelt2);
  881 + hdu->card("CRPIX1", m_crpix(0),
  882 + "X index of reference pixel (starting from 1)");
  883 + hdu->card("CRPIX2", m_crpix(1),
  884 + "Y index of reference pixel (starting from 1)");
  885 + hdu->card("CRVAL1", m_crval(0), c_crval1);
  886 + hdu->card("CRVAL2", m_crval(1), c_crval2);
  887 + hdu->card("CROTA2", 0.0, "[deg] Rotation Angle");
  888 + hdu->card("LONPOLE", m_npole(0), c_lonpole);
  889 + hdu->card("LATPOLE", m_npole(1), c_lonpole);
  890 + hdu->card("PV2_1", 0.0, "Projection parameter 1");
  891 + hdu->card("PV2_2", 0.0, "Projection parameter 2");
  892 +
  893 + } // endif: HDU was valid
  894 +
  895 + // Return
  896 + return;
  897 +}
  898 +
  899 +
  900 +/***********************************************************************//**
  901 + * @brief Return crval1 string
  902 + *
  903 + * @exception GException::wcs_invalid
  904 + * WCS projection not valid.
  905 + * @exception GException::wcs_bad_coords
  906 + * Coordinate system is not of valid type.
  907 + ***************************************************************************/
  908 +std::string GWcs::wcs_crval1(void) const
  909 +{
  910 + // Initialise result
  911 + std::string crval;
  912 +
  913 + // Check on correct type length
  914 + if (type().length() != 3)
  915 + throw GException::wcs_invalid(G_WCS_CRVAL1, type(),
  916 + "3-character type required.");
  917 +
  918 + // Set coordinate system dependent value
  919 + if (m_coordsys == 0)
  920 + crval = "RA---" + type();
  921 + else if (m_coordsys == 1)
  922 + crval = "GLON-" + type();
  923 + else
  924 + throw GException::wcs_bad_coords(G_WCS_CRVAL1, coordsys());
  925 +
  926 + // Return
  927 + return crval;
  928 +}
  929 +
  930 +
  931 +/***********************************************************************//**
  932 + * @brief Return crval2 string
  933 + *
  934 + * @exception GException::wcs_invalid
  935 + * WCS projection not valid.
  936 + * @exception GException::wcs_bad_coords
  937 + * Coordinate system is not of valid type.
  938 + ***************************************************************************/
  939 +std::string GWcs::wcs_crval2(void) const
  940 +{
  941 + // Initialise result
  942 + std::string crval;
  943 +
  944 + // Check on correct type length
  945 + if (type().length() != 3)
  946 + throw GException::wcs_invalid(G_WCS_CRVAL2, type(),
  947 + "3-character type required.");
  948 +
  949 + // Set coordinate system dependent value
  950 + if (m_coordsys == 0)
  951 + crval = "DEC--" + type();
  952 + else if (m_coordsys == 1)
  953 + crval = "GLAT-" + type();
  954 + else
  955 + throw GException::wcs_bad_coords(G_WCS_CRVAL1, coordsys());
  956 +
  957 + // Return
  958 + return crval;
  959 +}
  960 +
  961 +
  962 +/***********************************************************************//**
756 963 * @brief Dump WCS information into output stream
757 964 *
758 965 * @param[in] os Output stream into which WCS is dumped.
759 966 ***************************************************************************/
760   -void GWcs::dump_wcs(std::ostream& os) const
  967 +void GWcs::wcs_dump(std::ostream& os) const
761 968 {
762 969 // Put information into stream
763 970 os << " Coordinate system .........: " << coordsys() << std::endl;
... ... @@ -793,6 +1000,7 @@ void GWcs::dump_wcs(std::ostream&amp; os) const
793 1000 void GWcs::init_members(void)
794 1001 {
795 1002 // Initialise members
  1003 + m_type.clear();
796 1004 m_coordsys = 0;
797 1005 m_reverse = 0;
798 1006 m_crval = GVector(2);
... ... @@ -828,6 +1036,7 @@ void GWcs::init_members(void)
828 1036 void GWcs::copy_members(const GWcs& wcs)
829 1037 {
830 1038 // Copy attributes
  1039 + m_type = wcs.m_type;
831 1040 m_coordsys = wcs.m_coordsys;
832 1041 m_reverse = wcs.m_reverse;
833 1042 m_crval = wcs.m_crval;
... ...
src/sky/GWcsCAR.cpp
... ... @@ -188,12 +188,46 @@ GWcsCAR&amp; GWcsCAR::operator= (const GWcsCAR&amp; wcs)
188 188 ==========================================================================*/
189 189  
190 190 /***********************************************************************//**
191   - * @brief Returns WCS type
  191 + * @brief Read WCS definiton from FITS header
  192 + *
  193 + * @param[in] hdu FITS HDU containing the Healpix definition.
192 194 ***************************************************************************/
193   -std::string GWcsCAR::type(void) const
  195 +void GWcsCAR::read(const GFitsHDU* hdu)
194 196 {
195   - // Return Healix type
196   - return "CAR";
  197 + // Free memory and initialise members
  198 + free_members();
  199 + init_members();
  200 +
  201 + // Continue only if HDU is valid
  202 + if (hdu != NULL) {
  203 +
  204 + // Read standard WCS definition
  205 + wcs_read(hdu);
  206 +
  207 + } // endif: HDU was valid
  208 +
  209 + // Return
  210 + return;
  211 +}
  212 +
  213 +
  214 +/***********************************************************************//**
  215 + * @brief Write Healpix definiton into FITS HDU
  216 + *
  217 + * @param[in] hdu FITS HDU to which the Healpix definition will be written.
  218 + ***************************************************************************/
  219 +void GWcsCAR::write(GFitsHDU* hdu) const
  220 +{
  221 + // Continue only if HDU is valid
  222 + if (hdu != NULL) {
  223 +
  224 + // Write standard WCS definition
  225 + wcs_write(hdu);
  226 +
  227 + } // endif: HDU was valid
  228 +
  229 + // Return
  230 + return;
197 231 }
198 232  
199 233  
... ... @@ -223,6 +257,7 @@ double GWcsCAR::omega(const GSkyPixel&amp; pix) const
223 257 void GWcsCAR::init_members(void)
224 258 {
225 259 // Initialise members
  260 + m_type = "CAR";
226 261  
227 262 // Return
228 263 return;
... ... @@ -315,7 +350,7 @@ std::ostream&amp; operator&lt;&lt; (std::ostream&amp; os, const GWcsCAR&amp; wcs)
315 350 os << "=== GWcsCAR ===" << std::endl;
316 351  
317 352 // Add WCS information
318   - wcs.dump_wcs(os);
  353 + wcs.wcs_dump(os);
319 354  
320 355 // Return output stream
321 356 return os;
... ...
src/sky/GWcsHPX.cpp
... ... @@ -211,16 +211,6 @@ GWcsHPX&amp; GWcsHPX::operator= (const GWcsHPX&amp; wcs)
211 211 ==========================================================================*/
212 212  
213 213 /***********************************************************************//**
214   - * @brief Returns WCS type
215   - ***************************************************************************/
216   -std::string GWcsHPX::type(void) const
217   -{
218   - // Return Healix type
219   - return "HPX";
220   -}
221   -
222   -
223   -/***********************************************************************//**
224 214 * @brief Read Healpix definiton from FITS header
225 215 *
226 216 * @param[in] hdu FITS HDU containing the Healpix definition.
... ... @@ -498,6 +488,7 @@ void GWcsHPX::ordering(const std::string&amp; ordering)
498 488 void GWcsHPX::init_members(void)
499 489 {
500 490 // Initialise members
  491 + m_type = "HPX";
501 492 m_nside = 0;
502 493 m_npface = 0;
503 494 m_ncap = 0;
... ...
test/test_GSky.cpp
... ... @@ -267,8 +267,8 @@ void test_GSkymap_healpix_io(void)
267 267 }
268 268 catch (std::exception &e) {
269 269 std::cout << std::endl
270   - << "TEST ERROR: Unable to create Healpix instance from FITS file."
271   - << std::endl;
  270 + << "TEST ERROR: Unable to create Healpix instance"
  271 + " from FITS file." << std::endl;
272 272 std::cout << e.what() << std::endl;
273 273 throw;
274 274 }
... ... @@ -284,15 +284,15 @@ void test_GSkymap_healpix_io(void)
284 284  
285 285  
286 286 /***************************************************************************
287   - * Test: GSkymap_map_construct *
  287 + * Test: GSkymap_wcs_construct *
288 288 ***************************************************************************/
289   -void test_GSkymap_map_construct(void)
  289 +void test_GSkymap_wcs_construct(void)
290 290 {
291 291 // Set precision
292   - double eps = 1.0e-10;
293   -
  292 + double eps = 1.0e-6;
  293 +
294 294 // Dump header
295   - std::cout << "Test non Healpix GSkymap constructors: ";
  295 + std::cout << "Test WCS GSkymap constructors: ";
296 296  
297 297 // Test void constructor
298 298 try {
... ... @@ -323,19 +323,18 @@ void test_GSkymap_map_construct(void)
323 323 // Test CAR projection
324 324 try {
325 325 GSkymap map1("CAR", "GAL", 138.817, 37.293, 0.521, 0.931, 100, 100);
326   -std::cout << map1 << std::endl;
327 326 GSkyDir dir;
328   - for (int l = -179; l < 179; ++l) {
329   - for (int b = -89; b < 89; ++b) {
330   - dir.lb_deg(double(l),double(b));
  327 + for (int l = -180; l < 180; ++l) {
  328 + for (int b = -90; b < 90; ++b) {
  329 + dir.lb_deg(double(l),double(b));
331 330 GSkyPixel pixel = map1.dir2xy(dir);
332 331 GSkyDir dir_back = map1.xy2dir(pixel);
333   - double dl = dir.l_deg() - dir_back.l_deg();
334   - double db = dir.b_deg() - dir_back.b_deg();
335   - if (fabs(dl) > eps || fabs(db) > eps) {
  332 + double dist = dir.dist_deg(dir_back);
  333 + if (dist > eps) {
336 334 std::cout << std::endl
337 335 << "TEST ERROR: Sky direction differs: dir="
338   - << dir << " dir_back=" << dir_back << std::endl;
  336 + << dir << " dir_back=" << dir_back << " dist="
  337 + << dist << " deg" << std::endl;
339 338 throw;
340 339 }
341 340 }
... ... @@ -361,6 +360,101 @@ std::cout &lt;&lt; map1 &lt;&lt; std::endl;
361 360  
362 361  
363 362 /***************************************************************************
  363 + * Test: GSkymap_wcs_io *
  364 + ***************************************************************************/
  365 +void test_GSkymap_wcs_io(void)
  366 +{
  367 + // Set filenames
  368 + const std::string file1 = "test_skymap_wcs_1.fits";
  369 + const std::string file2 = "test_skymap_wcs_2.fits";
  370 +
  371 + // Dump header
  372 + std::cout << "Test WCS GSkymap I/O: ";
  373 +
  374 + // Define WCS map for comparison
  375 + GSkymap refmap1("CAR", "GAL", 0.0, 0.0, -1.0, 1.0, 100, 100);
  376 + GSkymap refmap2("CAR", "CEL", 0.0, 0.0, -0.3, 0.3, 200, 200);
  377 +
  378 + // Test WCS map saving
  379 + try {
  380 + for (int pix = 0; pix < refmap1.npix(); ++pix)
  381 + refmap1(pix) = pix+1;
  382 + refmap1.save(file1, 1);
  383 + for (int pix = 0; pix < refmap2.npix(); ++pix)
  384 + refmap2(pix) = pix+1;
  385 + refmap2.save(file2, 1);
  386 + }
  387 + catch (std::exception &e) {
  388 + std::cout << std::endl
  389 + << "TEST ERROR: Unable to save WCS map."
  390 + << std::endl;
  391 + std::cout << e.what() << std::endl;
  392 + throw;
  393 + }
  394 + std::cout << ".";
  395 +
  396 + // Test WCS map loading (1)
  397 + try {
  398 + GSkymap map;
  399 + map.load(file1);
  400 + int diff = 0;
  401 + for (int pix = 0; pix < refmap1.npix(); ++pix) {
  402 + if (map(pix) != refmap1(pix))
  403 + diff++;
  404 + }
  405 + if (diff > 0) {
  406 + std::cout << std::endl
  407 + << "TEST ERROR: Loaded file "+file1+
  408 + " differs from saved file ."
  409 + << std::endl;
  410 + throw;
  411 + }
  412 + }
  413 + catch (std::exception &e) {
  414 + std::cout << std::endl
  415 + << "TEST ERROR: Unable to load WCS map."
  416 + << std::endl;
  417 + std::cout << e.what() << std::endl;
  418 + throw;
  419 + }
  420 + std::cout << ".";
  421 +
  422 + // Test WCS map loading (2)
  423 + try {
  424 + GSkymap map;
  425 + map.load(file2);
  426 + int diff = 0;
  427 + for (int pix = 0; pix < refmap2.npix(); ++pix) {
  428 + if (map(pix) != refmap2(pix))
  429 + diff++;
  430 + }
  431 + if (diff > 0) {
  432 + std::cout << std::endl
  433 + << "TEST ERROR: Loaded file "+file2+
  434 + " differs from saved file ."
  435 + << std::endl;
  436 + throw;
  437 + }
  438 + }
  439 + catch (std::exception &e) {
  440 + std::cout << std::endl
  441 + << "TEST ERROR: Unable to load WCS map."
  442 + << std::endl;
  443 + std::cout << e.what() << std::endl;
  444 + throw;
  445 + }
  446 + std::cout << ".";
  447 +
  448 + // Signal final test success
  449 + std::cout << " ok." << std::endl;
  450 +
  451 + // Exit test
  452 + return;
  453 +
  454 +}
  455 +
  456 +
  457 +/***************************************************************************
364 458 * Main test function *
365 459 ***************************************************************************/
366 460 int main(void)
... ... @@ -374,7 +468,8 @@ int main(void)
374 468 // Execute Healpix tests
375 469 test_GSkymap_healpix_construct();
376 470 test_GSkymap_healpix_io();
377   - test_GSkymap_map_construct();
  471 + test_GSkymap_wcs_construct();
  472 + test_GSkymap_wcs_io();
378 473  
379 474 // Return
380 475 return 0;
... ...