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

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

Moving the two drawSpectralRange functions from spectraUtils.cc to plotting.cc - they need the pgplot-related functions, and if pgplot wasn't enabled they were causing build errors where they were.

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