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

Last change on this file was 536, checked in by MatthewWhiting, 15 years ago

Including the recent minor changes to 1.1.7.

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(uint 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(uint 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(uint i=0;i<this->objectList->size();i++){
253        std::vector<Voxel> voxlist =
254          this->objectList->at(i).pixels().getPixelSet();
255        for(uint 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(uint 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(uint 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.