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

Last change on this file since 459 was 459, checked in by MatthewWhiting, 16 years ago

Modify the WCS axes plotting function to allow different colours to be used. The Duchamp colours are set as defaults.

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