source: tags/release-1.1.11/src/Detection/detection.cc @ 1213

Last change on this file since 1213 was 791, checked in by MatthewWhiting, 13 years ago

Making sure it works with the redesigned needBeamSize function (ie. not using it before reading beam from file). Also removing a lot of redundant code.

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