source: tags/release-1.0.7/src/Cubes/drawMomentCutout.cc

Last change on this file was 213, checked in by Matthew Whiting, 18 years ago

Summary:

  • Fixed the scale bar plotting for the spectral output, so that it can deal properly with very fine angular scales.
    • Improved the loop in Cube::drawScale
    • Included code in angularSeparation to deal with finely-separated positions.
  • Moved the ProgressBar? class to a new header file Utils/feedback.hh from duchamp.hh
    • Updated #include statements in many files
  • Fixed allocation bug in param copy constructor (related to offsets array).
File size: 9.5 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 = 17;
172      const double lengths[numLengths] =
173        {0.001/3600., 0.005/3600., 0.01/3600., 0.05/3600.,
174         0.1/3600., 0.5/3600.,
175         1./3600., 5./3600., 15./3600., 30./3600.,
176         1./60., 5./60., 15./60., 30./60.,
177         1., 5., 15.};
178      const ANGLE angleType[numLengths] =
179        {ARCSEC, ARCSEC, ARCSEC, ARCSEC,
180         ARCSEC, ARCSEC, ARCSEC, ARCSEC,
181         ARCSEC, ARCSEC,
182         ARCMIN, ARCMIN, ARCMIN, ARCMIN,
183         DEGREE, DEGREE, DEGREE};
184      const float desiredRatio = 0.2;
185
186      // first, work out what is the optimum length of the scale bar,
187      //   based on the pixel scale and size of the image.
188      float pixscale = this->head.getAvPixScale();
189      double *fraction = new double[numLengths];
190      int best;
191      float x1,x2,y1,y2;
192      cpgqwin(&x1,&x2,&y1,&y2);
193      for(int i=0;i<numLengths;i++){
194        fraction[i] = (lengths[i]/pixscale) / (x2-x1);
195        if(i==0) best=0;
196        else if(fabs(fraction[i] - desiredRatio) <
197                fabs(fraction[best] - desiredRatio)) best=i;
198      }
199      delete [] fraction;
200
201      // Now work out actual pixel locations for the ends of the scale bar
202      double *pix1   = new double[3];
203      double *pix2   = new double[3];
204      double *world1 = new double[3];
205      double *world2 = new double[3];
206      pix1[0] = pix2[0] = xstart + this->par.getXOffset();
207      pix1[1] = pix2[1] = ystart + this->par.getYOffset();
208      pix1[2] = pix2[2] = channel;
209      this->head.pixToWCS(pix1,world1);
210
211      double angSep=0.;
212      bool keepGoing=false;
213      double error;
214      double step=1.; // this is in pixels
215      double scaleLength = lengths[best];  // this is in degrees
216      pix2[0] = pix1[0] + scaleLength/(2.*pixscale);
217      do{
218        this->head.pixToWCS(pix2,world2);
219        angSep = angularSeparation(world1[0],world1[1],world2[0],world2[1]);
220        error = (angSep-scaleLength)/scaleLength;
221        if(error<0) error = 0 - error;
222        if(angSep>scaleLength){
223          pix2[0] -= step;
224          step /= 2.;
225        }
226        pix2[0] += step;
227      }while(error>0.05); // look for 1% change
228
229      float tickpt1 = pix1[0] - this->par.getXOffset();
230      float tickpt2 = pix2[0] - this->par.getXOffset();
231      float tickpt3 = ystart;
232      int colour;
233      cpgqci(&colour);
234      cpgsci(RED);
235      int thickness;
236      cpgqlw(&thickness);
237      cpgslw(3);
238      cpgerrx(1,&tickpt1,&tickpt2,&tickpt3,2.);
239      cpgslw(thickness);
240
241      std::stringstream text;
242      text << scaleLength * angleScale[angleType[best]]
243           << symbol[angleType[best]];
244      float size,xch,ych;
245      cpgqch(&size);
246      cpgsch(0.4);
247      cpgqcs(4,&xch,&ych); // get the character size in world coords
248      cpgptxt((tickpt1+tickpt2)/2., ystart+ych, 0, 0.5, text.str().c_str());
249      cpgsch(size);
250      cpgsci(colour);
251
252      delete [] pix1;
253      delete [] pix2;
254      delete [] world1;
255      delete [] world2;
256
257    }
258  }
259
260}
261
262void Detection::drawBorders(int xoffset, int yoffset)
263{
264
265  if(!cpgtest())
266    duchampError("drawBorders","There is no PGPlot device open!\n");
267  else{
268
269    float x1,x2,y1,y2;
270    cpgqwin(&x1,&x2,&y1,&y2);
271    int xsize = int(x2 - x1) + 1;
272    int ysize = int(y2 - y1) + 1;
273
274    bool *isObj = new bool[xsize*ysize];
275    for(int i=0;i<xsize*ysize;i++) isObj[i]=false;
276    for(int i=0;i<this->pix.size();i++)
277      isObj[ (this->pix[i].getY()-yoffset)*xsize + (this->pix[i].getX()-xoffset) ] = true;
278 
279
280    cpgswin(0,xsize-1,0,ysize-1);
281    for(int x=this->xmin; x<=this->xmax; x++){
282      // for each column...
283      for(int y=1;y<ysize;y++){
284        int current = y*xsize + (x-xoffset);
285        int previous = (y-1)*xsize + (x-xoffset);
286        if((isObj[current]&&!isObj[previous])||(!isObj[current]&&isObj[previous])){
287          cpgmove(x-xoffset+0, y+0);
288          cpgdraw(x-xoffset+1, y+0);
289        }
290      }
291    }
292    for(int y=this->ymin; y<=this->ymax; y++){
293      // now for each row...
294      for(int x=1;x<xsize;x++){
295        int current = (y-yoffset)*xsize + x;
296        int previous = (y-yoffset)*xsize + x - 1;
297        if((isObj[current]&&!isObj[previous])||(!isObj[current]&&isObj[previous])){
298          cpgmove(x+0, y-yoffset+0);
299          cpgdraw(x+0, y-yoffset+1);
300        }
301      }
302    }
303    cpgswin(x1,x2,y1,y2);
304 
305    delete [] isObj;
306
307  }   
308
309}
310
311void Cube::drawFieldEdge()
312{
313  if(!cpgtest())
314    duchampError("drawFieldEdge","There is no PGPlot device open!\n");
315  else{
316    int ci;
317    cpgqci(&ci);
318    cpgsci(YELLOW);
319 
320    cpgmove(-0.5,-0.5);
321    cpgdraw(-0.5,this->axisDim[1]-0.5);
322    cpgdraw(this->axisDim[0]-0.5,this->axisDim[1]-0.5);
323    cpgdraw(this->axisDim[0]-0.5,-0.5);
324    cpgdraw(-0.5,-0.5);
325
326    cpgsci(ci);
327  }
328}
Note: See TracBrowser for help on using the repository browser.