source: branches/OptimisedGrowerTesting/src/Cubes/drawMomentCutout.cc @ 1441

Last change on this file since 1441 was 1293, checked in by MatthewWhiting, 11 years ago

Ticket #200 - Largely resolved. The getVertexSet function now resides in Object3D, and returns a set of Voxel vectors, that describe the bounding vertices of the object (in 2D projection). Changes also made to the functions that call this, and to the verification outputs that are affected (notably the .ann and .reg files).

File size: 10.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 <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(),
155                 object.getMajorAxis()/this->head.getAvPixScale()/2./scale, object.getMinorAxis()/this->head.getAvPixScale()/2./scale,
156                 90.+object.getPositionAngle());
157
158      // Draw the beam
159      if(this->head.beam().isDefined()){
160          setDarkGreen();
161          cpgsci(DARKGREEN);
162          cpgellipse(xmax-0.5-this->head.beam().maj(), ymin-0.5+this->head.beam().maj(),
163                     this->head.beam().maj()/2., this->head.beam().min()/2., this->head.beam().pa()+90.);
164      }
165
166      // Draw the borders around the object
167      cpgsci(DUCHAMP_OBJECT_OUTLINE_COLOUR);
168      cpgsfs(OUTLINE);
169      if(this->par.drawBorders())
170        object.drawBorders(xmin,ymin);
171      else
172        cpgrect(object.getXmin()-xmin+0.5,object.getXmax()-xmin+1.5,
173                object.getYmin()-ymin+0.5,object.getYmax()-ymin+1.5);
174      /*
175        To get the borders localised correctly, we need to subtract (xmin-1)
176        from the X values. We then subtract 0.5 for the left hand border
177        (to place it on the pixel border), and add 0.5 for the right hand
178        border. Similarly for y.
179      */
180
181      if(this->head.isWCS()){
182        // Now draw a tick mark to indicate size -- 15 arcmin in length
183        //     this->drawScale(xmin+2.,ymin+2.,object.getZcentre(),0.25);
184        this->drawScale(xmin+2.,ymin+2.,object.getZcentre());
185      }
186
187      cpgsci(ci);
188
189      delete [] isGood;
190
191    }
192
193  }
194
195  void Cube::drawScale(float xstart, float ystart, float channel)
196  {
197    ///  @details
198    ///   A routine to draw a scale bar on a (pre-existing) PGPlot image.
199    ///   It uses an iterative technique to move from the given start position
200    ///    (xstart,ystart) along the positive x-direction so that the length is
201    ///    within 1% of the scaleLength (length in degrees), calculated
202    ///    according to the pixel scale of the cube.
203    ///  \param xstart X-coordinate of the start position (left-hand edge
204    ///  of tick mark typically).
205    ///  \param ystart Y-coordinate of the start position
206    ///  \param channel Which channel to base WCS calculations on: needed
207    ///  as the positions could theoretically change with channel.
208
209    if(!cpgtest()){
210      DUCHAMPERROR("Draw Cutout","There is no PGPlot device open.");
211    }
212    else{
213
214      if(this->head.isWCS()){  // can only do this if the WCS is good!
215
216        enum ANGLE {ARCSEC, ARCMIN, DEGREE};
217        const std::string symbol[3] = {"\"", "'", mycpgplot::degrees };
218        const float angleScale[3] = {3600., 60., 1.};
219        //  degree, arcmin, arcsec symbols
220   
221        const int numLengths = 17;
222        const double lengths[numLengths] =
223          {0.001/3600., 0.005/3600., 0.01/3600., 0.05/3600.,
224           0.1/3600., 0.5/3600.,
225           1./3600., 5./3600., 15./3600., 30./3600.,
226           1./60., 5./60., 15./60., 30./60.,
227           1., 5., 15.};
228        const ANGLE angleType[numLengths] =
229          {ARCSEC, ARCSEC, ARCSEC, ARCSEC,
230           ARCSEC, ARCSEC, ARCSEC, ARCSEC,
231           ARCSEC, ARCSEC,
232           ARCMIN, ARCMIN, ARCMIN, ARCMIN,
233           DEGREE, DEGREE, DEGREE};
234        const float desiredRatio = 0.2;
235
236        // first, work out what is the optimum length of the scale bar,
237        //   based on the pixel scale and size of the image.
238        float pixscale = this->head.getAvPixScale();
239        double *fraction = new double[numLengths];
240        int best=0;
241        float x1,x2,y1,y2;
242        cpgqwin(&x1,&x2,&y1,&y2);
243        for(int i=0;i<numLengths;i++){
244          fraction[i] = (lengths[i]/pixscale) / (x2-x1);
245          if(i==0) best=0;
246          else if(fabs(fraction[i] - desiredRatio) <
247                  fabs(fraction[best] - desiredRatio)) best=i;
248        }
249        delete [] fraction;
250
251        // Now work out actual pixel locations for the ends of the scale bar
252        double *pix1   = new double[3];
253        double *pix2   = new double[3];
254        double *world1 = new double[3];
255        double *world2 = new double[3];
256        pix1[0] = pix2[0] = xstart;
257        pix1[1] = pix2[1] = ystart;
258        pix1[2] = pix2[2] = channel;
259        this->head.pixToWCS(pix1,world1);
260
261        double angSep=0.;
262        double error;
263        double step=1.; // this is in pixels
264        double scaleLength = lengths[best];  // this is in degrees
265        pix2[0] = pix1[0] + scaleLength/(2.*pixscale);
266        do{
267          this->head.pixToWCS(pix2,world2);
268          angSep = angularSeparation(world1[0],world1[1],world2[0],world2[1]);
269          error = (angSep-scaleLength)/scaleLength;
270          if(error<0) error = 0 - error;
271          if(angSep>scaleLength){
272            pix2[0] -= step;
273            step /= 2.;
274          }
275          pix2[0] += step;
276        }while(error>0.05); // look for 1% change
277
278        float tickpt1 = pix1[0];
279        float tickpt2 = pix2[0];
280        float tickpt3 = ystart;
281        int colour;
282        cpgqci(&colour);
283        cpgsci(DUCHAMP_TICKMARK_COLOUR);
284        int thickness;
285        cpgqlw(&thickness);
286        cpgslw(3);
287        cpgerrx(1,&tickpt1,&tickpt2,&tickpt3,2.);
288        cpgslw(thickness);
289
290        std::stringstream text;
291        text << scaleLength * angleScale[angleType[best]]
292             << symbol[angleType[best]];
293        float size,xch,ych;
294        cpgqch(&size);
295        cpgsch(0.4);
296        cpgqcs(4,&xch,&ych); // get the character size in world coords
297        cpgptxt((tickpt1+tickpt2)/2., ystart+ych, 0, 0.5, text.str().c_str());
298        cpgsch(size);
299        cpgsci(colour);
300
301        delete [] pix1;
302        delete [] pix2;
303        delete [] world1;
304        delete [] world2;
305
306      }
307    }
308
309  }
310 
311  void Cube::drawFieldEdge()
312  {
313    /// @details
314    /// Draw a border around the spatial edge of the data. Lines are
315    /// drawn in yellow at 0 and the values of xdim & ydim.  There must
316    /// be a PGPLOT window open, else an error message is returned.
317
318    if(!cpgtest()){
319      DUCHAMPERROR("Draw Cutout","There is no PGPlot device open.");
320    }
321    else{
322      int ci;
323      cpgqci(&ci);
324      cpgsci(DUCHAMP_CUBE_EDGE_COLOUR);
325 
326      cpgmove(-0.5,-0.5);
327      cpgdraw(-0.5,this->axisDim[1]-0.5);
328      cpgdraw(this->axisDim[0]-0.5,this->axisDim[1]-0.5);
329      cpgdraw(this->axisDim[0]-0.5,-0.5);
330      cpgdraw(-0.5,-0.5);
331
332      cpgsci(ci);
333    }
334  }
335
336}
Note: See TracBrowser for help on using the repository browser.