source: trunk/src/Cubes/plotting.cc

Last change on this file was 1446, checked in by MatthewWhiting, 4 years ago

Adding plotting of V20 & V50 ranges

File size: 16.9 KB
RevLine 
[299]1// -----------------------------------------------------------------------
2// plotting.cc: Plot the moment map and detection maps, showing the
3//              location of the detected objects.
4// -----------------------------------------------------------------------
5// Copyright (C) 2006, Matthew Whiting, ATNF
6//
7// This program is free software; you can redistribute it and/or modify it
8// under the terms of the GNU General Public License as published by the
9// Free Software Foundation; either version 2 of the License, or (at your
10// option) any later version.
11//
12// Duchamp is distributed in the hope that it will be useful, but WITHOUT
13// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15// for more details.
16//
17// You should have received a copy of the GNU General Public License
18// along with Duchamp; if not, write to the Free Software Foundation,
19// Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
20//
21// Correspondence concerning Duchamp may be directed to:
22//    Internet email: Matthew.Whiting [at] atnf.csiro.au
23//    Postal address: Dr. Matthew Whiting
24//                    Australia Telescope National Facility, CSIRO
25//                    PO Box 76
26//                    Epping NSW 1710
27//                    AUSTRALIA
28// -----------------------------------------------------------------------
[3]29#include <iostream>
30#include <iomanip>
31#include <sstream>
32#include <math.h>
[656]33#include <string.h>
[3]34#include <cpgplot.h>
[401]35#include <wcslib/cpgsbox.h>
36#include <wcslib/pgwcsl.h>
[394]37#include <wcslib/wcs.h>
[393]38#include <duchamp/duchamp.hh>
39#include <duchamp/param.hh>
40#include <duchamp/fitsHeader.hh>
41#include <duchamp/PixelMap/Object3D.hh>
42#include <duchamp/Cubes/cubes.hh>
[463]43#include <duchamp/Cubes/cubeUtils.hh>
[1242]44#include <duchamp/Plotting/SpectralPlot.hh>
45#include <duchamp/Plotting/SimpleSpectralPlot.hh>
46#include <duchamp/Plotting/ImagePlot.hh>
[393]47#include <duchamp/Utils/utils.hh>
48#include <duchamp/Utils/mycpgplot.hh>
[3]49
[146]50using namespace mycpgplot;
[258]51using namespace PixelInfo;
[3]52
[378]53namespace duchamp
54{
55
[581]56  void Cube::plotDetectionMap(std::string pgDestination)
57  {
58    ///  @details
59    ///  Creates a map of the spatial locations of the detections, which is
60    ///   written to the PGPlot device given by pgDestination.
61    ///  The map is done in greyscale, where the scale indicates the number of
62    ///   velocity channels that each spatial pixel is detected in.
63    ///  The boundaries of each detection are drawn, and each object is numbered
64    ///   (to match the output list and spectra).
65    ///  The primary grid scale is pixel coordinate, and if the WCS is valid,
66    ///   the correct WCS gridlines are also drawn.
67    /// \param pgDestination The PGPLOT device to be opened, in the typical PGPLOT format.
[3]68
[581]69    // These are the minimum values for the X and Y ranges of the box drawn by
70    //   pgplot (without the half-pixel difference).
71    // The -1 is necessary because the arrays we are dealing with start at 0
72    //  index, while the ranges given in the subsection start at 1...
73    float boxXmin = this->par.getXOffset() - 1;
74    float boxYmin = this->par.getYOffset() - 1;
[3]75
[581]76    long xdim=this->axisDim[0];
77    long ydim=this->axisDim[1];
[985]78    long zdim=this->axisDim[2];
[3]79
[985]80    if( this->numNondegDim == 1){
81     
82      float *specx  = new float[zdim];
83      float *specy  = new float[zdim];
84      float *specy2 = new float[zdim];
85      float *base   = new float[zdim];
86      Plot::SimpleSpectralPlot spPlot;
87      int flag = spPlot.setUpPlot(pgDestination.c_str());
88      if(flag <= 0){
89        DUCHAMPERROR("Plot Detection Map", "Could not open PGPlot device " << pgDestination);
90      }
91      else{
92       
93        this->getSpectralArrays(-1,specx,specy,specy2,base);
94        float vmax,vmin,width;
95        vmax = vmin = specx[0];
96        for(int i=1;i<zdim;i++){
97          if(specx[i]>vmax) vmax=specx[i];
98          if(specx[i]<vmin) vmin=specx[i];
99        }
100     
[1242]101        // Find the maximum & minimum values of the spectrum, ignoring flagged channels.
[985]102        float max,min;
[1242]103        bool haveStarted=false;
104        for(int z=0;z<zdim;z++){
105            if(!this->par.isFlaggedChannel(z)){
[1251]106                if(specy[z]>max || !haveStarted) max=specy[z];
107                if(specy[z]<min || !haveStarted) min=specy[z];
[1242]108                haveStarted=true;
[985]109            }
110        }
[1242]111
[985]112        // widen the ranges slightly so that the top & bottom & edges don't
113        // lie on the axes.
114        width = max - min;
115        max += width * 0.15;
116        min -= width * 0.05;
117        width = vmax - vmin;
118        vmax += width * 0.01;
119        vmin -= width * 0.01;
120        std::string label,fluxLabel = "Flux ["+this->head.getFluxUnits()+"]";
121        if(this->head.isWCS()){
122          label = this->head.getSpectralDescription() + " [" +
123            this->head.getSpectralUnits() + "]";
124        }
125        else label="Spectral pixel";
126        std::string filename=this->pars().getImageFile();
127        filename = filename.substr(filename.rfind('/')+1);
[1366]128        if(this->par.getFlagSubsection()) filename += this->par.getSubsection();
[985]129        spPlot.label(label,fluxLabel,"Detection summary : " + filename);
130        spPlot.gotoMainSpectrum(vmin,vmax,min,max);
131        cpgline(zdim,specx,specy);
132        if(this->par.getFlagBaseline()){
133          cpgsci(DUCHAMP_BASELINE_SPECTRA_COLOUR);
134          cpgline(zdim,specx,base);
135          cpgsci(FOREGND);
136        }
137        if(this->reconExists){
138          cpgsci(DUCHAMP_RECON_SPECTRA_COLOUR);
139          cpgline(zdim,specx,specy2);   
140          cpgsci(FOREGND);
141        }
[1242]142        this->drawFlaggedChannels(spPlot,0.,0.);
[3]143
[1013]144        spPlot.markDetectedPixels(this->detectMap,zdim,this->head);
[615]145
[985]146        for(size_t i=0;i<this->getNumObj();i++){
147          drawSpectralRange(spPlot,this->objectList->at(i),this->head);
148        }
[3]149
[1258]150        if(this->par.getFlagBaseline())
151            spPlot.drawThresholds(this->par,this->Stats,specx,base,zdim);
152        else
153            spPlot.drawThresholds(this->par,this->Stats);
154
155
156        // cpgsci(RED);
157        // cpgsls(DASHED);
158        // float thresh = this->Stats.getThreshold();
159        // if(this->par.getFlagNegative()) thresh *= -1.;
160        // cpgmove(vmin,thresh);
161        // cpgdraw(vmax,thresh);
162        // if(this->par.getFlagGrowth()){
163        //   if(this->par.getFlagUserGrowthThreshold()) thresh= this->par.getGrowthThreshold();
164        //   else thresh= this->Stats.snrToValue(this->par.getGrowthCut());
165        //   if(this->par.getFlagNegative()) thresh *= -1.;     
166        //   cpgsls(DOTTED);
167        //   cpgmove(vmin,thresh);
168        //   cpgdraw(vmax,thresh);
169        // }
[985]170      }
[3]171
[985]172      spPlot.close();
173    }
[1104]174    else { // num dim > 1
[3]175
[1242]176      Plot::ImagePlot newplot(xdim,ydim);
177      int flag = newplot.setUpPlot(pgDestination);
[985]178
179      if(flag<=0){
180        DUCHAMPERROR("Plot Detection Map", "Could not open PGPlot device " << pgDestination);
[581]181      }
[985]182      else{
[3]183
[985]184        // get the list of objects that should be plotted. Only applies to outlines and labels.
185        std::vector<bool> objectChoice = this->par.getObjectChoices(this->objectList->size());
[3]186
[985]187        std::string filename=this->pars().getImageFile();
188        newplot.makeTitle(filename.substr(filename.rfind('/')+1,filename.size()));
[204]189
[985]190        newplot.drawMapBox(boxXmin+0.5,boxXmin+xdim+0.5,
191                           boxYmin+0.5,boxYmin+ydim+0.5,
192                           "X pixel","Y pixel");
[666]193
[985]194        //     if(this->objectList.size()>0){
195        // if there are no detections, there will be nothing to plot here
196
197        // Define a float equivalent of this->detectMap that can be plotted by cpggray.
198        // Also find the maximum value, so that we can get the greyscale right and plot a colour wedge.
199        float *detectionMap = new float[xdim*ydim];
200        int maxNum = this->detectMap[0];
201        detectionMap[0] = float(maxNum);
202        for(int pix=1;pix<xdim*ydim;pix++){
203          detectionMap[pix] = float(this->detectMap[pix]); 
204          if(this->detectMap[pix] > maxNum)  maxNum = this->detectMap[pix];
205        }
206
207        if(maxNum>0){ // if there are no detections, it will be 0.
208
209          maxNum = 5 * ((maxNum-1)/5 + 1);  // move to next multiple of 5
210
211          float tr[6] = {boxXmin,1.,0.,boxYmin,0.,1.};
212          cpggray(detectionMap,xdim,ydim,1,xdim,1,ydim,maxNum,0,tr); 
213          cpgbox("bcnst",0.,0,"bcnst",0.,0);
214          cpgsch(1.5);
215          cpgwedg("rg",3.2,2,maxNum,0,"Number of detected channels");
216        }
217
218        delete [] detectionMap;
[208]219     
[985]220        drawBlankEdges(this->array,this->axisDim[0],this->axisDim[1],this->par);
[208]221     
[985]222        if(this->head.isWCS()) this->plotWCSaxes();
[3]223 
[985]224        if(this->objectList->size()>0){
225          // now show and label each detection, drawing over the WCS lines.
[3]226
[985]227          cpgsch(1.0);
228          cpgslw(2);   
229          float xoff=0.;
230          float yoff=newplot.cmToCoord(0.5);
231          if(this->par.drawBorders()){
232            cpgsci(DUCHAMP_OBJECT_OUTLINE_COLOUR);
233            for(size_t i=0;i<this->objectList->size();i++)
234              if(objectChoice[i]) this->objectList->at(i).drawBorders(0,0);
[615]235          }
[985]236          cpgsci(DUCHAMP_ID_TEXT_COLOUR);
237          std::stringstream label;
238          cpgslw(1);
239          for(size_t i=0;i<this->objectList->size();i++){
240            if(objectChoice[i]) {
241              cpgpt1(this->par.getXOffset()+this->objectList->at(i).getXPeak(),
242                     this->par.getYOffset()+this->objectList->at(i).getYPeak(),
243                     CROSS);
244              label.str("");
245              label << this->objectList->at(i).getID();
246              cpgptxt(this->par.getXOffset()+this->objectList->at(i).getXPeak()-xoff,
247                      this->par.getYOffset()+this->objectList->at(i).getYPeak()-yoff,
248                      0, 0.5, label.str().c_str());
249            }
250          }
251
[581]252        }
253
[985]254        newplot.close();
[103]255      }
[3]256    }
257  }
258
[581]259  /*********************************************************/
[3]260
[581]261  void Cube::plotMomentMap(std::string pgDestination)
262  {
263    ///  @details
264    ///  Uses the other function
265    ///  Cube::plotMomentMap(std::vector<std::string>) to plot the moment
266    ///  map.
267    /// \param pgDestination The PGPLOT device that the map is to be written to.
[3]268
[581]269    std::vector<std::string> devicelist;
270    devicelist.push_back(pgDestination);
271    this->plotMomentMap(devicelist);
272  }
[3]273
[581]274  /*********************************************************/
[3]275
[581]276  void Cube::plotMomentMap(std::vector<std::string> pgDestination)
277  {
278    ///  @details
279    ///  Creates a 0th moment map of the detections, which is written to each
280    ///   of the PGPlot devices mentioned in pgDestination.
281    ///  The advantage of this function is that the map is only calculated once,
282    ///   even if multiple maps are required.
283    ///  The map is done in greyscale, where the scale indicates the integrated
284    ///   flux at each spatial pixel.
285    ///  The boundaries of each detection are drawn, and each object is numbered
286    ///   (to match the output list and spectra).
287    ///  The primary grid scale is pixel coordinate, and if the WCS is valid,
288    ///   the correct WCS gridlines are also drawn.
289    /// \param pgDestination A set of PGPLOT devices that are to be
290    /// opened, each in the typical PGPLOT format.
[3]291
[581]292    float boxXmin = this->par.getXOffset() - 1;
293    float boxYmin = this->par.getYOffset() - 1;
[203]294
[581]295    long xdim=this->axisDim[0];
296    long ydim=this->axisDim[1];
[203]297
[581]298    int numPlots = pgDestination.size();
[1242]299    std::vector<Plot::ImagePlot> plotList(numPlots,Plot::ImagePlot(xdim,ydim));
[581]300    std::vector<int> plotFlag(numPlots,0);
301    std::vector<bool> doPlot(numPlots,false);
302    bool plotNeeded = false;
[203]303
[581]304    for(int i=0;i<numPlots;i++){
[203]305   
[1242]306      plotFlag[i] = plotList[i].setUpPlot(pgDestination[i]);
[203]307       
[913]308      if(plotFlag[i]<=0){
309        DUCHAMPERROR("Plot Moment Map", "Could not open PGPlot device " << pgDestination[i]);
310      }
[581]311      else{
312        doPlot[i] = true;
313        plotNeeded = true;
314      }
315
[203]316    }
[485]317
[581]318    if(plotNeeded){
[203]319
[581]320      if(this->objectList->size()==0){
321        // if there are no detections, we plot an empty field.
[203]322
[581]323        for(int iplot=0; iplot<numPlots; iplot++){
324          plotList[iplot].goToPlot();
[755]325          std::string filename=this->pars().getImageFile();
326          plotList[iplot].makeTitle(filename.substr(filename.rfind('/')+1,filename.size()));
[203]327       
[581]328          plotList[iplot].drawMapBox(boxXmin+0.5,boxXmin+xdim+0.5,
329                                     boxYmin+0.5,boxYmin+ydim+0.5,
330                                     "X pixel","Y pixel");
[203]331       
[581]332          drawBlankEdges(this->array,this->axisDim[0],this->axisDim[1],this->par);
[203]333       
[581]334          if(this->head.isWCS()) this->plotWCSaxes();
335        }
336
[203]337      }
[581]338      else { 
339        // if there are some detections, do the calculations first before
340        //  plotting anything.
[203]341 
[615]342        // get the list of objects that should be plotted. Only applies to outlines and labels.
343        std::vector<bool> objectChoice = this->par.getObjectChoices(this->objectList->size());
344
[581]345        for(int iplot=0; iplot<numPlots; iplot++){
346          // Although plot the axes so that the user knows something is
347          //  being done (at least, they will if there is an /xs plot)
348          plotList[iplot].goToPlot();
[755]349          std::string filename=this->pars().getImageFile();
350          plotList[iplot].makeTitle(filename.substr(filename.rfind('/')+1,filename.size()));
[203]351   
[581]352          plotList[iplot].drawMapBox(boxXmin+0.5,boxXmin+xdim+0.5,
353                                     boxYmin+0.5,boxYmin+ydim+0.5,
354                                     "X pixel","Y pixel");
[203]355       
[581]356          if(pgDestination[iplot]=="/xs")
357            cpgptxt(boxXmin+0.5+xdim/2., boxYmin+0.5+ydim/2., 0, 0.5,
358                    "Calculating map...");
359        }
[203]360
[668]361        float *momentMap = new float[xdim*ydim];
[665]362        float z1=0.,z2=0.;
[668]363        this->getMomentMapForPlot(momentMap,z1,z2);
[581]364
[203]365
[581]366        // Have now done all necessary calculations for moment map.
367        // Now produce the plot
[203]368
[581]369        for(int iplot=0; iplot<numPlots; iplot++){
370          plotList[iplot].goToPlot();
[203]371     
[581]372          float tr[6] = {boxXmin,1.,0.,boxYmin,0.,1.};
373          cpggray(momentMap,xdim,ydim,1,xdim,1,ydim,z2,z1,tr);
374          cpgbox("bcnst",0.,0,"bcnst",0.,0);
375          cpgsch(1.5);
376          std::string wedgeLabel = "Integrated Flux ";
377          if(this->par.getFlagNegative())
378            wedgeLabel = "-1. " + times + " " + wedgeLabel;
379          if(this->head.isWCS())
380            wedgeLabel += "[" + this->head.getIntFluxUnits() + "]";
381          else wedgeLabel += "[" + this->head.getFluxUnits() + "]";
382          cpgwedglog("rg",3.2,2,z2,z1,wedgeLabel.c_str());
[203]383
384
[581]385          drawBlankEdges(this->array,this->axisDim[0],this->axisDim[1],this->par);
[203]386
[581]387          if(this->head.isWCS()) this->plotWCSaxes();
[203]388 
[581]389          // now show and label each detection, drawing over the WCS lines.
390          cpgsch(1.0);
391          cpgslw(2);
392          float xoff=0.;
393          float yoff=plotList[iplot].cmToCoord(0.5);
394          if(this->par.drawBorders()){
395            cpgsci(DUCHAMP_OBJECT_OUTLINE_COLOUR);
[623]396            for(size_t i=0;i<this->objectList->size();i++)
[615]397              if(objectChoice[i]) this->objectList->at(i).drawBorders(0,0);
[581]398          }
399          cpgsci(DUCHAMP_ID_TEXT_COLOUR);
400          std::stringstream label;
401          cpgslw(1);
[623]402          for(size_t i=0;i<this->objectList->size();i++){
[615]403            if(objectChoice[i]) {
404              cpgpt1(this->par.getXOffset()+this->objectList->at(i).getXPeak(),
405                     this->par.getYOffset()+this->objectList->at(i).getYPeak(),
406                     CROSS);
407              label.str("");
408              label << this->objectList->at(i).getID();
409              cpgptxt(this->par.getXOffset()+this->objectList->at(i).getXPeak()-xoff,
410                      this->par.getYOffset()+this->objectList->at(i).getYPeak()-yoff,
411                      0, 0.5, label.str().c_str());
412            }
[581]413          }
[203]414
[581]415        } // end of iplot loop over number of devices
[203]416
[581]417        delete [] momentMap;
[203]418
[581]419      } // end of else (from if(numdetections==0) )
[203]420
421 
[581]422      for(int iplot=0; iplot<numPlots; iplot++){
423        plotList[iplot].goToPlot();
[985]424        plotList[iplot].close();
[581]425      }
426   
[203]427    }
[581]428 
[203]429  }
430
[581]431  /*********************************************************/
[203]432
[998]433  void drawSpectralRange(Plot::SpectralPlot &plot, Detection &obj, FitsHeader &head)
434  {
435    /// @details
436
437    /// A front-end to drawing the lines delimiting the spectral
438    /// extent of the detection. This takes into account the channel
439    /// widths, offsetting outwards by half a channel (for instance, a
440    /// single-channel detection will not have the separation of one
441    /// channel).
442    /// If the world coordinate is being plotted, the correct offset
443    /// is calcuated by transforming from the central spatial
444    /// positions and the offsetted min/max z-pixel extents
445
446    if(head.isWCS()){
447      double x=obj.getXcentre(),y=obj.getYcentre(),z;
448      z=obj.getZmin()-0.5;
449      float vmin=head.pixToVel(x,y,z);
450      z=obj.getZmax()+0.5;
451      float vmax=head.pixToVel(x,y,z);
452      plot.drawVelRange(vmin,vmax);
[1446]453
454            int ci,ls;
455            float dud,min,max;
456            cpgqwin(&dud,&dud,&min,&max);
457            cpgqci(&ci);
458            cpgqls(&ls);
459            cpgsci(4);
460            cpgsls(4);
461            z=obj.getV20Min()-0.5;
462            vmin=obj.getV20Min();//head.pixToVel(x,y,z);
463            z=obj.getV20Max()+0.5;
464            vmax=obj.getV20Max();//head.pixToVel(x,y,z);
465            cpgmove(vmin,min);  cpgdraw(vmin,max);
466            cpgmove(vmax,min);  cpgdraw(vmax,max);
467            z=obj.getV50Min()-0.5;
468            vmin=obj.getV50Min();//head.pixToVel(x,y,z);
469            z=obj.getV50Max()+0.5;
470            vmax=obj.getV50Max();//head.pixToVel(x,y,z);
471            cpgmove(vmin,min);  cpgdraw(vmin,max);
472            cpgmove(vmax,min);  cpgdraw(vmax,max);
473            cpgsci(ci);
474            cpgsls(ls);
475
476
477     
[998]478    }
479    else{
480      plot.drawVelRange(obj.getZmin()-0.5,obj.getZmax()+0.5);
481    }
482
483
484  }
485
486  void drawSpectralRange(Plot::SimpleSpectralPlot &plot, Detection &obj, FitsHeader &head)
487  {
488    /// @details
489
490    /// A front-end to drawing the lines delimiting the spectral
491    /// extent of the detection. This takes into account the channel
492    /// widths, offsetting outwards by half a channel (for instance, a
493    /// single-channel detection will not have the separation of one
494    /// channel).
495    /// If the world coordinate is being plotted, the correct offset
496    /// is calcuated by transforming from the central spatial
497    /// positions and the offsetted min/max z-pixel extents
498
499    if(head.isWCS()){
500      double x=obj.getXcentre(),y=obj.getYcentre(),z;
501      z=obj.getZmin()-0.5;
502      float vmin=head.pixToVel(x,y,z);
503      z=obj.getZmax()+0.5;
504      float vmax=head.pixToVel(x,y,z);
505      plot.drawVelRange(vmin,vmax);
506    }
507    else{
508      plot.drawVelRange(obj.getZmin()-0.5,obj.getZmax()+0.5);
509    }
510
511  }
512
513
514
[378]515}
516
Note: See TracBrowser for help on using the repository browser.