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

Last change on this file since 1131 was 1131, checked in by MatthewWhiting, 12 years ago

Ticket #132 - further improvements: getting the calculation and reporting of the position angle right (in particular so that kvis displays it properly - yet to do the same for casa/DS9), and putting the maj/min/pa values in the various output catalogues. Also changing the headers for the spectral plots - these now have the shape info rather that W_RA/W_DEC.

File size: 39.4 KB
RevLine 
[300]1// -----------------------------------------------------------------------
2// detection.cc : Member functions for the Detection class.
3// -----------------------------------------------------------------------
4// Copyright (C) 2006, Matthew Whiting, ATNF
5//
6// This program is free software; you can redistribute it and/or modify it
7// under the terms of the GNU General Public License as published by the
8// Free Software Foundation; either version 2 of the License, or (at your
9// option) any later version.
10//
11// Duchamp is distributed in the hope that it will be useful, but WITHOUT
12// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14// for more details.
15//
16// You should have received a copy of the GNU General Public License
17// along with Duchamp; if not, write to the Free Software Foundation,
18// Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
19//
20// Correspondence concerning Duchamp may be directed to:
21//    Internet email: Matthew.Whiting [at] atnf.csiro.au
22//    Postal address: Dr. Matthew Whiting
23//                    Australia Telescope National Facility, CSIRO
24//                    PO Box 76
25//                    Epping NSW 1710
26//                    AUSTRALIA
27// -----------------------------------------------------------------------
[3]28#include <iostream>
29#include <iomanip>
30#include <vector>
[863]31#include <map>
[3]32#include <string>
[394]33#include <wcslib/wcs.h>
[69]34#include <math.h>
[393]35#include <duchamp/duchamp.hh>
36#include <duchamp/param.hh>
37#include <duchamp/fitsHeader.hh>
38#include <duchamp/Utils/utils.hh>
39#include <duchamp/PixelMap/Voxel.hh>
40#include <duchamp/PixelMap/Object3D.hh>
41#include <duchamp/Detection/detection.hh>
[463]42#include <duchamp/Cubes/cubeUtils.hh>
[1061]43#include <duchamp/Outputs/columns.hh>
[3]44
[258]45using namespace PixelInfo;
46
[378]47namespace duchamp
[365]48{
49
[570]50  void Detection::defaultDetection()
[378]51  {
[570]52    this->xSubOffset = 0;
53    this->ySubOffset = 0;
54    this->zSubOffset = 0;
[681]55    this->haveParams = false;
[570]56    this->totalFlux = 0.;
57    this->peakFlux = 0.;
58    this->intFlux = 0.;
59    this->xpeak = 0;
60    this->ypeak = 0;
61    this->zpeak = 0;
62    this->peakSNR = 0.;
63    this->xCentroid = 0.;
64    this->yCentroid = 0.;
65    this->zCentroid = 0.;
66    this->centreType="centroid";
[378]67    this->negSource = false;
68    this->flagText="";
[468]69    this->id = -1;
[570]70    this->name = "";
71    this->flagWCS=false;
72    this->specOK = true;
73    this->raS = "";
74    this->decS = "";
75    this->ra = 0.;
76    this->dec = 0.;
77    this->raWidth = 0.;
78    this->decWidth = 0.;
79    this->majorAxis = 0.;
80    this->minorAxis = 0.;
81    this->posang = 0.;
82    this->specUnits = "";
[947]83    this->specType = "";
[570]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.;
[1061]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;
[378]104  }
[218]105
[570]106  Detection::Detection():
107    Object3D()
[378]108  {
[570]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  {
[378]121    operator=(d);
122  }
[218]123
[378]124  Detection& Detection::operator= (const Detection& d)
125  {
[570]126    ((Object3D &) *this) = d;
[378]127    this->xSubOffset   = d.xSubOffset;
128    this->ySubOffset   = d.ySubOffset;
129    this->zSubOffset   = d.zSubOffset;
[681]130    this->haveParams   = d.haveParams;
[378]131    this->totalFlux    = d.totalFlux;
[461]132    this->intFlux      = d.intFlux;
[378]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;
[461]151    this->dec          = d.dec;
152    this->raWidth      = d.raWidth;
[378]153    this->decWidth     = d.decWidth;
[473]154    this->majorAxis    = d.majorAxis;
155    this->minorAxis    = d.minorAxis;
156    this->posang       = d.posang;
[378]157    this->specUnits    = d.specUnits;
[947]158    this->specType     = d.specType;
[378]159    this->fluxUnits    = d.fluxUnits;
160    this->intFluxUnits = d.intFluxUnits;
[461]161    this->lngtype      = d.lngtype;
162    this->lattype      = d.lattype;
[378]163    this->vel          = d.vel;
164    this->velWidth     = d.velWidth;
165    this->velMin       = d.velMin;
166    this->velMax       = d.velMax;
[463]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;
[378]173    this->posPrec      = d.posPrec;
174    this->xyzPrec      = d.xyzPrec;
175    this->fintPrec     = d.fintPrec;
176    this->fpeakPrec    = d.fpeakPrec;
[461]177    this->velPrec      = d.velPrec;
[378]178    this->snrPrec      = d.snrPrec;
179    return *this;
180  }
[3]181
[378]182  //--------------------------------------------------------------------
[570]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  }
[258]189
[570]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
[417]206  bool Detection::voxelListsMatch(std::vector<Voxel> voxelList)
207  {
[528]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.
[417]215
216    bool listsMatch = true;
217    // compare sizes
218    listsMatch = listsMatch && (voxelList.size() == this->getSize());
219    if(!listsMatch) return listsMatch;
220
[463]221    // make sure all Detection pixels are in voxel list
222    listsMatch = listsMatch && this->voxelListCovered(voxelList);
223
[417]224    // make sure all voxels are in Detection
[623]225    std::vector<Voxel>::iterator vox;
226    for(vox=voxelList.begin();vox<voxelList.end();vox++)
227      listsMatch = listsMatch && this->isInObject(*vox);
[463]228
229    return listsMatch;
230
231  }
232  //--------------------------------------------------------------------
233
234  bool Detection::voxelListCovered(std::vector<Voxel> voxelList)
235  {
[528]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.
[463]243
244    bool listsMatch = true;
245
[417]246    // make sure all Detection pixels are in voxel list
[623]247    size_t v1=0;
[570]248    std::vector<Voxel> detpixlist = this->getPixelSet();
249    while(listsMatch && v1<detpixlist.size()){
[417]250      bool inList = false;
[623]251      size_t v2=0;
[417]252      while(!inList && v2<voxelList.size()){
[570]253        inList = inList || detpixlist[v1].match(voxelList[v2]);
[417]254        v2++;
255      }
256      listsMatch = listsMatch && inList;
[418]257      v1++;
[417]258    }
259
260    return listsMatch;
261
262  }
263  //--------------------------------------------------------------------
264
265  void Detection::calcFluxes(std::vector<Voxel> voxelList)
266  {
[528]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.
[681]274   
275    //    this->haveParams = true;
[417]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
[463]283    if(!this->voxelListCovered(voxelList)){
[913]284      DUCHAMPERROR("Detection::calcFluxes","Voxel list provided does not match");
[417]285      return;
286    }
287
[623]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();
[463]295        this->totalFlux += f;
296        this->xCentroid += x*f;
297        this->yCentroid += y*f;
298        this->zCentroid += z*f;
[623]299        if( (vox==voxelList.begin()) ||  //first time round
[463]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      }
[417]309    }
310
311    this->xCentroid /= this->totalFlux;
312    this->yCentroid /= this->totalFlux;
313    this->zCentroid /= this->totalFlux;
314  }
315  //--------------------------------------------------------------------
316
[863]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()){
[913]336        DUCHAMPERROR("Detection::calcFluxes","Voxel list provided does not match");
[863]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
[884]366  void Detection::calcFluxes(float *fluxArray, size_t *dim)
[378]367  {
[528]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.
[258]375
[681]376    //    this->haveParams = true;
377
[378]378    this->totalFlux = this->peakFlux = 0;
379    this->xCentroid = this->yCentroid = this->zCentroid = 0.;
380
[570]381    std::vector<Voxel> voxList = this->getPixelSet();
[473]382    std::vector<Voxel>::iterator vox=voxList.begin();
383    for(;vox<voxList.end();vox++){
[378]384
[473]385      long x=vox->getX();
386      long y=vox->getY();
387      long z=vox->getZ();
[884]388      size_t ind = vox->arrayIndex(dim);
[473]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;
[378]402        }
[473]403 
[45]404    }
[378]405
406    this->xCentroid /= this->totalFlux;
407    this->yCentroid /= this->totalFlux;
408    this->zCentroid /= this->totalFlux;
[263]409  }
[378]410  //--------------------------------------------------------------------
[263]411
[417]412  void Detection::calcWCSparams(FitsHeader &head)
[378]413  {
[528]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.
[3]428
[378]429    if(head.isWCS()){
[3]430
[378]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;
[913]450      if(flag!=0) {
451        DUCHAMPERROR("calcWCSparams", "Error in calculating the WCS for this object.");
452      }
[378]453      else{
[913]454       
[378]455        // world now has the WCS coords for the five points
456        //    -- use this to work out WCS params
[22]457 
[681]458        this->haveParams = true;
459
[378]460        this->specOK = head.canUseThirdAxis();
[963]461        this->lngtype = head.lngtype();
462        this->lattype = head.lattype();
[378]463        this->specUnits = head.getSpectralUnits();
[947]464        this->specType  = head.getSpectralType();
[378]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];
[907]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);
[378]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.;
[473]477
[570]478        Object2D spatMap = this->getSpatialMap();
[473]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;
[1130]483        //      std::cerr << majorAxis << " " << minorAxis << " " << posang << "   " << majorAxis/head.getAvPixScale() << " " << minorAxis/head.getAvPixScale() << " " << posang*M_PI/180.<<"\n";
[473]484
[378]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);
[3]490
[378]491        this->flagWCS = true;
492      }
493      delete [] world;
494
[270]495    }
[982]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     
[103]505  }
[378]506  //--------------------------------------------------------------------
[3]507
[884]508  void Detection::calcIntegFlux(size_t zdim, std::vector<Voxel> voxelList, FitsHeader &head)
[417]509  {
[528]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    ///
[719]524    ///  \param zdim The size of the spectral axis (needed to find the velocity widths)
[528]525    ///  \param voxelList The list of Voxels with flux information
526    ///  \param head FitsHeader object that contains the WCS information.
[417]527
[463]528    if(!this->voxelListCovered(voxelList)){
[913]529      DUCHAMPERROR("Detection::calcIntegFlux","Voxel list provided does not match");
[417]530      return;
531    }
532
[513]533    if(!head.is2D()){
[417]534
[681]535      this->haveParams = true;
536
[982]537      const int border = 1;  // include one pixel either side in each direction
[983]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);
[982]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));
[884]544      size_t spatsize=xsize*ysize;
545      size_t size = xsize*ysize*zsize;
[473]546      std::vector <bool> isObj(size,false);
[463]547      double *localFlux = new double[size];
[884]548      for(size_t i=0;i<size;i++) localFlux[i]=0.;
[417]549
[623]550      std::vector<Voxel>::iterator vox;
551      for(vox=voxelList.begin();vox<voxelList.end();vox++){
552        if(this->isInObject(*vox)){
[983]553          size_t pos=(vox->getX()-xzero) + (vox->getY()-yzero)*xsize + (vox->getZ()-zzero)*spatsize;
[623]554          localFlux[pos] = vox->getF();
[463]555          isObj[pos] = true;
556        }
[417]557      }
558 
559      // work out the WCS coords for each pixel
[463]560      double *world  = new double[size];
[417]561      double xpt,ypt,zpt;
[924]562      for(size_t i=0;i<size;i++){
[463]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) );
[417]566        world[i] = head.pixToVel(xpt,ypt,zpt);
567      }
568
569      double integrated = 0.;
[884]570      for(size_t pix=0; pix<spatsize; pix++){ // loop over each spatial pixel.
571        for(size_t z=0; z<zsize; z++){
[921]572          size_t pos =  z*xsize*ysize + pix;
[417]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
[719]590      calcVelWidths(zdim,voxelList,head);
[464]591
[417]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"
[788]598      if(head.needBeamSize()) this->intFlux  /= head.beam().area();
[417]599    }
600
601  }
602  //--------------------------------------------------------------------
603
[884]604  void Detection::calcIntegFlux(size_t zdim, std::map<Voxel,float> voxelMap, FitsHeader &head)
[863]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
[982]628       const int border = 1; // include one pixel either side in each direction
[983]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);
[982]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));
[884]635      size_t spatsize=xsize*ysize;
636      size_t size = xsize*ysize*zsize;
[863]637      std::vector <bool> isObj(size,false);
638      double *localFlux = new double[size];
[884]639      for(size_t i=0;i<size;i++) localFlux[i]=0.;
[863]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()){
[913]645          DUCHAMPERROR("Detection::calcIntegFlux","Voxel list provided does not match");
[863]646          return;
647        }       
648        else {
[983]649          size_t pos=(vox->getX()-xzero) + (vox->getY()-yzero)*xsize + (vox->getZ()-zzero)*spatsize;
[863]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;
[884]658      for(size_t i=0;i<xsize*ysize*zsize;i++){
[863]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.;
[884]666      for(size_t pix=0; pix<spatsize; pix++){ // loop over each spatial pixel.
667        for(size_t z=0; z<zsize; z++){
[921]668          size_t pos =  z*xsize*ysize + pix;
[863]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
[884]700  void Detection::calcIntegFlux(float *fluxArray, size_t *dim, FitsHeader &head)
[378]701  {
[528]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.
[3]717
[513]718    if(!head.is2D()){
[271]719
[681]720      this->haveParams = true;
721
[982]722      const int border=1; // include one pixel either side in each direction
723      size_t xsize = std::min(size_t(this->xmax-this->xmin+2*border+1),dim[0]);
724      size_t ysize = std::min(size_t(this->ymax-this->ymin+2*border+1),dim[1]);
725      size_t zsize = std::min(size_t(this->zmax-this->zmin+2*border+1),dim[2]);
726      size_t xzero = size_t(std::max(0L,this->xmin-border));
727      size_t yzero = size_t(std::max(0L,this->ymin-border));
728      size_t zzero = size_t(std::max(0L,this->zmin-border));
[884]729      size_t spatsize = xsize*ysize;
730      size_t size = xsize*ysize*zsize;
[473]731      std::vector <bool> isObj(size,false);
[463]732      double *localFlux = new double[size];
[894]733      for(size_t i=0;i<size;i++) localFlux[i]=0.;
[1130]734      float *momMap = new float[spatsize];
735      for(size_t i=0;i<spatsize;i++) momMap[i]=0.;
[378]736      // work out which pixels are object pixels
[774]737      std::vector<Voxel> voxlist = this->getPixelSet();
738      for(std::vector<Voxel>::iterator v=voxlist.begin();v<voxlist.end();v++){
[1130]739        size_t spatpos=(v->getX()-xzero) + (v->getY()-yzero)*xsize;
740        size_t pos= spatpos + (v->getZ()-zzero)*spatsize;
[778]741        localFlux[pos] = fluxArray[v->arrayIndex(dim)];
[1130]742        momMap[spatpos] += fluxArray[v->arrayIndex(dim)];
[774]743        isObj[pos] = true;
[258]744      }
[22]745 
[378]746      // work out the WCS coords for each pixel
[463]747      double *world  = new double[size];
[378]748      double xpt,ypt,zpt;
[884]749      size_t i=0;
[986]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);
[781]756            world[i++] = head.pixToVel(xpt,ypt,zpt);
757          }
758        }
[378]759      }
[3]760
[378]761      double integrated = 0.;
[884]762      for(size_t pix=0; pix<xsize*ysize; pix++){ // loop over each spatial pixel.
763        for(size_t z=0; z<zsize; z++){
[921]764          size_t pos =  z*xsize*ysize + pix;
[378]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          }
[271]775        }
[3]776      }
[378]777      this->intFlux = integrated;
778
[1130]779      bool ellipseGood=this->spatialMap.findEllipse(true,momMap,xsize,ysize,xzero,yzero);  // try first by weighting the pixels by their flux
[1131]780      if(!ellipseGood) {
781        ellipseGood=this->spatialMap.findEllipse(false,momMap,xsize,ysize,xzero,yzero); // if that fails, remove the flux weighting
782        this->flagText += "W";
783      }
[1130]784      if(ellipseGood){
785        this->majorAxis = this->spatialMap.major() * head.getAvPixScale();
786        this->minorAxis = this->spatialMap.minor() * head.getAvPixScale();
787        this->posang = this->spatialMap.posAng() * 180. / M_PI;
788      }
789
[464]790      delete [] world;
791      delete [] localFlux;
[1130]792      delete [] momMap;
[463]793
[464]794      calcVelWidths(fluxArray, dim, head);
[463]795
[464]796    }
797    else // in this case there is just a 2D image.
798      this->intFlux = this->totalFlux;
[463]799
[464]800    if(head.isWCS()){
[789]801      // correct for the beam size if the flux units string ends in "/beam" and we have beam info
[788]802      if(head.needBeamSize()) this->intFlux  /= head.beam().area();
[464]803    }
[463]804
[464]805  }
806  //--------------------------------------------------------------------
807
[884]808  void Detection::calcVelWidths(size_t zdim, std::vector<Voxel> voxelList, FitsHeader &head)
[464]809  {
[528]810    ///  @details
811    /// Calculates the widths of the detection at 20% and 50% of the
812    /// peak integrated flux. The procedure is as follows: first
813    /// generate an integrated flux spectrum (using all given voxels
814    /// that lie in the object's spatial map); find the peak; starting
815    /// at the spectral edges of the detection, move in or out until
816    /// you reach the 20% or 50% peak flux level. Linear interpolation
817    /// between points is done.
818    ///
[719]819    ///  \param zdim The size of the spectral axis in the cube
[528]820    ///  \param voxelList The list of Voxels with flux information
821    ///  \param head FitsHeader object that contains the WCS information.
[464]822
[719]823    float *intSpec = new float[zdim];
[894]824    for(size_t i=0;i<zdim;i++) intSpec[i]=0;
[464]825       
[570]826    Object2D spatMap = this->getSpatialMap();
[464]827    for(int s=0;s<spatMap.getNumScan();s++){
[623]828      std::vector<Voxel>::iterator vox;
829      for(vox=voxelList.begin();vox<voxelList.end();vox++){
830        if(spatMap.isInObject(*vox)){
[719]831          intSpec[vox->getZ()] += vox->getF();
[464]832        }
[463]833      }
[464]834    }
835   
[719]836    calcVelWidths(zdim, intSpec, head);
837
838    delete [] intSpec;
839
840  }
841
842  //--------------------------------------------------------------------
843
[884]844  void Detection::calcVelWidths(size_t zdim, std::map<Voxel,float> voxelMap, FitsHeader &head)
[863]845  {
846    ///  @details
847    /// Calculates the widths of the detection at 20% and 50% of the
848    /// peak integrated flux. The procedure is as follows: first
849    /// generate an integrated flux spectrum (using all given voxels
850    /// that lie in the object's spatial map); find the peak; starting
851    /// at the spectral edges of the detection, move in or out until
852    /// you reach the 20% or 50% peak flux level. Linear interpolation
853    /// between points is done.
854    ///
855    ///  \param zdim The size of the spectral axis in the cube
856    ///  \param voxelList The list of Voxels with flux information
857    ///  \param head FitsHeader object that contains the WCS information.
858
859    float *intSpec = new float[zdim];
[894]860    for(size_t i=0;i<zdim;i++) intSpec[i]=0;
[863]861       
862    std::vector<Voxel> voxelList = this->getPixelSet();
863    std::vector<Voxel>::iterator vox;
864    for(vox=voxelList.begin();vox<voxelList.end();vox++){
865      if(voxelMap.find(*vox) == voxelMap.end()){
[913]866        DUCHAMPERROR("Detection::calcVelWidths","Voxel list provided does not match");
[863]867        return;
868      }
869      else {
870        intSpec[vox->getZ()] += voxelMap[*vox];
871      }
872    }
873
874    calcVelWidths(zdim, intSpec, head);
875
876    delete [] intSpec;
877
878  }
879
880  //--------------------------------------------------------------------
881
[884]882  void Detection::calcVelWidths(size_t zdim, float *intSpec, FitsHeader &head)
[719]883  {
884
[913]885    // finding the 20% & 50% points.  Start at the velmin & velmax
886    //  points. Then, if the int flux there is above the 20%/50%
887    //  limit, go out, otherwise go in. This is to deal with the
888    //  problems from double- (or multi-) peaked sources.
[719]889
890    this->haveParams = true;
891
[924]892    double zpt,xpt=double(this->getXcentre()),ypt=double(this->getYcentre());
[719]893    bool goLeft;
894   
[1054]895    if(this->negSource){
896      // if we've inverted the source, need to make the feature
897      // positive for the interpolation/extrapolation to work
898      for(size_t i=0;i<zdim;i++) intSpec[i] *= -1.;
899    }
900
[634]901    float peak=0.;
[894]902    size_t peakLoc=0;
903    for(size_t z=this->getZmin();z<=size_t(this->getZmax());z++) {
[464]904      if(z==0 || peak<intSpec[z]){
905        peak = intSpec[z];
906        peakLoc = z;
[463]907      }
[464]908    }
[719]909   
[894]910    size_t z=this->getZmin();
[464]911    goLeft = intSpec[z]>peak*0.5;
912    if(goLeft) while(z>0 && intSpec[z]>peak*0.5) z--;
913    else       while(z<peakLoc && intSpec[z]<peak*0.5) z++;
914    if(z==0) this->v50min = this->velMin;
915    else{
[719]916      if(goLeft) zpt = z + (peak*0.5-intSpec[z])/(intSpec[z+1]-intSpec[z]);
917      else       zpt = z - (peak*0.5-intSpec[z])/(intSpec[z-1]-intSpec[z]);
[464]918      this->v50min = head.pixToVel(xpt,ypt,zpt);
919    }
[719]920    z=this->getZmax();
[464]921    goLeft = intSpec[z]<peak*0.5;
922    if(goLeft) while(z>peakLoc && intSpec[z]<peak*0.5) z--;
[719]923    else       while(z<zdim    && intSpec[z]>peak*0.5) z++;
924    if(z==zdim) this->v50max = this->velMax;
[464]925    else{
[719]926      if(goLeft) zpt = z + (peak*0.5-intSpec[z])/(intSpec[z+1]-intSpec[z]);
927      else       zpt = z - (peak*0.5-intSpec[z])/(intSpec[z-1]-intSpec[z]);
[464]928      this->v50max = head.pixToVel(xpt,ypt,zpt);
929    }
[719]930    z=this->getZmin();
[588]931    goLeft = intSpec[z]>peak*0.2;
[464]932    if(goLeft) while(z>0 && intSpec[z]>peak*0.2) z--;
933    else       while(z<peakLoc && intSpec[z]<peak*0.2) z++;
934    if(z==0) this->v20min = this->velMin;
935    else{
[719]936      if(goLeft) zpt = z + (peak*0.2-intSpec[z])/(intSpec[z+1]-intSpec[z]);
937      else       zpt = z - (peak*0.2-intSpec[z])/(intSpec[z-1]-intSpec[z]);
[464]938      this->v20min = head.pixToVel(xpt,ypt,zpt);
939    }
[719]940    z=this->getZmax();
[588]941    goLeft = intSpec[z]<peak*0.2;
[464]942    if(goLeft) while(z>peakLoc && intSpec[z]<peak*0.2) z--;
[719]943    else       while(z<zdim    && intSpec[z]>peak*0.2) z++;
944    if(z==zdim) this->v20max = this->velMax;
[464]945    else{
[719]946      if(goLeft) zpt = z + (peak*0.2-intSpec[z])/(intSpec[z+1]-intSpec[z]);
947      else       zpt = z - (peak*0.2-intSpec[z])/(intSpec[z-1]-intSpec[z]);
[464]948      this->v20max = head.pixToVel(xpt,ypt,zpt);
949    }
[463]950
[464]951    this->w20 = fabs(this->v20min - this->v20max);
952    this->w50 = fabs(this->v50min - this->v50max);
[982]953   
[1054]954    if(this->negSource){
955      // un-do the inversion, in case intSpec is needed elsewhere
956      for(size_t i=0;i<zdim;i++) intSpec[i] *= -1.;
957    }
958
959
[464]960  }
[781]961  //--------------------------------------------------------------------
[464]962
[884]963  void Detection::calcVelWidths(float *fluxArray, size_t *dim, FitsHeader &head)
[464]964  {
[528]965    ///  @details
966    /// Calculates the widths of the detection at 20% and 50% of the
967    /// peak integrated flux. The procedure is as follows: first
968    /// generate an integrated flux spectrum (summing each spatial
969    /// pixel's spectrum); find the peak; starting at the spectral
970    /// edges of the detection, move in or out until you reach the 20%
971    /// or 50% peak flux level. Linear interpolation between points is
972    /// done.
973    ///
974    ///  \param fluxArray The array of flux values.
975    ///  \param dim The dimensions of the flux array.
976    ///  \param head FitsHeader object that contains the WCS information.
[464]977
[465]978    if(dim[2] > 2){
[464]979
[465]980      float *intSpec = new float[dim[2]];
[884]981      size_t size=dim[0]*dim[1]*dim[2];
[748]982      std::vector<bool> mask(size,true);
[465]983      getIntSpec(*this,fluxArray,dim,mask,1.,intSpec);
984
[719]985      this->calcVelWidths(dim[2],intSpec,head);
986
[465]987      delete [] intSpec;
988
[378]989    }
[464]990    else{
[465]991      this->v50min = this->v20min = this->velMin;
992      this->v50max = this->v20max = this->velMax;
[719]993      this->w20 = fabs(this->v20min - this->v20max);
994      this->w50 = fabs(this->v50min - this->v50max);
[464]995    }
[300]996
997  }
[378]998  //--------------------------------------------------------------------
[300]999
[378]1000  void Detection::setOffsets(Param &par)
1001  {
[528]1002    ///  @details
1003    /// This function stores the values of the offsets for each cube axis.
1004    /// The offsets are the starting values of the cube axes that may differ from
1005    ///  the default value of 0 (for instance, if a subsection is being used).
1006    /// The values will be used when the detection is outputted.
1007
[378]1008    this->xSubOffset = par.getXOffset();
1009    this->ySubOffset = par.getYOffset();
1010    this->zSubOffset = par.getZOffset();
1011  }
1012  //--------------------------------------------------------------------
[3]1013
[378]1014  bool Detection::hasEnoughChannels(int minNumber)
1015  {
[528]1016    ///  @details
1017    /// A function to determine if the Detection has enough
1018    /// contiguous channels to meet the minimum requirement
1019    /// given as the argument.
1020    /// \param minNumber How many channels is the minimum acceptable number?
1021    /// \return True if there is at least one occurence of minNumber consecutive
1022    /// channels present to return true. False otherwise.
[3]1023
[378]1024    // Preferred method -- need a set of minNumber consecutive channels present.
[3]1025
[570]1026    int numChan = this->getMaxAdjacentChannels();
1027    bool result = (numChan >= minNumber);
1028
[378]1029    return result;
1030 
1031  }
1032  //--------------------------------------------------------------------
[3]1033
[452]1034  std::vector<int> Detection::getVertexSet()
1035  {
[528]1036    ///  @details
1037    /// Gets a list of points being the end-points of 1-pixel long
1038    /// segments drawing a border around the spatial extend of a
1039    /// detection. The vector is a series of 4 integers, being: x_0,
1040    /// y_0, x_1, y_1.
1041    /// \return The vector of vertex positions.
1042
[452]1043    std::vector<int> vertexSet;
1044
1045    int xmin = this->getXmin() - 1;
1046    int xmax = this->getXmax() + 1;
1047    int ymin = this->getYmin() - 1;
1048    int ymax = this->getYmax() + 1;
1049    int xsize = xmax - xmin + 1;
1050    int ysize = ymax - ymin + 1;
1051
[570]1052    std::vector<Voxel> voxlist = this->getPixelSet();
[452]1053    std::vector<bool> isObj(xsize*ysize,false);
[623]1054    std::vector<Voxel>::iterator vox;
1055    for(vox=voxlist.begin();vox<voxlist.end();vox++){
[921]1056      size_t pos = (vox->getX()-xmin) +
[623]1057        (vox->getY()-ymin)*xsize;
[452]1058      isObj[pos] = true;
1059    }
1060    voxlist.clear();
1061   
1062    for(int x=xmin; x<=xmax; x++){
1063      // for each column...
1064      for(int y=ymin+1;y<=ymax;y++){
1065        int current  = (y-ymin)*xsize + x-xmin;
1066        int previous = (y-ymin-1)*xsize + x-xmin;
1067        if((isObj[current]&&!isObj[previous])   ||
1068           (!isObj[current]&&isObj[previous])){
1069          vertexSet.push_back(x);
1070          vertexSet.push_back(y);
1071          vertexSet.push_back(x+1);
1072          vertexSet.push_back(y);
1073        }
1074      }
1075    }
1076    for(int y=ymin; y<=ymax; y++){
1077      // now for each row...
1078      for(int x=xmin+1;x<=xmax;x++){
1079        int current  = (y-ymin)*xsize + x-xmin;
1080        int previous = (y-ymin)*xsize + x-xmin - 1;
1081        if((isObj[current]&&!isObj[previous])   ||
1082           (!isObj[current]&&isObj[previous])){
1083          vertexSet.push_back(x);
1084          vertexSet.push_back(y);
1085          vertexSet.push_back(x);
1086          vertexSet.push_back(y+1);
1087        }
1088      }
1089    }
1090
1091    return vertexSet;
1092 
1093  }
1094
[747]1095 
[770]1096  void Detection::addDetection(Detection &other)
[747]1097  {
1098    for(std::map<long, Object2D>::iterator it = other.chanlist.begin(); it!=other.chanlist.end();it++)
[770]1099      //      this->addChannel(*it);
[747]1100      this->addChannel(it->first, it->second);
1101    this->haveParams = false; // make it appear as if the parameters haven't been calculated, so that we can re-calculate them 
1102  }
[624]1103
[770]1104  Detection operator+ (Detection &lhs, Detection &rhs)
[624]1105  {
1106    Detection output = lhs;
1107    for(std::map<long, Object2D>::iterator it = rhs.chanlist.begin(); it!=rhs.chanlist.end();it++)
1108      output.addChannel(it->first, it->second);
[681]1109    output.haveParams = false; // make it appear as if the parameters haven't been calculated, so that we can re-calculate them
[624]1110    return output;
1111  }
1112   
1113
[770]1114  bool Detection::canMerge(Detection &other, Param &par)
1115  {
1116    bool near = this->isNear(other,par);
1117    if(near) return this->isClose(other,par);
1118    else return near;
1119  }
[624]1120
[770]1121  bool Detection::isNear(Detection &other, Param &par)
1122  {
[624]1123
[770]1124    bool flagAdj = par.getFlagAdjacent();
1125    float threshS = par.getThreshS();
1126    float threshV = par.getThreshV();
1127
1128    long gap;
1129    if(flagAdj) gap = 1;
1130    else gap = long( ceil(threshS) );
1131
1132    bool areNear;
1133    // Test X ranges
1134    if((this->xmin-gap)<other.xmin) areNear=((this->xmax+gap)>=other.xmin);
1135    else areNear=(other.xmax>=(this->xmin-gap));
1136    // Test Y ranges
1137    if(areNear){
1138      if((this->ymin-gap)<other.ymin) areNear=areNear&&((this->ymax+gap)>=other.ymin);
1139      else areNear=areNear&&(other.ymax>=(this->ymin-gap));
1140    }
1141    // Test Z ranges
1142    if(areNear){
1143      gap = long(ceil(threshV));
1144      if((this->zmin-gap)<other.zmin) areNear=areNear&&((this->zmax+gap)>=other.zmin);
1145      else areNear=areNear&&(other.zmax>=(this->zmin-gap));
1146    }
1147   
1148    return areNear;
1149
1150  }
1151
1152  bool Detection::isClose(Detection &other, Param &par)
1153  {
1154    bool close = false;   // this will be the value returned
1155   
1156    bool flagAdj = par.getFlagAdjacent();
1157    float threshS = par.getThreshS();
1158    float threshV = par.getThreshV();
1159
1160    //
1161    // If we get to here, the pixel ranges overlap -- so we do a
1162    // pixel-by-pixel comparison to make sure they are actually
1163    // "close" according to the thresholds.  Otherwise, close=false,
1164    // and so don't need to do anything else before returning.
1165    //
1166
1167    std::vector<long> zlist1 = this->getChannelList();
1168    std::vector<long> zlist2 = other.getChannelList();
1169    Scan test1,test2;
1170
1171    for(size_t ct1=0; (!close && (ct1<zlist1.size())); ct1++){
1172      for(size_t ct2=0; (!close && (ct2<zlist2.size())); ct2++){
1173       
1174        if(abs(zlist1[ct1]-zlist2[ct2])<=threshV){
1175             
1176          Object2D temp1 = this->getChanMap(zlist1[ct1]);
1177          Object2D temp2 = other.getChanMap(zlist2[ct2]);
1178
1179          close = temp1.canMerge(temp2,threshS,flagAdj);
1180
1181        }
1182
1183      }
1184    }
1185       
1186    return close;
1187   
1188  }
1189
1190
1191
[3]1192}
Note: See TracBrowser for help on using the repository browser.