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

Last change on this file since 434 was 431, checked in by MatthewWhiting, 16 years ago

Changed the way the borders are calculated, and the way the Annotation file is written. Now shows outline of object in same manner as moment map plots.

File size: 12.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 <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  std::vector<int> Detection::getVertexSet()
297  {
298    /**
299     * Gets a list of points being the end-points of 1-pixel long
300     * segments drawing a border around the spatial extend of a
301     * detection. The vector is a series of 4 integers, being: x_0,
302     * y_0, x_1, y_1.
303     * \return The vector of vertex positions.
304     */
305    std::vector<int> vertexSet;
306
307    int xmin = this->getXmin() - 1;
308    int xmax = this->getXmax() + 1;
309    int ymin = this->getYmin() - 1;
310    int ymax = this->getYmax() + 1;
311    int xsize = xmax - xmin + 1;
312    int ysize = ymax - ymin + 1;
313
314    std::vector<Voxel> voxlist = this->pixelArray.getPixelSet();
315    std::vector<bool> isObj(xsize*ysize,false);
316    for(int i=0;i<voxlist.size();i++){
317      int pos = (voxlist[i].getX()-xmin) +
318        (voxlist[i].getY()-ymin)*xsize;
319      isObj[pos] = true;
320    }
321    voxlist.clear();
322   
323    for(int x=xmin; x<=xmax; x++){
324      // for each column...
325      for(int y=ymin+1;y<=ymax;y++){
326        int current  = (y-ymin)*xsize + x-xmin;
327        int previous = (y-ymin-1)*xsize + x-xmin;
328        if((isObj[current]&&!isObj[previous])   ||
329           (!isObj[current]&&isObj[previous])){
330          vertexSet.push_back(x);
331          vertexSet.push_back(y);
332          vertexSet.push_back(x+1);
333          vertexSet.push_back(y);
334        }
335      }
336    }
337    for(int y=ymin; y<=ymax; y++){
338      // now for each row...
339      for(int x=xmin+1;x<=xmax;x++){
340        int current  = (y-ymin)*xsize + x-xmin;
341        int previous = (y-ymin)*xsize + x-xmin - 1;
342        if((isObj[current]&&!isObj[previous])   ||
343           (!isObj[current]&&isObj[previous])){
344          vertexSet.push_back(x);
345          vertexSet.push_back(y);
346          vertexSet.push_back(x);
347          vertexSet.push_back(y+1);
348        }
349      }
350    }
351
352    return vertexSet;
353 
354  }
355 
356  void Detection::drawBorders(int xoffset, int yoffset)
357  {
358    /**
359     * For a given object, draw borders around the spatial extent of the object.
360     * \param xoffset The offset from 0 of the x-axis of the plotting window
361     * \param yoffset The offset from 0 of the y-axis of the plotting window
362     */
363    if(!cpgtest())
364      duchampError("Draw Borders","There is no PGPlot device open!\n");
365    else{
366
367      float x1,x2,y1,y2;
368      cpgqwin(&x1,&x2,&y1,&y2);
369      int xsize = int(x2 - x1) + 1;
370      int ysize = int(y2 - y1) + 1;
371
372      std::vector<int> vertexSet = this->getVertexSet();
373
374      cpgswin(0,xsize-1,0,ysize-1);
375
376      if(vertexSet.size()%4 != 0)
377        duchampError("drawBorders","Vertex set wrong size!");
378      else{
379        for(int i=0;i<vertexSet.size()/4;i++){
380          cpgmove(vertexSet[i*4]-xoffset,vertexSet[i*4+1]-yoffset);
381          cpgdraw(vertexSet[i*4+2]-xoffset,vertexSet[i*4+3]-yoffset);
382        }
383      }
384      cpgswin(x1,x2,y1,y2);
385 
386    }   
387
388  }
389
390  void Cube::drawFieldEdge()
391  {
392    /**
393     * Draw a border around the spatial edge of the data. Lines are
394     * drawn in yellow at 0 and the values of xdim & ydim.  There must
395     * be a PGPLOT window open, else an error message is returned.
396     */
397    if(!cpgtest())
398      duchampError("Draw Cutout","There is no PGPlot device open!\n");
399    else{
400      int ci;
401      cpgqci(&ci);
402      cpgsci(DUCHAMP_CUBE_EDGE_COLOUR);
403 
404      cpgmove(-0.5,-0.5);
405      cpgdraw(-0.5,this->axisDim[1]-0.5);
406      cpgdraw(this->axisDim[0]-0.5,this->axisDim[1]-0.5);
407      cpgdraw(this->axisDim[0]-0.5,-0.5);
408      cpgdraw(-0.5,-0.5);
409
410      cpgsci(ci);
411    }
412  }
413
414}
Note: See TracBrowser for help on using the repository browser.