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

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

Ticket #193 - Removing all the MW-related code. Most of it was commented out, but Param now no longer has anything referring to MW. The flag array in ObjectGrower? has also changed to FLAG from MW.

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/Plotting/SpectralPlot.hh>
46#include <duchamp/Plotting/SimpleSpectralPlot.hh>
47#include <duchamp/Plotting/ImagePlot.hh>
48#include <duchamp/Utils/utils.hh>
49#include <duchamp/Utils/mycpgplot.hh>
50
51using namespace mycpgplot;
52using namespace PixelInfo;
53
54namespace duchamp
55{
56
57  void Cube::plotDetectionMap(std::string pgDestination)
58  {
59    ///  @details
60    ///  Creates a map of the spatial locations of the detections, which is
61    ///   written to the PGPlot device given by pgDestination.
62    ///  The map is done in greyscale, where the scale indicates the number of
63    ///   velocity channels that each spatial pixel is detected in.
64    ///  The boundaries of each detection are drawn, and each object is numbered
65    ///   (to match the output list and spectra).
66    ///  The primary grid scale is pixel coordinate, and if the WCS is valid,
67    ///   the correct WCS gridlines are also drawn.
68    /// \param pgDestination The PGPLOT device to be opened, in the typical PGPLOT format.
69
70    // These are the minimum values for the X and Y ranges of the box drawn by
71    //   pgplot (without the half-pixel difference).
72    // The -1 is necessary because the arrays we are dealing with start at 0
73    //  index, while the ranges given in the subsection start at 1...
74    float boxXmin = this->par.getXOffset() - 1;
75    float boxYmin = this->par.getYOffset() - 1;
76
77    long xdim=this->axisDim[0];
78    long ydim=this->axisDim[1];
79    long zdim=this->axisDim[2];
80
81    if( this->numNondegDim == 1){
82     
83      float *specx  = new float[zdim];
84      float *specy  = new float[zdim];
85      float *specy2 = new float[zdim];
86      float *base   = new float[zdim];
87      Plot::SimpleSpectralPlot spPlot;
88      int flag = spPlot.setUpPlot(pgDestination.c_str());
89//       int flag=cpgopen(pgDestination.c_str());
90//       cpgpap(spPlot.getPaperWidth(),spPlot.getAspectRatio());
91//       cpgsch(Plot::spLabelSize);
92      if(flag <= 0){
93        DUCHAMPERROR("Plot Detection Map", "Could not open PGPlot device " << pgDestination);
94      }
95      else{
96       
97//      std::vector<bool> flaggedChans=this->par.getChannelFlags(zdim);
98        this->getSpectralArrays(-1,specx,specy,specy2,base);
99        float vmax,vmin,width;
100        vmax = vmin = specx[0];
101        for(int i=1;i<zdim;i++){
102          if(specx[i]>vmax) vmax=specx[i];
103          if(specx[i]<vmin) vmin=specx[i];
104        }
105     
106        // Find the maximum & minimum values of the spectrum, ignoring flagged channels.
107        float max,min;
108        bool haveStarted=false;
109        for(int z=0;z<zdim;z++){
110//          if(!flaggedChans[z]){
111            if(!this->par.isFlaggedChannel(z)){
112                if(specy[z]>max && !haveStarted) max=specy[z];
113                if(specy[z]<min && !haveStarted) min=specy[z];
114                haveStarted=true;
115            }
116        }
117
118        // widen the ranges slightly so that the top & bottom & edges don't
119        // lie on the axes.
120        width = max - min;
121        max += width * 0.15;
122        min -= width * 0.05;
123        width = vmax - vmin;
124        vmax += width * 0.01;
125        vmin -= width * 0.01;
126        std::string label,fluxLabel = "Flux ["+this->head.getFluxUnits()+"]";
127        if(this->head.isWCS()){
128          label = this->head.getSpectralDescription() + " [" +
129            this->head.getSpectralUnits() + "]";
130        }
131        else label="Spectral pixel";
132        std::string filename=this->pars().getImageFile();
133        filename = filename.substr(filename.rfind('/')+1);
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 Cube::plotWCSaxes(int textColour, int axisColour)
433//   {
434//     duchamp::plotWCSaxes(this->head.getWCS(), this->axisDim, textColour, axisColour);
435//   }
436 
437 
438  void plotWCSaxes(struct wcsprm *wcs, size_t *axes, int textColour, int axisColour)
439  {
440
441    /// @details
442    ///  A front-end to the cpgsbox command, to draw the gridlines for the WCS
443    ///    over the current plot.
444    ///  Lines are drawn in dark green over the full plot area, and the axis
445    ///    labels are written on the top and on the right hand sides, so as not
446    ///    to conflict with other labels.
447    ///  \param textColour The colour index to use for the text labels --
448    ///  defaults to duchamp::DUCHAMP_ID_TEXT_COLOUR
449    ///  \param axisColour The colour index to use for the axes --
450    ///  defaults to duchamp::DUCHAMP_WCS_AXIS_COLOUR
451
452    float boxXmin=0,boxYmin=0;
453
454    char idents[3][80], opt[2], nlcprm[1];
455
456    strcpy(idents[0], wcs->lngtyp);
457    strcpy(idents[1], wcs->lattyp);
458    strcpy(idents[2], "");
459    if(strcmp(wcs->lngtyp,"RA")==0) opt[0] = 'G';
460    else opt[0] = 'D';
461    opt[1] = 'E';
462
463    float  blc[2], trc[2];
464    //   float  scl; // --> unused here.
465    blc[0] = boxXmin + 0.5;
466    blc[1] = boxYmin + 0.5;
467    trc[0] = boxXmin + axes[0]+0.5;
468    trc[1] = boxYmin + axes[1]+0.5;
469 
470    int existingLineWidth;
471    cpgqlw(&existingLineWidth);
472    int existingColour;
473    cpgqci(&existingColour);
474    float existingSize;
475    cpgqch(&existingSize);
476    cpgsci(textColour);
477    cpgsch(0.8);
478    int    c0[7], ci[7], gcode[2], ic, ierr;
479    for(int i=0;i<7;i++) c0[i] = -1;
480    /* define the WCS axes colour */
481    setWCSGreen();
482
483    gcode[0] = 2;  // type of grid to draw: 0=none, 1=ticks only, 2=full grid
484    gcode[1] = 2;
485
486    double cache[257][4], grid1[9], grid2[9], nldprm[8];
487    grid1[0] = 0.0; 
488    grid2[0] = 0.0;
489
490    // Draw the celestial grid with no intermediate tick marks.
491    // Set LABCTL=2100 to write 1st coord on top, and 2nd on right
492
493    //Colour indices used by cpgsbox: make it all the same colour for thin
494    // line case.
495    ci[0] = axisColour; // grid lines, coord 1
496    ci[1] = axisColour; // grid lines, coord 2
497    ci[2] = axisColour; // numeric labels, coord 1
498    ci[3] = axisColour; // numeric labels, coord 2
499    ci[4] = axisColour; // axis annotation, coord 1
500    ci[5] = axisColour; // axis annotation, coord 2
501    ci[6] = axisColour; // title
502
503    cpgsbox(blc, trc, idents, opt, 2100, 0, ci, gcode, 0.0, 0, grid1, 0, grid2,
504            0, pgwcsl_, 1, WCSLEN, 1, nlcprm, (int *)wcs,
505            nldprm, 256, &ic, cache, &ierr);
506
507    cpgsci(existingColour);
508    cpgsch(existingSize);
509    cpgslw(existingLineWidth);
510  }
511
512
513
514  void drawSpectralRange(Plot::SpectralPlot &plot, Detection &obj, FitsHeader &head)
515  {
516    /// @details
517
518    /// A front-end to drawing the lines delimiting the spectral
519    /// extent of the detection. This takes into account the channel
520    /// widths, offsetting outwards by half a channel (for instance, a
521    /// single-channel detection will not have the separation of one
522    /// channel).
523    /// If the world coordinate is being plotted, the correct offset
524    /// is calcuated by transforming from the central spatial
525    /// positions and the offsetted min/max z-pixel extents
526
527    if(head.isWCS()){
528      double x=obj.getXcentre(),y=obj.getYcentre(),z;
529      z=obj.getZmin()-0.5;
530      float vmin=head.pixToVel(x,y,z);
531      z=obj.getZmax()+0.5;
532      float vmax=head.pixToVel(x,y,z);
533      plot.drawVelRange(vmin,vmax);
534    }
535    else{
536      plot.drawVelRange(obj.getZmin()-0.5,obj.getZmax()+0.5);
537    }
538
539
540  }
541
542  void drawSpectralRange(Plot::SimpleSpectralPlot &plot, Detection &obj, FitsHeader &head)
543  {
544    /// @details
545
546    /// A front-end to drawing the lines delimiting the spectral
547    /// extent of the detection. This takes into account the channel
548    /// widths, offsetting outwards by half a channel (for instance, a
549    /// single-channel detection will not have the separation of one
550    /// channel).
551    /// If the world coordinate is being plotted, the correct offset
552    /// is calcuated by transforming from the central spatial
553    /// positions and the offsetted min/max z-pixel extents
554
555    if(head.isWCS()){
556      double x=obj.getXcentre(),y=obj.getYcentre(),z;
557      z=obj.getZmin()-0.5;
558      float vmin=head.pixToVel(x,y,z);
559      z=obj.getZmax()+0.5;
560      float vmax=head.pixToVel(x,y,z);
561      plot.drawVelRange(vmin,vmax);
562    }
563    else{
564      plot.drawVelRange(obj.getZmin()-0.5,obj.getZmax()+0.5);
565    }
566
567  }
568
569
570
571}
572
Note: See TracBrowser for help on using the repository browser.