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

Last change on this file since 463 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
RevLine 
[299]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// -----------------------------------------------------------------------
[3]28#include <iostream>
[103]29#include <sstream>
[3]30#include <cpgplot.h>
31#include <math.h>
[394]32#include <wcslib/wcs.h>
[393]33#include <duchamp/duchamp.hh>
34#include <duchamp/param.hh>
35#include <duchamp/fitsHeader.hh>
36#include <duchamp/Cubes/cubes.hh>
[463]37#include <duchamp/Cubes/cubeUtils.hh>
[393]38#include <duchamp/Utils/utils.hh>
39#include <duchamp/Utils/mycpgplot.hh>
40#include <duchamp/PixelMap/Voxel.hh>
41#include <duchamp/PixelMap/Object3D.hh>
[258]42#include <vector>
[3]43
[142]44const int MIN_WIDTH=20;
[146]45using namespace mycpgplot;
[258]46using namespace PixelInfo;
[142]47
[378]48namespace duchamp
[3]49{
[83]50
[378]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     */
[117]63
[378]64    if(!cpgtest())
65      duchampError("Draw Cutout","There is no PGPlot device open!\n");
66    else{
[3]67
[378]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;
[3]72
[378]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          }
[142]90        }
[137]91      }
92
[378]93      float *image = new float[size*size];
94      for(int i=0;i<size*size;i++) image[i]=0.;
[285]95
[378]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++){
[137]100
[378]101            imPos = (y-ymin) * size + (x-xmin);
102            cubePos = z*this->axisDim[0]*this->axisDim[1] +
103              y*this->axisDim[0] + x;
[137]104
[378]105            if(isGood[imPos]) image[imPos] += this->array[cubePos];
[137]106
[378]107          }
[142]108        }
[3]109      }
110
[378]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      }
[3]115
[378]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        }
[142]127      }
[3]128
[378]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.;
[3]132
[285]133
[378]134      float tr[6] = {xmin-1,1.,0.,ymin-1,0.,1.};
[3]135
[378]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);
[3]140
[378]141      delete [] image;
[137]142
[378]143      int ci;
144      cpgqci(&ci);
[137]145
[378]146      // Draw the border of the BLANK region, if there is one...
147      drawBlankEdges(this->array,this->axisDim[0],this->axisDim[1],this->par);
[3]148
[378]149      // Draw the border of cube's pixels
150      this->drawFieldEdge();
[3]151
[378]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      */
[3]166
[378]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      }
[142]172
[378]173      cpgsci(ci);
[142]174
[378]175      delete [] isGood;
[309]176
[378]177    }
178
[135]179  }
[117]180
[378]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     */
[3]195
[378]196    if(!cpgtest())
197      duchampError("Draw Cutout","There is no PGPlot device open!\n");
198    else{
[83]199
[378]200      if(this->head.isWCS()){  // can only do this if the WCS is good!
[83]201
[378]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
[142]206   
[378]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;
[142]221
[378]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;
[83]236
[378]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);
[83]246
[378]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
[83]263
[378]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);
[83]275
[378]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);
[142]286
[378]287        delete [] pix1;
288        delete [] pix2;
289        delete [] world1;
290        delete [] world2;
[142]291
[378]292      }
[146]293    }
[378]294
[142]295  }
[431]296 
[378]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{
[83]307
[378]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;
[3]312
[431]313      std::vector<int> vertexSet = this->getVertexSet();
314
[378]315      cpgswin(0,xsize-1,0,ysize-1);
[431]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);
[142]323        }
[3]324      }
[378]325      cpgswin(x1,x2,y1,y2);
[3]326 
[378]327    }   
[142]328
[378]329  }
[142]330
[378]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);
[142]344 
[378]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);
[142]350
[378]351      cpgsci(ci);
352    }
[142]353  }
[378]354
[142]355}
Note: See TracBrowser for help on using the repository browser.