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

Last change on this file since 529 was 528, checked in by MatthewWhiting, 16 years ago

Changing the documentation comments to match the askapsoft style. Also have split ChanMap? and Object3D into separate files.

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