source: trunk/src/Cubes/outputSpectra.cc @ 425

Last change on this file since 425 was 425, checked in by MatthewWhiting, 16 years ago

Documenting recent changes, and upping the version numbers to 1.1.4.

File size: 18.6 KB
Line 
1// -----------------------------------------------------------------------
2// outputSpectra.cc: Print the spectra of the detected objects.
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 <fstream>
30#include <iomanip>
31#include <sstream>
32#include <string>
33#include <cpgplot.h>
34#include <math.h>
35#include <wcslib/wcs.h>
36#include <duchamp/param.hh>
37#include <duchamp/duchamp.hh>
38#include <duchamp/fitsHeader.hh>
39#include <duchamp/PixelMap/Object3D.hh>
40#include <duchamp/Cubes/cubes.hh>
41#include <duchamp/Cubes/plots.hh>
42#include <duchamp/Utils/utils.hh>
43#include <duchamp/Utils/mycpgplot.hh>
44
45using namespace mycpgplot;
46using namespace PixelInfo;
47
48namespace duchamp
49{
50
51  void getSmallVelRange(Detection &obj, FitsHeader head, float *minvel, float *maxvel);
52  void getSmallZRange(Detection &obj, float *minz, float *maxz);
53
54  void Cube::outputSpectra()
55  {
56    /**
57     * The way to display individual detected objects. The standard way
58     * is plot the full spectrum, plus a zoomed-in spectrum showing just
59     * the object, plus the 0th-moment map. If there is no spectral
60     * axis, just the 0th moment map is plotted (using
61     * Cube::plotSource() rather than Cube::plotSpectrum()).
62     *
63     * It makes use of the SpectralPlot or CutoutPlot classes from
64     * plots.h, which size everything correctly.
65     *
66     * The main choice for SpectralPlot() is whether to use the peak
67     * pixel, in which case the spectrum is just that of the peak pixel,
68     * or the sum, where the spectrum is summed over all spatial pixels
69     * that are in the object.  If a reconstruction has been done, that
70     * spectrum is plotted in red.  The limits of the detection are
71     * marked in blue.  A 0th moment map of the detection is also
72     * plotted, with a scale bar indicating the spatial scale.
73     */
74
75    if(this->fullCols.size()==0) this->setupColumns();
76    // in case cols haven't been set -- need the precisions for printing values.
77
78    std::string spectrafile = this->par.getSpectraFile() + "/vcps";
79    if(this->getDimZ()<=1){
80      Plot::CutoutPlot newplot;
81      if(newplot.setUpPlot(spectrafile.c_str())>0) {
82
83        for(int nobj=0;nobj<this->objectList->size();nobj++){
84          // for each object in the cube:
85          this->plotSource(this->objectList->at(nobj),newplot);
86     
87        }// end of loop over objects.
88
89        cpgclos();
90      }
91    }
92    else{
93      Plot::SpectralPlot newplot;
94      if(newplot.setUpPlot(spectrafile.c_str())>0) {
95
96        for(int nobj=0;nobj<this->objectList->size();nobj++){
97          // for each object in the cube:
98          this->plotSpectrum(nobj,newplot);
99     
100        }// end of loop over objects.
101
102        cpgclos();
103      }
104
105      if(this->par.getFlagTextSpectra()){
106        if(this->par.isVerbose()) std::cout << "Saving spectra in text file ... ";
107        this->writeSpectralData();
108        if(this->par.isVerbose()) std::cout << "Done. ";
109      }
110    }
111  }
112  //--------------------------------------------------------------------
113
114  void Cube::writeSpectralData()
115  {
116    /**
117     *  A function to write, in ascii form, the spectra of each
118     *  detected object to a file. The file consists of a column for
119     *  the spectral coordinates, and one column for each object
120     *  showing the flux at that spectral position. The units are the
121     *  same as those shown in the graphical output. The filename is
122     *  given by the Param::spectraTextFile parameter in the Cube::par
123     *  parameter set.
124     */
125
126    const int zdim = this->axisDim[2];
127    const int numObj = this->objectList->size();
128    float *specxOut = new float[zdim];
129    float *spectra = new float[numObj*zdim];
130   
131    for(int obj=0; obj<numObj; obj++){
132      float *temp = new float[zdim];
133      float *specx = new float[zdim];
134      float *recon = new float[zdim];
135      float *base = new float[zdim];
136      getSpectralArrays(obj, specx, temp, recon, base);
137      for(int z=0;z<zdim;z++) spectra[obj*zdim+z] = temp[z];
138      if(obj==0) for(int z=0;z<zdim;z++) specxOut[z] = specx[z];
139      delete [] specx;
140      delete [] recon;
141      delete [] base;
142      delete [] temp;
143    }
144   
145    std::ofstream fspec(this->par.getSpectraTextFile().c_str());
146    fspec.setf(std::ios::fixed);
147
148    for(int z=0;z<zdim;z++){
149     
150      fspec << std::setprecision(8);
151      fspec << specxOut[z] << "  ";
152      for(int obj=0;obj<numObj; obj++) {
153        fspec << spectra[obj*zdim+z] << "  ";
154      }
155      fspec << "\n";
156
157    }
158    fspec.close();
159
160    delete [] spectra;
161    delete [] specxOut;
162
163  }
164  //--------------------------------------------------------------------
165
166  void Cube::getSpectralArrays(int objNum, float *specx, float *specy,
167                               float *specRecon, float *specBase)
168  {
169    /**
170     *  A utility function that goes and calculates, for a given
171     *  Detection, the spectral arrays, according to whether we want
172     *  the peak or integrated flux. The arrays can be used by
173     *  Cube::plotSpectrum() and Cube::writeSpectralData(). The arrays
174     *  calculated are listed below. Their length is given by the
175     *  length of the Cube's spectral dimension.
176     *
177     *  Note that 'new' is used to allocate the array space, so the
178     *  array parameters need to be suitably defined
179     *
180     *  \param objNum The number of the object under consideration
181     *  \param specx The array of frequency/velocity/channel/etc
182     *         values (the x-axis on the spectral plot).
183     *  \param specy The array of flux values, matching the specx
184     *         array.
185     *  \param specRecon The reconstructed or smoothed array, done in
186     *         the same way as specy.
187     *  \param specBase The fitted baseline values, done in the same
188     *         way as specy.
189     */
190
191    long xdim = this->axisDim[0];
192    long ydim = this->axisDim[1];
193    long zdim = this->axisDim[2];
194
195    for(int i=0;i<zdim;i++) specy[i]     = 0.;
196    for(int i=0;i<zdim;i++) specRecon[i] = 0.;
197    for(int i=0;i<zdim;i++) specBase[i]  = 0.;
198
199    if(this->head.isWCS()){
200      double xval = double(this->objectList->at(objNum).getXcentre());
201      double yval = double(this->objectList->at(objNum).getYcentre());
202      for(double zval=0;zval<zdim;zval++)
203        specx[int(zval)] = this->head.pixToVel(xval,yval,zval);
204    }
205    else
206      for(double zval=0;zval<zdim;zval++) specx[int(zval)] = zval;
207
208    float beamCorrection;
209    if(this->header().needBeamSize())
210      beamCorrection = this->par.getBeamSize();
211    else beamCorrection = 1.;
212
213    if(this->par.getSpectralMethod()=="sum"){
214      bool *done = new bool[xdim*ydim];
215      for(int i=0;i<xdim*ydim;i++) done[i]=false;
216      std::vector<Voxel> voxlist = this->objectList->at(objNum).pixels().getPixelSet();
217      for(int pix=0;pix<voxlist.size();pix++){
218        int pos = voxlist[pix].getX() + xdim * voxlist[pix].getY();
219        if(!done[pos]){
220          done[pos] = true;
221          for(int z=0;z<zdim;z++){
222            if(!(this->isBlank(pos+z*xdim*ydim))){
223              specy[z] += this->array[pos + z*xdim*ydim] / beamCorrection;
224              if(this->reconExists)
225                specRecon[z] += this->recon[pos + z*xdim*ydim] / beamCorrection;
226              if(this->par.getFlagBaseline())
227                specBase[z] += this->baseline[pos + z*xdim*ydim] / beamCorrection;
228            }       
229          }
230        }
231      }
232      delete [] done;
233    }
234    else {// if(par.getSpectralMethod()=="peak"){
235      int pos = this->objectList->at(objNum).getXPeak() +
236        xdim*this->objectList->at(objNum).getYPeak();
237      for(int z=0;z<zdim;z++){
238        specy[z] = this->array[pos + z*xdim*ydim];
239        if(this->reconExists)
240          specRecon[z] = this->recon[pos + z*xdim*ydim];
241        if(this->par.getFlagBaseline())
242          specBase[z] = this->baseline[pos + z*xdim*ydim];
243      }
244    }
245
246  }
247  //--------------------------------------------------------------------
248
249  void Cube::plotSpectrum(int objNum, Plot::SpectralPlot &plot)
250  {
251    /**
252     * The way to print out the spectrum of a Detection.
253     * Makes use of the SpectralPlot class in plots.hh, which sizes
254     *  everything correctly.
255     *
256     * The main choice for the user is whether to use the peak pixel, in
257     * which case the spectrum is just that of the peak pixel, or the
258     * sum, where the spectrum is summed over all spatial pixels that
259     * are in the object.
260     *
261     * If a reconstruction has been done, that spectrum is plotted in
262     * red, and if a baseline has been calculated that is also shown, in
263     * yellow.  The spectral limits of the detection are marked in blue.
264     * A 0th moment map of the detection is also plotted, with a scale
265     * bar indicating the spatial size.
266     *
267     * \param objNum The number of the Detection to be plotted.
268     * \param plot The SpectralPlot object defining the PGPLOT device
269     *        to plot the spectrum on.
270     */
271
272    long xdim = this->axisDim[0];
273    long ydim = this->axisDim[1];
274    long zdim = this->axisDim[2];
275
276    this->objectList->at(objNum).calcFluxes(this->array, this->axisDim);
277
278    double minMWvel,maxMWvel,xval,yval,zval;
279    xval = double(this->objectList->at(objNum).getXcentre());
280    yval = double(this->objectList->at(objNum).getYcentre());
281    if(this->par.getFlagMW()){
282      zval = double(this->par.getMinMW());
283      minMWvel = this->head.pixToVel(xval,yval,zval);
284      zval = double(this->par.getMaxMW());
285      maxMWvel = this->head.pixToVel(xval,yval,zval);
286    }
287
288    float *specx  = new float[zdim];
289    float *specy  = new float[zdim];
290    float *specy2 = new float[zdim];
291    float *base   = new float[zdim];
292
293    this->getSpectralArrays(objNum,specx,specy,specy2,base);
294
295    std::string fluxLabel = "Flux";
296 
297    if(this->par.getSpectralMethod()=="sum"){
298      fluxLabel = "Integrated " + fluxLabel;
299      if(this->head.isWCS())
300        fluxLabel += " ["+this->head.getIntFluxUnits()+"]";
301    }
302    else {// if(par.getSpectralMethod()=="peak"){
303      fluxLabel = "Peak " + fluxLabel;
304      if(this->head.isWCS()) fluxLabel += " ["+this->head.getFluxUnits()+"]";
305    }
306   
307    float vmax,vmin,width;
308    vmax = vmin = specx[0];
309    for(int i=1;i<zdim;i++){
310      if(specx[i]>vmax) vmax=specx[i];
311      if(specx[i]<vmin) vmin=specx[i];
312    }
313 
314    float max,min;
315    int loc=0;
316    if(this->par.getMinMW()>0) max = min = specy[0];
317    else max = min = specx[this->par.getMaxMW()+1];
318    for(int i=0;i<zdim;i++){
319      if(!this->par.isInMW(i)){
320        if(specy[i]>max) max=specy[i];
321        if(specy[i]<min){
322          min=specy[i];
323          loc = i;
324        }
325      }
326    }
327    // widen the ranges slightly so that the top & bottom & edges don't
328    // lie on the axes.
329    width = max - min;
330    max += width * 0.05;
331    min -= width * 0.05;
332    width = vmax -vmin;
333    vmax += width * 0.01;
334    vmin -= width * 0.01;
335
336    // now plot the resulting spectrum
337    std::string label;
338    if(this->head.isWCS()){
339      label = this->head.getSpectralDescription() + " [" +
340        this->head.getSpectralUnits() + "]";
341      plot.gotoHeader(label);
342    }
343    else plot.gotoHeader("Spectral pixel value");
344
345    if(this->head.isWCS()){
346      label = this->objectList->at(objNum).outputLabelWCS();
347      plot.firstHeaderLine(label);
348      label = this->objectList->at(objNum).outputLabelFluxes();
349      plot.secondHeaderLine(label);
350    }
351    label = this->objectList->at(objNum).outputLabelWidths();
352    plot.thirdHeaderLine(label);
353    label = this->objectList->at(objNum).outputLabelPix();
354    plot.fourthHeaderLine(label);
355   
356    plot.gotoMainSpectrum(vmin,vmax,min,max,fluxLabel);
357    cpgline(zdim,specx,specy);
358    if(this->par.getFlagBaseline()){
359      cpgsci(DUCHAMP_BASELINE_SPECTRA_COLOUR);
360      cpgline(zdim,specx,base);
361      cpgsci(FOREGND);
362    }
363    if(this->reconExists){
364      cpgsci(DUCHAMP_RECON_SPECTRA_COLOUR);
365      cpgline(zdim,specx,specy2);   
366      cpgsci(FOREGND);
367    }
368    if(this->par.getFlagMW()) plot.drawMWRange(minMWvel,maxMWvel);
369    if(this->head.isWCS()) plot.drawVelRange(this->objectList->at(objNum).getVelMin(),this->objectList->at(objNum).getVelMax());
370    else plot.drawVelRange(this->objectList->at(objNum).getZmin(),this->objectList->at(objNum).getZmax());
371
372    /**************************/
373    // ZOOM IN SPECTRALLY ON THE DETECTION.
374
375    float minvel,maxvel;
376    if(this->head.isWCS()) getSmallVelRange(this->objectList->at(objNum),this->head,&minvel,&maxvel);
377    else getSmallZRange(this->objectList->at(objNum),&minvel,&maxvel);
378
379    // Find new max & min flux values
380    std::swap(max,min);
381    int ct = 0;
382    for(int i=0;i<zdim;i++){
383      if((!this->par.isInMW(i))&&(specx[i]>=minvel)&&(specx[i]<=maxvel)){
384        ct++;
385        if(specy[i]>max) max=specy[i];
386        if(specy[i]<min) min=specy[i];
387      }
388    }
389    // widen the flux range slightly so that the top & bottom don't lie
390    // on the axes.
391    width = max - min;
392    max += width * 0.05;
393    min -= width * 0.05;
394
395    plot.gotoZoomSpectrum(minvel,maxvel,min,max);
396    cpgline(zdim,specx,specy);
397    if(this->par.getFlagBaseline()){
398      cpgsci(DUCHAMP_BASELINE_SPECTRA_COLOUR);
399      cpgline(zdim,specx,base);
400      cpgsci(FOREGND);
401    }
402    if(this->reconExists){
403      cpgsci(DUCHAMP_RECON_SPECTRA_COLOUR);
404      cpgline(zdim,specx,specy2);   
405      cpgsci(FOREGND);
406    }
407    if(this->par.getFlagMW()) plot.drawMWRange(minMWvel,maxMWvel);
408    if(this->head.isWCS()) plot.drawVelRange(this->objectList->at(objNum).getVelMin(),
409                                             this->objectList->at(objNum).getVelMax());
410    else plot.drawVelRange(this->objectList->at(objNum).getZmin(),this->objectList->at(objNum).getZmax());
411   
412    /**************************/
413
414    // DRAW THE MOMENT MAP OF THE DETECTION -- SUMMED OVER ALL CHANNELS
415    plot.gotoMap();
416    this->drawMomentCutout(this->objectList->at(objNum));
417
418    delete [] specx;
419    delete [] specy;
420    delete [] specy2;
421    delete [] base;
422 
423  }
424  //--------------------------------------------------------------------
425
426  void getSmallVelRange(Detection &obj, FitsHeader head,
427                        float *minvel, float *maxvel)
428  {
429    /**
430     *  Routine to calculate the velocity range for the zoomed-in region.
431     *  This range should be the maximum of 20 pixels, or 3x the wdith of
432     *   the detection.
433     *  Need to :
434     *      Calculate pixel width of a 3x-detection-width region.
435     *      If smaller than 20, calculate velocities of central vel +- 10 pixels
436     *      If not, use the 3x-detection-width
437     *  Range returned via "minvel" and "maxvel" parameters.
438     *  \param obj Detection under examination.
439     *  \param head FitsHeader, containing the WCS information.
440     *  \param minvel Returned value of minimum velocity
441     *  \param maxvel Returned value of maximum velocity
442     */
443
444    double *pixcrd = new double[3];
445    double *world  = new double[3];
446    float minpix,maxpix;
447    // define new velocity extrema
448    //    -- make it 3x wider than the width of the detection.
449    *minvel = 0.5*(obj.getVelMin()+obj.getVelMax()) - 1.5*obj.getVelWidth();
450    *maxvel = 0.5*(obj.getVelMin()+obj.getVelMax()) + 1.5*obj.getVelWidth();
451    // Find velocity range in number of pixels:
452    world[0] = obj.getRA();
453    world[1] = obj.getDec();
454    world[2] = head.velToSpec(*minvel);
455    head.wcsToPix(world,pixcrd);
456    minpix = pixcrd[2];
457    world[2] = head.velToSpec(*maxvel);
458    head.wcsToPix(world,pixcrd);
459    maxpix = pixcrd[2];
460    if(maxpix<minpix) std::swap(maxpix,minpix);
461   
462    if((maxpix - minpix + 1) < 20){
463      pixcrd[0] = double(obj.getXcentre());
464      pixcrd[1] = double(obj.getYcentre());
465      pixcrd[2] = obj.getZcentre() - 10.;
466      head.pixToWCS(pixcrd,world);
467      //    *minvel = setVel_kms(wcs,world[2]);
468      *minvel = head.specToVel(world[2]);
469      pixcrd[2] = obj.getZcentre() + 10.;
470      head.pixToWCS(pixcrd,world);
471      //     *maxvel = setVel_kms(wcs,world[2]);
472      *maxvel = head.specToVel(world[2]);
473      if(*maxvel<*minvel) std::swap(*maxvel,*minvel);
474    }
475    delete [] pixcrd;
476    delete [] world;
477
478  }
479  //--------------------------------------------------------------------
480
481  void getSmallZRange(Detection &obj, float *minz, float *maxz)
482  {
483    /**
484     *  Routine to calculate the pixel range for the zoomed-in spectrum.
485     *  This range should be the maximum of 20 pixels, or 3x the width
486     *   of the detection.
487     *  Need to :
488     *      Calculate pixel width of a 3x-detection-width region.
489     *       If smaller than 20, use central pixel +- 10 pixels
490     *  Range returned via "minz" and "maxz" parameters.
491     *  \param obj Detection under examination.
492     *  \param minz Returned value of minimum z-pixel coordinate
493     *  \param maxz Returned value of maximum z-pixel coordinate
494     */
495
496    *minz = 2.*obj.getZmin() - obj.getZmax();
497    *maxz = 2.*obj.getZmax() - obj.getZmin();
498   
499    if((*maxz - *minz + 1) < 20){
500      *minz = obj.getZcentre() - 10.;
501      *maxz = obj.getZcentre() + 10.;
502    }
503
504  }
505  //--------------------------------------------------------------------
506
507  void Cube::plotSource(Detection obj, Plot::CutoutPlot &plot)
508  {
509    /**
510     * The way to print out the 2d image cutout of a Detection.
511     * Makes use of the CutoutPlot class in plots.hh, which sizes
512     *  everything correctly.
513     *
514     * A 0th moment map of the detection is plotted, with a scale
515     * bar indicating the spatial size.
516     *
517     * Basic information on the source is printed next to it as well.
518     *
519     * \param obj The Detection to be plotted.
520     * \param plot The PGPLOT device to plot the spectrum on.
521     */
522
523    obj.calcFluxes(this->array, this->axisDim);
524
525    std::string label;
526    plot.gotoHeader();
527
528    if(this->head.isWCS()){
529      label = obj.outputLabelWCS();
530      plot.firstHeaderLine(label);
531      label = obj.outputLabelFluxes();
532      plot.secondHeaderLine(label);
533    }
534    label = obj.outputLabelWidths();
535    plot.thirdHeaderLine(label);
536    label = obj.outputLabelPix();
537    plot.fourthHeaderLine(label);
538   
539    // DRAW THE MOMENT MAP OF THE DETECTION -- SUMMED OVER ALL CHANNELS
540    plot.gotoMap();
541    this->drawMomentCutout(obj);
542
543  }
544
545}
Note: See TracBrowser for help on using the repository browser.