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

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

Some minor changes, primarily affecting the text written to screen.

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