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

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

Silencing some compiler warnings that are cropping up with the new Xcode on the mac.

File size: 40.5 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       const int border = 1; // include one pixel either side in each direction
642      size_t xsize = size_t(this->xmax-this->xmin+2*border+1);
643      size_t ysize = size_t(this->ymax-this->ymin+2*border+1);
644      size_t zsize = size_t(this->zmax-this->zmin+2*border+1);
645      size_t xzero = size_t(std::max(0L,this->xmin-border));
646      size_t yzero = size_t(std::max(0L,this->ymin-border));
647      size_t zzero = size_t(std::max(0L,this->zmin-border));
648      size_t spatsize=xsize*ysize;
649      size_t size = xsize*ysize*zsize;
650
651      this->intFlux = 0.;
652      std::vector<Voxel> voxelList = this->getPixelSet();
653      std::vector<Voxel>::iterator vox;
654      for(vox=voxelList.begin();vox<voxelList.end();vox++){
655          if(voxelMap.find(*vox) == voxelMap.end()){
656              DUCHAMPERROR("Detection::calcIntegFlux","Voxel list provided does not match");
657              return;
658          }     
659          else {
660              this->intFlux += voxelMap[*vox];
661          }
662      }
663      this->intFlux *= fabs(head.WCS().cdelt[head.WCS().spec]);
664
665      calcVelWidths(zdim,voxelMap,head);
666
667    }
668    else // in this case there is just a 2D image.
669      this->intFlux = this->totalFlux;
670
671    if(head.isWCS()){
672      // correct for the beam size if the flux units string ends in "/beam"
673      if(head.needBeamSize()) this->intFlux  /= head.beam().area();
674    }
675
676  }
677  //--------------------------------------------------------------------
678
679  void Detection::calcIntegFlux(float *fluxArray, size_t *dim, FitsHeader &head, Param &par)
680  {
681    ///  @details
682    ///  Uses the input WCS to calculate the velocity-integrated flux,
683    ///   putting velocity in units of km/s.
684    ///  Integrates over full spatial and velocity range as given
685    ///   by the extrema calculated by calcWCSparams.
686    ///
687    ///  If the flux units end in "/beam" (eg. Jy/beam), then the flux is
688    ///  corrected by the beam size (in pixels). This is done by
689    ///  multiplying the integrated flux by the number of spatial pixels,
690    ///  and dividing by the beam size in pixels (e.g. Jy/beam * pix /
691    ///  pix/beam --> Jy)
692    ///
693    ///  \param fluxArray The array of flux values.
694    ///  \param dim The dimensions of the flux array.
695    ///  \param head FitsHeader object that contains the WCS information.
696
697    this->haveParams = true;
698
699    const int border=1; // include one pixel either side in each direction
700    size_t xsize = std::min(size_t(this->xmax-this->xmin+2*border+1),dim[0]);
701    size_t ysize = std::min(size_t(this->ymax-this->ymin+2*border+1),dim[1]);
702    size_t zsize = std::min(size_t(this->zmax-this->zmin+2*border+1),dim[2]);
703    size_t xzero = size_t(std::max(0L,this->xmin-border));
704    size_t yzero = size_t(std::max(0L,this->ymin-border));
705    size_t zzero = size_t(std::max(0L,this->zmin-border));
706    size_t spatsize = xsize*ysize;
707    size_t size = xsize*ysize*zsize;
708    std::vector <bool> isObj(size,false);
709    double *localFlux = new double[size];
710    for(size_t i=0;i<size;i++) localFlux[i]=0.;
711    float *momMap = new float[spatsize];
712    for(size_t i=0;i<spatsize;i++) momMap[i]=0.;
713    // work out which pixels are object pixels
714    std::vector<Voxel> voxlist = this->getPixelSet();
715    for(std::vector<Voxel>::iterator v=voxlist.begin();v<voxlist.end();v++){
716      size_t spatpos=(v->getX()-xzero) + (v->getY()-yzero)*xsize;
717      size_t pos= spatpos + (v->getZ()-zzero)*spatsize;
718      localFlux[pos] = fluxArray[v->arrayIndex(dim)];
719      momMap[spatpos] += fluxArray[v->arrayIndex(dim)]*head.WCS().cdelt[head.WCS().spec];
720      isObj[pos] = true;
721    }
722 
723     if(!head.is2D()){
724
725     // work out the WCS coords for each pixel
726      double *world  = new double[size];
727      double xpt,ypt,zpt;
728      size_t i=0;
729      for(size_t z=zzero;z<zzero+zsize;z++){
730        for(size_t y=yzero;y<yzero+ysize;y++){
731          for(size_t x=xzero;x<xzero+xsize;x++){
732            xpt=double(x);
733            ypt=double(y);
734            zpt=double(z);
735            world[i++] = head.pixToVel(xpt,ypt,zpt);
736          }
737        }
738      }
739
740      double integrated = 0.;
741      for(size_t pix=0; pix<xsize*ysize; pix++){ // loop over each spatial pixel.
742        for(size_t z=0; z<zsize; z++){
743          size_t pos =  z*xsize*ysize + pix;
744          if(isObj[pos]){ // if it's an object pixel...
745            double deltaVel;
746            if(z==0)
747              deltaVel = (world[pos+xsize*ysize] - world[pos]);
748            else if(z==(zsize-1))
749              deltaVel = (world[pos] - world[pos-xsize*ysize]);
750            else
751              deltaVel = (world[pos+xsize*ysize] - world[pos-xsize*ysize]) / 2.;
752            integrated += localFlux[pos] * fabs(deltaVel);
753          }
754        }
755      }
756
757      delete [] world;
758     
759      this->intFlux = integrated;
760     
761      calcVelWidths(fluxArray, dim, head, par);
762
763     }
764      else // in this case there is just a 2D image.
765      this->intFlux = this->totalFlux;
766   
767     delete [] localFlux;
768     delete [] momMap;
769   
770
771    if(head.isWCS()){
772      // correct for the beam size if the flux units string ends in "/beam" and we have beam info
773      if(head.needBeamSize()) this->intFlux  /= head.beam().area();
774    }
775
776  }
777  //--------------------------------------------------------------------
778
779  void Detection::findShape(const float *fluxArray, const size_t *dim, FitsHeader &head)
780  {
781      const long border=1; // include one pixel either side in each direction
782      size_t x1 = size_t(std::max(long(0),this->xmin-border));
783      size_t y1 = size_t(std::max(long(0),this->ymin-border));
784      size_t x2 = size_t(std::min(long(dim[0])-1,this->xmax+border));
785      size_t y2 = size_t(std::min(long(dim[1])-1,this->ymax+border));
786      size_t xsize = x2-x1+1;
787      size_t ysize = y2-y1+1;
788      size_t spatsize = xsize*ysize;
789
790      float *momentMap = new float[spatsize];
791      for(size_t i=0;i<spatsize;i++) momentMap[i]=0.;
792      // work out which pixels are object pixels
793      std::vector<Voxel> voxlist = this->getPixelSet();
794      float delta = (head.isWCS() && head.getWCS()->spec>=0) ? fabs(head.WCS().cdelt[head.WCS().spec]) : 1.;
795      float sign = this->negSource ? -1. : 1.;
796      for(std::vector<Voxel>::iterator v=voxlist.begin();v<voxlist.end();v++){
797          size_t spatpos=(v->getX()-x1) + (v->getY()-y1)*xsize;
798          if(spatpos<spatsize)
799              momentMap[spatpos] += fluxArray[v->arrayIndex(dim)] * delta * sign;
800          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);
801      }
802
803      size_t smldim[2]; smldim[0]=xsize; smldim[1]=ysize;
804      Image *smlIm = new Image(smldim);
805      smlIm->saveArray(momentMap,spatsize);
806      smlIm->setMinSize(1);
807      float max = *std::max_element(momentMap,momentMap+spatsize);
808      smlIm->stats().setThreshold(max/2.);
809      std::vector<Object2D> objlist=smlIm->findSources2D();
810
811      Object2D combined;
812      for(size_t i=0;i<objlist.size();i++) combined = combined + objlist[i];
813      std::string extraFlag="";
814      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
815      if(!ellipseGood) {
816          ellipseGood = combined.findEllipse(false, momentMap, xsize, ysize, 0,0, this->getXcentre()-x1, this->getYcentre()-y1); // if that fails, remove the flux weighting
817          extraFlag="W";
818      }
819      float scale=head.getShapeScale();
820      if(ellipseGood){
821          // multiply axes by 2 to go from semi-major to FWHM...
822          this->majorAxis = combined.major() * head.getAvPixScale() * 2. * scale;
823          this->minorAxis = combined.minor() * head.getAvPixScale() * 2. * scale;
824          this->posang =    combined.posAng() * 180. / M_PI;
825          // std::cerr << "*** " << combined.getSize()<< " " << majorAxis<<" " << minorAxis << " " << posang<< " " << scale <<"\n";
826      }
827      else {
828          extraFlag="w";
829          std::pair<double,double> axes = combined.getPrincipalAxes();
830          this->majorAxis = std::max(axes.first,axes.second) * head.getAvPixScale() * scale;
831          this->minorAxis = std::min(axes.first,axes.second) * head.getAvPixScale() * scale;
832          this->posang = combined.getPositionAngle() * 180. / M_PI;
833          // std::cerr << "** " << combined.getSize()<< " " << majorAxis<<" " << minorAxis << " " << posang<< " " << scale <<"\n";
834     }
835      this->flagText += extraFlag;
836     
837
838      delete smlIm;
839      delete [] momentMap;
840  }
841
842  //--------------------------------------------------------------------
843
844  void Detection::calcVelWidths(size_t zdim, std::vector<Voxel> voxelList, FitsHeader &head)
845  {
846    ///  @details
847    /// Calculates the widths of the detection at 20% and 50% of the
848    /// peak integrated flux. The procedure is as follows: first
849    /// generate an integrated flux spectrum (using all given voxels
850    /// that lie in the object's spatial map); find the peak; starting
851    /// at the spectral edges of the detection, move in or out until
852    /// you reach the 20% or 50% peak flux level. Linear interpolation
853    /// between points is done.
854    ///
855    ///  \param zdim The size of the spectral axis in the cube
856    ///  \param voxelList The list of Voxels with flux information
857    ///  \param head FitsHeader object that contains the WCS information.
858
859    float *intSpec = new float[zdim];
860    for(size_t i=0;i<zdim;i++) intSpec[i]=0;
861       
862    Object2D spatMap = this->getSpatialMap();
863    for(int s=0;s<spatMap.getNumScan();s++){
864      std::vector<Voxel>::iterator vox;
865      for(vox=voxelList.begin();vox<voxelList.end();vox++){
866        if(spatMap.isInObject(*vox)){
867          intSpec[vox->getZ()] += vox->getF();
868        }
869      }
870    }
871   
872    calcVelWidths(zdim, intSpec, head);
873
874    delete [] intSpec;
875
876  }
877
878  //--------------------------------------------------------------------
879
880  void Detection::calcVelWidths(size_t zdim, std::map<Voxel,float> voxelMap, FitsHeader &head)
881  {
882    ///  @details
883    /// Calculates the widths of the detection at 20% and 50% of the
884    /// peak integrated flux. The procedure is as follows: first
885    /// generate an integrated flux spectrum (using all given voxels
886    /// that lie in the object's spatial map); find the peak; starting
887    /// at the spectral edges of the detection, move in or out until
888    /// you reach the 20% or 50% peak flux level. Linear interpolation
889    /// between points is done.
890    ///
891    ///  \param zdim The size of the spectral axis in the cube
892    ///  \param voxelList The list of Voxels with flux information
893    ///  \param head FitsHeader object that contains the WCS information.
894
895    float *intSpec = new float[zdim];
896    for(size_t i=0;i<zdim;i++) intSpec[i]=0;
897       
898    std::vector<Voxel> voxelList = this->getPixelSet();
899    std::vector<Voxel>::iterator vox;
900    for(vox=voxelList.begin();vox<voxelList.end();vox++){
901      if(voxelMap.find(*vox) == voxelMap.end()){
902        DUCHAMPERROR("Detection::calcVelWidths","Voxel list provided does not match");
903        return;
904      }
905      else {
906        intSpec[vox->getZ()] += voxelMap[*vox];
907      }
908    }
909
910    calcVelWidths(zdim, intSpec, head);
911
912    delete [] intSpec;
913
914  }
915
916  //--------------------------------------------------------------------
917
918  void Detection::calcVelWidths(size_t zdim, float *intSpec, FitsHeader &head)
919  {
920
921    // finding the 20% & 50% points.  Start at the velmin & velmax
922    //  points. Then, if the int flux there is above the 20%/50%
923    //  limit, go out, otherwise go in. This is to deal with the
924    //  problems from double- (or multi-) peaked sources.
925
926    this->haveParams = true;
927
928    double zpt,xpt=double(this->getXcentre()),ypt=double(this->getYcentre());
929    bool goLeft;
930   
931    if(this->negSource){
932      // if we've inverted the source, need to make the feature
933      // positive for the interpolation/extrapolation to work
934      for(size_t i=0;i<zdim;i++) intSpec[i] *= -1.;
935    }
936
937    float peak=0.;
938    size_t peakLoc=0;
939    for(size_t z=this->getZmin();z<=size_t(this->getZmax());z++) {
940      if(z==0 || peak<intSpec[z]){
941        peak = intSpec[z];
942        peakLoc = z;
943      }
944    }
945   
946    size_t z=this->getZmin();
947    goLeft = intSpec[z]>peak*0.5;
948    if(goLeft) while(z>0 && intSpec[z]>peak*0.5) z--;
949    else       while(z<peakLoc && intSpec[z]<peak*0.5) z++;
950    if(z==0) this->v50min = this->velMin;
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->v50min = head.pixToVel(xpt,ypt,zpt);
955    }
956    z=this->getZmax();
957    goLeft = intSpec[z]<peak*0.5;
958    if(goLeft) while(z>peakLoc && intSpec[z]<peak*0.5) z--;
959    else       while(z<zdim    && intSpec[z]>peak*0.5) z++;
960    if(z==zdim) this->v50max = this->velMax;
961    else{
962      if(goLeft) zpt = z + (peak*0.5-intSpec[z])/(intSpec[z+1]-intSpec[z]);
963      else       zpt = z - (peak*0.5-intSpec[z])/(intSpec[z-1]-intSpec[z]);
964      this->v50max = head.pixToVel(xpt,ypt,zpt);
965    }
966    z=this->getZmin();
967    goLeft = intSpec[z]>peak*0.2;
968    if(goLeft) while(z>0 && intSpec[z]>peak*0.2) z--;
969    else       while(z<peakLoc && intSpec[z]<peak*0.2) z++;
970    if(z==0) this->v20min = this->velMin;
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->v20min = head.pixToVel(xpt,ypt,zpt);
975    }
976    z=this->getZmax();
977    goLeft = intSpec[z]<peak*0.2;
978    if(goLeft) while(z>peakLoc && intSpec[z]<peak*0.2) z--;
979    else       while(z<zdim    && intSpec[z]>peak*0.2) z++;
980    if(z==zdim) this->v20max = this->velMax;
981    else{
982      if(goLeft) zpt = z + (peak*0.2-intSpec[z])/(intSpec[z+1]-intSpec[z]);
983      else       zpt = z - (peak*0.2-intSpec[z])/(intSpec[z-1]-intSpec[z]);
984      this->v20max = head.pixToVel(xpt,ypt,zpt);
985    }
986
987    this->w20 = fabs(this->v20min - this->v20max);
988    this->w50 = fabs(this->v50min - this->v50max);
989   
990    if(this->negSource){
991      // un-do the inversion, in case intSpec is needed elsewhere
992      for(size_t i=0;i<zdim;i++) intSpec[i] *= -1.;
993    }
994
995
996  }
997  //--------------------------------------------------------------------
998
999  void Detection::calcVelWidths(float *fluxArray, size_t *dim, FitsHeader &head, Param &par)
1000  {
1001    ///  @details
1002    /// Calculates the widths of the detection at 20% and 50% of the
1003    /// peak integrated flux. The procedure is as follows: first
1004    /// generate an integrated flux spectrum (summing each spatial
1005    /// pixel's spectrum); find the peak; starting at the spectral
1006    /// edges of the detection, move in or out until you reach the 20%
1007    /// or 50% peak flux level. Linear interpolation between points is
1008    /// done.
1009    ///
1010    ///  \param fluxArray The array of flux values.
1011    ///  \param dim The dimensions of the flux array.
1012    ///  \param head FitsHeader object that contains the WCS information.
1013
1014    if(dim[2] > 2){
1015
1016      float *intSpec = new float[dim[2]];
1017      size_t size=dim[0]*dim[1]*dim[2];
1018      bool *mask = par.makeBlankMask(fluxArray,size);
1019      getIntSpec(*this,fluxArray,dim,mask,1.,intSpec);
1020
1021      this->calcVelWidths(dim[2],intSpec,head);
1022
1023      delete [] intSpec;
1024
1025    }
1026    else{
1027      this->v50min = this->v20min = this->velMin;
1028      this->v50max = this->v20max = this->velMax;
1029      this->w20 = fabs(this->v20min - this->v20max);
1030      this->w50 = fabs(this->v50min - this->v50max);
1031    }
1032
1033  }
1034  //--------------------------------------------------------------------
1035
1036  void Detection::setOffsets(Param &par)
1037  {
1038    ///  @details
1039    /// This function stores the values of the offsets for each cube axis.
1040    /// The offsets are the starting values of the cube axes that may differ from
1041    ///  the default value of 0 (for instance, if a subsection is being used).
1042    /// The values will be used when the detection is outputted.
1043
1044    this->xSubOffset = par.getXOffset();
1045    this->ySubOffset = par.getYOffset();
1046    this->zSubOffset = par.getZOffset();
1047  }
1048  //--------------------------------------------------------------------
1049
1050  bool Detection::hasEnoughChannels(int minNumber)
1051  {
1052    ///  @details
1053    /// A function to determine if the Detection has enough
1054    /// contiguous channels to meet the minimum requirement
1055    /// given as the argument.
1056    /// \param minNumber How many channels is the minimum acceptable number?
1057    /// \return True if there is at least one occurence of minNumber consecutive
1058    /// channels present to return true. False otherwise.
1059
1060    // Preferred method -- need a set of minNumber consecutive channels present.
1061
1062    int numChan = this->getMaxAdjacentChannels();
1063    bool result = (numChan >= minNumber);
1064
1065    return result;
1066 
1067  }
1068  //--------------------------------------------------------------------
1069
1070  void Detection::drawBorders(int xoffset, int yoffset)
1071  {
1072    ///  @details
1073    /// For a given object, draw borders around the spatial extent of the object.
1074    /// \param xoffset The offset from 0 of the x-axis of the plotting window
1075    /// \param yoffset The offset from 0 of the y-axis of the plotting window
1076
1077    if(!cpgtest()){
1078      DUCHAMPERROR("Draw Borders","There is no PGPlot device open.");
1079    }
1080    else{
1081
1082      float x1,x2,y1,y2;
1083      cpgqwin(&x1,&x2,&y1,&y2);
1084      int xsize = int(x2 - x1) + 1;
1085      int ysize = int(y2 - y1) + 1;
1086
1087      cpgswin(0,xsize-1,0,ysize-1);
1088
1089      std::vector<std::vector<Voxel> > vertexSets = this->getVertexSet();
1090
1091      for(size_t n=0;n<vertexSets.size();n++){
1092          // for each set of vertices
1093
1094          cpgmove(vertexSets[n][0].getX()-xoffset,vertexSets[n][0].getY()-yoffset);
1095          for(size_t i=1;i<vertexSets[n].size();i++)
1096              cpgdraw(vertexSets[n][i].getX()-xoffset,vertexSets[n][i].getY()-yoffset);
1097
1098      }
1099
1100      cpgswin(x1,x2,y1,y2);
1101 
1102    }   
1103
1104  }
1105
1106 
1107  void Detection::addDetection(Detection &other)
1108  {
1109    for(std::map<long, Object2D>::iterator it = other.chanlist.begin(); it!=other.chanlist.end();it++)
1110      //      this->addChannel(*it);
1111      this->addChannel(it->first, it->second);
1112    this->haveParams = false; // make it appear as if the parameters haven't been calculated, so that we can re-calculate them 
1113  }
1114
1115  Detection operator+ (Detection &lhs, Detection &rhs)
1116  {
1117    Detection output = lhs;
1118    for(std::map<long, Object2D>::iterator it = rhs.chanlist.begin(); it!=rhs.chanlist.end();it++)
1119      output.addChannel(it->first, it->second);
1120    output.haveParams = false; // make it appear as if the parameters haven't been calculated, so that we can re-calculate them
1121    return output;
1122  }
1123   
1124
1125  bool Detection::canMerge(Detection &other, Param &par)
1126  {
1127    bool near = this->isNear(other,par);
1128    if(near) return this->isClose(other,par);
1129    else return near;
1130  }
1131
1132  bool Detection::isNear(Detection &other, Param &par)
1133  {
1134
1135    bool flagAdj = par.getFlagAdjacent();
1136    float threshS = par.getThreshS();
1137    float threshV = par.getThreshV();
1138
1139    long gap;
1140    if(flagAdj) gap = 1;
1141    else gap = long( ceil(threshS) );
1142
1143    bool areNear;
1144    // Test X ranges
1145    if((this->xmin-gap)<other.xmin) areNear=((this->xmax+gap)>=other.xmin);
1146    else areNear=(other.xmax>=(this->xmin-gap));
1147    // Test Y ranges
1148    if(areNear){
1149      if((this->ymin-gap)<other.ymin) areNear=areNear&&((this->ymax+gap)>=other.ymin);
1150      else areNear=areNear&&(other.ymax>=(this->ymin-gap));
1151    }
1152    // Test Z ranges
1153    if(areNear){
1154      gap = long(ceil(threshV));
1155      if((this->zmin-gap)<other.zmin) areNear=areNear&&((this->zmax+gap)>=other.zmin);
1156      else areNear=areNear&&(other.zmax>=(this->zmin-gap));
1157    }
1158   
1159    return areNear;
1160
1161  }
1162
1163  bool Detection::isClose(Detection &other, Param &par)
1164  {
1165    bool close = false;   // this will be the value returned
1166   
1167    bool flagAdj = par.getFlagAdjacent();
1168    float threshS = par.getThreshS();
1169    float threshV = par.getThreshV();
1170
1171    //
1172    // If we get to here, the pixel ranges overlap -- so we do a
1173    // pixel-by-pixel comparison to make sure they are actually
1174    // "close" according to the thresholds.  Otherwise, close=false,
1175    // and so don't need to do anything else before returning.
1176    //
1177
1178    std::vector<long> zlist1 = this->getChannelList();
1179    std::vector<long> zlist2 = other.getChannelList();
1180    Scan test1,test2;
1181
1182    for(size_t ct1=0; (!close && (ct1<zlist1.size())); ct1++){
1183      for(size_t ct2=0; (!close && (ct2<zlist2.size())); ct2++){
1184       
1185        if(abs(zlist1[ct1]-zlist2[ct2])<=threshV){
1186             
1187          Object2D temp1 = this->getChanMap(zlist1[ct1]);
1188          Object2D temp2 = other.getChanMap(zlist2[ct2]);
1189
1190          close = temp1.canMerge(temp2,threshS,flagAdj);
1191
1192        }
1193
1194      }
1195    }
1196       
1197    return close;
1198   
1199  }
1200
1201
1202
1203}
Note: See TracBrowser for help on using the repository browser.