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

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

Adding in versions of the parametrisation functions that take std:maps of Voxels (suggested in #123).

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