source: trunk/src/Cubes/plotting.cc @ 1251

Last change on this file since 1251 was 1251, checked in by MatthewWhiting, 11 years ago

Ticket #152 - adding the cube subsection to the filename in the title of the 1D detection plot (and making sure the min/max values are calculated correctly). Also removing some commented-out include statements.

File size: 16.3 KB
Line 
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// -----------------------------------------------------------------------
29#include <iostream>
30#include <iomanip>
31#include <sstream>
32#include <math.h>
33#include <string.h>
34#include <cpgplot.h>
35#include <wcslib/cpgsbox.h>
36#include <wcslib/pgwcsl.h>
37#include <wcslib/wcs.h>
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>
43#include <duchamp/Cubes/cubeUtils.hh>
44#include <duchamp/Plotting/SpectralPlot.hh>
45#include <duchamp/Plotting/SimpleSpectralPlot.hh>
46#include <duchamp/Plotting/ImagePlot.hh>
47#include <duchamp/Utils/utils.hh>
48#include <duchamp/Utils/mycpgplot.hh>
49
50using namespace mycpgplot;
51using namespace PixelInfo;
52
53namespace duchamp
54{
55
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.
68
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;
75
76    long xdim=this->axisDim[0];
77    long ydim=this->axisDim[1];
78    long zdim=this->axisDim[2];
79
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//       int flag=cpgopen(pgDestination.c_str());
89//       cpgpap(spPlot.getPaperWidth(),spPlot.getAspectRatio());
90//       cpgsch(Plot::spLabelSize);
91      if(flag <= 0){
92        DUCHAMPERROR("Plot Detection Map", "Could not open PGPlot device " << pgDestination);
93      }
94      else{
95       
96//      std::vector<bool> flaggedChans=this->par.getChannelFlags(zdim);
97        this->getSpectralArrays(-1,specx,specy,specy2,base);
98        float vmax,vmin,width;
99        vmax = vmin = specx[0];
100        for(int i=1;i<zdim;i++){
101          if(specx[i]>vmax) vmax=specx[i];
102          if(specx[i]<vmin) vmin=specx[i];
103        }
104     
105        // Find the maximum & minimum values of the spectrum, ignoring flagged channels.
106        float max,min;
107        bool haveStarted=false;
108        for(int z=0;z<zdim;z++){
109//          if(!flaggedChans[z]){
110            if(!this->par.isFlaggedChannel(z)){
111                if(specy[z]>max || !haveStarted) max=specy[z];
112                if(specy[z]<min || !haveStarted) min=specy[z];
113                haveStarted=true;
114            }
115        }
116
117        // widen the ranges slightly so that the top & bottom & edges don't
118        // lie on the axes.
119        width = max - min;
120        max += width * 0.15;
121        min -= width * 0.05;
122        width = vmax - vmin;
123        vmax += width * 0.01;
124        vmin -= width * 0.01;
125        std::string label,fluxLabel = "Flux ["+this->head.getFluxUnits()+"]";
126        if(this->head.isWCS()){
127          label = this->head.getSpectralDescription() + " [" +
128            this->head.getSpectralUnits() + "]";
129        }
130        else label="Spectral pixel";
131        std::string filename=this->pars().getImageFile();
132        filename = filename.substr(filename.rfind('/')+1);
133        if(this->par.getSubsection().size()>0) filename += this->par.getSubsection();
134        spPlot.label(label,fluxLabel,"Detection summary : " + filename);
135        spPlot.gotoMainSpectrum(vmin,vmax,min,max);
136        cpgline(zdim,specx,specy);
137        if(this->par.getFlagBaseline()){
138          cpgsci(DUCHAMP_BASELINE_SPECTRA_COLOUR);
139          cpgline(zdim,specx,base);
140          cpgsci(FOREGND);
141        }
142        if(this->reconExists){
143          cpgsci(DUCHAMP_RECON_SPECTRA_COLOUR);
144          cpgline(zdim,specx,specy2);   
145          cpgsci(FOREGND);
146        }
147        this->drawFlaggedChannels(spPlot,0.,0.);
148
149        spPlot.markDetectedPixels(this->detectMap,zdim,this->head);
150
151        for(size_t i=0;i<this->getNumObj();i++){
152          drawSpectralRange(spPlot,this->objectList->at(i),this->head);
153        }
154
155        cpgsci(RED);
156        cpgsls(DASHED);
157        float thresh = this->Stats.getThreshold();
158        if(this->par.getFlagNegative()) thresh *= -1.;
159        cpgmove(vmin,thresh);
160        cpgdraw(vmax,thresh);
161        if(this->par.getFlagGrowth()){
162          if(this->par.getFlagUserGrowthThreshold()) thresh= this->par.getGrowthThreshold();
163          else thresh= this->Stats.snrToValue(this->par.getGrowthCut());
164          if(this->par.getFlagNegative()) thresh *= -1.;       
165          cpgsls(DOTTED);
166          cpgmove(vmin,thresh);
167          cpgdraw(vmax,thresh);
168        }
169      }
170
171      spPlot.close();
172    }
173    else { // num dim > 1
174
175      Plot::ImagePlot newplot(xdim,ydim);
176      int flag = newplot.setUpPlot(pgDestination);
177
178      if(flag<=0){
179        DUCHAMPERROR("Plot Detection Map", "Could not open PGPlot device " << pgDestination);
180      }
181      else{
182
183        // get the list of objects that should be plotted. Only applies to outlines and labels.
184        std::vector<bool> objectChoice = this->par.getObjectChoices(this->objectList->size());
185
186        std::string filename=this->pars().getImageFile();
187        newplot.makeTitle(filename.substr(filename.rfind('/')+1,filename.size()));
188
189        newplot.drawMapBox(boxXmin+0.5,boxXmin+xdim+0.5,
190                           boxYmin+0.5,boxYmin+ydim+0.5,
191                           "X pixel","Y pixel");
192
193        //     if(this->objectList.size()>0){
194        // if there are no detections, there will be nothing to plot here
195
196        // Define a float equivalent of this->detectMap that can be plotted by cpggray.
197        // Also find the maximum value, so that we can get the greyscale right and plot a colour wedge.
198        float *detectionMap = new float[xdim*ydim];
199        int maxNum = this->detectMap[0];
200        detectionMap[0] = float(maxNum);
201        for(int pix=1;pix<xdim*ydim;pix++){
202          detectionMap[pix] = float(this->detectMap[pix]); 
203          if(this->detectMap[pix] > maxNum)  maxNum = this->detectMap[pix];
204        }
205
206        if(maxNum>0){ // if there are no detections, it will be 0.
207
208          maxNum = 5 * ((maxNum-1)/5 + 1);  // move to next multiple of 5
209
210          float tr[6] = {boxXmin,1.,0.,boxYmin,0.,1.};
211          cpggray(detectionMap,xdim,ydim,1,xdim,1,ydim,maxNum,0,tr); 
212          cpgbox("bcnst",0.,0,"bcnst",0.,0);
213          cpgsch(1.5);
214          cpgwedg("rg",3.2,2,maxNum,0,"Number of detected channels");
215        }
216
217        delete [] detectionMap;
218     
219        drawBlankEdges(this->array,this->axisDim[0],this->axisDim[1],this->par);
220     
221        if(this->head.isWCS()) this->plotWCSaxes();
222 
223        if(this->objectList->size()>0){
224          // now show and label each detection, drawing over the WCS lines.
225
226          cpgsch(1.0);
227          cpgslw(2);   
228          float xoff=0.;
229          float yoff=newplot.cmToCoord(0.5);
230          if(this->par.drawBorders()){
231            cpgsci(DUCHAMP_OBJECT_OUTLINE_COLOUR);
232            for(size_t i=0;i<this->objectList->size();i++)
233              if(objectChoice[i]) this->objectList->at(i).drawBorders(0,0);
234          }
235          cpgsci(DUCHAMP_ID_TEXT_COLOUR);
236          std::stringstream label;
237          cpgslw(1);
238          for(size_t i=0;i<this->objectList->size();i++){
239            if(objectChoice[i]) {
240              cpgpt1(this->par.getXOffset()+this->objectList->at(i).getXPeak(),
241                     this->par.getYOffset()+this->objectList->at(i).getYPeak(),
242                     CROSS);
243              label.str("");
244              label << this->objectList->at(i).getID();
245              cpgptxt(this->par.getXOffset()+this->objectList->at(i).getXPeak()-xoff,
246                      this->par.getYOffset()+this->objectList->at(i).getYPeak()-yoff,
247                      0, 0.5, label.str().c_str());
248            }
249          }
250
251        }
252
253        newplot.close();
254      }
255    }
256  }
257
258  /*********************************************************/
259
260  void Cube::plotMomentMap(std::string pgDestination)
261  {
262    ///  @details
263    ///  Uses the other function
264    ///  Cube::plotMomentMap(std::vector<std::string>) to plot the moment
265    ///  map.
266    /// \param pgDestination The PGPLOT device that the map is to be written to.
267
268    std::vector<std::string> devicelist;
269    devicelist.push_back(pgDestination);
270    this->plotMomentMap(devicelist);
271  }
272
273  /*********************************************************/
274
275  void Cube::plotMomentMap(std::vector<std::string> pgDestination)
276  {
277    ///  @details
278    ///  Creates a 0th moment map of the detections, which is written to each
279    ///   of the PGPlot devices mentioned in pgDestination.
280    ///  The advantage of this function is that the map is only calculated once,
281    ///   even if multiple maps are required.
282    ///  The map is done in greyscale, where the scale indicates the integrated
283    ///   flux at each spatial pixel.
284    ///  The boundaries of each detection are drawn, and each object is numbered
285    ///   (to match the output list and spectra).
286    ///  The primary grid scale is pixel coordinate, and if the WCS is valid,
287    ///   the correct WCS gridlines are also drawn.
288    /// \param pgDestination A set of PGPLOT devices that are to be
289    /// opened, each in the typical PGPLOT format.
290
291    float boxXmin = this->par.getXOffset() - 1;
292    float boxYmin = this->par.getYOffset() - 1;
293
294    long xdim=this->axisDim[0];
295    long ydim=this->axisDim[1];
296
297    int numPlots = pgDestination.size();
298    std::vector<Plot::ImagePlot> plotList(numPlots,Plot::ImagePlot(xdim,ydim));
299    std::vector<int> plotFlag(numPlots,0);
300    std::vector<bool> doPlot(numPlots,false);
301    bool plotNeeded = false;
302
303    for(int i=0;i<numPlots;i++){
304   
305      plotFlag[i] = plotList[i].setUpPlot(pgDestination[i]);
306       
307      if(plotFlag[i]<=0){
308        DUCHAMPERROR("Plot Moment Map", "Could not open PGPlot device " << pgDestination[i]);
309      }
310      else{
311        doPlot[i] = true;
312        plotNeeded = true;
313      }
314
315    }
316
317    if(plotNeeded){
318
319      if(this->objectList->size()==0){
320        // if there are no detections, we plot an empty field.
321
322        for(int iplot=0; iplot<numPlots; iplot++){
323          plotList[iplot].goToPlot();
324          std::string filename=this->pars().getImageFile();
325          plotList[iplot].makeTitle(filename.substr(filename.rfind('/')+1,filename.size()));
326       
327          plotList[iplot].drawMapBox(boxXmin+0.5,boxXmin+xdim+0.5,
328                                     boxYmin+0.5,boxYmin+ydim+0.5,
329                                     "X pixel","Y pixel");
330       
331          drawBlankEdges(this->array,this->axisDim[0],this->axisDim[1],this->par);
332       
333          if(this->head.isWCS()) this->plotWCSaxes();
334        }
335
336      }
337      else { 
338        // if there are some detections, do the calculations first before
339        //  plotting anything.
340 
341        // get the list of objects that should be plotted. Only applies to outlines and labels.
342        std::vector<bool> objectChoice = this->par.getObjectChoices(this->objectList->size());
343
344        for(int iplot=0; iplot<numPlots; iplot++){
345          // Although plot the axes so that the user knows something is
346          //  being done (at least, they will if there is an /xs plot)
347          plotList[iplot].goToPlot();
348          std::string filename=this->pars().getImageFile();
349          plotList[iplot].makeTitle(filename.substr(filename.rfind('/')+1,filename.size()));
350   
351          plotList[iplot].drawMapBox(boxXmin+0.5,boxXmin+xdim+0.5,
352                                     boxYmin+0.5,boxYmin+ydim+0.5,
353                                     "X pixel","Y pixel");
354       
355          if(pgDestination[iplot]=="/xs")
356            cpgptxt(boxXmin+0.5+xdim/2., boxYmin+0.5+ydim/2., 0, 0.5,
357                    "Calculating map...");
358        }
359
360        float *momentMap = new float[xdim*ydim];
361        float z1=0.,z2=0.;
362        this->getMomentMapForPlot(momentMap,z1,z2);
363
364
365        // Have now done all necessary calculations for moment map.
366        // Now produce the plot
367
368        for(int iplot=0; iplot<numPlots; iplot++){
369          plotList[iplot].goToPlot();
370     
371          float tr[6] = {boxXmin,1.,0.,boxYmin,0.,1.};
372          cpggray(momentMap,xdim,ydim,1,xdim,1,ydim,z2,z1,tr);
373          cpgbox("bcnst",0.,0,"bcnst",0.,0);
374          cpgsch(1.5);
375          std::string wedgeLabel = "Integrated Flux ";
376          if(this->par.getFlagNegative())
377            wedgeLabel = "-1. " + times + " " + wedgeLabel;
378          if(this->head.isWCS())
379            wedgeLabel += "[" + this->head.getIntFluxUnits() + "]";
380          else wedgeLabel += "[" + this->head.getFluxUnits() + "]";
381          cpgwedglog("rg",3.2,2,z2,z1,wedgeLabel.c_str());
382
383
384          drawBlankEdges(this->array,this->axisDim[0],this->axisDim[1],this->par);
385
386          if(this->head.isWCS()) this->plotWCSaxes();
387 
388          // now show and label each detection, drawing over the WCS lines.
389          cpgsch(1.0);
390          cpgslw(2);
391          float xoff=0.;
392          float yoff=plotList[iplot].cmToCoord(0.5);
393          if(this->par.drawBorders()){
394            cpgsci(DUCHAMP_OBJECT_OUTLINE_COLOUR);
395            for(size_t i=0;i<this->objectList->size();i++)
396              if(objectChoice[i]) this->objectList->at(i).drawBorders(0,0);
397          }
398          cpgsci(DUCHAMP_ID_TEXT_COLOUR);
399          std::stringstream label;
400          cpgslw(1);
401          for(size_t i=0;i<this->objectList->size();i++){
402            if(objectChoice[i]) {
403              cpgpt1(this->par.getXOffset()+this->objectList->at(i).getXPeak(),
404                     this->par.getYOffset()+this->objectList->at(i).getYPeak(),
405                     CROSS);
406              label.str("");
407              label << this->objectList->at(i).getID();
408              cpgptxt(this->par.getXOffset()+this->objectList->at(i).getXPeak()-xoff,
409                      this->par.getYOffset()+this->objectList->at(i).getYPeak()-yoff,
410                      0, 0.5, label.str().c_str());
411            }
412          }
413
414        } // end of iplot loop over number of devices
415
416        delete [] momentMap;
417
418      } // end of else (from if(numdetections==0) )
419
420 
421      for(int iplot=0; iplot<numPlots; iplot++){
422        plotList[iplot].goToPlot();
423        plotList[iplot].close();
424      }
425   
426    }
427 
428  }
429
430  /*********************************************************/
431
432  void drawSpectralRange(Plot::SpectralPlot &plot, Detection &obj, FitsHeader &head)
433  {
434    /// @details
435
436    /// A front-end to drawing the lines delimiting the spectral
437    /// extent of the detection. This takes into account the channel
438    /// widths, offsetting outwards by half a channel (for instance, a
439    /// single-channel detection will not have the separation of one
440    /// channel).
441    /// If the world coordinate is being plotted, the correct offset
442    /// is calcuated by transforming from the central spatial
443    /// positions and the offsetted min/max z-pixel extents
444
445    if(head.isWCS()){
446      double x=obj.getXcentre(),y=obj.getYcentre(),z;
447      z=obj.getZmin()-0.5;
448      float vmin=head.pixToVel(x,y,z);
449      z=obj.getZmax()+0.5;
450      float vmax=head.pixToVel(x,y,z);
451      plot.drawVelRange(vmin,vmax);
452    }
453    else{
454      plot.drawVelRange(obj.getZmin()-0.5,obj.getZmax()+0.5);
455    }
456
457
458  }
459
460  void drawSpectralRange(Plot::SimpleSpectralPlot &plot, Detection &obj, FitsHeader &head)
461  {
462    /// @details
463
464    /// A front-end to drawing the lines delimiting the spectral
465    /// extent of the detection. This takes into account the channel
466    /// widths, offsetting outwards by half a channel (for instance, a
467    /// single-channel detection will not have the separation of one
468    /// channel).
469    /// If the world coordinate is being plotted, the correct offset
470    /// is calcuated by transforming from the central spatial
471    /// positions and the offsetted min/max z-pixel extents
472
473    if(head.isWCS()){
474      double x=obj.getXcentre(),y=obj.getYcentre(),z;
475      z=obj.getZmin()-0.5;
476      float vmin=head.pixToVel(x,y,z);
477      z=obj.getZmax()+0.5;
478      float vmax=head.pixToVel(x,y,z);
479      plot.drawVelRange(vmin,vmax);
480    }
481    else{
482      plot.drawVelRange(obj.getZmin()-0.5,obj.getZmax()+0.5);
483    }
484
485  }
486
487
488
489}
490
Note: See TracBrowser for help on using the repository browser.