source: trunk/src/fitsHeader.cc @ 963

Last change on this file since 963 was 963, checked in by MatthewWhiting, 12 years ago

Solving a problem with Cormac's bugfixing testcase (fred.fits), where the GLON/GLAT WCS types were not being recognised by the wcslib code - wcsset was not reading them correctly and was providing blank values for lngtyp & lattyp. The new functions check these parameters and, if blank, use the first four letters of the CTYPEi keyword. A bit of a kludge, but this should be what wcsset does...

File size: 9.9 KB
Line 
1// -----------------------------------------------------------------------
2// fitsHeader.cc: Information about the FITS file's header.
3// -----------------------------------------------------------------------
4// Copyright (C) 2006, Matthew Whiting, ATNF
5//
6// This program is free software; you can redistribute it and/or modify it
7// under the terms of the GNU General Public License as published by the
8// Free Software Foundation; either version 2 of the License, or (at your
9// option) any later version.
10//
11// Duchamp is distributed in the hope that it will be useful, but WITHOUT
12// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14// for more details.
15//
16// You should have received a copy of the GNU General Public License
17// along with Duchamp; if not, write to the Free Software Foundation,
18// Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
19//
20// Correspondence concerning Duchamp may be directed to:
21//    Internet email: Matthew.Whiting [at] atnf.csiro.au
22//    Postal address: Dr. Matthew Whiting
23//                    Australia Telescope National Facility, CSIRO
24//                    PO Box 76
25//                    Epping NSW 1710
26//                    AUSTRALIA
27// -----------------------------------------------------------------------
28#include <iostream>
29#include <sstream>
30#include <string>
31#include <stdlib.h>
32#include <wcslib/wcs.h>
33#include <wcslib/wcsunits.h>
34#include <duchamp/duchamp.hh>
35#include <duchamp/param.hh>
36#include <duchamp/fitsHeader.hh>
37#include <duchamp/Utils/utils.hh>
38
39namespace duchamp
40{
41
42  FitsHeader::FitsHeader()
43  {
44    this->wcs = (struct wcsprm *)calloc(1,sizeof(struct wcsprm));
45    this->wcs->flag=-1;
46    wcsini(true, 3, this->wcs);
47    this->wcsIsGood = false;
48    this->nwcs = 0;
49    this->flag2D=false;
50    this->spectralUnits="";
51    this->spectralDescription=duchamp::duchampSpectralDescription[FREQUENCY];
52    this->originalFluxUnits="counts";
53    this->fluxUnits="counts";
54    this->intFluxUnits="counts";
55    this->scale=1.;
56    this->offset=0.;
57    this->power=1.;
58    this->itsBeam.empty();
59  }
60
61  FitsHeader::~FitsHeader()
62  {
63    /// @details Uses the WCSLIB function wcsvfree to clear the wcsprm struct.
64    wcsvfree(&nwcs,&wcs);
65  }
66
67  FitsHeader::FitsHeader(const FitsHeader& h)
68  {
69    operator=(h);
70  }
71
72  FitsHeader& FitsHeader::operator= (const FitsHeader& h)
73  {
74    if(this == &h) return *this;
75    this->wcs = (struct wcsprm *)calloc(1,sizeof(struct wcsprm));
76    this->wcs->flag     = -1;
77    wcsini(true, h.wcs->naxis, this->wcs);
78    wcscopy(true, h.wcs, this->wcs);
79    wcsset(this->wcs);
80    this->nwcs          = h.nwcs;
81    this->wcsIsGood     = h.wcsIsGood;
82    this->naxis         = h.naxis;
83    this->flag2D        = h.flag2D;
84    this->spectralUnits = h.spectralUnits;
85    this->spectralType  = h.spectralType;
86    this->spectralDescription = h.spectralDescription;
87    this->originalFluxUnits = h.originalFluxUnits;
88    this->fluxUnits     = h.fluxUnits;
89    this->intFluxUnits  = h.intFluxUnits;
90    this->itsBeam       = h.itsBeam;
91    this->blankKeyword  = h.blankKeyword;
92    this->bzeroKeyword  = h.bzeroKeyword;
93    this->bscaleKeyword = h.bscaleKeyword;
94    this->scale         = h.scale;
95    this->offset        = h.offset;
96    this->power         = h.power;
97    return *this;
98  }
99
100  void FitsHeader::setWCS(struct wcsprm *w)
101  {
102    ///  A function that assigns the wcs parameters, and runs
103    ///   wcsset to set it up correctly.
104    ///  Performs a check to see if the WCS is good (by looking at
105    ///   the lng and lat wcsprm parameters), and sets the wcsIsGood
106    ///   flag accordingly.
107    /// \param w A WCSLIB wcsprm struct with the correct parameters.
108
109    wcscopy(true, w, this->wcs);
110    wcsset(this->wcs);
111    if( (w->lng!=-1) && (w->lat!=-1) ) this->wcsIsGood = true;
112  }
113
114  struct wcsprm *FitsHeader::getWCS()
115  {
116    ///  A function that returns a properly initilized wcsprm object
117    ///  corresponding to the WCS.
118
119    struct wcsprm *wNew = (struct wcsprm *)calloc(1,sizeof(struct wcsprm));
120    wNew->flag=-1;
121    wcsini(true, this->wcs->naxis, wNew);
122    wcscopy(true, this->wcs, wNew);
123    wcsset(wNew);
124    return wNew;
125  }
126
127  int FitsHeader::wcsToPix(const double *world, double *pix)
128  {     
129    return wcsToPixSingle(this->wcs, world, pix); 
130  }
131  int FitsHeader::wcsToPix(const double *world, double *pix, const int npts)
132  {
133    return wcsToPixMulti(this->wcs, world, pix, npts); 
134  }
135  int FitsHeader::pixToWCS(const double *pix, double *world)
136  {   
137    return pixToWCSSingle(this->wcs, pix, world); 
138  }
139  int FitsHeader::pixToWCS(const double *pix, double *world, const int npts)
140  {
141    return pixToWCSMulti(this->wcs, pix,world, npts); 
142  }
143
144
145  double FitsHeader::pixToVel(double &x, double &y, double &z)
146  {
147    double vel;
148    if(this->wcsIsGood){
149      double *pix   = new double[3];
150      double *world = new double[3];
151      pix[0] = x; pix[1] = y; pix[2] = z;
152      pixToWCSSingle(this->wcs,pix,world);
153      vel = this->specToVel(world[2]);
154      delete [] pix;
155      delete [] world;
156    }
157    else vel = z;
158    return vel;
159  }
160
161  double* FitsHeader::pixToVel(double &x, double &y, double *zarray, int size)
162  {
163    double *newzarray = new double[size];
164    if(this->wcsIsGood){
165      double *pix   = new double[size*3];
166      for(int i=0;i<size;i++){
167        pix[3*i]   = x;
168        pix[3*i+1] = y;
169        pix[3*i+2] = zarray[i];
170      }
171      double *world = new double[size*3];
172      pixToWCSMulti(this->wcs,pix,world,size);
173      delete [] pix;
174      for(int i=0;i<size;i++) newzarray[i] = this->specToVel(world[3*i+2]);
175      delete [] world;
176    }
177    else{
178      for(int i=0;i<size;i++) newzarray[i] = zarray[i];
179    }
180    return newzarray;
181  }
182
183  double FitsHeader::specToVel(const double &coord)
184  {
185    double vel;
186    if(power==1.0) vel =  coord*this->scale + this->offset;
187    else vel = pow( (coord*this->scale + this->offset), this->power);
188    return vel;
189  }
190
191  double FitsHeader::velToSpec(const float &velocity)
192  {
193    //   return velToCoord(this->wcs,velocity,this->spectralUnits);};
194    return (pow(velocity, 1./this->power) - this->offset) / this->scale;
195  }
196
197  std::string FitsHeader::getIAUName(double ra, double dec)
198  {
199    if(std::string(this->wcs->lngtyp)=="RA")
200      return getIAUNameEQ(ra, dec, this->wcs->equinox);
201    else
202      return getIAUNameGAL(ra, dec);
203  }
204
205  void FitsHeader::fixSpectralUnits(std::string units)
206  {
207    ///  Put the units for the FITS header into some sort of standard form.
208    ///
209    ///  We first get the desired spectral units from the Parameter set,
210    ///  and then transform the spectral units of the wcsprm struct to
211    ///  those units. If this doesn't work, we leave them as they are. If
212    ///  they are blank, we make them SPC and give an error message --
213    ///  this should hopefully NOT happen.
214    ///
215    ///  We also work out the units for the integrated flux. If there are
216    ///  three axes, we just append the spectral units to the flux units
217    ///  (removing "/beam" if it is present). If there are just two, we
218    ///  simply keep it the same, removing the "/beam".
219    ///
220    ///  \param par The parameter set telling us what the desired
221    ///             spectral units are.
222
223
224    double sc=1.;
225    double of=0.;
226    double po=1.;
227 
228    this->spectralUnits = this->wcs->cunit[this->wcs->spec];
229
230    if(units != ""){
231
232      if(this->wcsIsGood){
233 
234        int status = wcsunits( this->wcs->cunit[this->wcs->spec], units.c_str(), &sc, &of, &po);
235
236        if(status > 0){
237          DUCHAMPERROR("fixSpectralUnits","Conversion of spectral units from '" << this->wcs->cunit[this->wcs->spec] << "' to '" << units
238                       << "' failed, wcslib code = " << status << ": " << wcsunits_errmsg[status]);
239          if(this->spectralUnits==""){
240            DUCHAMPERROR("fixSpectralUnits", "Spectral units not specified. For data presentation, we will use dummy units of \"SPC\".\n"
241                         << "Please report this occurence -- it should not happen! In the meantime, you may want to set the CUNIT"
242                         << this->wcs->spec + 1 <<" keyword to make this work.");
243            this->spectralUnits = "SPC";
244          }
245        }
246        else this->spectralUnits = units;
247      }
248    }
249    this->scale = sc;
250    this->offset= of;
251    this->power = po;
252   
253    this->setIntFluxUnits();
254
255  }
256
257  bool FitsHeader::needBeamSize()
258  {
259    ///  A function that tells you whether the beam correction is
260    ///  needed. It checks to see whether the flux units string ends in
261    ///  "/beam" (in which case the beam size etc is needed and
262    ///  integrated fluxes need to be corrected). If we don't have any beam
263    ///  information, this will return false.
264    ///  /return True if FitsHeader::fluxUnits ends in "/beam". False
265    ///  otherwise.
266
267    int size = this->fluxUnits.size();
268    if(this->itsBeam.origin()==EMPTY) return false; // we have no beam to correct with!
269    else if(size<6) return false;
270    else {
271      std::string tailOfFluxUnits = makelower(this->fluxUnits.substr(size-5,size));
272      return (tailOfFluxUnits == "/beam");
273    }
274  }
275
276  void FitsHeader::setIntFluxUnits()
277  {
278
279    /// Work out the integrated flux units, based on the spectral
280    /// units. If flux is per beam, trim the /beam from the flux
281    /// units. Then, if as long as the image is 3D, multiply by the
282    /// spectral units.
283   
284    if(this->needBeamSize())
285      this->intFluxUnits = this->fluxUnits.substr(0,this->fluxUnits.size()-5);
286    else
287      this->intFluxUnits = this->fluxUnits;
288
289    if(!this->flag2D) this->intFluxUnits += std::string(" ") + this->spectralUnits;
290
291  }
292
293  std::string FitsHeader::lngtype()
294  {
295    std::string lngtyp(this->wcs->lngtyp);
296    if(removeLeadingBlanks(lngtyp)==""){
297      lngtyp = this->wcs->ctype[this->wcs->lng];
298      return lngtyp.substr(0,4);
299    }
300    else return lngtyp;
301  }
302
303  std::string FitsHeader::lattype()
304  {
305    std::string lattyp(this->wcs->lattyp);
306    if(removeLeadingBlanks(lattyp)==""){
307      lattyp = this->wcs->ctype[this->wcs->lat];
308      return lattyp.substr(0,4);
309    }
310    else return lattyp;
311  }
312
313}
Note: See TracBrowser for help on using the repository browser.