source: trunk/src/Cubes/drawMomentCutout.cc @ 203

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