source: branches/NewStructure/src/Detection/detection.cc @ 1441

Last change on this file since 1441 was 1410, checked in by MatthewWhiting, 10 years ago

Results of merging Detections with trunk

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