source: tags/release-1.3.2/src/Detection/detection.cc

Last change on this file 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.