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
RevLine 
[299]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// -----------------------------------------------------------------------
[3]29#include <iostream>
30#include <iomanip>
31#include <sstream>
32#include <math.h>
33#include <cpgplot.h>
[401]34#include <wcslib/cpgsbox.h>
35#include <wcslib/pgwcsl.h>
[394]36#include <wcslib/wcs.h>
[393]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>
[463]42#include <duchamp/Cubes/cubeUtils.hh>
[393]43#include <duchamp/Cubes/plots.hh>
44#include <duchamp/Utils/utils.hh>
45#include <duchamp/Utils/mycpgplot.hh>
[3]46
[146]47using namespace mycpgplot;
[258]48using namespace PixelInfo;
[3]49
[378]50namespace duchamp
51{
52
[232]53void Cube::plotDetectionMap(std::string pgDestination)
[3]54{
55  /**
[220]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.
[222]64   * \param pgDestination The PGPLOT device to be opened, in the typical PGPLOT format.
[3]65   */
66
[186]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...
[3]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;
[103]77  int flag = newplot.setUpPlot(pgDestination.c_str(),float(xdim),float(ydim));
[3]78
[103]79  if(flag<=0){
[293]80    duchampError("Plot Detection Map",
[186]81                 "Could not open PGPlot device " + pgDestination + ".\n");
[103]82  }
83  else{
[3]84
[103]85    newplot.makeTitle(this->pars().getImageFile());
[3]86
[186]87    newplot.drawMapBox(boxXmin+0.5,boxXmin+xdim+0.5,
88                       boxYmin+0.5,boxYmin+ydim+0.5,
89                       "X pixel","Y pixel");
[3]90
[208]91    //     if(this->objectList.size()>0){
92    // if there are no detections, there will be nothing to plot here
[3]93
[208]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    }
[3]101
[208]102    if(maxNum>0){ // if there are no detections, it will be 0.
[3]103
[204]104      maxNum = 5 * ((maxNum-1)/5 + 1);  // move to next multiple of 5
105
[103]106      float tr[6] = {boxXmin,1.,0.,boxYmin,0.,1.};
107      cpggray(detectMap,xdim,ydim,1,xdim,1,ydim,maxNum,0,tr); 
[208]108       
109      //       delete [] detectMap;
[103]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    }
[208]114    delete [] detectMap;
115     
[316]116    drawBlankEdges(this->array,this->axisDim[0],this->axisDim[1],this->par);
[208]117     
[103]118    if(this->head.isWCS()) this->plotWCSaxes();
[3]119 
[291]120    if(this->objectList->size()>0){
[186]121      // now show and label each detection, drawing over the WCS lines.
[3]122
[103]123      cpgsch(1.0);
124      cpgslw(2);   
[186]125      float xoff=0.;
126      float yoff=newplot.cmToCoord(0.5);
[103]127      if(this->par.drawBorders()){
[316]128        cpgsci(DUCHAMP_OBJECT_OUTLINE_COLOUR);
[291]129        for(int i=0;i<this->objectList->size();i++)
130          this->objectList->at(i).drawBorders(0,0);
[103]131      }
[316]132      cpgsci(DUCHAMP_ID_TEXT_COLOUR);
[103]133      std::stringstream label;
134      cpgslw(1);
[291]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(),
[146]138               CROSS);
[103]139        label.str("");
[291]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,
[103]143                0, 0.5, label.str().c_str());
144      }
145
[3]146    }
147
[103]148    cpgclos();
[3]149  }
150}
151
152/*********************************************************/
153
[232]154void Cube::plotMomentMap(std::string pgDestination)
[3]155{
156  /**
[290]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.
[3]161   */
162
[290]163  std::vector<std::string> devicelist;
164  devicelist.push_back(pgDestination);
165  this->plotMomentMap(devicelist);
[3]166}
167
168/*********************************************************/
169
[232]170void Cube::plotMomentMap(std::vector<std::string> pgDestination)
[203]171{
172  /**
[220]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.
[222]183   * \param pgDestination A set of PGPLOT devices that are to be
184   * opened, each in the typical PGPLOT format.
[203]185   */
[3]186
[203]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();
[232]195  std::vector<Plot::ImagePlot> plotList(numPlots);
196  std::vector<int> plotFlag(numPlots,0);
197  std::vector<bool> doPlot(numPlots,false);
[203]198  bool plotNeeded = false;
199
200  for(int i=0;i<numPlots;i++){
201   
[485]202    plotFlag[i] = plotList[i].setUpPlot(pgDestination[i],
[203]203                                        float(xdim),float(ydim));
204       
[293]205    if(plotFlag[i]<=0) duchampError("Plot Moment Map",
[203]206                                    "Could not open PGPlot device "
207                                    + pgDestination[i] + ".\n");
208    else{
209      doPlot[i] = true;
210      plotNeeded = true;
211    }
[485]212
[203]213  }
214
215  if(plotNeeded){
216
[291]217    if(this->objectList->size()==0){
[203]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       
[316]228        drawBlankEdges(this->array,this->axisDim[0],this->axisDim[1],this->par);
[203]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;
[291]255      for(int i=0;i<this->objectList->size();i++){
[258]256        std::vector<Voxel> voxlist =
[291]257          this->objectList->at(i).pixels().getPixelSet();
[258]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();
[203]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);
[232]349        std::string wedgeLabel = "Integrated Flux ";
[203]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
[316]358        drawBlankEdges(this->array,this->axisDim[0],this->axisDim[1],this->par);
[203]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()){
[316]368          cpgsci(DUCHAMP_OBJECT_OUTLINE_COLOUR);
[291]369          for(int i=0;i<this->objectList->size();i++)
370            this->objectList->at(i).drawBorders(0,0);
[203]371        }
[316]372        cpgsci(DUCHAMP_ID_TEXT_COLOUR);
[203]373        std::stringstream label;
374        cpgslw(1);
[291]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(),
[203]378                 CROSS);
379          label.str("");
[291]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,
[203]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
[459]405void Cube::plotWCSaxes(int textColour, int axisColour)
[3]406{
407  /**
[220]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.
[459]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
[3]417   */
418
[203]419  float boxXmin=0,boxYmin=0;
[3]420
421  char idents[3][80], opt[2], nlcprm[1];
[103]422
[204]423  struct wcsprm *tempwcs;
[103]424  tempwcs = this->head.getWCS();
425
426  strcpy(idents[0], tempwcs->lngtyp);
427  strcpy(idents[1], tempwcs->lattyp);
[3]428  strcpy(idents[2], "");
[103]429  if(strcmp(tempwcs->lngtyp,"RA")==0) opt[0] = 'G';
[3]430  else opt[0] = 'D';
431  opt[1] = 'E';
432
[270]433  float  blc[2], trc[2];
434  //   float  scl; // --> unused here.
[3]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 
[459]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);
[3]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;
[316]451  /* define the WCS axes colour */
452  setWCSGreen();
[3]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
[83]461  // Draw the celestial grid with no intermediate tick marks.
[3]462  // Set LABCTL=2100 to write 1st coord on top, and 2nd on right
[146]463
[186]464  //Colour indices used by cpgsbox: make it all the same colour for thin
465  // line case.
[459]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
[3]473
474  cpgsbox(blc, trc, idents, opt, 2100, 0, ci, gcode, 0.0, 0, grid1, 0, grid2,
[103]475          0, pgwcsl_, 1, WCSLEN, 1, nlcprm, (int *)tempwcs,
476          nldprm, 256, &ic, cache, &ierr);
[3]477
[103]478  wcsfree(tempwcs);
[309]479  free(tempwcs);
[103]480
[459]481  cpgsci(existingColour);
482  cpgsch(existingSize);
483  cpgslw(existingLineWidth);
[3]484}
485
[378]486}
487
Note: See TracBrowser for help on using the repository browser.