source: tags/release-1.1.7/src/Cubes/plotting.cc @ 533

Last change on this file since 533 was 528, checked in by MatthewWhiting, 15 years ago

Changing the documentation comments to match the askapsoft style. Also have split ChanMap? and Object3D into separate files.

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