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

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