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

Last change on this file since 894 was 884, checked in by MatthewWhiting, 12 years ago

A large set of changes aimed at making the use of indexing variables consistent. We have moved to size_t as much as possible to represent the location in memory. This includes making the dimension array within DataArray? and derived classes an array of size_t variables. Still plenty of compilation warnings (principally comparing signed and unsigned variables) - these will need to be cleaned up.

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