source: tags/release-1.1.5/src/Cubes/drawMomentCutout.cc @ 1391

Last change on this file since 1391 was 452, checked in by MatthewWhiting, 16 years ago

Move getVertexSet out of drawMomentCutout.cc, otherwise it isn't visible when pgplot is not being used.

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