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

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

Large range of changes -- related to image cutouts and spectral units mostly.

duchamp.hh -- changed name of spectral type, and introduced one for frequency.
Cubes/getImage.cc -- changed the way the spectral type and spectral units are

looked at. Now is able to use frequency units (defaults
to MHz) when there isn't a good velocity transformation
possible (ie. if there is no restfreq defined).

Cubes/drawMomentCutout.cc -- Range of changes here:

  • improved the way unwanted pixels (those BLANK and outside image coundaries) are dealt with in the image array in drawMomentCutout
  • removed the way the blank pixel information was changed. Now behaves in a consistent way whether or not there are blank pixels
  • improved call to plotBlankEdges()
  • added call to drawFieldEdge() -- a new function that draws a yellow line around the boundary of the pixels of the image.
  • improved the tick mark that shows the angular scale of the cutout. Now adaptable to any pixel scale.
  • added calls to cpgtest() to all functions

Also these files were changed in relation to these edits:
Utils/drawBlankEdges.cc -- improved execution, with "blank" array.
Cubes/cubes.cc -- added call to Param::drawBlankEdge in Cube::plotBlankEdges()
Cubes/outputSpectra.cc -- moved spectra away from left and right axes.
param.cc -- added necessary calls for the new parameter. Other minor changes

to formatting. Added a missed call to stringize.

mainDuchamp.cc -- added #include <param.hh>
param.hh -- Added a new parameter: blankEdge
Cubes/cubes.hh -- improved appearance of comments
Cubes/plots.hh -- improved appearance of comments
InputComplete? -- added new parameter.
docs/Guide.tex -- added text about blank edge plotting.
All six images were changed as well.

CHANGES -- some of these changes -- not up to date yet!

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