source: trunk/src/Detection/detection.cc

Last change on this file was 1444, checked in by MatthewWhiting, 7 years ago

Another fix for build errors

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