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

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

Ticket #132 - New code to fit the ellipse to the moment-0 map thresholded at half its peak - this then provides FWHM esimates for major/minor axes. Also have adaptive units for these axes, scaling to get the numbers not too small and adjusting the units accordingly. 2D images also have the shape calculated too now.

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