source: branches/pixel-map-branch/src/Cubes/drawMomentCutout.cc

Last change on this file was 252, checked in by Matthew Whiting, 17 years ago
  • Have put all classes in the files in src/PixelMap/ into a PixelInfo? namespace.
  • Added "using namespace PixelInfo?" to all necessary files.
  • Removed "friend class Detection" from Voxel and Object3D classes -- not necessary and complicated now by them being in the namespace
  • Various minor changes, including fixing up commentary.
File size: 10.8 KB
Line 
1#include <iostream>
2#include <sstream>
3#include <cpgplot.h>
4#include <math.h>
5#include <wcs.h>
6#include <duchamp.hh>
7#include <param.hh>
8#include <Cubes/cubes.hh>
9#include <Utils/utils.hh>
10#include <Utils/mycpgplot.hh>
11#include <PixelMap/Voxel.hh>
12#include <PixelMap/Object3D.hh>
13#include <vector>
14
15const int MIN_WIDTH=20;
16using namespace mycpgplot;
17using namespace PixelInfo;
18
19void Cube::drawMomentCutout(Detection &object)
20{
21  /**
22   *   A routine to draw the 0th moment for the given detection
23   *    using the flux given by the pixel array in the Cube.
24   *   The 0th moment is constructed by adding the flux of each
25   *    pixel within the full extent of the object (this may be more
26   *    pixels than were actually detected in the object)
27   *   A tick mark is also drawn to indicate angular scale (but only
28   *    if the WCS for the Cube is valid).
29   * \param object The Detection to be drawn.
30   */
31
32  if(!cpgtest())
33    duchampError("drawMomentCutout","There is no PGPlot device open!\n");
34  else{
35
36    long size = (object.getXmax()-object.getXmin()+1);
37    if(size<(object.getYmax()-object.getYmin()+1))
38      size = object.getYmax()-object.getYmin()+1;
39    size += MIN_WIDTH;
40
41    long xmin = (object.getXmax()+object.getXmin())/2 - size/2 + 1;
42    long xmax = (object.getXmax()+object.getXmin())/2 + size/2;
43    long ymin = (object.getYmax()+object.getYmin())/2 - size/2 + 1;
44    long ymax = (object.getYmax()+object.getYmin())/2 + size/2;
45    long zmin = object.getZmin();
46    long zmax = object.getZmax();
47
48    float *image = new float[size*size];
49    for(int i=0;i<size*size;i++) image[i]=0.;
50
51    bool *isGood = new bool[size*size];
52    for(int i=0;i<size*size;i++) isGood[i]=true;
53    for(int z=zmin; z<=zmax; z++){
54      for(int x=xmin; x<=xmax; x++){
55        for(int y=ymin; y<=ymax; y++){
56          isGood[(y-ymin) * size + (x-xmin)] =
57            ((x>=0)&&(x<this->axisDim[0]))    // if inside the boundaries
58            && ((y>=0)&&(y<this->axisDim[1])) // if inside the boundaries
59            && !this->isBlank(x,y,z);         // if not blank
60        }
61      }
62    }
63
64    int imPos,cubePos;
65    for(int z=zmin; z<=zmax; z++){
66      for(int x=xmin; x<=xmax; x++){
67        for(int y=ymin; y<=ymax; y++){
68
69          imPos = (y-ymin) * size + (x-xmin);
70          cubePos = z*this->axisDim[0]*this->axisDim[1] +
71            y*this->axisDim[0] + x;
72
73          if(isGood[imPos]) image[imPos] += this->array[cubePos];
74
75        }
76      }
77    }
78
79    for(int i=0;i<size*size;i++){
80      // if there is some signal on this pixel, normalise by the velocity width
81      if(isGood[i]) image[i] /= float(zmax-zmin+1);
82    }
83
84    // now work out the greyscale display limits,
85    //   excluding blank pixels where necessary.
86    float z1,z2,median,madfm;
87    int ct=0;
88    while(!isGood[ct]) ct++; // move to first non-blank pixel
89    z1 = z2 = image[ct];
90    for(int i=1;i<size*size;i++){
91      if(isGood[i]){
92        if(image[i]<z1) z1=image[i];
93        if(image[i]>z2) z2=image[i];
94      }
95    }
96
97    // adjust the values of the blank and extra-image pixels
98    for(int i=0;i<size*size;i++){
99      if(!isGood[i]){
100        if(this->par.getFlagBlankPix()) //blank pixels --> BLANK
101          image[i] = this->par.getBlankPixVal();
102        else        // lies outside image boundary --> black
103          image[i] = z1 - 1.;
104      }
105    }
106
107    float tr[6] = {xmin-1,1.,0.,ymin-1,0.,1.};
108
109    cpgswin(xmin-0.5,xmax-0.5,ymin-0.5,ymax-0.5);
110    cpggray(image, size, size, 1, size, 1, size, z1, z2, tr);
111
112    delete [] image;
113
114    int ci;
115    cpgqci(&ci);
116
117    // Draw the border of the BLANK region, if there is one...
118    this->plotBlankEdges();
119
120    // Draw the border of cube's pixels
121    this->drawFieldEdge();
122
123    // Draw the borders around the object
124    cpgsci(BLUE);
125    cpgsfs(OUTLINE);
126    if(this->par.drawBorders())
127      object.drawBorders(xmin,ymin);
128    else
129      cpgrect(object.getXmin()-xmin+0.5,object.getXmax()-xmin+1.5,
130              object.getYmin()-ymin+0.5,object.getYmax()-ymin+1.5);
131    /*
132      To get the borders localised correctly, we need to subtract (xmin-1)
133      from the X values. We then subtract 0.5 for the left hand border
134      (to place it on the pixel border), and add 0.5 for the right hand
135      border. Similarly for y.
136    */
137
138    if(this->head.isWCS()){
139      // Now draw a tick mark to indicate size -- 15 arcmin in length
140      //     this->drawScale(xmin+2.,ymin+2.,object.getZcentre(),0.25);
141      this->drawScale(xmin+2.,ymin+2.,object.getZcentre());
142    }
143
144    cpgsci(ci);
145
146  }
147
148}
149
150void Cube::drawScale(float xstart, float ystart, float channel)
151{
152  /**
153   *   A routine to draw a scale bar on a (pre-existing) PGPlot image.
154   *   It uses an iterative technique to move from the given start position
155   *    (xstart,ystart) along the positive x-direction so that the length is
156   *    within 1% of the scaleLength (length in degrees), calculated
157   *    according to the pixel scale of the cube.
158   *  \param xstart X-coordinate of the start position (left-hand edge
159   *  of tick mark typically).
160   *  \param ystart Y-coordinate of the start position
161   *  \param channel Which channel to base WCS calculations on: needed
162   *  as the positions could theoretically change with channel.
163   */
164
165  if(!cpgtest())
166    duchampError("drawScale","There is no PGPlot device open!\n");
167  else{
168
169    if(this->head.isWCS()){  // can only do this if the WCS is good!
170
171      enum ANGLE {ARCSEC, ARCMIN, DEGREE};
172      const std::string symbol[3] = {"\"", "'", mycpgplot::degrees };
173      const float angleScale[3] = {3600., 60., 1.};
174      //  degree, arcmin, arcsec symbols
175   
176      const int numLengths = 17;
177      const double lengths[numLengths] =
178        {0.001/3600., 0.005/3600., 0.01/3600., 0.05/3600.,
179         0.1/3600., 0.5/3600.,
180         1./3600., 5./3600., 15./3600., 30./3600.,
181         1./60., 5./60., 15./60., 30./60.,
182         1., 5., 15.};
183      const ANGLE angleType[numLengths] =
184        {ARCSEC, ARCSEC, ARCSEC, ARCSEC,
185         ARCSEC, ARCSEC, ARCSEC, ARCSEC,
186         ARCSEC, ARCSEC,
187         ARCMIN, ARCMIN, ARCMIN, ARCMIN,
188         DEGREE, DEGREE, DEGREE};
189      const float desiredRatio = 0.2;
190
191      // first, work out what is the optimum length of the scale bar,
192      //   based on the pixel scale and size of the image.
193      float pixscale = this->head.getAvPixScale();
194      double *fraction = new double[numLengths];
195      int best;
196      float x1,x2,y1,y2;
197      cpgqwin(&x1,&x2,&y1,&y2);
198      for(int i=0;i<numLengths;i++){
199        fraction[i] = (lengths[i]/pixscale) / (x2-x1);
200        if(i==0) best=0;
201        else if(fabs(fraction[i] - desiredRatio) <
202                fabs(fraction[best] - desiredRatio)) best=i;
203      }
204      delete [] fraction;
205
206      // Now work out actual pixel locations for the ends of the scale bar
207      double *pix1   = new double[3];
208      double *pix2   = new double[3];
209      double *world1 = new double[3];
210      double *world2 = new double[3];
211      pix1[0] = pix2[0] = xstart + this->par.getXOffset();
212      pix1[1] = pix2[1] = ystart + this->par.getYOffset();
213      pix1[2] = pix2[2] = channel;
214      this->head.pixToWCS(pix1,world1);
215
216      double angSep=0.;
217      bool keepGoing=false;
218      double error;
219      double step=1.; // this is in pixels
220      double scaleLength = lengths[best];  // this is in degrees
221      pix2[0] = pix1[0] + scaleLength/(2.*pixscale);
222      do{
223        this->head.pixToWCS(pix2,world2);
224        angSep = angularSeparation(world1[0],world1[1],world2[0],world2[1]);
225        error = (angSep-scaleLength)/scaleLength;
226        if(error<0) error = 0 - error;
227        if(angSep>scaleLength){
228          pix2[0] -= step;
229          step /= 2.;
230        }
231        pix2[0] += step;
232      }while(error>0.05); // look for 1% change
233
234      float tickpt1 = pix1[0] - this->par.getXOffset();
235      float tickpt2 = pix2[0] - this->par.getXOffset();
236      float tickpt3 = ystart;
237      int colour;
238      cpgqci(&colour);
239      cpgsci(RED);
240      int thickness;
241      cpgqlw(&thickness);
242      cpgslw(3);
243      cpgerrx(1,&tickpt1,&tickpt2,&tickpt3,2.);
244      cpgslw(thickness);
245
246      std::stringstream text;
247      text << scaleLength * angleScale[angleType[best]]
248           << symbol[angleType[best]];
249      float size,xch,ych;
250      cpgqch(&size);
251      cpgsch(0.4);
252      cpgqcs(4,&xch,&ych); // get the character size in world coords
253      cpgptxt((tickpt1+tickpt2)/2., ystart+ych, 0, 0.5, text.str().c_str());
254      cpgsch(size);
255      cpgsci(colour);
256
257      delete [] pix1;
258      delete [] pix2;
259      delete [] world1;
260      delete [] world2;
261
262    }
263  }
264
265}
266
267void Detection::drawBorders(int xoffset, int yoffset)
268{
269  /**
270   * For a given object, draw borders around the spatial extent of the object.
271   * \param xoffset The offset from 0 of the x-axis of the plotting window
272   * \param yoffset The offset from 0 of the y-axis of the plotting window
273   */
274  if(!cpgtest())
275    duchampError("drawBorders","There is no PGPlot device open!\n");
276  else{
277
278    float x1,x2,y1,y2;
279    cpgqwin(&x1,&x2,&y1,&y2);
280    int xsize = int(x2 - x1) + 1;
281    int ysize = int(y2 - y1) + 1;
282
283//     std::cerr << xsize << "   " << ysize << "\n";
284
285//     bool *isObj = new bool[xsize*ysize];
286//     for(int i=0;i<xsize*ysize;i++)
287//       isObj[i] = spatmap.isInObject(i%xsize + xoffset, i/xsize + yoffset);
288    std::vector<Voxel> voxlist = this->pixelArray.getPixelSet();
289    std::vector<bool> isObj(xsize*ysize,false);
290    for(int i=0;i<voxlist.size();i++){
291      int pos = (voxlist[i].getX()-xoffset) + (voxlist[i].getY()-yoffset)*xsize;
292      if(pos<xsize*ysize) isObj[pos] = true;
293//       else std::cerr << i<<"/"<<voxlist.size()<< " " <<pos << ": " << voxlist[i]<<"   " << voxlist[i].getX() << " " << voxlist[i].getY()<<"\n";
294    }
295    voxlist.clear();
296   
297//     for(int i=0;i<xsize*ysize;i++)
298//       if(isObj[i]) std::cerr << i%xsize << " " << i/xsize << "\n";
299
300    cpgswin(0,xsize-1,0,ysize-1);
301    for(int x=this->getXmin(); x<=this->getXmax(); x++){
302      // for each column...
303      for(int y=1;y<ysize;y++){
304        int current = y*xsize + (x-xoffset);
305        int previous = (y-1)*xsize + (x-xoffset);
306        if((isObj[current]&&!isObj[previous])   ||
307           (!isObj[current]&&isObj[previous])){
308          cpgmove(x-xoffset+0, y+0);
309          cpgdraw(x-xoffset+1, y+0);
310        }
311      }
312    }
313    for(int y=this->getYmin(); y<=this->getYmax(); y++){
314      // now for each row...
315      for(int x=1;x<xsize;x++){
316        int current = (y-yoffset)*xsize + x;
317        int previous = (y-yoffset)*xsize + x - 1;
318        if((isObj[current]&&!isObj[previous])   ||
319           (!isObj[current]&&isObj[previous])){
320          cpgmove(x+0, y-yoffset+0);
321          cpgdraw(x+0, y-yoffset+1);
322        }
323      }
324    }
325    cpgswin(x1,x2,y1,y2);
326 
327//     delete [] isObj;
328
329  }   
330
331}
332
333void Cube::drawFieldEdge()
334{
335  /**
336   * Draw a border around the spatial edge of the data. Lines are
337   * drawn in yellow at 0 and the values of xdim & ydim.  There must
338   * be a PGPLOT window open, else an error message is returned.
339   */
340  if(!cpgtest())
341    duchampError("drawFieldEdge","There is no PGPlot device open!\n");
342  else{
343    int ci;
344    cpgqci(&ci);
345    cpgsci(YELLOW);
346 
347    cpgmove(-0.5,-0.5);
348    cpgdraw(-0.5,this->axisDim[1]-0.5);
349    cpgdraw(this->axisDim[0]-0.5,this->axisDim[1]-0.5);
350    cpgdraw(this->axisDim[0]-0.5,-0.5);
351    cpgdraw(-0.5,-0.5);
352
353    cpgsci(ci);
354  }
355}
Note: See TracBrowser for help on using the repository browser.