source: tags/release-1.2.2/src/Cubes/plotting.cc

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

Fixing #165 - was ignoring spatial pixels that were blank in the first channel, but this omitted the bright source I was looking at. Removed this criterion (we select only pixels that are detected anyway, so don't need this step).

File size: 18.8 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/Cubes/plots.hh>
45#include <duchamp/Utils/utils.hh>
46#include <duchamp/Utils/mycpgplot.hh>
47
48using namespace mycpgplot;
49using namespace PixelInfo;
50
51namespace duchamp
52{
53
54  void Cube::plotDetectionMap(std::string pgDestination)
55  {
56    ///  @details
57    ///  Creates a map of the spatial locations of the detections, which is
58    ///   written to the PGPlot device given by pgDestination.
59    ///  The map is done in greyscale, where the scale indicates the number of
60    ///   velocity channels that each spatial pixel is detected in.
61    ///  The boundaries of each detection are drawn, and each object is numbered
62    ///   (to match the output list and spectra).
63    ///  The primary grid scale is pixel coordinate, and if the WCS is valid,
64    ///   the correct WCS gridlines are also drawn.
65    /// \param pgDestination The PGPLOT device to be opened, in the typical PGPLOT format.
66
67    // These are the minimum values for the X and Y ranges of the box drawn by
68    //   pgplot (without the half-pixel difference).
69    // The -1 is necessary because the arrays we are dealing with start at 0
70    //  index, while the ranges given in the subsection start at 1...
71    float boxXmin = this->par.getXOffset() - 1;
72    float boxYmin = this->par.getYOffset() - 1;
73
74    long xdim=this->axisDim[0];
75    long ydim=this->axisDim[1];
76    long zdim=this->axisDim[2];
77
78    if( this->numNondegDim == 1){
79     
80      float *specx  = new float[zdim];
81      float *specy  = new float[zdim];
82      float *specy2 = new float[zdim];
83      float *base   = new float[zdim];
84      Plot::SimpleSpectralPlot spPlot;
85      int flag = spPlot.setUpPlot(pgDestination.c_str());
86//       int flag=cpgopen(pgDestination.c_str());
87//       cpgpap(spPlot.getPaperWidth(),spPlot.getAspectRatio());
88//       cpgsch(Plot::spLabelSize);
89      if(flag <= 0){
90        DUCHAMPERROR("Plot Detection Map", "Could not open PGPlot device " << pgDestination);
91      }
92      else{
93       
94        this->getSpectralArrays(-1,specx,specy,specy2,base);
95        float vmax,vmin,width;
96        vmax = vmin = specx[0];
97        for(int i=1;i<zdim;i++){
98          if(specx[i]>vmax) vmax=specx[i];
99          if(specx[i]<vmin) vmin=specx[i];
100        }
101     
102        float max,min;
103        int loc=0;
104        if(this->par.getMinMW()>0) max = min = specy[0];
105        else max = min = specy[this->par.getMaxMW()+1];
106        for(int i=0;i<zdim;i++){
107          if(!this->par.isInMW(i)){
108            if(specy[i]>max) max=specy[i];
109            if(specy[i]<min){
110              min=specy[i];
111              loc = i;
112            }
113          }
114        }
115        // widen the ranges slightly so that the top & bottom & edges don't
116        // lie on the axes.
117        width = max - min;
118        max += width * 0.15;
119        min -= width * 0.05;
120        width = vmax - vmin;
121        vmax += width * 0.01;
122        vmin -= width * 0.01;
123        std::string label,fluxLabel = "Flux ["+this->head.getFluxUnits()+"]";
124        if(this->head.isWCS()){
125          label = this->head.getSpectralDescription() + " [" +
126            this->head.getSpectralUnits() + "]";
127        }
128        else label="Spectral pixel";
129        std::string filename=this->pars().getImageFile();
130        filename = filename.substr(filename.rfind('/')+1);
131        spPlot.label(label,fluxLabel,"Detection summary : " + filename);
132        spPlot.gotoMainSpectrum(vmin,vmax,min,max);
133        cpgline(zdim,specx,specy);
134        if(this->par.getFlagBaseline()){
135          cpgsci(DUCHAMP_BASELINE_SPECTRA_COLOUR);
136          cpgline(zdim,specx,base);
137          cpgsci(FOREGND);
138        }
139        if(this->reconExists){
140          cpgsci(DUCHAMP_RECON_SPECTRA_COLOUR);
141          cpgline(zdim,specx,specy2);   
142          cpgsci(FOREGND);
143        }
144        if(this->par.getFlagMW()){
145          double zval = double(this->par.getMinMW()),zero=0.;
146          double minMWvel = this->head.pixToVel(zero,zero,zval);
147          zval = double(this->par.getMaxMW());
148          double maxMWvel = this->head.pixToVel(zero,zero,zval);
149          spPlot.drawMWRange(minMWvel,maxMWvel);
150        }
151
152        spPlot.markDetectedPixels(this->detectMap,zdim,this->head);
153
154        for(size_t i=0;i<this->getNumObj();i++){
155          drawSpectralRange(spPlot,this->objectList->at(i),this->head);
156        }
157
158        cpgsci(RED);
159        cpgsls(DASHED);
160        float thresh = this->Stats.getThreshold();
161        if(this->par.getFlagNegative()) thresh *= -1.;
162        cpgmove(vmin,thresh);
163        cpgdraw(vmax,thresh);
164        if(this->par.getFlagGrowth()){
165          if(this->par.getFlagUserGrowthThreshold()) thresh= this->par.getGrowthThreshold();
166          else thresh= this->Stats.snrToValue(this->par.getGrowthCut());
167          if(this->par.getFlagNegative()) thresh *= -1.;       
168          cpgsls(DOTTED);
169          cpgmove(vmin,thresh);
170          cpgdraw(vmax,thresh);
171        }
172      }
173
174      spPlot.close();
175    }
176    else { // num dim > 1
177
178      Plot::ImagePlot newplot;
179      int flag = newplot.setUpPlot(pgDestination.c_str(),float(xdim),float(ydim));
180
181      if(flag<=0){
182        DUCHAMPERROR("Plot Detection Map", "Could not open PGPlot device " << pgDestination);
183      }
184      else{
185
186        // get the list of objects that should be plotted. Only applies to outlines and labels.
187        std::vector<bool> objectChoice = this->par.getObjectChoices(this->objectList->size());
188
189        std::string filename=this->pars().getImageFile();
190        newplot.makeTitle(filename.substr(filename.rfind('/')+1,filename.size()));
191
192        newplot.drawMapBox(boxXmin+0.5,boxXmin+xdim+0.5,
193                           boxYmin+0.5,boxYmin+ydim+0.5,
194                           "X pixel","Y pixel");
195
196        //     if(this->objectList.size()>0){
197        // if there are no detections, there will be nothing to plot here
198
199        // Define a float equivalent of this->detectMap that can be plotted by cpggray.
200        // Also find the maximum value, so that we can get the greyscale right and plot a colour wedge.
201        float *detectionMap = new float[xdim*ydim];
202        int maxNum = this->detectMap[0];
203        detectionMap[0] = float(maxNum);
204        for(int pix=1;pix<xdim*ydim;pix++){
205          detectionMap[pix] = float(this->detectMap[pix]); 
206          if(this->detectMap[pix] > maxNum)  maxNum = this->detectMap[pix];
207        }
208
209        if(maxNum>0){ // if there are no detections, it will be 0.
210
211          maxNum = 5 * ((maxNum-1)/5 + 1);  // move to next multiple of 5
212
213          float tr[6] = {boxXmin,1.,0.,boxYmin,0.,1.};
214          cpggray(detectionMap,xdim,ydim,1,xdim,1,ydim,maxNum,0,tr); 
215          cpgbox("bcnst",0.,0,"bcnst",0.,0);
216          cpgsch(1.5);
217          cpgwedg("rg",3.2,2,maxNum,0,"Number of detected channels");
218        }
219
220        delete [] detectionMap;
221     
222        drawBlankEdges(this->array,this->axisDim[0],this->axisDim[1],this->par);
223     
224        if(this->head.isWCS()) this->plotWCSaxes();
225 
226        if(this->objectList->size()>0){
227          // now show and label each detection, drawing over the WCS lines.
228
229          cpgsch(1.0);
230          cpgslw(2);   
231          float xoff=0.;
232          float yoff=newplot.cmToCoord(0.5);
233          if(this->par.drawBorders()){
234            cpgsci(DUCHAMP_OBJECT_OUTLINE_COLOUR);
235            for(size_t i=0;i<this->objectList->size();i++)
236              if(objectChoice[i]) this->objectList->at(i).drawBorders(0,0);
237          }
238          cpgsci(DUCHAMP_ID_TEXT_COLOUR);
239          std::stringstream label;
240          cpgslw(1);
241          for(size_t i=0;i<this->objectList->size();i++){
242            if(objectChoice[i]) {
243              cpgpt1(this->par.getXOffset()+this->objectList->at(i).getXPeak(),
244                     this->par.getYOffset()+this->objectList->at(i).getYPeak(),
245                     CROSS);
246              label.str("");
247              label << this->objectList->at(i).getID();
248              cpgptxt(this->par.getXOffset()+this->objectList->at(i).getXPeak()-xoff,
249                      this->par.getYOffset()+this->objectList->at(i).getYPeak()-yoff,
250                      0, 0.5, label.str().c_str());
251            }
252          }
253
254        }
255
256        newplot.close();
257      }
258    }
259  }
260
261  /*********************************************************/
262
263  void Cube::plotMomentMap(std::string pgDestination)
264  {
265    ///  @details
266    ///  Uses the other function
267    ///  Cube::plotMomentMap(std::vector<std::string>) to plot the moment
268    ///  map.
269    /// \param pgDestination The PGPLOT device that the map is to be written to.
270
271    std::vector<std::string> devicelist;
272    devicelist.push_back(pgDestination);
273    this->plotMomentMap(devicelist);
274  }
275
276  /*********************************************************/
277
278  void Cube::plotMomentMap(std::vector<std::string> pgDestination)
279  {
280    ///  @details
281    ///  Creates a 0th moment map of the detections, which is written to each
282    ///   of the PGPlot devices mentioned in pgDestination.
283    ///  The advantage of this function is that the map is only calculated once,
284    ///   even if multiple maps are required.
285    ///  The map is done in greyscale, where the scale indicates the integrated
286    ///   flux at each spatial pixel.
287    ///  The boundaries of each detection are drawn, and each object is numbered
288    ///   (to match the output list and spectra).
289    ///  The primary grid scale is pixel coordinate, and if the WCS is valid,
290    ///   the correct WCS gridlines are also drawn.
291    /// \param pgDestination A set of PGPLOT devices that are to be
292    /// opened, each in the typical PGPLOT format.
293
294    float boxXmin = this->par.getXOffset() - 1;
295    float boxYmin = this->par.getYOffset() - 1;
296
297    long xdim=this->axisDim[0];
298    long ydim=this->axisDim[1];
299
300    int numPlots = pgDestination.size();
301    std::vector<Plot::ImagePlot> plotList(numPlots);
302    std::vector<int> plotFlag(numPlots,0);
303    std::vector<bool> doPlot(numPlots,false);
304    bool plotNeeded = false;
305
306    for(int i=0;i<numPlots;i++){
307   
308      plotFlag[i] = plotList[i].setUpPlot(pgDestination[i],
309                                          float(xdim),float(ydim));
310       
311      if(plotFlag[i]<=0){
312        DUCHAMPERROR("Plot Moment Map", "Could not open PGPlot device " << pgDestination[i]);
313      }
314      else{
315        doPlot[i] = true;
316        plotNeeded = true;
317      }
318
319    }
320
321    if(plotNeeded){
322
323      if(this->objectList->size()==0){
324        // if there are no detections, we plot an empty field.
325
326        for(int iplot=0; iplot<numPlots; iplot++){
327          plotList[iplot].goToPlot();
328          std::string filename=this->pars().getImageFile();
329          plotList[iplot].makeTitle(filename.substr(filename.rfind('/')+1,filename.size()));
330       
331          plotList[iplot].drawMapBox(boxXmin+0.5,boxXmin+xdim+0.5,
332                                     boxYmin+0.5,boxYmin+ydim+0.5,
333                                     "X pixel","Y pixel");
334       
335          drawBlankEdges(this->array,this->axisDim[0],this->axisDim[1],this->par);
336       
337          if(this->head.isWCS()) this->plotWCSaxes();
338        }
339
340      }
341      else { 
342        // if there are some detections, do the calculations first before
343        //  plotting anything.
344 
345        // get the list of objects that should be plotted. Only applies to outlines and labels.
346        std::vector<bool> objectChoice = this->par.getObjectChoices(this->objectList->size());
347
348        for(int iplot=0; iplot<numPlots; iplot++){
349          // Although plot the axes so that the user knows something is
350          //  being done (at least, they will if there is an /xs plot)
351          plotList[iplot].goToPlot();
352          std::string filename=this->pars().getImageFile();
353          plotList[iplot].makeTitle(filename.substr(filename.rfind('/')+1,filename.size()));
354   
355          plotList[iplot].drawMapBox(boxXmin+0.5,boxXmin+xdim+0.5,
356                                     boxYmin+0.5,boxYmin+ydim+0.5,
357                                     "X pixel","Y pixel");
358       
359          if(pgDestination[iplot]=="/xs")
360            cpgptxt(boxXmin+0.5+xdim/2., boxYmin+0.5+ydim/2., 0, 0.5,
361                    "Calculating map...");
362        }
363
364        float *momentMap = new float[xdim*ydim];
365        float z1=0.,z2=0.;
366        this->getMomentMapForPlot(momentMap,z1,z2);
367
368
369        // Have now done all necessary calculations for moment map.
370        // Now produce the plot
371
372        for(int iplot=0; iplot<numPlots; iplot++){
373          plotList[iplot].goToPlot();
374     
375          float tr[6] = {boxXmin,1.,0.,boxYmin,0.,1.};
376          cpggray(momentMap,xdim,ydim,1,xdim,1,ydim,z2,z1,tr);
377          cpgbox("bcnst",0.,0,"bcnst",0.,0);
378          cpgsch(1.5);
379          std::string wedgeLabel = "Integrated Flux ";
380          if(this->par.getFlagNegative())
381            wedgeLabel = "-1. " + times + " " + wedgeLabel;
382          if(this->head.isWCS())
383            wedgeLabel += "[" + this->head.getIntFluxUnits() + "]";
384          else wedgeLabel += "[" + this->head.getFluxUnits() + "]";
385          cpgwedglog("rg",3.2,2,z2,z1,wedgeLabel.c_str());
386
387
388          drawBlankEdges(this->array,this->axisDim[0],this->axisDim[1],this->par);
389
390          if(this->head.isWCS()) this->plotWCSaxes();
391 
392          // now show and label each detection, drawing over the WCS lines.
393          cpgsch(1.0);
394          cpgslw(2);
395          float xoff=0.;
396          float yoff=plotList[iplot].cmToCoord(0.5);
397          if(this->par.drawBorders()){
398            cpgsci(DUCHAMP_OBJECT_OUTLINE_COLOUR);
399            for(size_t i=0;i<this->objectList->size();i++)
400              if(objectChoice[i]) this->objectList->at(i).drawBorders(0,0);
401          }
402          cpgsci(DUCHAMP_ID_TEXT_COLOUR);
403          std::stringstream label;
404          cpgslw(1);
405          for(size_t i=0;i<this->objectList->size();i++){
406            if(objectChoice[i]) {
407              cpgpt1(this->par.getXOffset()+this->objectList->at(i).getXPeak(),
408                     this->par.getYOffset()+this->objectList->at(i).getYPeak(),
409                     CROSS);
410              label.str("");
411              label << this->objectList->at(i).getID();
412              cpgptxt(this->par.getXOffset()+this->objectList->at(i).getXPeak()-xoff,
413                      this->par.getYOffset()+this->objectList->at(i).getYPeak()-yoff,
414                      0, 0.5, label.str().c_str());
415            }
416          }
417
418        } // end of iplot loop over number of devices
419
420        delete [] momentMap;
421
422      } // end of else (from if(numdetections==0) )
423
424 
425      for(int iplot=0; iplot<numPlots; iplot++){
426        plotList[iplot].goToPlot();
427        plotList[iplot].close();
428      }
429   
430    }
431 
432  }
433
434  /*********************************************************/
435
436//   void Cube::plotWCSaxes(int textColour, int axisColour)
437//   {
438//     duchamp::plotWCSaxes(this->head.getWCS(), this->axisDim, textColour, axisColour);
439//   }
440 
441 
442  void plotWCSaxes(struct wcsprm *wcs, size_t *axes, int textColour, int axisColour)
443  {
444
445    /// @details
446    ///  A front-end to the cpgsbox command, to draw the gridlines for the WCS
447    ///    over the current plot.
448    ///  Lines are drawn in dark green over the full plot area, and the axis
449    ///    labels are written on the top and on the right hand sides, so as not
450    ///    to conflict with other labels.
451    ///  \param textColour The colour index to use for the text labels --
452    ///  defaults to duchamp::DUCHAMP_ID_TEXT_COLOUR
453    ///  \param axisColour The colour index to use for the axes --
454    ///  defaults to duchamp::DUCHAMP_WCS_AXIS_COLOUR
455
456    float boxXmin=0,boxYmin=0;
457
458    char idents[3][80], opt[2], nlcprm[1];
459
460    strcpy(idents[0], wcs->lngtyp);
461    strcpy(idents[1], wcs->lattyp);
462    strcpy(idents[2], "");
463    if(strcmp(wcs->lngtyp,"RA")==0) opt[0] = 'G';
464    else opt[0] = 'D';
465    opt[1] = 'E';
466
467    float  blc[2], trc[2];
468    //   float  scl; // --> unused here.
469    blc[0] = boxXmin + 0.5;
470    blc[1] = boxYmin + 0.5;
471    trc[0] = boxXmin + axes[0]+0.5;
472    trc[1] = boxYmin + axes[1]+0.5;
473 
474    int existingLineWidth;
475    cpgqlw(&existingLineWidth);
476    int existingColour;
477    cpgqci(&existingColour);
478    float existingSize;
479    cpgqch(&existingSize);
480    cpgsci(textColour);
481    cpgsch(0.8);
482    int    c0[7], ci[7], gcode[2], ic, ierr;
483    for(int i=0;i<7;i++) c0[i] = -1;
484    /* define the WCS axes colour */
485    setWCSGreen();
486
487    gcode[0] = 2;  // type of grid to draw: 0=none, 1=ticks only, 2=full grid
488    gcode[1] = 2;
489
490    double cache[257][4], grid1[9], grid2[9], nldprm[8];
491    grid1[0] = 0.0; 
492    grid2[0] = 0.0;
493
494    // Draw the celestial grid with no intermediate tick marks.
495    // Set LABCTL=2100 to write 1st coord on top, and 2nd on right
496
497    //Colour indices used by cpgsbox: make it all the same colour for thin
498    // line case.
499    ci[0] = axisColour; // grid lines, coord 1
500    ci[1] = axisColour; // grid lines, coord 2
501    ci[2] = axisColour; // numeric labels, coord 1
502    ci[3] = axisColour; // numeric labels, coord 2
503    ci[4] = axisColour; // axis annotation, coord 1
504    ci[5] = axisColour; // axis annotation, coord 2
505    ci[6] = axisColour; // title
506
507    cpgsbox(blc, trc, idents, opt, 2100, 0, ci, gcode, 0.0, 0, grid1, 0, grid2,
508            0, pgwcsl_, 1, WCSLEN, 1, nlcprm, (int *)wcs,
509            nldprm, 256, &ic, cache, &ierr);
510
511    cpgsci(existingColour);
512    cpgsch(existingSize);
513    cpgslw(existingLineWidth);
514  }
515
516
517
518  void drawSpectralRange(Plot::SpectralPlot &plot, Detection &obj, FitsHeader &head)
519  {
520    /// @details
521
522    /// A front-end to drawing the lines delimiting the spectral
523    /// extent of the detection. This takes into account the channel
524    /// widths, offsetting outwards by half a channel (for instance, a
525    /// single-channel detection will not have the separation of one
526    /// channel).
527    /// If the world coordinate is being plotted, the correct offset
528    /// is calcuated by transforming from the central spatial
529    /// positions and the offsetted min/max z-pixel extents
530
531    if(head.isWCS()){
532      double x=obj.getXcentre(),y=obj.getYcentre(),z;
533      z=obj.getZmin()-0.5;
534      float vmin=head.pixToVel(x,y,z);
535      z=obj.getZmax()+0.5;
536      float vmax=head.pixToVel(x,y,z);
537      plot.drawVelRange(vmin,vmax);
538    }
539    else{
540      plot.drawVelRange(obj.getZmin()-0.5,obj.getZmax()+0.5);
541    }
542
543
544  }
545
546  void drawSpectralRange(Plot::SimpleSpectralPlot &plot, Detection &obj, FitsHeader &head)
547  {
548    /// @details
549
550    /// A front-end to drawing the lines delimiting the spectral
551    /// extent of the detection. This takes into account the channel
552    /// widths, offsetting outwards by half a channel (for instance, a
553    /// single-channel detection will not have the separation of one
554    /// channel).
555    /// If the world coordinate is being plotted, the correct offset
556    /// is calcuated by transforming from the central spatial
557    /// positions and the offsetted min/max z-pixel extents
558
559    if(head.isWCS()){
560      double x=obj.getXcentre(),y=obj.getYcentre(),z;
561      z=obj.getZmin()-0.5;
562      float vmin=head.pixToVel(x,y,z);
563      z=obj.getZmax()+0.5;
564      float vmax=head.pixToVel(x,y,z);
565      plot.drawVelRange(vmin,vmax);
566    }
567    else{
568      plot.drawVelRange(obj.getZmin()-0.5,obj.getZmax()+0.5);
569    }
570
571  }
572
573
574
575}
576
Note: See TracBrowser for help on using the repository browser.