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

Last change on this file since 621 was 617, checked in by MatthewWhiting, 15 years ago

A new change to plotting, so that only the selected objects are plotted on the large moment map (when using the objectList option). This way the moment map isn't
cluttered by objects that aren't in the final list of detections.

File size: 16.1 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 <cpgplot.h>
34#include <wcslib/cpgsbox.h>
35#include <wcslib/pgwcsl.h>
36#include <wcslib/wcs.h>
37#include <duchamp/duchamp.hh>
38#include <duchamp/param.hh>
39#include <duchamp/fitsHeader.hh>
40#include <duchamp/PixelMap/Object3D.hh>
41#include <duchamp/Cubes/cubes.hh>
42#include <duchamp/Cubes/cubeUtils.hh>
43#include <duchamp/Cubes/plots.hh>
44#include <duchamp/Utils/utils.hh>
45#include <duchamp/Utils/mycpgplot.hh>
46
47using namespace mycpgplot;
48using namespace PixelInfo;
49
50namespace duchamp
51{
52
53  void Cube::plotDetectionMap(std::string pgDestination)
54  {
55    ///  @details
56    ///  Creates a map of the spatial locations of the detections, which is
57    ///   written to the PGPlot device given by pgDestination.
58    ///  The map is done in greyscale, where the scale indicates the number of
59    ///   velocity channels that each spatial pixel is detected in.
60    ///  The boundaries of each detection are drawn, and each object is numbered
61    ///   (to match the output list and spectra).
62    ///  The primary grid scale is pixel coordinate, and if the WCS is valid,
63    ///   the correct WCS gridlines are also drawn.
64    /// \param pgDestination The PGPLOT device to be opened, in the typical PGPLOT format.
65
66    // These are the minimum values for the X and Y ranges of the box drawn by
67    //   pgplot (without the half-pixel difference).
68    // The -1 is necessary because the arrays we are dealing with start at 0
69    //  index, while the ranges given in the subsection start at 1...
70    float boxXmin = this->par.getXOffset() - 1;
71    float boxYmin = this->par.getYOffset() - 1;
72
73    long xdim=this->axisDim[0];
74    long ydim=this->axisDim[1];
75    Plot::ImagePlot newplot;
76    int flag = newplot.setUpPlot(pgDestination.c_str(),float(xdim),float(ydim));
77
78    if(flag<=0){
79      duchampError("Plot Detection Map",
80                   "Could not open PGPlot device " + pgDestination + ".\n");
81    }
82    else{
83
84      // get the list of objects that should be plotted. Only applies to outlines and labels.
85      std::vector<bool> objectChoice = this->par.getObjectChoices(this->objectList->size());
86
87      newplot.makeTitle(this->pars().getImageFile());
88
89      newplot.drawMapBox(boxXmin+0.5,boxXmin+xdim+0.5,
90                         boxYmin+0.5,boxYmin+ydim+0.5,
91                         "X pixel","Y pixel");
92
93      //     if(this->objectList.size()>0){
94      // if there are no detections, there will be nothing to plot here
95
96      float *detectMap = new float[xdim*ydim];
97      int maxNum = this->detectMap[0];
98      detectMap[0] = float(maxNum);
99      for(int pix=1;pix<xdim*ydim;pix++){
100        detectMap[pix] = float(this->detectMap[pix]); 
101        if(this->detectMap[pix] > maxNum)  maxNum = this->detectMap[pix];
102      }
103
104      if(maxNum>0){ // if there are no detections, it will be 0.
105
106        maxNum = 5 * ((maxNum-1)/5 + 1);  // move to next multiple of 5
107
108        float tr[6] = {boxXmin,1.,0.,boxYmin,0.,1.};
109        cpggray(detectMap,xdim,ydim,1,xdim,1,ydim,maxNum,0,tr); 
110       
111        //       delete [] detectMap;
112        cpgbox("bcnst",0.,0,"bcnst",0.,0);
113        cpgsch(1.5);
114        cpgwedg("rg",3.2,2,maxNum,0,"Number of detected channels");
115      }
116      delete [] detectMap;
117     
118      drawBlankEdges(this->array,this->axisDim[0],this->axisDim[1],this->par);
119     
120      if(this->head.isWCS()) this->plotWCSaxes();
121 
122      if(this->objectList->size()>0){
123        // now show and label each detection, drawing over the WCS lines.
124
125        cpgsch(1.0);
126        cpgslw(2);   
127        float xoff=0.;
128        float yoff=newplot.cmToCoord(0.5);
129        if(this->par.drawBorders()){
130          cpgsci(DUCHAMP_OBJECT_OUTLINE_COLOUR);
131          for(unsigned int i=0;i<this->objectList->size();i++)
132            if(objectChoice[i]) this->objectList->at(i).drawBorders(0,0);
133        }
134        cpgsci(DUCHAMP_ID_TEXT_COLOUR);
135        std::stringstream label;
136        cpgslw(1);
137        for(unsigned int i=0;i<this->objectList->size();i++){
138          if(objectChoice[i]) {
139            cpgpt1(this->par.getXOffset()+this->objectList->at(i).getXPeak(),
140                   this->par.getYOffset()+this->objectList->at(i).getYPeak(),
141                   CROSS);
142            label.str("");
143            label << this->objectList->at(i).getID();
144            cpgptxt(this->par.getXOffset()+this->objectList->at(i).getXPeak()-xoff,
145                    this->par.getYOffset()+this->objectList->at(i).getYPeak()-yoff,
146                    0, 0.5, label.str().c_str());
147          }
148        }
149
150      }
151
152      cpgclos();
153    }
154  }
155
156  /*********************************************************/
157
158  void Cube::plotMomentMap(std::string pgDestination)
159  {
160    ///  @details
161    ///  Uses the other function
162    ///  Cube::plotMomentMap(std::vector<std::string>) to plot the moment
163    ///  map.
164    /// \param pgDestination The PGPLOT device that the map is to be written to.
165
166    std::vector<std::string> devicelist;
167    devicelist.push_back(pgDestination);
168    this->plotMomentMap(devicelist);
169  }
170
171  /*********************************************************/
172
173  void Cube::plotMomentMap(std::vector<std::string> pgDestination)
174  {
175    ///  @details
176    ///  Creates a 0th moment map of the detections, which is written to each
177    ///   of the PGPlot devices mentioned in pgDestination.
178    ///  The advantage of this function is that the map is only calculated once,
179    ///   even if multiple maps are required.
180    ///  The map is done in greyscale, where the scale indicates the integrated
181    ///   flux at each spatial pixel.
182    ///  The boundaries of each detection are drawn, and each object is numbered
183    ///   (to match the output list and spectra).
184    ///  The primary grid scale is pixel coordinate, and if the WCS is valid,
185    ///   the correct WCS gridlines are also drawn.
186    /// \param pgDestination A set of PGPLOT devices that are to be
187    /// opened, each in the typical PGPLOT format.
188
189    float boxXmin = this->par.getXOffset() - 1;
190    float boxYmin = this->par.getYOffset() - 1;
191
192    long xdim=this->axisDim[0];
193    long ydim=this->axisDim[1];
194    long zdim=this->axisDim[2];
195
196    int numPlots = pgDestination.size();
197    std::vector<Plot::ImagePlot> plotList(numPlots);
198    std::vector<int> plotFlag(numPlots,0);
199    std::vector<bool> doPlot(numPlots,false);
200    bool plotNeeded = false;
201
202    for(int i=0;i<numPlots;i++){
203   
204      plotFlag[i] = plotList[i].setUpPlot(pgDestination[i],
205                                          float(xdim),float(ydim));
206       
207      if(plotFlag[i]<=0) duchampError("Plot Moment Map",
208                                      "Could not open PGPlot device "
209                                      + pgDestination[i] + ".\n");
210      else{
211        doPlot[i] = true;
212        plotNeeded = true;
213      }
214
215    }
216
217    if(plotNeeded){
218
219      if(this->objectList->size()==0){
220        // if there are no detections, we plot an empty field.
221
222        for(int iplot=0; iplot<numPlots; iplot++){
223          plotList[iplot].goToPlot();
224          plotList[iplot].makeTitle(this->pars().getImageFile());
225       
226          plotList[iplot].drawMapBox(boxXmin+0.5,boxXmin+xdim+0.5,
227                                     boxYmin+0.5,boxYmin+ydim+0.5,
228                                     "X pixel","Y pixel");
229       
230          drawBlankEdges(this->array,this->axisDim[0],this->axisDim[1],this->par);
231       
232          if(this->head.isWCS()) this->plotWCSaxes();
233        }
234
235      }
236      else { 
237        // if there are some detections, do the calculations first before
238        //  plotting anything.
239 
240        // get the list of objects that should be plotted. Only applies to outlines and labels.
241        std::vector<bool> objectChoice = this->par.getObjectChoices(this->objectList->size());
242
243        for(int iplot=0; iplot<numPlots; iplot++){
244          // Although plot the axes so that the user knows something is
245          //  being done (at least, they will if there is an /xs plot)
246          plotList[iplot].goToPlot();
247          plotList[iplot].makeTitle(this->pars().getImageFile());
248   
249          plotList[iplot].drawMapBox(boxXmin+0.5,boxXmin+xdim+0.5,
250                                     boxYmin+0.5,boxYmin+ydim+0.5,
251                                     "X pixel","Y pixel");
252       
253          if(pgDestination[iplot]=="/xs")
254            cpgptxt(boxXmin+0.5+xdim/2., boxYmin+0.5+ydim/2., 0, 0.5,
255                    "Calculating map...");
256        }
257
258        bool *isObj = new bool[xdim*ydim*zdim];
259        for(int i=0;i<xdim*ydim*zdim;i++) isObj[i] = false;
260        for(unsigned int i=0;i<this->objectList->size();i++){
261          if(objectChoice[i]){
262            std::vector<Voxel> this->objectList->at(i).pixels().getPixelSet();
263            for(unsigned int p=0;p<voxlist.size();p++){
264              int pixelpos = voxlist[p].getX() + xdim*voxlist[p].getY() +
265                xdim*ydim*voxlist[p].getZ();
266              isObj[pixelpos] = true;
267            }
268          }
269        }
270
271        float *momentMap = new float[xdim*ydim];
272        // Initialise to zero
273        for(int i=0;i<xdim*ydim;i++) momentMap[i] = 0.;
274
275        // if we are looking for negative features, we need to invert the
276        //  detected pixels for the moment map
277        float sign = 1.;
278        if(this->pars().getFlagNegative()) sign = -1.;
279
280        float deltaVel;
281        double x,y;
282
283        double *zArray  = new double[zdim];
284        for(int z=0; z<zdim; z++) zArray[z] = double(z);
285   
286        double *world  = new double[zdim];
287
288        for(int pix=0; pix<xdim*ydim; pix++){
289
290          x = double(pix%xdim);
291          y = double(pix/xdim);
292
293          if(!this->isBlank(pix)){ // only do this for non-blank pixels. Judge this by the first pixel of the channel.
294
295            delete [] world;
296            world = this->head.pixToVel(x,y,zArray,zdim);
297     
298            for(int z=0; z<zdim; z++){     
299              int pos =  z*xdim*ydim + pix;  // the voxel in the cube
300              if(isObj[pos]){ // if it's an object pixel...
301                // delta-vel is half the distance between adjacent channels.
302                // if at end, then just use 0-1 or (zdim-1)-(zdim-2) distance
303                if(z==0){
304                  if(zdim==1) deltaVel=1.; // pathological case -- if 2D image.
305                  else deltaVel = world[z+1] - world[z];
306                }
307                else if(z==(zdim-1)) deltaVel = world[z-1] - world[z];
308                else deltaVel = (world[z+1] - world[z-1]) / 2.;
309
310                momentMap[pix] += sign * this->array[pos] * fabs(deltaVel);
311
312              }
313            }
314          }
315
316        }
317   
318        delete [] world;
319        delete [] zArray;
320
321        float *temp = new float[xdim*ydim];
322        int count=0;
323        for(int i=0;i<xdim*ydim;i++) {
324          if(momentMap[i]>0.){
325            bool addPixel = false;
326            for(int z=0;z<zdim;z++) addPixel = addPixel || isObj[z*xdim*ydim+i];
327            if(addPixel) temp[count++] = log10(momentMap[i]);
328          }
329        }
330        float z1,z2;
331        z1 = z2 = temp[0];
332        for(int i=1;i<count;i++){
333          if(temp[i]<z1) z1 = temp[i];
334          if(temp[i]>z2) z2 = temp[i];
335        }
336        delete [] temp;
337
338        for(int i=0;i<xdim*ydim;i++) {
339          bool addPixel = false;
340          for(int z=0;z<zdim;z++) addPixel = addPixel || isObj[z*xdim*ydim+i];
341          addPixel = addPixel && (momentMap[i]>0.);
342          if(!addPixel) momentMap[i] = z1-1.;
343          else momentMap[i] = log10(momentMap[i]);
344        }
345
346        delete [] isObj;
347
348        // Have now done all necessary calculations for moment map.
349        // Now produce the plot
350
351        for(int iplot=0; iplot<numPlots; iplot++){
352          plotList[iplot].goToPlot();
353     
354          float tr[6] = {boxXmin,1.,0.,boxYmin,0.,1.};
355          cpggray(momentMap,xdim,ydim,1,xdim,1,ydim,z2,z1,tr);
356          cpgbox("bcnst",0.,0,"bcnst",0.,0);
357          cpgsch(1.5);
358          std::string wedgeLabel = "Integrated Flux ";
359          if(this->par.getFlagNegative())
360            wedgeLabel = "-1. " + times + " " + wedgeLabel;
361          if(this->head.isWCS())
362            wedgeLabel += "[" + this->head.getIntFluxUnits() + "]";
363          else wedgeLabel += "[" + this->head.getFluxUnits() + "]";
364          cpgwedglog("rg",3.2,2,z2,z1,wedgeLabel.c_str());
365
366
367          drawBlankEdges(this->array,this->axisDim[0],this->axisDim[1],this->par);
368
369          if(this->head.isWCS()) this->plotWCSaxes();
370 
371          // now show and label each detection, drawing over the WCS lines.
372          cpgsch(1.0);
373          cpgslw(2);
374          float xoff=0.;
375          float yoff=plotList[iplot].cmToCoord(0.5);
376          if(this->par.drawBorders()){
377            cpgsci(DUCHAMP_OBJECT_OUTLINE_COLOUR);
378            for(unsigned int i=0;i<this->objectList->size();i++)
379              if(objectChoice[i]) this->objectList->at(i).drawBorders(0,0);
380          }
381          cpgsci(DUCHAMP_ID_TEXT_COLOUR);
382          std::stringstream label;
383          cpgslw(1);
384          for(unsigned int i=0;i<this->objectList->size();i++){
385            if(objectChoice[i]) {
386              cpgpt1(this->par.getXOffset()+this->objectList->at(i).getXPeak(),
387                     this->par.getYOffset()+this->objectList->at(i).getYPeak(),
388                     CROSS);
389              label.str("");
390              label << this->objectList->at(i).getID();
391              cpgptxt(this->par.getXOffset()+this->objectList->at(i).getXPeak()-xoff,
392                      this->par.getYOffset()+this->objectList->at(i).getYPeak()-yoff,
393                      0, 0.5, label.str().c_str());
394            }
395          }
396
397        } // end of iplot loop over number of devices
398
399        delete [] momentMap;
400
401      } // end of else (from if(numdetections==0) )
402
403 
404      for(int iplot=0; iplot<numPlots; iplot++){
405        plotList[iplot].goToPlot();
406        cpgclos();
407      }
408   
409    }
410 
411  }
412
413  /*********************************************************/
414
415//   void Cube::plotWCSaxes(int textColour, int axisColour)
416//   {
417//     duchamp::plotWCSaxes(this->head.getWCS(), this->axisDim, textColour, axisColour);
418//   }
419 
420 
421  void plotWCSaxes(struct wcsprm *wcs, long *axes, int textColour, int axisColour)
422  {
423
424    /// @details
425    ///  A front-end to the cpgsbox command, to draw the gridlines for the WCS
426    ///    over the current plot.
427    ///  Lines are drawn in dark green over the full plot area, and the axis
428    ///    labels are written on the top and on the right hand sides, so as not
429    ///    to conflict with other labels.
430    ///  \param textColour The colour index to use for the text labels --
431    ///  defaults to duchamp::DUCHAMP_ID_TEXT_COLOUR
432    ///  \param axisColour The colour index to use for the axes --
433    ///  defaults to duchamp::DUCHAMP_WCS_AXIS_COLOUR
434
435    float boxXmin=0,boxYmin=0;
436
437    char idents[3][80], opt[2], nlcprm[1];
438
439    strcpy(idents[0], wcs->lngtyp);
440    strcpy(idents[1], wcs->lattyp);
441    strcpy(idents[2], "");
442    if(strcmp(wcs->lngtyp,"RA")==0) opt[0] = 'G';
443    else opt[0] = 'D';
444    opt[1] = 'E';
445
446    float  blc[2], trc[2];
447    //   float  scl; // --> unused here.
448    blc[0] = boxXmin + 0.5;
449    blc[1] = boxYmin + 0.5;
450    trc[0] = boxXmin + axes[0]+0.5;
451    trc[1] = boxYmin + axes[1]+0.5;
452 
453    int existingLineWidth;
454    cpgqlw(&existingLineWidth);
455    int existingColour;
456    cpgqci(&existingColour);
457    float existingSize;
458    cpgqch(&existingSize);
459    cpgsci(textColour);
460    cpgsch(0.8);
461    int    c0[7], ci[7], gcode[2], ic, ierr;
462    for(int i=0;i<7;i++) c0[i] = -1;
463    /* define the WCS axes colour */
464    setWCSGreen();
465
466    gcode[0] = 2;  // type of grid to draw: 0=none, 1=ticks only, 2=full grid
467    gcode[1] = 2;
468
469    double cache[257][4], grid1[9], grid2[9], nldprm[8];
470    grid1[0] = 0.0; 
471    grid2[0] = 0.0;
472
473    // Draw the celestial grid with no intermediate tick marks.
474    // Set LABCTL=2100 to write 1st coord on top, and 2nd on right
475
476    //Colour indices used by cpgsbox: make it all the same colour for thin
477    // line case.
478    ci[0] = axisColour; // grid lines, coord 1
479    ci[1] = axisColour; // grid lines, coord 2
480    ci[2] = axisColour; // numeric labels, coord 1
481    ci[3] = axisColour; // numeric labels, coord 2
482    ci[4] = axisColour; // axis annotation, coord 1
483    ci[5] = axisColour; // axis annotation, coord 2
484    ci[6] = axisColour; // title
485
486    cpgsbox(blc, trc, idents, opt, 2100, 0, ci, gcode, 0.0, 0, grid1, 0, grid2,
487            0, pgwcsl_, 1, WCSLEN, 1, nlcprm, (int *)wcs,
488            nldprm, 256, &ic, cache, &ierr);
489
490    cpgsci(existingColour);
491    cpgsch(existingSize);
492    cpgslw(existingLineWidth);
493  }
494
495
496
497}
498
Note: See TracBrowser for help on using the repository browser.