source: trunk/src/Detection/detection.cc @ 482

Last change on this file since 482 was 473, checked in by MatthewWhiting, 16 years ago

Added major & minor axes and position angle parameters (for spatial map) and improved fluxCalc function

File size: 30.3 KB
Line 
1// -----------------------------------------------------------------------
2// detection.cc : Member functions for the Detection class.
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 <iomanip>
30#include <vector>
31#include <string>
32#include <wcslib/wcs.h>
33#include <math.h>
34#include <duchamp/duchamp.hh>
35#include <duchamp/param.hh>
36#include <duchamp/fitsHeader.hh>
37#include <duchamp/Utils/utils.hh>
38#include <duchamp/PixelMap/Voxel.hh>
39#include <duchamp/PixelMap/Object3D.hh>
40#include <duchamp/Detection/detection.hh>
41#include <duchamp/Cubes/cubeUtils.hh>
42
43using namespace PixelInfo;
44
45namespace duchamp
46{
47
48
49  Detection::Detection()
50  {
51    this->flagWCS=false;
52    this->negSource = false;
53    this->flagText="";
54    this->totalFlux = this->peakFlux = this->intFlux = 0.;
55    this->centreType="centroid";
56    this->id = -1;
57    this->raS = this->decS = "";
58    this->ra = this->dec = this->vel = 0.;
59    this->raWidth = this->decWidth = 0.;
60    this->majorAxis = this->minorAxis = this->posang = 0.;
61    this->w20 = this->w50 = this->velWidth = 0.;
62  }
63
64  Detection::Detection(const Detection& d)
65  {
66    operator=(d);
67  }
68
69  Detection& Detection::operator= (const Detection& d)
70  {
71    if(this == &d) return *this;
72    this->pixelArray   = d.pixelArray;
73    this->xSubOffset   = d.xSubOffset;
74    this->ySubOffset   = d.ySubOffset;
75    this->zSubOffset   = d.zSubOffset;
76    this->totalFlux    = d.totalFlux;
77    this->intFlux      = d.intFlux;
78    this->peakFlux     = d.peakFlux;
79    this->xpeak        = d.xpeak;
80    this->ypeak        = d.ypeak;
81    this->zpeak        = d.zpeak;
82    this->peakSNR      = d.peakSNR;
83    this->xCentroid    = d.xCentroid;
84    this->yCentroid    = d.yCentroid;
85    this->zCentroid    = d.zCentroid;
86    this->centreType   = d.centreType;
87    this->negSource    = d.negSource;
88    this->flagText     = d.flagText;
89    this->id           = d.id;
90    this->name         = d.name;
91    this->flagWCS      = d.flagWCS;
92    this->specOK       = d.specOK;
93    this->raS          = d.raS;
94    this->decS         = d.decS;
95    this->ra           = d.ra;
96    this->dec          = d.dec;
97    this->raWidth      = d.raWidth;
98    this->decWidth     = d.decWidth;
99    this->majorAxis    = d.majorAxis;
100    this->minorAxis    = d.minorAxis;
101    this->posang       = d.posang;
102    this->specUnits    = d.specUnits;
103    this->fluxUnits    = d.fluxUnits;
104    this->intFluxUnits = d.intFluxUnits;
105    this->lngtype      = d.lngtype;
106    this->lattype      = d.lattype;
107    this->vel          = d.vel;
108    this->velWidth     = d.velWidth;
109    this->velMin       = d.velMin;
110    this->velMax       = d.velMax;
111    this->w20          = d.w20;
112    this->v20min       = d.v20min;
113    this->v20max       = d.v20max;
114    this->w50          = d.w50;
115    this->v50min       = d.v50min;
116    this->v50max       = d.v50max;
117    this->posPrec      = d.posPrec;
118    this->xyzPrec      = d.xyzPrec;
119    this->fintPrec     = d.fintPrec;
120    this->fpeakPrec    = d.fpeakPrec;
121    this->velPrec      = d.velPrec;
122    this->snrPrec      = d.snrPrec;
123    return *this;
124  }
125
126  //--------------------------------------------------------------------
127
128  bool Detection::voxelListsMatch(std::vector<Voxel> voxelList)
129  {
130    /**
131     *  A test to see whether there is a 1-1 correspondence between
132     *  the given list of Voxels and the voxel positions contained in
133     *  this Detection's pixel list. No testing of the fluxes of the
134     *  Voxels is done.
135     *
136     * \param voxelList The std::vector list of Voxels to be tested.
137     */
138
139    bool listsMatch = true;
140    // compare sizes
141    listsMatch = listsMatch && (voxelList.size() == this->getSize());
142    if(!listsMatch) return listsMatch;
143
144    // make sure all Detection pixels are in voxel list
145    listsMatch = listsMatch && this->voxelListCovered(voxelList);
146
147    // make sure all voxels are in Detection
148    for(int i=0;i<voxelList.size();i++)
149      listsMatch = listsMatch && this->pixelArray.isInObject(voxelList[i]);
150
151    return listsMatch;
152
153  }
154  //--------------------------------------------------------------------
155
156  //--------------------------------------------------------------------
157
158  bool Detection::voxelListCovered(std::vector<Voxel> voxelList)
159  {
160    /**
161     *  A test to see whether the given list of Voxels contains each
162     *  position in this Detection's pixel list. It does not look for
163     *  a 1-1 correspondence: the given list can be a super-set of the
164     *  Detection. No testing of the fluxes of the Voxels is done.
165     *
166     * \param voxelList The std::vector list of Voxels to be tested.
167     */
168
169    bool listsMatch = true;
170
171    // make sure all Detection pixels are in voxel list
172    int v1=0, mysize=this->getSize();
173    while(listsMatch && v1<mysize){
174      bool inList = false;
175      int v2=0;
176      Voxel test = this->getPixel(v1);
177      while(!inList && v2<voxelList.size()){
178        inList = inList || test.match(voxelList[v2]);
179        v2++;
180      }
181      listsMatch = listsMatch && inList;
182      v1++;
183    }
184
185    return listsMatch;
186
187  }
188  //--------------------------------------------------------------------
189
190  void Detection::calcFluxes(std::vector<Voxel> voxelList)
191  {
192    /**
193     *  A function that calculates total & peak fluxes (and the location
194     *  of the peak flux) for a Detection.
195     *
196     *  \param fluxArray The array of flux values to calculate the
197     *  flux parameters from.
198     *  \param dim The dimensions of the flux array.
199     */
200
201    this->totalFlux = this->peakFlux = 0;
202    this->xCentroid = this->yCentroid = this->zCentroid = 0.;
203
204    // first check that the voxel list and the Detection's pixel list
205    // have a 1-1 correspondence
206
207    if(!this->voxelListCovered(voxelList)){
208      duchampError("Detection::calcFluxes","Voxel list provided does not match");
209      return;
210    }
211
212    for(int i=0;i<voxelList.size();i++) {
213      if(this->pixelArray.isInObject(voxelList[i])){
214        long x = voxelList[i].getX();
215        long y = voxelList[i].getY();
216        long z = voxelList[i].getZ();
217        float f = voxelList[i].getF();
218        this->totalFlux += f;
219        this->xCentroid += x*f;
220        this->yCentroid += y*f;
221        this->zCentroid += z*f;
222        if( (i==0) ||  //first time round
223            (this->negSource&&(f<this->peakFlux)) ||
224            (!this->negSource&&(f>this->peakFlux))   )
225          {
226            this->peakFlux = f;
227            this->xpeak =    x;
228            this->ypeak =    y;
229            this->zpeak =    z;
230          }
231      }
232    }
233
234    this->xCentroid /= this->totalFlux;
235    this->yCentroid /= this->totalFlux;
236    this->zCentroid /= this->totalFlux;
237  }
238  //--------------------------------------------------------------------
239
240  void Detection::calcFluxes(float *fluxArray, long *dim)
241  {
242    /**
243     *  A function that calculates total & peak fluxes (and the location
244     *  of the peak flux) for a Detection.
245     *
246     *  \param fluxArray The array of flux values to calculate the
247     *  flux parameters from.
248     *  \param dim The dimensions of the flux array.
249     */
250
251    this->totalFlux = this->peakFlux = 0;
252    this->xCentroid = this->yCentroid = this->zCentroid = 0.;
253
254    std::vector<Voxel> voxList = this->pixelArray.getPixelSet();
255    std::vector<Voxel>::iterator vox=voxList.begin();
256    for(;vox<voxList.end();vox++){
257
258      long x=vox->getX();
259      long y=vox->getY();
260      long z=vox->getZ();
261      long ind = vox->arrayIndex(dim);
262      float f = fluxArray[ind];
263      this->totalFlux += f;
264      this->xCentroid += x*f;
265      this->yCentroid += y*f;
266      this->zCentroid += z*f;
267      if( (vox==voxList.begin()) ||
268          (this->negSource&&(f<this->peakFlux)) ||
269          (!this->negSource&&(f>this->peakFlux))   )
270        {
271          this->peakFlux = f;
272          this->xpeak = x;
273          this->ypeak = y;
274          this->zpeak = z;
275        }
276 
277    }
278
279    this->xCentroid /= this->totalFlux;
280    this->yCentroid /= this->totalFlux;
281    this->zCentroid /= this->totalFlux;
282  }
283  //--------------------------------------------------------------------
284
285  void Detection::calcWCSparams(FitsHeader &head)
286  {
287    /**
288     *  Use the input wcs to calculate the position and velocity
289     *    information for the Detection.
290     *  Quantities calculated:
291     *  <ul><li> RA: ra [deg], ra (string), ra width.
292     *      <li> Dec: dec [deg], dec (string), dec width.
293     *      <li> Vel: vel [km/s], min & max vel, vel width.
294     *      <li> coord type for all three axes, nuRest,
295     *      <li> name (IAU-style, in equatorial or Galactic)
296     *  </ul>
297     *
298     *  Note that the regular parameters are NOT recalculated!
299     *
300     *  \param head FitsHeader object that contains the WCS information.
301     */
302
303    if(head.isWCS()){
304
305      double *pixcrd = new double[15];
306      double *world  = new double[15];
307      /*
308        define a five-point array in 3D:
309        (x,y,z), (x,y,z1), (x,y,z2), (x1,y1,z), (x2,y2,z)
310        [note: x = central point, x1 = minimum x, x2 = maximum x etc.]
311        and convert to world coordinates.   
312      */
313      pixcrd[0]  = pixcrd[3] = pixcrd[6] = this->getXcentre();
314      pixcrd[9]  = this->getXmin()-0.5;
315      pixcrd[12] = this->getXmax()+0.5;
316      pixcrd[1]  = pixcrd[4] = pixcrd[7] = this->getYcentre();
317      pixcrd[10] = this->getYmin()-0.5;
318      pixcrd[13] = this->getYmax()+0.5;
319      pixcrd[2] = pixcrd[11] = pixcrd[14] = this->getZcentre();
320      pixcrd[5] = this->getZmin();
321      pixcrd[8] = this->getZmax();
322      int flag = head.pixToWCS(pixcrd, world, 5);
323      delete [] pixcrd;
324      if(flag!=0) duchampError("calcWCSparams",
325                               "Error in calculating the WCS for this object.\n");
326      else{
327
328        // world now has the WCS coords for the five points
329        //    -- use this to work out WCS params
330 
331        this->specOK = head.canUseThirdAxis();
332        this->lngtype = head.WCS().lngtyp;
333        this->lattype = head.WCS().lattyp;
334        this->specUnits = head.getSpectralUnits();
335        this->fluxUnits = head.getFluxUnits();
336        // if fluxUnits are eg. Jy/beam, make intFluxUnits = Jy km/s
337        this->intFluxUnits = head.getIntFluxUnits();
338        this->ra   = world[0];
339        this->dec  = world[1];
340        this->raS  = decToDMS(this->ra, this->lngtype);
341        this->decS = decToDMS(this->dec,this->lattype);
342        this->raWidth = angularSeparation(world[9],world[1],
343                                          world[12],world[1]) * 60.;
344        this->decWidth  = angularSeparation(world[0],world[10],
345                                            world[0],world[13]) * 60.;
346
347        Object2D spatMap = this->pixelArray.getSpatialMap();
348        std::pair<double,double> axes = spatMap.getPrincipleAxes();
349        this->majorAxis = std::max(axes.first,axes.second) * head.getAvPixScale();
350        this->minorAxis = std::min(axes.first,axes.second) * head.getAvPixScale();
351        this->posang = spatMap.getPositionAngle() * 180. / M_PI;
352
353        this->name = head.getIAUName(this->ra, this->dec);
354        this->vel    = head.specToVel(world[2]);
355        this->velMin = head.specToVel(world[5]);
356        this->velMax = head.specToVel(world[8]);
357        this->velWidth = fabs(this->velMax - this->velMin);
358
359        //      this->calcIntegFlux(fluxArray,dim,head);
360   
361        this->flagWCS = true;
362      }
363      delete [] world;
364
365    }
366  }
367  //--------------------------------------------------------------------
368
369  void Detection::calcIntegFlux(std::vector<Voxel> voxelList, FitsHeader &head)
370  {
371    /**
372     *  Uses the input WCS to calculate the velocity-integrated flux,
373     *   putting velocity in units of km/s.
374     *  The fluxes used are taken from the Voxels, rather than an
375     *   array of flux values.
376     *  Integrates over full spatial and velocity range as given
377     *   by the extrema calculated by calcWCSparams.
378     *
379     *  If the flux units end in "/beam" (eg. Jy/beam), then the flux is
380     *  corrected by the beam size (in pixels). This is done by
381     *  multiplying the integrated flux by the number of spatial pixels,
382     *  and dividing by the beam size in pixels (e.g. Jy/beam * pix /
383     *  pix/beam --> Jy)
384     *
385     *  \param voxelList The list of Voxels with flux information
386     *  \param head FitsHeader object that contains the WCS information.
387     */
388
389    const int border = 1;
390
391    if(!this->voxelListCovered(voxelList)){
392      duchampError("Detection::calcIntegFlux","Voxel list provided does not match");
393      return;
394    }
395
396    if(head.getNumAxes() > 2) {
397
398      // include one pixel either side in each direction
399      long xsize = (this->getXmax()-this->getXmin()+border*2+1);
400      long ysize = (this->getYmax()-this->getYmin()+border*2+1);
401      long zsize = (this->getZmax()-this->getZmin()+border*2+1);
402      long size = xsize*ysize*zsize;
403      std::vector <bool> isObj(size,false);
404      double *localFlux = new double[size];
405      for(int i=0;i<size;i++) localFlux[i]=0.;
406
407      for(int i=0;i<voxelList.size();i++){
408        if(this->pixelArray.isInObject(voxelList[i])){
409          long x = voxelList[i].getX();
410          long y = voxelList[i].getY();
411          long z = voxelList[i].getZ();
412          long pos = (x-this->getXmin()+border) + (y-this->getYmin()+border)*xsize
413            + (z-this->getZmin()+border)*xsize*ysize;
414          localFlux[pos] = voxelList[i].getF();
415          isObj[pos] = true;
416        }
417      }
418 
419      // work out the WCS coords for each pixel
420      double *world  = new double[size];
421      double xpt,ypt,zpt;
422      for(int i=0;i<xsize*ysize*zsize;i++){
423        xpt = double( this->getXmin() - border + i%xsize );
424        ypt = double( this->getYmin() - border + (i/xsize)%ysize );
425        zpt = double( this->getZmin() - border + i/(xsize*ysize) );
426        world[i] = head.pixToVel(xpt,ypt,zpt);
427      }
428
429      double integrated = 0.;
430      for(int pix=0; pix<xsize*ysize; pix++){ // loop over each spatial pixel.
431        for(int z=0; z<zsize; z++){
432          int pos =  z*xsize*ysize + pix;
433          if(isObj[pos]){ // if it's an object pixel...
434            double deltaVel;
435            if(z==0)
436              deltaVel = (world[pos+xsize*ysize] - world[pos]);
437            else if(z==(zsize-1))
438              deltaVel = (world[pos] - world[pos-xsize*ysize]);
439            else
440              deltaVel = (world[pos+xsize*ysize] - world[pos-xsize*ysize]) / 2.;
441            integrated += localFlux[pos] * fabs(deltaVel);
442          }
443        }
444      }
445      this->intFlux = integrated;
446
447      delete [] world;
448      delete [] localFlux;
449
450      calcVelWidths(voxelList,head);
451
452    }
453    else // in this case there is just a 2D image.
454      this->intFlux = this->totalFlux;
455
456    if(head.isWCS()){
457      // correct for the beam size if the flux units string ends in "/beam"
458      if(head.needBeamSize()) this->intFlux  /= head.getBeamSize();
459    }
460
461  }
462  //--------------------------------------------------------------------
463
464  void Detection::calcIntegFlux(float *fluxArray, long *dim, FitsHeader &head)
465  {
466    /**
467     *  Uses the input WCS to calculate the velocity-integrated flux,
468     *   putting velocity in units of km/s.
469     *  Integrates over full spatial and velocity range as given
470     *   by the extrema calculated by calcWCSparams.
471     *
472     *  If the flux units end in "/beam" (eg. Jy/beam), then the flux is
473     *  corrected by the beam size (in pixels). This is done by
474     *  multiplying the integrated flux by the number of spatial pixels,
475     *  and dividing by the beam size in pixels (e.g. Jy/beam * pix /
476     *  pix/beam --> Jy)
477     *
478     *  \param fluxArray The array of flux values.
479     *  \param dim The dimensions of the flux array.
480     *  \param head FitsHeader object that contains the WCS information.
481     */
482
483    if(head.getNumAxes() > 2) {
484
485      // include one pixel either side in each direction
486      long xsize = (this->getXmax()-this->getXmin()+3);
487      long ysize = (this->getYmax()-this->getYmin()+3);
488      long zsize = (this->getZmax()-this->getZmin()+3);
489      long size = xsize*ysize*zsize;
490      std::vector <bool> isObj(size,false);
491      double *localFlux = new double[size];
492      for(int i=0;i<size;i++) localFlux[i]=0.;
493      // work out which pixels are object pixels
494      for(int m=0; m<this->pixelArray.getNumChanMap(); m++){
495        ChanMap tempmap = this->pixelArray.getChanMap(m);
496        long z = this->pixelArray.getChanMap(m).getZ();
497        for(int s=0; s<this->pixelArray.getChanMap(m).getNumScan(); s++){
498          long y = this->pixelArray.getChanMap(m).getScan(s).getY();
499          for(long x=this->pixelArray.getChanMap(m).getScan(s).getX();
500              x<=this->pixelArray.getChanMap(m).getScan(s).getXmax();
501              x++){
502            long pos = (x-this->getXmin()+1) + (y-this->getYmin()+1)*xsize
503              + (z-this->getZmin()+1)*xsize*ysize;
504            localFlux[pos] = fluxArray[x + y*dim[0] + z*dim[0]*dim[1]];
505            isObj[pos] = true;
506          }
507        }
508      }
509 
510      // work out the WCS coords for each pixel
511      double *world  = new double[size];
512      double xpt,ypt,zpt;
513      for(int i=0;i<xsize*ysize*zsize;i++){
514        xpt = double( this->getXmin() -1 + i%xsize );
515        ypt = double( this->getYmin() -1 + (i/xsize)%ysize );
516        zpt = double( this->getZmin() -1 + i/(xsize*ysize) );
517        world[i] = head.pixToVel(xpt,ypt,zpt);
518      }
519
520      double integrated = 0.;
521      for(int pix=0; pix<xsize*ysize; pix++){ // loop over each spatial pixel.
522        for(int z=0; z<zsize; z++){
523          int pos =  z*xsize*ysize + pix;
524          if(isObj[pos]){ // if it's an object pixel...
525            double deltaVel;
526            if(z==0)
527              deltaVel = (world[pos+xsize*ysize] - world[pos]);
528            else if(z==(zsize-1))
529              deltaVel = (world[pos] - world[pos-xsize*ysize]);
530            else
531              deltaVel = (world[pos+xsize*ysize] - world[pos-xsize*ysize]) / 2.;
532            integrated += localFlux[pos] * fabs(deltaVel);
533          }
534        }
535      }
536      this->intFlux = integrated;
537
538      delete [] world;
539      delete [] localFlux;
540
541      calcVelWidths(fluxArray, dim, head);
542
543    }
544    else // in this case there is just a 2D image.
545      this->intFlux = this->totalFlux;
546
547    if(head.isWCS()){
548      // correct for the beam size if the flux units string ends in "/beam"
549      if(head.needBeamSize()) this->intFlux  /= head.getBeamSize();
550    }
551
552  }
553  //--------------------------------------------------------------------
554
555  void Detection::calcVelWidths(std::vector<Voxel> voxelList, FitsHeader &head)
556  {
557    /**
558     * Calculates the widths of the detection at 20% and 50% of the
559     * peak integrated flux. The procedure is as follows: first
560     * generate an integrated flux spectrum (using all given voxels
561     * that lie in the object's spatial map); find the peak; starting
562     * at the spectral edges of the detection, move in or out until
563     * you reach the 20% or 50% peak flux level. Linear interpolation
564     * between points is done.
565     *
566     *  \param voxelList The list of Voxels with flux information
567     *  \param head FitsHeader object that contains the WCS information.
568     */
569
570    const int border = 1;
571    long zsize = (this->getZmax()-this->getZmin()+border*2+1);
572    double xpt = double(this->getXcentre());
573    double ypt = double(this->getYcentre());
574    double zpt;
575
576    float *intSpec = new float[zsize];
577    for(int i=0;i<zsize;i++) intSpec[i]=0;
578       
579    Object2D spatMap = this->pixelArray.getSpatialMap();
580    for(int s=0;s<spatMap.getNumScan();s++){
581      for(int i=0;i<voxelList.size();i++){
582        if(spatMap.isInObject(voxelList[i])){
583          if(voxelList[i].getZ()>=this->getZmin()-border &&
584             voxelList[i].getZ()<=this->getZmax()+border)
585            intSpec[voxelList[i].getZ()-this->getZmin()+1] += voxelList[i].getF();
586        }
587      }
588    }
589   
590    std::vector<std::pair<int,float> > goodPix;
591    float peak;
592    int peakLoc;
593    for(int z=0;z<zsize;z++) {
594      if(z==0 || peak<intSpec[z]){
595        peak = intSpec[z];
596        peakLoc = z;
597      }
598      goodPix.push_back(std::pair<int,float>(z,intSpec[z]));
599    }
600
601    // finding the 20% & 50% points.  Start at the velmin & velmax
602    //  points. Then, if the int flux there is above the 20%/50%
603    //  limit, go out, otherwise go in. This is to deal with the
604    //  problems from double peaked sources.
605
606    int z;
607    bool goLeft;
608    z=border;
609    goLeft = intSpec[z]>peak*0.5;
610    if(goLeft) while(z>0 && intSpec[z]>peak*0.5) z--;
611    else       while(z<peakLoc && intSpec[z]<peak*0.5) z++;
612    if(z==0) this->v50min = this->velMin;
613    else{
614      if(goLeft) zpt = z + (peak*0.5-intSpec[z])/(intSpec[z+1]-intSpec[z]) + this->getZmin() - border;
615      else       zpt = z - (peak*0.5-intSpec[z])/(intSpec[z-1]-intSpec[z]) + this->getZmin() - border;
616      this->v50min = head.pixToVel(xpt,ypt,zpt);
617    }
618    z=this->getZmax()-this->getZmin();
619    goLeft = intSpec[z]<peak*0.5;
620    if(goLeft) while(z>peakLoc && intSpec[z]<peak*0.5) z--;
621    else       while(z<zsize && intSpec[z]>peak*0.5) z++;
622    if(z==zsize) this->v50max = this->velMax;
623    else{
624      if(goLeft) zpt = z + (peak*0.5-intSpec[z])/(intSpec[z+1]-intSpec[z]) + this->getZmin() - border;
625      else       zpt = z - (peak*0.5-intSpec[z])/(intSpec[z-1]-intSpec[z]) + this->getZmin() - border;
626      this->v50max = head.pixToVel(xpt,ypt,zpt);
627    }
628    z=border;
629    goLeft = intSpec[z]>peak*0.5;
630    if(goLeft) while(z>0 && intSpec[z]>peak*0.2) z--;
631    else       while(z<peakLoc && intSpec[z]<peak*0.2) z++;
632    if(z==0) this->v20min = this->velMin;
633    else{
634      if(goLeft) zpt = z + (peak*0.2-intSpec[z])/(intSpec[z+1]-intSpec[z]) + this->getZmin() - border;
635      else       zpt = z - (peak*0.2-intSpec[z])/(intSpec[z-1]-intSpec[z]) + this->getZmin() - border;
636      this->v20min = head.pixToVel(xpt,ypt,zpt);
637    }
638    z=this->getZmax()-this->getZmin();
639    goLeft = intSpec[z]<peak*0.5;
640    if(goLeft) while(z>peakLoc && intSpec[z]<peak*0.2) z--;
641    else       while(z<zsize && intSpec[z]>peak*0.2) z++;
642    if(z==zsize) this->v20max = this->velMax;
643    else{
644      if(goLeft) zpt = z + (peak*0.2-intSpec[z])/(intSpec[z+1]-intSpec[z]) + this->getZmin() - border;
645      else       zpt = z - (peak*0.2-intSpec[z])/(intSpec[z-1]-intSpec[z]) + this->getZmin() - border;
646      this->v20max = head.pixToVel(xpt,ypt,zpt);
647    }
648
649    this->w20 = fabs(this->v20min - this->v20max);
650    this->w50 = fabs(this->v50min - this->v50max);
651
652    delete [] intSpec;
653
654  }
655
656  //--------------------------------------------------------------------
657
658  void Detection::calcVelWidths(float *fluxArray, long *dim, FitsHeader &head)
659  {
660    /**
661     * Calculates the widths of the detection at 20% and 50% of the
662     * peak integrated flux. The procedure is as follows: first
663     * generate an integrated flux spectrum (summing each spatial
664     * pixel's spectrum); find the peak; starting at the spectral
665     * edges of the detection, move in or out until you reach the 20%
666     * or 50% peak flux level. Linear interpolation between points is
667     * done.
668     *
669     *  \param fluxArray The array of flux values.
670     *  \param dim The dimensions of the flux array.
671     *  \param head FitsHeader object that contains the WCS information.
672     */
673
674    if(dim[2] > 2){
675
676      double xpt = double(this->getXcentre());
677      double ypt = double(this->getYcentre());
678      double zpt;
679
680      float *intSpec = new float[dim[2]];
681      bool *mask = new bool[dim[0]*dim[1]*dim[2]];
682      for(int i=0;i<dim[0]*dim[1]*dim[2];i++) mask[i] = true;
683      getIntSpec(*this,fluxArray,dim,mask,1.,intSpec);
684
685      std::vector<std::pair<int,float> > goodPix;
686      float peak;
687      int peakLoc;
688      for(int z=this->getZmin();z<=this->getZmax();z++) {
689        if(z==this->getZmin() || peak<intSpec[z]){
690          peak = intSpec[z];
691          peakLoc = z;
692        }
693        goodPix.push_back(std::pair<int,float>(z,intSpec[z]));
694      }
695
696      // finding the 20% & 50% points.  Start at the velmin & velmax
697      //  points. Then, if the int flux there is above the 20%/50%
698      //  limit, go out, otherwise go in. This is to deal with the
699      //  problems from double- (or multi-) peaked sources.
700
701      int z;
702      bool goLeft;
703      z=this->getZmin();
704      goLeft = intSpec[z]>peak*0.5;
705      if(goLeft) while(z>0 && intSpec[z]>peak*0.5) z--;
706      else       while(z<peakLoc && intSpec[z]<peak*0.5) z++;
707      if(z==0) this->v50min = this->velMin;
708      else{
709        if(goLeft) zpt = z + (peak*0.5-intSpec[z])/(intSpec[z+1]-intSpec[z]);
710        else       zpt = z - (peak*0.5-intSpec[z])/(intSpec[z-1]-intSpec[z]);
711        this->v50min = head.pixToVel(xpt,ypt,zpt);
712      }
713      z=this->getZmax();
714      goLeft = intSpec[z]<peak*0.5;
715      if(goLeft) while(z>peakLoc && intSpec[z]<peak*0.5) z--;
716      else       while(z<dim[2] && intSpec[z]>peak*0.5) z++;
717      if(z==dim[2]) this->v50max = this->velMax;
718      else{
719        if(goLeft) zpt = z + (peak*0.5-intSpec[z])/(intSpec[z+1]-intSpec[z]);
720        else       zpt = z - (peak*0.5-intSpec[z])/(intSpec[z-1]-intSpec[z]);
721        this->v50max = head.pixToVel(xpt,ypt,zpt);
722      }
723      z=this->getZmin();
724      goLeft = intSpec[z]>peak*0.5;
725      if(goLeft) while(z>0 && intSpec[z]>peak*0.2) z--;
726      else       while(z<peakLoc && intSpec[z]<peak*0.2) z++;
727      if(z==0) this->v20min = this->velMin;
728      else{
729        if(goLeft) zpt = z + (peak*0.2-intSpec[z])/(intSpec[z+1]-intSpec[z]);
730        else       zpt = z - (peak*0.2-intSpec[z])/(intSpec[z-1]-intSpec[z]);
731        this->v20min = head.pixToVel(xpt,ypt,zpt);
732      }
733      z=this->getZmax();
734      goLeft = intSpec[z]<peak*0.5;
735      if(goLeft) while(z>peakLoc && intSpec[z]<peak*0.2) z--;
736      else       while(z<dim[2] && intSpec[z]>peak*0.2) z++;
737      if(z==dim[2]) this->v20max = this->velMax;
738      else{
739        if(goLeft) zpt = z + (peak*0.2-intSpec[z])/(intSpec[z+1]-intSpec[z]);
740        else       zpt = z - (peak*0.2-intSpec[z])/(intSpec[z-1]-intSpec[z]);
741        this->v20max = head.pixToVel(xpt,ypt,zpt);
742      }
743
744      delete [] intSpec;
745      delete [] mask;
746
747    }
748    else{
749      this->v50min = this->v20min = this->velMin;
750      this->v50max = this->v20max = this->velMax;
751    }
752
753    this->w20 = fabs(this->v20min - this->v20max);
754    this->w50 = fabs(this->v50min - this->v50max);
755
756  }
757  //--------------------------------------------------------------------
758
759  Detection operator+ (Detection lhs, Detection rhs)
760  {
761    /**
762     *  Combines two objects by adding all the pixels using the Object3D
763     *  operator.
764     *
765     *  The pixel parameters are recalculated in the process (equivalent
766     *  to calling pixels().calcParams()), but WCS parameters are not.
767     */
768    Detection output = lhs;
769    output.pixelArray = lhs.pixelArray + rhs.pixelArray;
770    return output;
771  }
772  //--------------------------------------------------------------------
773
774  void Detection::setOffsets(Param &par)
775  {
776    /**
777     * This function stores the values of the offsets for each cube axis.
778     * The offsets are the starting values of the cube axes that may differ from
779     *  the default value of 0 (for instance, if a subsection is being used).
780     * The values will be used when the detection is outputted.
781     */
782    this->xSubOffset = par.getXOffset();
783    this->ySubOffset = par.getYOffset();
784    this->zSubOffset = par.getZOffset();
785  }
786  //--------------------------------------------------------------------
787
788  bool Detection::hasEnoughChannels(int minNumber)
789  {
790    /**
791     * A function to determine if the Detection has enough
792     * contiguous channels to meet the minimum requirement
793     * given as the argument.
794     * \param minNumber How many channels is the minimum acceptable number?
795     * \return True if there is at least one occurence of minNumber consecutive
796     * channels present to return true. False otherwise.
797     */
798
799    // Preferred method -- need a set of minNumber consecutive channels present.
800    this->pixelArray.order();
801    int numChannels = 0;
802    bool result = false;
803    int size = this->pixelArray.getNumChanMap();
804    if(size>0) numChannels++;
805    if( numChannels >= minNumber) result = true;
806    for(int i=1;(i<size && !result);i++) {
807      if( (this->pixelArray.getZ(i) - this->pixelArray.getZ(i-1)) == 1)
808        numChannels++;
809      else if( (this->pixelArray.getZ(i) - this->pixelArray.getZ(i-1)) >= 2)
810        numChannels = 1;
811
812      if( numChannels >= minNumber) result = true;
813    }
814    return result;
815 
816  }
817  //--------------------------------------------------------------------
818
819  std::vector<int> Detection::getVertexSet()
820  {
821    /**
822     * Gets a list of points being the end-points of 1-pixel long
823     * segments drawing a border around the spatial extend of a
824     * detection. The vector is a series of 4 integers, being: x_0,
825     * y_0, x_1, y_1.
826     * \return The vector of vertex positions.
827     */
828    std::vector<int> vertexSet;
829
830    int xmin = this->getXmin() - 1;
831    int xmax = this->getXmax() + 1;
832    int ymin = this->getYmin() - 1;
833    int ymax = this->getYmax() + 1;
834    int xsize = xmax - xmin + 1;
835    int ysize = ymax - ymin + 1;
836
837    std::vector<Voxel> voxlist = this->pixelArray.getPixelSet();
838    std::vector<bool> isObj(xsize*ysize,false);
839    for(int i=0;i<voxlist.size();i++){
840      int pos = (voxlist[i].getX()-xmin) +
841        (voxlist[i].getY()-ymin)*xsize;
842      isObj[pos] = true;
843    }
844    voxlist.clear();
845   
846    for(int x=xmin; x<=xmax; x++){
847      // for each column...
848      for(int y=ymin+1;y<=ymax;y++){
849        int current  = (y-ymin)*xsize + x-xmin;
850        int previous = (y-ymin-1)*xsize + x-xmin;
851        if((isObj[current]&&!isObj[previous])   ||
852           (!isObj[current]&&isObj[previous])){
853          vertexSet.push_back(x);
854          vertexSet.push_back(y);
855          vertexSet.push_back(x+1);
856          vertexSet.push_back(y);
857        }
858      }
859    }
860    for(int y=ymin; y<=ymax; y++){
861      // now for each row...
862      for(int x=xmin+1;x<=xmax;x++){
863        int current  = (y-ymin)*xsize + x-xmin;
864        int previous = (y-ymin)*xsize + x-xmin - 1;
865        if((isObj[current]&&!isObj[previous])   ||
866           (!isObj[current]&&isObj[previous])){
867          vertexSet.push_back(x);
868          vertexSet.push_back(y);
869          vertexSet.push_back(x);
870          vertexSet.push_back(y+1);
871        }
872      }
873    }
874
875    return vertexSet;
876 
877  }
878  //--------------------------------------------------------------------
879
880  std::ostream& operator<< ( std::ostream& theStream, Detection& obj)
881  {
882    /**
883     *  A convenient way of printing the coordinate values for each
884     *  pixel in the Detection. 
885     *
886     *  NOTE THAT THERE IS CURRENTLY NO FLUX INFORMATION BEING PRINTED!
887     *
888     *  Use as front end to the Object3D::operator<< function.
889     */ 
890
891    theStream << obj.pixelArray << "---\n";
892    return theStream;
893  }
894  //--------------------------------------------------------------------
895
896}
Note: See TracBrowser for help on using the repository browser.