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

Last change on this file since 1298 was 1298, checked in by MatthewWhiting, 11 years ago

Fixing further compiler warnings. Should be no functional change.

File size: 40.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 = !this->negSource;
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            (this->negSource&&(f<this->peakFlux)) ||
321            (!this->negSource&&(f>this->peakFlux))   )
322          {
323            this->peakFlux = f;
324            this->xpeak =    x;
325            this->ypeak =    y;
326            this->zpeak =    z;
327          }
328      }
329    }
330
331    this->xCentroid /= this->totalFlux;
332    this->yCentroid /= this->totalFlux;
333    this->zCentroid /= this->totalFlux;
334  }
335  //--------------------------------------------------------------------
336
337  void Detection::calcFluxes(std::map<Voxel,float> &voxelMap)
338  {
339    ///  @details
340    ///  A function that calculates total & peak fluxes (and the location
341    ///  of the peak flux) for a Detection.
342    ///
343    ///  \param fluxArray The array of flux values to calculate the
344    ///  flux parameters from.
345    ///  \param dim The dimensions of the flux array.
346   
347    //    this->haveParams = true;
348
349    this->totalFlux = this->peakFlux = 0;
350    this->xCentroid = this->yCentroid = this->zCentroid = 0.;
351
352    std::vector<Voxel> voxelList = this->getPixelSet();
353    std::vector<Voxel>::iterator vox;
354    for(vox=voxelList.begin();vox<voxelList.end();vox++){
355      if(voxelMap.find(*vox) == voxelMap.end()){
356        DUCHAMPERROR("Detection::calcFluxes","Voxel list provided does not match");
357        return;
358      }
359      else {
360        long x = vox->getX();
361        long y = vox->getY();
362        long z = vox->getZ();
363        float f = voxelMap[*vox];
364        this->totalFlux += f;
365        this->xCentroid += x*f;
366        this->yCentroid += y*f;
367        this->zCentroid += z*f;
368        if( (vox==voxelList.begin()) ||  //first time round
369            (this->negSource&&(f<this->peakFlux)) ||
370            (!this->negSource&&(f>this->peakFlux))   )
371          {
372            this->peakFlux = f;
373            this->xpeak =    x;
374            this->ypeak =    y;
375            this->zpeak =    z;
376          }
377      }
378    }
379
380    this->xCentroid /= this->totalFlux;
381    this->yCentroid /= this->totalFlux;
382    this->zCentroid /= this->totalFlux;
383  }
384  //--------------------------------------------------------------------
385
386  void Detection::calcFluxes(float *fluxArray, size_t *dim)
387  {
388    ///  @details
389    ///  A function that calculates total & peak fluxes (and the location
390    ///  of the peak flux) for a Detection.
391    ///
392    ///  \param fluxArray The array of flux values to calculate the
393    ///  flux parameters from.
394    ///  \param dim The dimensions of the flux array.
395
396    //    this->haveParams = true;
397
398    this->totalFlux = this->peakFlux = 0;
399    this->xCentroid = this->yCentroid = this->zCentroid = 0.;
400
401    std::vector<Voxel> voxList = this->getPixelSet();
402    std::vector<Voxel>::iterator vox=voxList.begin();
403    for(;vox<voxList.end();vox++){
404
405      long x=vox->getX();
406      long y=vox->getY();
407      long z=vox->getZ();
408      size_t ind = vox->arrayIndex(dim);
409      float f = fluxArray[ind];
410      this->totalFlux += f;
411      this->xCentroid += x*f;
412      this->yCentroid += y*f;
413      this->zCentroid += z*f;
414      if( (vox==voxList.begin()) ||
415          (this->negSource&&(f<this->peakFlux)) ||
416          (!this->negSource&&(f>this->peakFlux))   )
417        {
418          this->peakFlux = f;
419          this->xpeak = x;
420          this->ypeak = y;
421          this->zpeak = z;
422        }
423 
424    }
425
426    this->xCentroid /= this->totalFlux;
427    this->yCentroid /= this->totalFlux;
428    this->zCentroid /= this->totalFlux;
429  }
430  //--------------------------------------------------------------------
431
432  void Detection::calcWCSparams(FitsHeader &head)
433  {
434    ///  @details
435    ///  Use the input wcs to calculate the position and velocity
436    ///    information for the Detection.
437    ///  Quantities calculated:
438    ///  <ul><li> RA: ra [deg], ra (string), ra width.
439    ///      <li> Dec: dec [deg], dec (string), dec width.
440    ///      <li> Vel: vel [km/s], min & max vel, vel width.
441    ///      <li> coord type for all three axes, nuRest,
442    ///      <li> name (IAU-style, in equatorial or Galactic)
443    ///  </ul>
444    ///
445    ///  Note that the regular parameters are NOT recalculated!
446    ///
447    ///  \param head FitsHeader object that contains the WCS information.
448
449    if(head.isWCS()){
450
451      double *pixcrd = new double[15];
452      double *world  = new double[15];
453      /*
454        define a five-point array in 3D:
455        (x,y,z), (x,y,z1), (x,y,z2), (x1,y1,z), (x2,y2,z)
456        [note: x = central point, x1 = minimum x, x2 = maximum x etc.]
457        and convert to world coordinates.   
458      */
459      pixcrd[0]  = pixcrd[3] = pixcrd[6] = this->getXcentre();
460      pixcrd[9]  = this->getXmin()-0.5;
461      pixcrd[12] = this->getXmax()+0.5;
462      pixcrd[1]  = pixcrd[4] = pixcrd[7] = this->getYcentre();
463      pixcrd[10] = this->getYmin()-0.5;
464      pixcrd[13] = this->getYmax()+0.5;
465      pixcrd[2] = pixcrd[11] = pixcrd[14] = this->getZcentre();
466      pixcrd[5] = this->getZmin();
467      pixcrd[8] = this->getZmax();
468      int flag = head.pixToWCS(pixcrd, world, 5);
469      delete [] pixcrd;
470      if(flag!=0) {
471        DUCHAMPERROR("calcWCSparams", "Error in calculating the WCS for this object.");
472      }
473      else{
474       
475        // world now has the WCS coords for the five points
476        //    -- use this to work out WCS params
477 
478        this->haveParams = true;
479
480        this->specOK = head.canUseThirdAxis();
481        this->lngtype = head.lngtype();
482        this->lattype = head.lattype();
483        this->specUnits = head.getSpectralUnits();
484        this->specType  = head.getSpectralType();
485        this->fluxUnits = head.getFluxUnits();
486        // if fluxUnits are eg. Jy/beam, make intFluxUnits = Jy km/s
487        this->intFluxUnits = head.getIntFluxUnits();
488        this->ra   = world[0];
489        this->dec  = world[1];
490        int precision = -int(log10(fabs(head.WCS().cdelt[head.WCS().lng]*3600./10.)));
491        this->raS  = decToDMS(this->ra, this->lngtype,precision);
492        this->decS = decToDMS(this->dec,this->lattype,precision);
493        this->raWidth = angularSeparation(world[9],world[1],
494                                          world[12],world[1]) * 60.;
495        this->decWidth  = angularSeparation(world[0],world[10],
496                                            world[0],world[13]) * 60.;
497
498        this->name = head.getIAUName(this->ra, this->dec);
499        this->vel    = head.specToVel(world[2]);
500        this->velMin = head.specToVel(world[5]);
501        this->velMax = head.specToVel(world[8]);
502        this->velWidth = fabs(this->velMax - this->velMin);
503
504        this->flagWCS = true;
505      }
506      delete [] world;
507
508    }
509    else {
510      double x=this->getXcentre(),y=this->getYcentre(),z=this->getZmin();
511      this->velMin = head.pixToVel(x,y,z);
512      z=this->getZmax();
513      this->velMax = head.pixToVel(x,y,z);
514      this->velWidth = fabs(this->velMax - this->velMin);
515    }
516
517     
518  }
519  //--------------------------------------------------------------------
520
521  void Detection::calcIntegFlux(size_t zdim, std::vector<Voxel> voxelList, FitsHeader &head)
522  {
523    ///  @details
524    ///  Uses the input WCS to calculate the velocity-integrated flux,
525    ///   putting velocity in units of km/s.
526    ///  The fluxes used are taken from the Voxels, rather than an
527    ///   array of flux values.
528    ///  Integrates over full spatial and velocity range as given
529    ///   by the extrema calculated by calcWCSparams.
530    ///
531    ///  If the flux units end in "/beam" (eg. Jy/beam), then the flux is
532    ///  corrected by the beam size (in pixels). This is done by
533    ///  multiplying the integrated flux by the number of spatial pixels,
534    ///  and dividing by the beam size in pixels (e.g. Jy/beam * pix /
535    ///  pix/beam --> Jy)
536    ///
537    ///  \param zdim The size of the spectral axis (needed to find the velocity widths)
538    ///  \param voxelList The list of Voxels with flux information
539    ///  \param head FitsHeader object that contains the WCS information.
540
541    if(!this->voxelListCovered(voxelList)){
542      DUCHAMPERROR("Detection::calcIntegFlux","Voxel list provided does not match");
543      return;
544    }
545
546    if(!head.is2D()){
547
548      this->haveParams = true;
549
550      const int border = 1;  // include one pixel either side in each direction
551      size_t xsize = size_t(this->xmax-this->xmin+2*border+1);
552      size_t ysize = size_t(this->ymax-this->ymin+2*border+1);
553      size_t zsize = size_t(this->zmax-this->zmin+2*border+1);
554      size_t xzero = size_t(std::max(0L,this->xmin-border));
555      size_t yzero = size_t(std::max(0L,this->ymin-border));
556      size_t zzero = size_t(std::max(0L,this->zmin-border));
557      size_t spatsize=xsize*ysize;
558      size_t size = xsize*ysize*zsize;
559      std::vector <bool> isObj(size,false);
560      double *localFlux = new double[size];
561      for(size_t i=0;i<size;i++) localFlux[i]=0.;
562
563      std::vector<Voxel>::iterator vox;
564      for(vox=voxelList.begin();vox<voxelList.end();vox++){
565        if(this->isInObject(*vox)){
566          size_t pos=(vox->getX()-xzero) + (vox->getY()-yzero)*xsize + (vox->getZ()-zzero)*spatsize;
567          localFlux[pos] = vox->getF();
568          isObj[pos] = true;
569        }
570      }
571 
572      // work out the WCS coords for each pixel
573      double *world  = new double[size];
574      double xpt,ypt,zpt;
575      for(size_t i=0;i<size;i++){
576        xpt = double( this->getXmin() - border + i%xsize );
577        ypt = double( this->getYmin() - border + (i/xsize)%ysize );
578        zpt = double( this->getZmin() - border + i/(xsize*ysize) );
579        world[i] = head.pixToVel(xpt,ypt,zpt);
580      }
581
582      double integrated = 0.;
583      for(size_t pix=0; pix<spatsize; pix++){ // loop over each spatial pixel.
584        for(size_t z=0; z<zsize; z++){
585          size_t pos =  z*xsize*ysize + pix;
586          if(isObj[pos]){ // if it's an object pixel...
587            double deltaVel;
588            if(z==0)
589              deltaVel = (world[pos+xsize*ysize] - world[pos]);
590            else if(z==(zsize-1))
591              deltaVel = (world[pos] - world[pos-xsize*ysize]);
592            else
593              deltaVel = (world[pos+xsize*ysize] - world[pos-xsize*ysize]) / 2.;
594            integrated += localFlux[pos] * fabs(deltaVel);
595          }
596        }
597      }
598      this->intFlux = integrated;
599
600      delete [] world;
601      delete [] localFlux;
602
603      calcVelWidths(zdim,voxelList,head);
604
605    }
606    else // in this case there is just a 2D image.
607      this->intFlux = this->totalFlux;
608
609    if(head.isWCS()){
610      // correct for the beam size if the flux units string ends in "/beam"
611      if(head.needBeamSize()) this->intFlux  /= head.beam().area();
612    }
613
614  }
615  //--------------------------------------------------------------------
616
617  void Detection::calcIntegFlux(size_t zdim, std::map<Voxel,float> voxelMap, FitsHeader &head)
618  {
619    ///  @details
620    ///  Uses the input WCS to calculate the velocity-integrated flux,
621    ///   putting velocity in units of km/s.
622    ///  The fluxes used are taken from the Voxels, rather than an
623    ///   array of flux values.
624    ///  Integrates over full spatial and velocity range as given
625    ///   by the extrema calculated by calcWCSparams.
626    ///
627    ///  If the flux units end in "/beam" (eg. Jy/beam), then the flux is
628    ///  corrected by the beam size (in pixels). This is done by
629    ///  multiplying the integrated flux by the number of spatial pixels,
630    ///  and dividing by the beam size in pixels (e.g. Jy/beam * pix /
631    ///  pix/beam --> Jy)
632    ///
633    ///  \param zdim The size of the spectral axis (needed to find the velocity widths)
634    ///  \param voxelList The list of Voxels with flux information
635    ///  \param head FitsHeader object that contains the WCS information.
636
637    if(!head.is2D()){
638
639      this->haveParams = true;
640
641      this->intFlux = 0.;
642      std::vector<Voxel> voxelList = this->getPixelSet();
643      std::vector<Voxel>::iterator vox;
644      for(vox=voxelList.begin();vox<voxelList.end();vox++){
645          if(voxelMap.find(*vox) == voxelMap.end()){
646              DUCHAMPERROR("Detection::calcIntegFlux","Voxel list provided does not match");
647              return;
648          }     
649          else {
650              this->intFlux += voxelMap[*vox];
651          }
652      }
653      this->intFlux *= fabs(head.WCS().cdelt[head.WCS().spec]);
654
655      calcVelWidths(zdim,voxelMap,head);
656
657    }
658    else // in this case there is just a 2D image.
659      this->intFlux = this->totalFlux;
660
661    if(head.isWCS()){
662      // correct for the beam size if the flux units string ends in "/beam"
663      if(head.needBeamSize()) this->intFlux  /= head.beam().area();
664    }
665
666  }
667  //--------------------------------------------------------------------
668
669  void Detection::calcIntegFlux(float *fluxArray, size_t *dim, FitsHeader &head, Param &par)
670  {
671    ///  @details
672    ///  Uses the input WCS to calculate the velocity-integrated flux,
673    ///   putting velocity in units of km/s.
674    ///  Integrates over full spatial and velocity range as given
675    ///   by the extrema calculated by calcWCSparams.
676    ///
677    ///  If the flux units end in "/beam" (eg. Jy/beam), then the flux is
678    ///  corrected by the beam size (in pixels). This is done by
679    ///  multiplying the integrated flux by the number of spatial pixels,
680    ///  and dividing by the beam size in pixels (e.g. Jy/beam * pix /
681    ///  pix/beam --> Jy)
682    ///
683    ///  \param fluxArray The array of flux values.
684    ///  \param dim The dimensions of the flux array.
685    ///  \param head FitsHeader object that contains the WCS information.
686
687    this->haveParams = true;
688
689    const int border=1; // include one pixel either side in each direction
690    size_t xsize = std::min(size_t(this->xmax-this->xmin+2*border+1),dim[0]);
691    size_t ysize = std::min(size_t(this->ymax-this->ymin+2*border+1),dim[1]);
692    size_t zsize = std::min(size_t(this->zmax-this->zmin+2*border+1),dim[2]);
693    size_t xzero = size_t(std::max(0L,this->xmin-border));
694    size_t yzero = size_t(std::max(0L,this->ymin-border));
695    size_t zzero = size_t(std::max(0L,this->zmin-border));
696    size_t spatsize = xsize*ysize;
697    size_t size = xsize*ysize*zsize;
698    std::vector <bool> isObj(size,false);
699    double *localFlux = new double[size];
700    for(size_t i=0;i<size;i++) localFlux[i]=0.;
701    float *momMap = new float[spatsize];
702    for(size_t i=0;i<spatsize;i++) momMap[i]=0.;
703    // work out which pixels are object pixels
704    std::vector<Voxel> voxlist = this->getPixelSet();
705    for(std::vector<Voxel>::iterator v=voxlist.begin();v<voxlist.end();v++){
706      size_t spatpos=(v->getX()-xzero) + (v->getY()-yzero)*xsize;
707      size_t pos= spatpos + (v->getZ()-zzero)*spatsize;
708      localFlux[pos] = fluxArray[v->arrayIndex(dim)];
709      momMap[spatpos] += fluxArray[v->arrayIndex(dim)]*head.WCS().cdelt[head.WCS().spec];
710      isObj[pos] = true;
711    }
712 
713     if(!head.is2D()){
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     }
754      else // in this case there is just a 2D image.
755      this->intFlux = this->totalFlux;
756   
757     delete [] localFlux;
758     delete [] momMap;
759   
760
761    if(head.isWCS()){
762      // correct for the beam size if the flux units string ends in "/beam" and we have beam info
763      if(head.needBeamSize()) this->intFlux  /= head.beam().area();
764    }
765
766  }
767  //--------------------------------------------------------------------
768
769  void Detection::findShape(const float *fluxArray, const size_t *dim, FitsHeader &head)
770  {
771      const long border=1; // include one pixel either side in each direction
772      size_t x1 = size_t(std::max(long(0),this->xmin-border));
773      size_t y1 = size_t(std::max(long(0),this->ymin-border));
774      size_t x2 = size_t(std::min(long(dim[0])-1,this->xmax+border));
775      size_t y2 = size_t(std::min(long(dim[1])-1,this->ymax+border));
776      size_t xsize = x2-x1+1;
777      size_t ysize = y2-y1+1;
778      size_t spatsize = xsize*ysize;
779
780      float *momentMap = new float[spatsize];
781      for(size_t i=0;i<spatsize;i++) momentMap[i]=0.;
782      // work out which pixels are object pixels
783      std::vector<Voxel> voxlist = this->getPixelSet();
784      float delta = (head.isWCS() && head.getWCS()->spec>=0) ? fabs(head.WCS().cdelt[head.WCS().spec]) : 1.;
785      float sign = this->negSource ? -1. : 1.;
786      for(std::vector<Voxel>::iterator v=voxlist.begin();v<voxlist.end();v++){
787          size_t spatpos=(v->getX()-x1) + (v->getY()-y1)*xsize;
788          if(spatpos<spatsize)
789              momentMap[spatpos] += fluxArray[v->arrayIndex(dim)] * delta * sign;
790          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);
791      }
792
793      size_t smldim[2]; smldim[0]=xsize; smldim[1]=ysize;
794      Image *smlIm = new Image(smldim);
795      smlIm->saveArray(momentMap,spatsize);
796      smlIm->setMinSize(1);
797      float max = *std::max_element(momentMap,momentMap+spatsize);
798      smlIm->stats().setThreshold(max/2.);
799      std::vector<Object2D> objlist=smlIm->findSources2D();
800
801      Object2D combined;
802      for(size_t i=0;i<objlist.size();i++) combined = combined + objlist[i];
803      std::string extraFlag="";
804      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
805      if(!ellipseGood) {
806          ellipseGood = combined.findEllipse(false, momentMap, xsize, ysize, 0,0, this->getXcentre()-x1, this->getYcentre()-y1); // if that fails, remove the flux weighting
807          extraFlag="W";
808      }
809      float scale=head.getShapeScale();
810      if(ellipseGood){
811          // multiply axes by 2 to go from semi-major to FWHM...
812          this->majorAxis = combined.major() * head.getAvPixScale() * 2. * scale;
813          this->minorAxis = combined.minor() * head.getAvPixScale() * 2. * scale;
814          this->posang =    combined.posAng() * 180. / M_PI;
815          // std::cerr << "*** " << combined.getSize()<< " " << majorAxis<<" " << minorAxis << " " << posang<< " " << scale <<"\n";
816      }
817      else {
818          extraFlag="w";
819          std::pair<double,double> axes = combined.getPrincipalAxes();
820          this->majorAxis = std::max(axes.first,axes.second) * head.getAvPixScale() * scale;
821          this->minorAxis = std::min(axes.first,axes.second) * head.getAvPixScale() * scale;
822          this->posang = combined.getPositionAngle() * 180. / M_PI;
823          // std::cerr << "** " << combined.getSize()<< " " << majorAxis<<" " << minorAxis << " " << posang<< " " << scale <<"\n";
824     }
825      this->flagText += extraFlag;
826     
827
828      delete smlIm;
829      delete [] momentMap;
830  }
831
832  //--------------------------------------------------------------------
833
834  void Detection::calcVelWidths(size_t zdim, std::vector<Voxel> voxelList, FitsHeader &head)
835  {
836    ///  @details
837    /// Calculates the widths of the detection at 20% and 50% of the
838    /// peak integrated flux. The procedure is as follows: first
839    /// generate an integrated flux spectrum (using all given voxels
840    /// that lie in the object's spatial map); find the peak; starting
841    /// at the spectral edges of the detection, move in or out until
842    /// you reach the 20% or 50% peak flux level. Linear interpolation
843    /// between points is done.
844    ///
845    ///  \param zdim The size of the spectral axis in the cube
846    ///  \param voxelList The list of Voxels with flux information
847    ///  \param head FitsHeader object that contains the WCS information.
848
849    float *intSpec = new float[zdim];
850    for(size_t i=0;i<zdim;i++) intSpec[i]=0;
851       
852    Object2D spatMap = this->getSpatialMap();
853    for(int s=0;s<spatMap.getNumScan();s++){
854      std::vector<Voxel>::iterator vox;
855      for(vox=voxelList.begin();vox<voxelList.end();vox++){
856        if(spatMap.isInObject(*vox)){
857          intSpec[vox->getZ()] += vox->getF();
858        }
859      }
860    }
861   
862    calcVelWidths(zdim, intSpec, head);
863
864    delete [] intSpec;
865
866  }
867
868  //--------------------------------------------------------------------
869
870  void Detection::calcVelWidths(size_t zdim, std::map<Voxel,float> voxelMap, FitsHeader &head)
871  {
872    ///  @details
873    /// Calculates the widths of the detection at 20% and 50% of the
874    /// peak integrated flux. The procedure is as follows: first
875    /// generate an integrated flux spectrum (using all given voxels
876    /// that lie in the object's spatial map); find the peak; starting
877    /// at the spectral edges of the detection, move in or out until
878    /// you reach the 20% or 50% peak flux level. Linear interpolation
879    /// between points is done.
880    ///
881    ///  \param zdim The size of the spectral axis in the cube
882    ///  \param voxelList The list of Voxels with flux information
883    ///  \param head FitsHeader object that contains the WCS information.
884
885    float *intSpec = new float[zdim];
886    for(size_t i=0;i<zdim;i++) intSpec[i]=0;
887       
888    std::vector<Voxel> voxelList = this->getPixelSet();
889    std::vector<Voxel>::iterator vox;
890    for(vox=voxelList.begin();vox<voxelList.end();vox++){
891      if(voxelMap.find(*vox) == voxelMap.end()){
892        DUCHAMPERROR("Detection::calcVelWidths","Voxel list provided does not match");
893        return;
894      }
895      else {
896        intSpec[vox->getZ()] += voxelMap[*vox];
897      }
898    }
899
900    calcVelWidths(zdim, intSpec, head);
901
902    delete [] intSpec;
903
904  }
905
906  //--------------------------------------------------------------------
907
908  void Detection::calcVelWidths(size_t zdim, float *intSpec, FitsHeader &head)
909  {
910
911    // finding the 20% & 50% points.  Start at the velmin & velmax
912    //  points. Then, if the int flux there is above the 20%/50%
913    //  limit, go out, otherwise go in. This is to deal with the
914    //  problems from double- (or multi-) peaked sources.
915
916    this->haveParams = true;
917
918    double zpt,xpt=double(this->getXcentre()),ypt=double(this->getYcentre());
919    bool goLeft;
920   
921    if(this->negSource){
922      // if we've inverted the source, need to make the feature
923      // positive for the interpolation/extrapolation to work
924      for(size_t i=0;i<zdim;i++) intSpec[i] *= -1.;
925    }
926
927    float peak=0.;
928    size_t peakLoc=0;
929    for(size_t z=this->getZmin();z<=size_t(this->getZmax());z++) {
930      if(z==0 || peak<intSpec[z]){
931        peak = intSpec[z];
932        peakLoc = z;
933      }
934    }
935   
936    size_t z=this->getZmin();
937    goLeft = intSpec[z]>peak*0.5;
938    if(goLeft) while(z>0 && intSpec[z]>peak*0.5) z--;
939    else       while(z<peakLoc && intSpec[z]<peak*0.5) z++;
940    if(z==0) this->v50min = this->velMin;
941    else{
942      if(goLeft) zpt = z + (peak*0.5-intSpec[z])/(intSpec[z+1]-intSpec[z]);
943      else       zpt = z - (peak*0.5-intSpec[z])/(intSpec[z-1]-intSpec[z]);
944      this->v50min = head.pixToVel(xpt,ypt,zpt);
945    }
946    z=this->getZmax();
947    goLeft = intSpec[z]<peak*0.5;
948    if(goLeft) while(z>peakLoc && intSpec[z]<peak*0.5) z--;
949    else       while(z<zdim    && intSpec[z]>peak*0.5) z++;
950    if(z==zdim) this->v50max = this->velMax;
951    else{
952      if(goLeft) zpt = z + (peak*0.5-intSpec[z])/(intSpec[z+1]-intSpec[z]);
953      else       zpt = z - (peak*0.5-intSpec[z])/(intSpec[z-1]-intSpec[z]);
954      this->v50max = head.pixToVel(xpt,ypt,zpt);
955    }
956    z=this->getZmin();
957    goLeft = intSpec[z]>peak*0.2;
958    if(goLeft) while(z>0 && intSpec[z]>peak*0.2) z--;
959    else       while(z<peakLoc && intSpec[z]<peak*0.2) z++;
960    if(z==0) this->v20min = this->velMin;
961    else{
962      if(goLeft) zpt = z + (peak*0.2-intSpec[z])/(intSpec[z+1]-intSpec[z]);
963      else       zpt = z - (peak*0.2-intSpec[z])/(intSpec[z-1]-intSpec[z]);
964      this->v20min = head.pixToVel(xpt,ypt,zpt);
965    }
966    z=this->getZmax();
967    goLeft = intSpec[z]<peak*0.2;
968    if(goLeft) while(z>peakLoc && intSpec[z]<peak*0.2) z--;
969    else       while(z<zdim    && intSpec[z]>peak*0.2) z++;
970    if(z==zdim) this->v20max = this->velMax;
971    else{
972      if(goLeft) zpt = z + (peak*0.2-intSpec[z])/(intSpec[z+1]-intSpec[z]);
973      else       zpt = z - (peak*0.2-intSpec[z])/(intSpec[z-1]-intSpec[z]);
974      this->v20max = head.pixToVel(xpt,ypt,zpt);
975    }
976
977    this->w20 = fabs(this->v20min - this->v20max);
978    this->w50 = fabs(this->v50min - this->v50max);
979   
980    if(this->negSource){
981      // un-do the inversion, in case intSpec is needed elsewhere
982      for(size_t i=0;i<zdim;i++) intSpec[i] *= -1.;
983    }
984
985
986  }
987  //--------------------------------------------------------------------
988
989  void Detection::calcVelWidths(float *fluxArray, size_t *dim, FitsHeader &head, Param &par)
990  {
991    ///  @details
992    /// Calculates the widths of the detection at 20% and 50% of the
993    /// peak integrated flux. The procedure is as follows: first
994    /// generate an integrated flux spectrum (summing each spatial
995    /// pixel's spectrum); find the peak; starting at the spectral
996    /// edges of the detection, move in or out until you reach the 20%
997    /// or 50% peak flux level. Linear interpolation between points is
998    /// done.
999    ///
1000    ///  \param fluxArray The array of flux values.
1001    ///  \param dim The dimensions of the flux array.
1002    ///  \param head FitsHeader object that contains the WCS information.
1003
1004    if(dim[2] > 2){
1005
1006      float *intSpec = new float[dim[2]];
1007      size_t size=dim[0]*dim[1]*dim[2];
1008      bool *mask = par.makeBlankMask(fluxArray,size);
1009      getIntSpec(*this,fluxArray,dim,mask,1.,intSpec);
1010
1011      this->calcVelWidths(dim[2],intSpec,head);
1012
1013      delete [] intSpec;
1014
1015    }
1016    else{
1017      this->v50min = this->v20min = this->velMin;
1018      this->v50max = this->v20max = this->velMax;
1019      this->w20 = fabs(this->v20min - this->v20max);
1020      this->w50 = fabs(this->v50min - this->v50max);
1021    }
1022
1023  }
1024  //--------------------------------------------------------------------
1025
1026  void Detection::setOffsets(Param &par)
1027  {
1028    ///  @details
1029    /// This function stores the values of the offsets for each cube axis.
1030    /// The offsets are the starting values of the cube axes that may differ from
1031    ///  the default value of 0 (for instance, if a subsection is being used).
1032    /// The values will be used when the detection is outputted.
1033
1034    this->xSubOffset = par.getXOffset();
1035    this->ySubOffset = par.getYOffset();
1036    this->zSubOffset = par.getZOffset();
1037  }
1038  //--------------------------------------------------------------------
1039
1040  bool Detection::hasEnoughChannels(int minNumber)
1041  {
1042    ///  @details
1043    /// A function to determine if the Detection has enough
1044    /// contiguous channels to meet the minimum requirement
1045    /// given as the argument.
1046    /// \param minNumber How many channels is the minimum acceptable number?
1047    /// \return True if there is at least one occurence of minNumber consecutive
1048    /// channels present to return true. False otherwise.
1049
1050    // Preferred method -- need a set of minNumber consecutive channels present.
1051
1052    int numChan = this->getMaxAdjacentChannels();
1053    bool result = (numChan >= minNumber);
1054
1055    return result;
1056 
1057  }
1058  //--------------------------------------------------------------------
1059
1060  void Detection::drawBorders(int xoffset, int yoffset)
1061  {
1062    ///  @details
1063    /// For a given object, draw borders around the spatial extent of the object.
1064    /// \param xoffset The offset from 0 of the x-axis of the plotting window
1065    /// \param yoffset The offset from 0 of the y-axis of the plotting window
1066
1067    if(!cpgtest()){
1068      DUCHAMPERROR("Draw Borders","There is no PGPlot device open.");
1069    }
1070    else{
1071
1072      float x1,x2,y1,y2;
1073      cpgqwin(&x1,&x2,&y1,&y2);
1074      int xsize = int(x2 - x1) + 1;
1075      int ysize = int(y2 - y1) + 1;
1076
1077      cpgswin(0,xsize-1,0,ysize-1);
1078
1079      std::vector<std::vector<Voxel> > vertexSets = this->getVertexSet();
1080
1081      for(size_t n=0;n<vertexSets.size();n++){
1082          // for each set of vertices
1083
1084          cpgmove(vertexSets[n][0].getX()-xoffset,vertexSets[n][0].getY()-yoffset);
1085          for(size_t i=1;i<vertexSets[n].size();i++)
1086              cpgdraw(vertexSets[n][i].getX()-xoffset,vertexSets[n][i].getY()-yoffset);
1087
1088      }
1089
1090      cpgswin(x1,x2,y1,y2);
1091 
1092    }   
1093
1094  }
1095
1096 
1097  void Detection::addDetection(Detection &other)
1098  {
1099    for(std::map<long, Object2D>::iterator it = other.chanlist.begin(); it!=other.chanlist.end();it++)
1100      //      this->addChannel(*it);
1101      this->addChannel(it->first, it->second);
1102    this->haveParams = false; // make it appear as if the parameters haven't been calculated, so that we can re-calculate them 
1103  }
1104
1105  Detection operator+ (Detection &lhs, Detection &rhs)
1106  {
1107    Detection output = lhs;
1108    for(std::map<long, Object2D>::iterator it = rhs.chanlist.begin(); it!=rhs.chanlist.end();it++)
1109      output.addChannel(it->first, it->second);
1110    output.haveParams = false; // make it appear as if the parameters haven't been calculated, so that we can re-calculate them
1111    return output;
1112  }
1113   
1114
1115  bool Detection::canMerge(Detection &other, Param &par)
1116  {
1117    bool near = this->isNear(other,par);
1118    if(near) return this->isClose(other,par);
1119    else return near;
1120  }
1121
1122  bool Detection::isNear(Detection &other, Param &par)
1123  {
1124
1125    bool flagAdj = par.getFlagAdjacent();
1126    float threshS = par.getThreshS();
1127    float threshV = par.getThreshV();
1128
1129    long gap;
1130    if(flagAdj) gap = 1;
1131    else gap = long( ceil(threshS) );
1132
1133    bool areNear;
1134    // Test X ranges
1135    if((this->xmin-gap)<other.xmin) areNear=((this->xmax+gap)>=other.xmin);
1136    else areNear=(other.xmax>=(this->xmin-gap));
1137    // Test Y ranges
1138    if(areNear){
1139      if((this->ymin-gap)<other.ymin) areNear=areNear&&((this->ymax+gap)>=other.ymin);
1140      else areNear=areNear&&(other.ymax>=(this->ymin-gap));
1141    }
1142    // Test Z ranges
1143    if(areNear){
1144      gap = long(ceil(threshV));
1145      if((this->zmin-gap)<other.zmin) areNear=areNear&&((this->zmax+gap)>=other.zmin);
1146      else areNear=areNear&&(other.zmax>=(this->zmin-gap));
1147    }
1148   
1149    return areNear;
1150
1151  }
1152
1153  bool Detection::isClose(Detection &other, Param &par)
1154  {
1155    bool close = false;   // this will be the value returned
1156   
1157    bool flagAdj = par.getFlagAdjacent();
1158    float threshS = par.getThreshS();
1159    float threshV = par.getThreshV();
1160
1161    //
1162    // If we get to here, the pixel ranges overlap -- so we do a
1163    // pixel-by-pixel comparison to make sure they are actually
1164    // "close" according to the thresholds.  Otherwise, close=false,
1165    // and so don't need to do anything else before returning.
1166    //
1167
1168    std::vector<long> zlist1 = this->getChannelList();
1169    std::vector<long> zlist2 = other.getChannelList();
1170    Scan test1,test2;
1171
1172    for(size_t ct1=0; (!close && (ct1<zlist1.size())); ct1++){
1173      for(size_t ct2=0; (!close && (ct2<zlist2.size())); ct2++){
1174       
1175        if(abs(zlist1[ct1]-zlist2[ct2])<=threshV){
1176             
1177          Object2D temp1 = this->getChanMap(zlist1[ct1]);
1178          Object2D temp2 = other.getChanMap(zlist2[ct2]);
1179
1180          close = temp1.canMerge(temp2,threshS,flagAdj);
1181
1182        }
1183
1184      }
1185    }
1186       
1187    return close;
1188   
1189  }
1190
1191
1192
1193}
Note: See TracBrowser for help on using the repository browser.