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

Last change on this file since 1160 was 1133, checked in by MatthewWhiting, 11 years ago

Ticket #132 - New code to fit the ellipse to the moment-0 map thresholded at half its peak - this then provides FWHM esimates for major/minor axes. Also have adaptive units for these axes, scaling to get the numbers not too small and adjusting the units accordingly. 2D images also have the shape calculated too now.

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