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

Last change on this file since 850 was 850, checked in by MatthewWhiting, 13 years ago

Solving ticket #120, where the spectral range takes into account channel widths. Have implemented a new function to take care of this (save repeating code, as we use this in two places)

File size: 16.8 KB
RevLine 
[299]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// -----------------------------------------------------------------------
[3]28#include <iostream>
[424]29#include <fstream>
[3]30#include <iomanip>
31#include <sstream>
32#include <string>
33#include <cpgplot.h>
34#include <math.h>
[394]35#include <wcslib/wcs.h>
[393]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>
[258]44
[146]45using namespace mycpgplot;
[258]46using namespace PixelInfo;
[3]47
[378]48namespace duchamp
[3]49{
50
[850]51  void drawSpectralRange(Plot::SpectralPlot &plot, Detection &obj, FitsHeader &head);
[378]52  void getSmallVelRange(Detection &obj, FitsHeader head, float *minvel, float *maxvel);
53  void getSmallZRange(Detection &obj, float *minz, float *maxz);
[144]54
[475]55  std::string getIndivPlotName(std::string baseName, int objNum, int maxNumObj)
56  {
57    int width = int(floor(log10(float(maxNumObj))))+1;
58    if(baseName.substr(baseName.size()-3,baseName.size())==".ps"){
59      std::stringstream ss;
60      ss << baseName.substr(0,baseName.size()-3)
61         << "-" << std::setw(width) << std::setfill('0') << objNum
62         << ".ps";
63      return ss.str();
64    }
65    else{
66      std::stringstream ss;
67      ss << baseName
68         << "-" << std::setw(width) << std::setfill('0') << objNum
69         << ".ps";
70      return ss.str();
71    }
72  }
73
[378]74  void Cube::outputSpectra()
75  {
[528]76    ///  @details
77    /// The way to display individual detected objects. The standard way
78    /// is plot the full spectrum, plus a zoomed-in spectrum showing just
79    /// the object, plus the 0th-moment map. If there is no spectral
80    /// axis, just the 0th moment map is plotted (using
81    /// Cube::plotSource() rather than Cube::plotSpectrum()).
82    ///
83    /// It makes use of the SpectralPlot or CutoutPlot classes from
84    /// plots.h, which size everything correctly.
85    ///
86    /// The main choice for SpectralPlot() is whether to use the peak
87    /// pixel, in which case the spectrum is just that of the peak pixel,
88    /// or the sum, where the spectrum is summed over all spatial pixels
89    /// that are in the object.  If a reconstruction has been done, that
90    /// spectrum is plotted in red.  The limits of the detection are
91    /// marked in blue.  A 0th moment map of the detection is also
92    /// plotted, with a scale bar indicating the spatial scale.
[3]93
[378]94    if(this->fullCols.size()==0) this->setupColumns();
95    // in case cols haven't been set -- need the precisions for printing values.
96
[475]97    std::vector<bool> objectChoice = this->par.getObjectChoices(this->objectList->size());
98
[378]99    std::string spectrafile = this->par.getSpectraFile() + "/vcps";
100    if(this->getDimZ()<=1){
101      Plot::CutoutPlot newplot;
102      if(newplot.setUpPlot(spectrafile.c_str())>0) {
103
[623]104        for(size_t nobj=0;nobj<this->objectList->size();nobj++){
[475]105          // for each object in the cube, assuming it is wanted:
106          if(objectChoice[nobj]) this->plotSource(this->objectList->at(nobj),newplot);
[378]107        }// end of loop over objects.
[475]108        cpgclos();
[485]109       
110        if(this->par.getFlagUsePrevious()) std::cout << "\n";
[623]111        for(size_t nobj=0;nobj<this->objectList->size();nobj++){
[475]112          if(objectChoice[nobj] && this->par.getFlagUsePrevious()){
[485]113            std::cout << "  Will output individual plot to "
[475]114                      << getIndivPlotName(this->par.getSpectraFile(),nobj+1,this->objectList->size()) << "\n";
115            Plot::CutoutPlot indivplot;
116            indivplot.setUpPlot(getIndivPlotName(this->par.getSpectraFile(),nobj+1,this->objectList->size())+"/vcps");
117            this->plotSource(this->objectList->at(nobj),indivplot);
118            cpgclos();
119          }
120        }               
[378]121      }
[367]122    }
[378]123    else{
124      Plot::SpectralPlot newplot;
125      if(newplot.setUpPlot(spectrafile.c_str())>0) {
[367]126
[623]127        for(size_t nobj=0;nobj<this->objectList->size();nobj++){
[475]128          // for each object in the cube, assuming it is wanted:
129          if(objectChoice[nobj])  this->plotSpectrum(nobj,newplot);
[378]130        }// end of loop over objects.
[475]131        cpgclos();
[367]132
[485]133        if(this->par.getFlagUsePrevious()) std::cout << "\n";
[623]134        for(size_t nobj=0;nobj<this->objectList->size();nobj++){
[475]135          if(objectChoice[nobj] && this->par.getFlagUsePrevious()){
[485]136            std::cout << "  Will output individual plot to "
[475]137                      << getIndivPlotName(this->par.getSpectraFile(),nobj+1,this->objectList->size()) << "\n";
138            Plot::SpectralPlot indivplot;
139            indivplot.setUpPlot(getIndivPlotName(this->par.getSpectraFile(),nobj+1,this->objectList->size())+"/vcps");
140            this->plotSpectrum(nobj,indivplot);
141            cpgclos();
142          }
143        }               
144
[378]145      }
[475]146     
[367]147    }
148  }
[424]149  //--------------------------------------------------------------------
[103]150
[424]151  void Cube::plotSpectrum(int objNum, Plot::SpectralPlot &plot)
152  {
[528]153    ///  @details
154    /// The way to print out the spectrum of a Detection.
155    /// Makes use of the SpectralPlot class in plots.hh, which sizes
156    ///  everything correctly.
157    ///
158    /// The main choice for the user is whether to use the peak pixel, in
159    /// which case the spectrum is just that of the peak pixel, or the
160    /// sum, where the spectrum is summed over all spatial pixels that
161    /// are in the object.
162    ///
163    /// If a reconstruction has been done, that spectrum is plotted in
164    /// red, and if a baseline has been calculated that is also shown, in
165    /// yellow.  The spectral limits of the detection are marked in blue.
166    /// A 0th moment map of the detection is also plotted, with a scale
167    /// bar indicating the spatial size.
168    ///
169    /// \param objNum The number of the Detection to be plotted.
170    /// \param plot The SpectralPlot object defining the PGPLOT device
171    ///        to plot the spectrum on.
[103]172
[378]173    long zdim = this->axisDim[2];
[3]174
[424]175    this->objectList->at(objNum).calcFluxes(this->array, this->axisDim);
[103]176
[634]177    double minMWvel=0,maxMWvel=0,xval,yval,zval;
[424]178    xval = double(this->objectList->at(objNum).getXcentre());
179    yval = double(this->objectList->at(objNum).getYcentre());
[378]180    if(this->par.getFlagMW()){
181      zval = double(this->par.getMinMW());
182      minMWvel = this->head.pixToVel(xval,yval,zval);
183      zval = double(this->par.getMaxMW());
184      maxMWvel = this->head.pixToVel(xval,yval,zval);
185    }
[103]186
[378]187    float *specx  = new float[zdim];
188    float *specy  = new float[zdim];
189    float *specy2 = new float[zdim];
190    float *base   = new float[zdim];
[463]191//     float *specx, *specy, *specy2, *base;
[3]192
[424]193    this->getSpectralArrays(objNum,specx,specy,specy2,base);
[3]194
[378]195    std::string fluxLabel = "Flux";
[436]196    std::string fluxUnits = this->head.getFluxUnits();
197    std::string intFluxUnits;// = this->head.getIntFluxUnits();
198    // Rather than use the intFluxUnits from the header, which will be like Jy MHz,
199    // we just use the pixel units, removing the /beam if necessary.
[498]200
[499]201    if(fluxUnits.size()>5 &&
[498]202       makelower(fluxUnits.substr(fluxUnits.size()-5,
[436]203                                  fluxUnits.size()   )) == "/beam"){
204      intFluxUnits = fluxUnits.substr(0,fluxUnits.size()-5);
205    }
206    else intFluxUnits = fluxUnits;
207   
208
[378]209    if(this->par.getSpectralMethod()=="sum"){
210      fluxLabel = "Integrated " + fluxLabel;
[436]211      if(this->head.isWCS()) {
212        fluxLabel += " ["+intFluxUnits+"]";
213      }
[3]214    }
[378]215    else {// if(par.getSpectralMethod()=="peak"){
216      fluxLabel = "Peak " + fluxLabel;
[436]217      if(this->head.isWCS()) fluxLabel += " ["+fluxUnits+"]";
[45]218    }
[3]219   
[378]220    float vmax,vmin,width;
221    vmax = vmin = specx[0];
222    for(int i=1;i<zdim;i++){
223      if(specx[i]>vmax) vmax=specx[i];
224      if(specx[i]<vmin) vmin=specx[i];
225    }
[142]226 
[378]227    float max,min;
228    int loc=0;
229    if(this->par.getMinMW()>0) max = min = specy[0];
[463]230    else max = min = specy[this->par.getMaxMW()+1];
[378]231    for(int i=0;i<zdim;i++){
232      if(!this->par.isInMW(i)){
233        if(specy[i]>max) max=specy[i];
234        if(specy[i]<min){
235          min=specy[i];
236          loc = i;
237        }
[3]238      }
239    }
[378]240    // widen the ranges slightly so that the top & bottom & edges don't
241    // lie on the axes.
242    width = max - min;
243    max += width * 0.05;
244    min -= width * 0.05;
[463]245    width = vmax - vmin;
[378]246    vmax += width * 0.01;
247    vmin -= width * 0.01;
[3]248
[378]249    // now plot the resulting spectrum
250    std::string label;
251    if(this->head.isWCS()){
252      label = this->head.getSpectralDescription() + " [" +
253        this->head.getSpectralUnits() + "]";
254      plot.gotoHeader(label);
255    }
256    else plot.gotoHeader("Spectral pixel value");
[3]257
[378]258    if(this->head.isWCS()){
[424]259      label = this->objectList->at(objNum).outputLabelWCS();
[378]260      plot.firstHeaderLine(label);
[424]261      label = this->objectList->at(objNum).outputLabelFluxes();
[378]262      plot.secondHeaderLine(label);
263    }
[424]264    label = this->objectList->at(objNum).outputLabelWidths();
[378]265    plot.thirdHeaderLine(label);
[424]266    label = this->objectList->at(objNum).outputLabelPix();
[378]267    plot.fourthHeaderLine(label);
[49]268   
[378]269    plot.gotoMainSpectrum(vmin,vmax,min,max,fluxLabel);
270    cpgline(zdim,specx,specy);
271    if(this->par.getFlagBaseline()){
272      cpgsci(DUCHAMP_BASELINE_SPECTRA_COLOUR);
273      cpgline(zdim,specx,base);
274      cpgsci(FOREGND);
275    }
276    if(this->reconExists){
277      cpgsci(DUCHAMP_RECON_SPECTRA_COLOUR);
278      cpgline(zdim,specx,specy2);   
279      cpgsci(FOREGND);
280    }
281    if(this->par.getFlagMW()) plot.drawMWRange(minMWvel,maxMWvel);
[850]282    drawSpectralRange(plot,this->objectList->at(objNum),this->head);
[3]283
[378]284    /**************************/
285    // ZOOM IN SPECTRALLY ON THE DETECTION.
[3]286
[378]287    float minvel,maxvel;
[424]288    if(this->head.isWCS()) getSmallVelRange(this->objectList->at(objNum),this->head,&minvel,&maxvel);
289    else getSmallZRange(this->objectList->at(objNum),&minvel,&maxvel);
[3]290
[378]291    // Find new max & min flux values
292    std::swap(max,min);
293    int ct = 0;
294    for(int i=0;i<zdim;i++){
295      if((!this->par.isInMW(i))&&(specx[i]>=minvel)&&(specx[i]<=maxvel)){
296        ct++;
297        if(specy[i]>max) max=specy[i];
298        if(specy[i]<min) min=specy[i];
299      }
[3]300    }
[378]301    // widen the flux range slightly so that the top & bottom don't lie
302    // on the axes.
303    width = max - min;
304    max += width * 0.05;
305    min -= width * 0.05;
[3]306
[378]307    plot.gotoZoomSpectrum(minvel,maxvel,min,max);
308    cpgline(zdim,specx,specy);
309    if(this->par.getFlagBaseline()){
310      cpgsci(DUCHAMP_BASELINE_SPECTRA_COLOUR);
311      cpgline(zdim,specx,base);
312      cpgsci(FOREGND);
313    }
314    if(this->reconExists){
315      cpgsci(DUCHAMP_RECON_SPECTRA_COLOUR);
316      cpgline(zdim,specx,specy2);   
317      cpgsci(FOREGND);
318    }
319    if(this->par.getFlagMW()) plot.drawMWRange(minMWvel,maxMWvel);
[850]320    drawSpectralRange(plot,this->objectList->at(objNum),this->head);
[3]321   
[378]322    /**************************/
[3]323
[378]324    // DRAW THE MOMENT MAP OF THE DETECTION -- SUMMED OVER ALL CHANNELS
325    plot.gotoMap();
[424]326    this->drawMomentCutout(this->objectList->at(objNum));
[3]327
[378]328    delete [] specx;
329    delete [] specy;
330    delete [] specy2;
331    delete [] base;
[3]332 
[378]333  }
[424]334  //--------------------------------------------------------------------
[3]335
[850]336  void drawSpectralRange(Plot::SpectralPlot &plot, Detection &obj, FitsHeader &head)
337  {
338    /// @details
339
340    /// A front-end to drawing the lines delimiting the spectral
341    /// extent of the detection. This takes into account the channel
342    /// widths, offsetting outwards by half a channel (for instance, a
343    /// single-channel detection will not have the separation of one
344    /// channel).
345    /// If the world coordinate is being plotted, the correct offset
346    /// is calcuated by transforming from the central spatial
347    /// positions and the offsetted min/max z-pixel extents
348
349    if(head.isWCS()){
350      double x=obj.getXcentre(),y=obj.getYcentre(),z;
351      z=obj.getZmin()-0.5;
352      float vmin=head.pixToVel(x,y,z);
353      z=obj.getZmax()+0.5;
354      float vmax=head.pixToVel(x,y,z);
355      plot.drawVelRange(vmin,vmax);
356    }
357    else{
358      plot.drawVelRange(obj.getZmin()-0.5,obj.getZmax()+0.5);
359    }
360
361
362  }
363
[378]364  void getSmallVelRange(Detection &obj, FitsHeader head,
365                        float *minvel, float *maxvel)
366  {
[528]367    ///  @details
368    ///  Routine to calculate the velocity range for the zoomed-in region.
369    ///  This range should be the maximum of 20 pixels, or 3x the wdith of
370    ///   the detection.
371    ///  Need to :
372    ///      Calculate pixel width of a 3x-detection-width region.
373    ///      If smaller than 20, calculate velocities of central vel +- 10 pixels
374    ///      If not, use the 3x-detection-width
375    ///  Range returned via "minvel" and "maxvel" parameters.
376    ///  \param obj Detection under examination.
377    ///  \param head FitsHeader, containing the WCS information.
378    ///  \param minvel Returned value of minimum velocity
379    ///  \param maxvel Returned value of maximum velocity
[3]380
[378]381    double *pixcrd = new double[3];
382    double *world  = new double[3];
383    float minpix,maxpix;
384    // define new velocity extrema
385    //    -- make it 3x wider than the width of the detection.
386    *minvel = 0.5*(obj.getVelMin()+obj.getVelMax()) - 1.5*obj.getVelWidth();
387    *maxvel = 0.5*(obj.getVelMin()+obj.getVelMax()) + 1.5*obj.getVelWidth();
388    // Find velocity range in number of pixels:
389    world[0] = obj.getRA();
390    world[1] = obj.getDec();
391    world[2] = head.velToSpec(*minvel);
392    head.wcsToPix(world,pixcrd);
393    minpix = pixcrd[2];
394    world[2] = head.velToSpec(*maxvel);
395    head.wcsToPix(world,pixcrd);
396    maxpix = pixcrd[2];
397    if(maxpix<minpix) std::swap(maxpix,minpix);
[3]398   
[378]399    if((maxpix - minpix + 1) < 20){
400      pixcrd[0] = double(obj.getXcentre());
401      pixcrd[1] = double(obj.getYcentre());
402      pixcrd[2] = obj.getZcentre() - 10.;
403      head.pixToWCS(pixcrd,world);
404      //    *minvel = setVel_kms(wcs,world[2]);
405      *minvel = head.specToVel(world[2]);
406      pixcrd[2] = obj.getZcentre() + 10.;
407      head.pixToWCS(pixcrd,world);
408      //     *maxvel = setVel_kms(wcs,world[2]);
409      *maxvel = head.specToVel(world[2]);
410      if(*maxvel<*minvel) std::swap(*maxvel,*minvel);
411    }
412    delete [] pixcrd;
413    delete [] world;
414
[3]415  }
[424]416  //--------------------------------------------------------------------
[3]417
[378]418  void getSmallZRange(Detection &obj, float *minz, float *maxz)
419  {
[528]420    ///  @details
421    ///  Routine to calculate the pixel range for the zoomed-in spectrum.
422    ///  This range should be the maximum of 20 pixels, or 3x the width
423    ///   of the detection.
424    ///  Need to :
425    ///      Calculate pixel width of a 3x-detection-width region.
426    ///       If smaller than 20, use central pixel +- 10 pixels
427    ///  Range returned via "minz" and "maxz" parameters.
428    ///  \param obj Detection under examination.
429    ///  \param minz Returned value of minimum z-pixel coordinate
430    ///  \param maxz Returned value of maximum z-pixel coordinate
[49]431
[378]432    *minz = 2.*obj.getZmin() - obj.getZmax();
433    *maxz = 2.*obj.getZmax() - obj.getZmin();
434   
435    if((*maxz - *minz + 1) < 20){
436      *minz = obj.getZcentre() - 10.;
437      *maxz = obj.getZcentre() + 10.;
438    }
[49]439
440  }
[424]441  //--------------------------------------------------------------------
[49]442
[378]443  void Cube::plotSource(Detection obj, Plot::CutoutPlot &plot)
444  {
[528]445    ///  @details
446    /// The way to print out the 2d image cutout of a Detection.
447    /// Makes use of the CutoutPlot class in plots.hh, which sizes
448    ///  everything correctly.
449    ///
450    /// A 0th moment map of the detection is plotted, with a scale
451    /// bar indicating the spatial size.
452    ///
453    /// Basic information on the source is printed next to it as well.
454    ///
455    /// \param obj The Detection to be plotted.
456    /// \param plot The PGPLOT device to plot the spectrum on.
[367]457
[378]458    obj.calcFluxes(this->array, this->axisDim);
[367]459
[378]460    std::string label;
461    plot.gotoHeader();
[367]462
[378]463    if(this->head.isWCS()){
464      label = obj.outputLabelWCS();
465      plot.firstHeaderLine(label);
466      label = obj.outputLabelFluxes();
467      plot.secondHeaderLine(label);
468    }
469    label = obj.outputLabelWidths();
470    plot.thirdHeaderLine(label);
471    label = obj.outputLabelPix();
472    plot.fourthHeaderLine(label);
473   
474    // DRAW THE MOMENT MAP OF THE DETECTION -- SUMMED OVER ALL CHANNELS
475    plot.gotoMap();
476    this->drawMomentCutout(obj);
[367]477
478  }
479
480}
Note: See TracBrowser for help on using the repository browser.