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

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

Changes aimed at calculating the w50 and w20 parameters, and printing them out.

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