source: tags/release-1.1.7/src/Detection/detection.cc @ 1455

Last change on this file since 1455 was 536, checked in by MatthewWhiting, 15 years ago

Including the recent minor changes to 1.1.7.

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