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

Last change on this file since 521 was 463, checked in by MatthewWhiting, 16 years ago

Changes aimed at calculating the w50 and w20 parameters, and printing them out.

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