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

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

Simplifying the principle axes calculations, and how they are called from findShape.

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