source: trunk/src/Cubes/drawMomentCutout.cc

Last change on this file was 1393, checked in by MatthewWhiting, 10 years ago

A large changeset that moves all use of bool arrays (ie. bool *) to std::vector<bool>. This will be a bit safer, plus potentially cheaper in memory usage. Interfaces to all stats functions with masks have changed accordingly, as well as the Gaussian smoothing functions and some spectral utility functions.

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