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

Last change on this file since 931 was 931, checked in by MatthewWhiting, 12 years ago

Fixing a bug that was giving WCS errors when drawing the scale bar - we were needlessly adding offsets to the pixel positions. Also fixing some int --> size_t conversions.

File size: 11.0 KB
RevLine 
[299]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// -----------------------------------------------------------------------
[3]28#include <iostream>
[103]29#include <sstream>
[3]30#include <cpgplot.h>
31#include <math.h>
[393]32#include <duchamp/duchamp.hh>
33#include <duchamp/param.hh>
34#include <duchamp/fitsHeader.hh>
35#include <duchamp/Cubes/cubes.hh>
[463]36#include <duchamp/Cubes/cubeUtils.hh>
[393]37#include <duchamp/Utils/utils.hh>
38#include <duchamp/Utils/mycpgplot.hh>
39#include <duchamp/PixelMap/Voxel.hh>
40#include <duchamp/PixelMap/Object3D.hh>
[258]41#include <vector>
[3]42
[142]43const int MIN_WIDTH=20;
[146]44using namespace mycpgplot;
[258]45using namespace PixelInfo;
[142]46
[378]47namespace duchamp
[3]48{
[83]49
[378]50  void Cube::drawMomentCutout(Detection &object)
51  {
[528]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.
[117]61
[913]62    if(!cpgtest()){
63      DUCHAMPERROR("Draw Cutout","There is no PGPlot device open.");
64    }
[378]65    else{
[3]66
[931]67      size_t size = (object.getXmax()-object.getXmin()+1);
68      if(size<size_t(object.getYmax()-object.getYmin()+1))
[378]69        size = object.getYmax()-object.getYmin()+1;
70      size += MIN_WIDTH;
[3]71
[378]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];
[931]80      for(size_t i=0;i<size*size;i++) isGood[i]=true;
[378]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)] =
[931]85              ((x>=0)&&(x<int(this->axisDim[0])))    // if inside the boundaries
86              && ((y>=0)&&(y<int(this->axisDim[1]))) // if inside the boundaries
[378]87              && !this->isBlank(x,y,z);         // if not blank
88          }
[142]89        }
[137]90      }
91
[378]92      float *image = new float[size*size];
[931]93      for(size_t i=0;i<size*size;i++) image[i]=0.;
[285]94
[378]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++){
[137]99
[378]100            imPos = (y-ymin) * size + (x-xmin);
101            cubePos = z*this->axisDim[0]*this->axisDim[1] +
102              y*this->axisDim[0] + x;
[137]103
[378]104            if(isGood[imPos]) image[imPos] += this->array[cubePos];
[137]105
[378]106          }
[142]107        }
[3]108      }
109
[931]110      for(size_t i=0;i<size*size;i++){
[378]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      }
[3]114
[378]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];
[931]121      for(size_t i=1;i<size*size;i++){
[378]122        if(isGood[i]){
123          if(image[i]<z1) z1=image[i];
124          if(image[i]>z2) z2=image[i];
125        }
[142]126      }
[3]127
[378]128      // adjust the values of the blank and extra-image pixels
[931]129      for(size_t i=0;i<size*size;i++)
[378]130        if(!isGood[i]) image[i] = z1 - 1.;
[3]131
[285]132
[378]133      float tr[6] = {xmin-1,1.,0.,ymin-1,0.,1.};
[3]134
[378]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);
[3]139
[378]140      delete [] image;
[137]141
[378]142      int ci;
143      cpgqci(&ci);
[137]144
[378]145      // Draw the border of the BLANK region, if there is one...
146      drawBlankEdges(this->array,this->axisDim[0],this->axisDim[1],this->par);
[3]147
[378]148      // Draw the border of cube's pixels
149      this->drawFieldEdge();
[3]150
[378]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      */
[3]165
[378]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      }
[142]171
[378]172      cpgsci(ci);
[142]173
[378]174      delete [] isGood;
[309]175
[378]176    }
177
[135]178  }
[117]179
[378]180  void Cube::drawScale(float xstart, float ystart, float channel)
181  {
[528]182    ///  @details
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.
[3]193
[913]194    if(!cpgtest()){
195      DUCHAMPERROR("Draw Cutout","There is no PGPlot device open.");
196    }
[378]197    else{
[83]198
[378]199      if(this->head.isWCS()){  // can only do this if the WCS is good!
[83]200
[378]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
[142]205   
[378]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;
[142]220
[378]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];
[634]225        int best=0;
[378]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;
[83]235
[378]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];
[931]241        pix1[0] = pix2[0] = xstart;
242        pix1[1] = pix2[1] = ystart;
[378]243        pix1[2] = pix2[2] = channel;
244        this->head.pixToWCS(pix1,world1);
[83]245
[378]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
[83]262
[931]263        float tickpt1 = pix1[0];
264        float tickpt2 = pix2[0];
[378]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);
[83]274
[378]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);
[142]285
[378]286        delete [] pix1;
287        delete [] pix2;
288        delete [] world1;
289        delete [] world2;
[142]290
[378]291      }
[146]292    }
[378]293
[142]294  }
[431]295 
[378]296  void Detection::drawBorders(int xoffset, int yoffset)
297  {
[528]298    ///  @details
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
[913]303    if(!cpgtest()){
304      DUCHAMPERROR("Draw Borders","There is no PGPlot device open.");
305    }
[378]306    else{
[83]307
[378]308      float x1,x2,y1,y2;
309      cpgqwin(&x1,&x2,&y1,&y2);
310      int xsize = int(x2 - x1) + 1;
311      int ysize = int(y2 - y1) + 1;
[3]312
[431]313      std::vector<int> vertexSet = this->getVertexSet();
314
[378]315      cpgswin(0,xsize-1,0,ysize-1);
[431]316
[913]317      if(vertexSet.size()%4 != 0){
318        DUCHAMPERROR("drawBorders","Vertex set wrong size!");
319      }
[431]320      else{
[623]321        for(size_t i=0;i<vertexSet.size()/4;i++){
[431]322          cpgmove(vertexSet[i*4]-xoffset,vertexSet[i*4+1]-yoffset);
323          cpgdraw(vertexSet[i*4+2]-xoffset,vertexSet[i*4+3]-yoffset);
[142]324        }
[3]325      }
[378]326      cpgswin(x1,x2,y1,y2);
[3]327 
[378]328    }   
[142]329
[378]330  }
[142]331
[378]332  void Cube::drawFieldEdge()
333  {
[528]334    /// @details
335    /// Draw a border around the spatial edge of the data. Lines are
336    /// drawn in yellow at 0 and the values of xdim & ydim.  There must
337    /// be a PGPLOT window open, else an error message is returned.
338
[913]339    if(!cpgtest()){
340      DUCHAMPERROR("Draw Cutout","There is no PGPlot device open.");
341    }
[378]342    else{
343      int ci;
344      cpgqci(&ci);
345      cpgsci(DUCHAMP_CUBE_EDGE_COLOUR);
[142]346 
[378]347      cpgmove(-0.5,-0.5);
348      cpgdraw(-0.5,this->axisDim[1]-0.5);
349      cpgdraw(this->axisDim[0]-0.5,this->axisDim[1]-0.5);
350      cpgdraw(this->axisDim[0]-0.5,-0.5);
351      cpgdraw(-0.5,-0.5);
[142]352
[378]353      cpgsci(ci);
354    }
[142]355  }
[378]356
[142]357}
Note: See TracBrowser for help on using the repository browser.