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

Last change on this file since 582 was 581, checked in by MatthewWhiting, 15 years ago

Tweaking the WCS axes plotting functions, and making it easier to call from other functions.

File size: 15.8 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
53  void 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(unsigned 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(unsigned 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
153  void 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
168  void 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(unsigned int i=0;i<this->objectList->size();i++){
253          std::vector<Voxel> voxlist = this->objectList->at(i).getPixelSet();
254          //      this->objectList->at(i).pixels().getPixelSet();
255          for(unsigned int 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          if(!this->isBlank(pix)){ // only do this for non-blank pixels. Judge this by the first pixel of the channel.
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   
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(unsigned 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(unsigned 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//   void Cube::plotWCSaxes(int textColour, int axisColour)
405//   {
406//     duchamp::plotWCSaxes(this->head.getWCS(), this->axisDim, textColour, axisColour);
407//   }
408 
409 
410  void plotWCSaxes(struct wcsprm *wcs, long *axes, int textColour, int axisColour)
411  {
412
413    /// @details
414    ///  A front-end to the cpgsbox command, to draw the gridlines for the WCS
415    ///    over the current plot.
416    ///  Lines are drawn in dark green over the full plot area, and the axis
417    ///    labels are written on the top and on the right hand sides, so as not
418    ///    to conflict with other labels.
419    ///  \param textColour The colour index to use for the text labels --
420    ///  defaults to duchamp::DUCHAMP_ID_TEXT_COLOUR
421    ///  \param axisColour The colour index to use for the axes --
422    ///  defaults to duchamp::DUCHAMP_WCS_AXIS_COLOUR
423
424    float boxXmin=0,boxYmin=0;
425
426    char idents[3][80], opt[2], nlcprm[1];
427
428//     struct wcsprm *tempwcs;
429//     tempwcs = this->head.getWCS();
430
431    strcpy(idents[0], wcs->lngtyp);
432    strcpy(idents[1], wcs->lattyp);
433    strcpy(idents[2], "");
434    if(strcmp(wcs->lngtyp,"RA")==0) opt[0] = 'G';
435    else opt[0] = 'D';
436    opt[1] = 'E';
437
438    float  blc[2], trc[2];
439    //   float  scl; // --> unused here.
440    blc[0] = boxXmin + 0.5;
441    blc[1] = boxYmin + 0.5;
442//     trc[0] = boxXmin + this->axisDim[0]+0.5;
443//     trc[1] = boxYmin + this->axisDim[1]+0.5;
444    trc[0] = boxXmin + axes[0]+0.5;
445    trc[1] = boxYmin + axes[1]+0.5;
446 
447    int existingLineWidth;
448    cpgqlw(&existingLineWidth);
449    int existingColour;
450    cpgqci(&existingColour);
451    float existingSize;
452    cpgqch(&existingSize);
453    //  cpgsci(DUCHAMP_ID_TEXT_COLOUR);
454    cpgsci(textColour);
455    cpgsch(0.8);
456    int    c0[7], ci[7], gcode[2], ic, ierr;
457    for(int i=0;i<7;i++) c0[i] = -1;
458    /* define the WCS axes colour */
459    setWCSGreen();
460
461    gcode[0] = 2;  // type of grid to draw: 0=none, 1=ticks only, 2=full grid
462    gcode[1] = 2;
463
464    double cache[257][4], grid1[9], grid2[9], nldprm[8];
465    grid1[0] = 0.0; 
466    grid2[0] = 0.0;
467
468    // Draw the celestial grid with no intermediate tick marks.
469    // Set LABCTL=2100 to write 1st coord on top, and 2nd on right
470
471    //Colour indices used by cpgsbox: make it all the same colour for thin
472    // line case.
473    ci[0] = axisColour; // grid lines, coord 1
474    ci[1] = axisColour; // grid lines, coord 2
475    ci[2] = axisColour; // numeric labels, coord 1
476    ci[3] = axisColour; // numeric labels, coord 2
477    ci[4] = axisColour; // axis annotation, coord 1
478    ci[5] = axisColour; // axis annotation, coord 2
479    ci[6] = axisColour; // title
480
481    cpgsbox(blc, trc, idents, opt, 2100, 0, ci, gcode, 0.0, 0, grid1, 0, grid2,
482            0, pgwcsl_, 1, WCSLEN, 1, nlcprm, (int *)wcs,
483            nldprm, 256, &ic, cache, &ierr);
484
485//     wcsfree(wcs);
486//     free(wcs);
487
488    cpgsci(existingColour);
489    cpgsch(existingSize);
490    cpgslw(existingLineWidth);
491  }
492
493
494
495}
496
Note: See TracBrowser for help on using the repository browser.