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

Last change on this file since 543 was 541, checked in by MatthewWhiting, 15 years ago

Changing all calls of uint to unsigned int, as there are sometimes compilers that don't know about that typedef. Also added an include call for stdlib.h to fitsHeader.cc so that it knows about calloc.

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(unsigned 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    ///  @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      unsigned int 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(unsigned int 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(unsigned int 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(unsigned int 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(unsigned int 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.