Region.C

Deil Christoph, 10/11/2012 01:53 PM

Download (41.9 KB)

 
1
#include <iostream>
2
#include <string>
3
#include <TPad.h>
4
#include <TBox.h>
5
#include <TLine.h>
6
#include <TRandom.h>
7
#define INFO_OUT_TAG "Region> "
8
#define DEBUG 0
9

    
10
#include <utilities/debugging.hh>
11
#include <sash/HESSArray.hh>
12
#include <sash/TimeDiff.hh>
13
#include <sash/Time.hh>
14
#include <stash/constants.h>
15
#include "Region.hh"
16

    
17
using namespace std;
18

    
19
/** 
20
 */
21
Tools::Region::Region(const Stash::Coordinate& center,string name,
22
                      string type, Stash::Lambda phi)
23
  : TNamed(name.c_str(),""),
24
    TAttLine(), TAttFill(),
25
    fCentre(center), 
26
    fAccuracy(1.0),
27
    fRotationPoint(center),
28
    fRotationAngle(phi),
29
    fType(type),
30
    fIsInverted(false)
31
{
32

    
33
}
34
  
35

    
36
/**
37
 * Currently Applies a rotation to the given point (for passively
38
 * rotating the region - this should be called in Contains() after
39
 * converting to the RegionSystem to get the correct point)
40
 */
41
Stash::Coordinate 
42
Tools::Region::
43
Transform( const Stash::Coordinate &src) const {
44

    
45
  if (fRotationPoint.GetSystem() == Stash::System::GetDummySystem() )
46
    return src;
47

    
48
  Stash::Coordinate tsrc = 
49
    src.GetCoordinate( *fRotationPoint.GetSystem() ); // eventually should be in RegionSystem
50
  Stash::DirectionVector dv = tsrc.GetDirectionVector();
51
  Stash::DirectionVector cendv =  fRotationPoint.GetDirectionVector();
52
  Stash::DirectionVector newdv = dv.RotateAroundPoint( cendv, fRotationAngle);
53

    
54
  return Stash::Coordinate(newdv, fRotationPoint.GetSystem());
55
}
56

    
57

    
58
/**
59
 * Undo the internal rotation transform of the region (useful for
60
 * getting back to proper "Sky" coordinates, rather than the internal
61
 * de-rotated coordinates of the region.  It should be used in
62
 * AsVertexList() to undo the internal transform.
63
 */
64
Stash::Coordinate 
65
Tools::Region::
66
InverseTransform( const Stash::Coordinate &src) const {
67
  Stash::Coordinate tsrc = 
68
    src.GetCoordinate( *fRotationPoint.GetSystem() ); // eventually should be in RegionSystem
69
  Stash::DirectionVector dv = tsrc.GetDirectionVector();
70
  Stash::DirectionVector cendv =  fRotationPoint.GetDirectionVector();
71
  Stash::DirectionVector newdv = dv.RotateAroundPoint( cendv, fRotationAngle.GetNegative() );
72
  return Stash::Coordinate(newdv, GetSystem());
73
}
74

    
75

    
76
/**
77
 * Set the rotation of this region. Each region can be rotated about
78
 * an arbitrary point (if no point is specified, the region center is
79
 * used).  The rotation angle is relative to North in the Region's
80
 * cordinate System, and increases counter-clockwise (the is the same
81
 * definition of the "position angle" in astronomy).
82
 *
83
 * \code
84
 *     // Example: rotate a region at the test position about the observation position
85
 *     Tools::Box *box = new Tools::Box( testpos, wid, len );
86
 *     box->SetRotation( Stash::Beta(45,Stash::Angle::Degrees), obspos );
87
 * \endcode
88
 *
89
 * If you want to apply successive rotations (e.g. rotate first about
90
 * one point, and then again about another), you should first rotate
91
 * the region and then add it to a Tools::Compound (which contains its
92
 * own rotation transform), and apply the second rotation to that.
93
 */
94
void 
95
Tools::Region::
96
SetRotation( Stash::Lambda phi, Stash::Coordinate point ) {
97
      
98
  if (point == Stash::Coordinate(0,0,Stash::System::GetDummySystem())) {
99
    DEBUG_OUT << GetName() << ": USING CENTER POINT FOR ROTATION POINT" << std::endl;
100
    point = GetCentre();
101
  }
102

    
103
  // don't rotate if region centre is uninitialised
104
  // exception: zero angle (needed by Compound::Reset())
105
  if (point == Stash::Coordinate(0,0,Stash::System::GetDummySystem())) {
106
    if( !(phi == Stash::Lambda(0)) ) {
107
      WARN_OUT << GetName() << ": Region Center is in dummy system. Can't rotate."
108
               << " If this is a Compound Region, you must add a region first to define"
109
               << " the proper coordinate system. "
110
               << std::endl;
111
      return;
112
    }
113
  }
114
  else
115
    fRotationPoint = point.GetCoordinate( *GetSystem() );
116

    
117
  fRotationAngle = phi;
118

    
119
  DEBUG_OUT << "Rotation set to: "<< fRotationAngle << " about "<<fRotationPoint
120
            << std::endl;
121

    
122
}
123

    
124

    
125
/**
126
 * \returns distance of the given coordinate to the true center-point of the region.
127
 */
128
double Tools::Region::GetDistanceToCentre(const Stash::Coordinate& cor) const
129
{
130
  Stash::Coordinate dir = cor.GetCoordinate(*GetSystem());
131
  return GetTrueCentre().GetAngularDistance(dir).GetDegrees();
132
}
133

    
134
/**
135
 * Loops over the region and calculates an approximate center of
136
 * gravity.  This can be re-implemented in sub-classes where the
137
 * center of gravity is known mathematically, to increase speed.
138
 */
139
Stash::Coordinate Tools::Region::GetCentreOfGravity() const
140
{
141

    
142
  double n=0;
143
  double suml=0.0, sumb=0.0;
144
  double radius = GetRadialBound();
145
  int steps = GetNumSpaceSteps(radius);
146

    
147
  DEBUG_OUT << "Steps=" << steps<< endl;
148

    
149
  double rl = radius/GetTrueCentre().GetBeta().Cos();
150
  for (int i=0; i<steps; ++i) {
151
    Stash::Lambda l = GetTrueCentre().GetLambda() 
152
      + Stash::Lambda(2.*rl*(i-steps*0.5)/(steps*0.9),Stash::Angle::Degrees);
153
    for (int j=0; j<steps; ++j) {
154
      Stash::Beta b = GetTrueCentre().GetBeta() 
155
        + Stash::Beta(2.*radius*(j-steps*0.5)/(steps*0.9),
156
                      Stash::Angle::Degrees);
157
      Stash::Coordinate test(l,b,GetSystem());
158
      if (Contains(test)) {
159
        sumb += b.GetDegrees();
160
        suml += l.GetDegrees();
161
        n += 1.0;
162
      }
163
    }
164
  }
165
  Stash::Coordinate cog(Stash::Lambda(suml/n,Stash::Angle::Degrees),
166
                        Stash::Beta(sumb/n,Stash::Angle::Degrees),
167
                        GetSystem());
168
 
169
  return cog;
170
}
171

    
172

    
173

    
174
/**
175
 * Returns the radius of a circle with the same area as the region
176
 */
177
double
178
Tools::Region::
179
GetEquivalentCircleRadius() const {
180
  return sqrt( GetArea()/M_PI );
181
}
182

    
183

    
184
/**
185
 * Fills a histogram of the Offset distribution from the given
186
 * reference position
187
 *
188
 * \param psicut - will be ignored, if set to -1.
189
 */
190
void
191
Tools::Region::FillOffsetDistribution(const Stash::Coordinate& refpos,
192
                                        TH1 &hist, double psicut ) const
193
{
194
  Stash::Coordinate pos = refpos.GetCoordinate(*GetSystem());
195

    
196
  double radius = GetRadialBound();
197
  double rl = radius/GetTrueCentre().GetBeta().Cos();
198

    
199
  const int steps = GetNumSpaceSteps(radius);
200

    
201
  DEBUG_OUT << "pos: " << pos
202
            << " refpos: " << refpos
203
            << " steps: "<< steps
204
            << endl;
205

    
206

    
207

    
208
  for (int i=0; i<steps; ++i) {
209

    
210
    Stash::Lambda l = GetTrueCentre().GetLambda() 
211
      + Stash::Lambda(2.*rl*(i-steps*0.5)/(steps*0.9),Stash::Angle::Degrees);
212

    
213
    for (int j=0; j<steps; ++j) {
214

    
215
      Stash::Beta b = GetTrueCentre().GetBeta() 
216
        + Stash::Beta(2.*radius*(j-steps*0.5)/(steps*0.9),
217
                      Stash::Angle::Degrees);
218
      Stash::Coordinate test(l,b,GetSystem());
219

    
220
      if (Contains(test)) {
221
        double d = pos.GetAngularDistance(test).GetDegrees();
222

    
223
        // ignore psi, if it is negativ
224
        if (psicut < 0. || d <= psicut) hist.Fill(d);
225
      }
226

    
227
    }
228
  }
229

    
230
  if( hist.Integral() == 0 ){
231
    WARN_OUT << "Offset histogram empty!" << std::endl;
232
  }
233
}
234

    
235
/**
236
 * \returns a string representing the coordinate system for use with AsFITSRegionString()
237
 */
238
string
239
Tools::Region::
240
GetFITSSystemName() const {
241

    
242
  string name = GetSystem().GetSystem()->GetName();
243
  if (name == "GalacticSystem")
244
    return "galactic";
245
  else if (name == "RADecJ2000System")
246
    return "fk5";
247

    
248
  return "unknown";
249
}
250

    
251

    
252
/**
253
 * \brief Check if dir is contained in this region.
254
 */
255
bool Tools::Region::Contains(const Stash::Coordinate& dir) const {
256
  Stash::Coordinate dir_trans = Transform(dir);
257
  if (fIsInverted)
258
    return !IsInside(dir_trans); // call the sub-class's virtual method
259
  else 
260
    return IsInside(dir_trans);
261
} 
262

    
263

    
264
/**
265
 * \brief Check if another region is completely contained in this region.
266
 */
267
bool Tools::Region::IsCompletelyContained(const Tools::Region& reg) const
268
{
269

    
270
  double radius = reg.GetRadialBound();
271
  double rl = radius/reg.GetTrueCentre().GetBeta().Cos();
272

    
273
  int steps = GetNumSpaceSteps(radius);
274

    
275
  for (int i=0; i<steps; ++i) {
276
    Stash::Lambda l = reg.GetTrueCentre().GetLambda() 
277
      + Stash::Lambda(2.*rl*(i-steps*0.5)/(steps*0.9),Stash::Angle::Degrees);
278
    for (int j=0; j<steps; ++j) {
279
      Stash::Beta b = reg.GetTrueCentre().GetBeta() 
280
        + Stash::Beta(2.*radius*(j-steps*0.5)/(steps*0.9),
281
                      Stash::Angle::Degrees);
282
      Stash::Coordinate test(l,b,GetSystem());
283
      if (reg.Contains(test) && (!Contains(test))) return false;
284
    }
285
  }
286

    
287
  return true;
288
}
289

    
290
/**
291
 * Check whether the given region overlaps this one
292
 */
293
bool Tools::Region::Overlaps(const Tools::Region& reg) const
294
{
295
  if( reg.GetSystem() == Stash::System::GetDummySystem() )
296
    return false;
297

    
298
  double radius = GetRadialBound();
299
  int steps = GetNumSpaceSteps(radius);
300
  double rl = radius/GetTrueCentre().GetBeta().Cos();
301

    
302
  // make sure to use the transformed region centre here
303
  // otherwise Overlaps() returns wrong results for rotated regions
304
  for (int i=0; i<steps; ++i) {
305
    Stash::Lambda l = GetTrueCentre().GetLambda() + 
306
      Stash::Lambda(2.*rl*(i-steps*0.5)/(steps*0.9),Stash::Angle::Degrees);
307
    for (int j=0; j<steps; ++j) {
308
      Stash::Beta b = GetTrueCentre().GetBeta() + 
309
        Stash::Beta(2.*radius*(j-steps*0.5)/(steps*0.9),Stash::Angle::Degrees);
310
      Stash::Coordinate test(l,b,GetSystem());
311
      if (reg.Contains(test) && (Contains(test))) return true;
312
    }
313
  }
314

    
315
  return false;
316
}
317

    
318

    
319
/**
320
 * Gives an approximation of the area using Monte-Carlo integration.
321
 * This is useful when you have a Compound region with overlapping
322
 * sub-regions, which would not be properly taken into account in
323
 * GetArea(), which simply sums the area of all sub-regions.  This
324
 * method is much slower than GetArea, however.
325
 *
326
 * \param samples_per_square_deg: approximate sample density to use
327
 *
328
 * \todo: this can be optimized to use the region's bounding box, but
329
 * bounding-boxes needs to be well-tested first.
330
 *
331
 * \note this function is used for testing purposes when debugging
332
 * regions and the Contains() function. It's not intended for
333
 * production use. Generally one should use the GetArea() function,
334
 * which does it analytically.
335
 */
336
double 
337
Tools::Region::
338
GetMonteCarloArea(int samples_per_square_deg) const {
339

    
340
  // here we use a bounding box that is the size of the containment
341
  // radius (just to be safe). Really the Bounding box of the region
342
  // could be used, but in some cases that hasn't been tested.
343
  double radius = GetRadialBound();
344

    
345

    
346
  Tools::Box box( GetTrueCentre(), radius*2, radius*2 );
347
  double thrownarea = box.GetArea();
348
  int nsamples = samples_per_square_deg * thrownarea;
349
  
350
  DEBUG_OUT << "Thrown Area: " << thrownarea << endl;
351
  DEBUG_OUT << "    Samples: " << nsamples << endl;
352

    
353
  // generate random samples over the box (defined as [-R,R] from the region center)
354

    
355
  int count = 0;
356
  for (int ii=0; ii < nsamples; ii++) {
357
    if (this->Contains( box.GetRandomSample() ))
358
        count++;
359
  }
360
  
361
  DEBUG_OUT << " Contained:" << count << endl;
362

    
363

    
364
  return thrownarea*count/double(nsamples);
365
    
366
}
367

    
368
/**
369
 * Generates the zenith or azimuth angle distribution of a given region over the
370
 * specified time range.
371
 *
372
 * \param hist - histogram to fill 
373
 * \param starttime - starting time of the run
374
 * \param endtime - ending time of the run
375
 * \param fillZenith - decide whether the zenith (true, default) or azimuth (false) angle will be filled
376
 *
377
 * \todo Document the sampling method implemented here!
378
 *
379
 */
380
void Tools::Region::FillZenithOrAzimuthDistribution( TH1 &hist, 
381
                                                     const Sash::Time starttime,
382
                                                     const Sash::Time endtime,
383
                                                     bool fillZenith ) const
384
{
385
  if( fillZenith ){
386
    DEBUG_OUT << "Filling zenith distribution..." << std::endl;
387
  }
388
  else{
389
    DEBUG_OUT << "Filling azimuth distribution..." << std::endl;
390
  }
391

    
392
  Sash::HESSArray *hess = &Sash::HESSArray::GetHESSArray();
393
  const Stash::System* altAzSys = hess->GetAltAzSystem().GetSystem();
394

    
395
  double radius = GetRadialBound();
396
  double rl = radius/GetTrueCentre().GetBeta().Cos();
397

    
398
  int tsteps = GetNumTimeSteps();
399
  int steps = GetNumSpaceSteps(radius);
400

    
401
  Sash::TimeDiff ttotal(endtime-starttime);
402
  double totsec = (double) ttotal;
403
  Sash::TimeDiff tdelta( totsec/((double) tsteps) );
404

    
405
  // store the HESS time for later resetting
406
  Sash::Time oldtime = hess->GetCurrentTime();
407

    
408
  for ( int t = 0; t<tsteps; t++) {
409

    
410
    Sash::Time tnow = starttime + Sash::TimeDiff(t*(double)tdelta);
411
    hess->SetCurrentTime( tnow );
412

    
413
    for (int i=0; i<steps; ++i) {
414
      
415
      Stash::Lambda l = GetTrueCentre().GetLambda() 
416
        + Stash::Lambda(2.*rl*(i-steps*0.5)/(steps*0.9),Stash::Angle::Degrees);
417
      
418
      for (int j=0; j<steps; ++j) {
419
        
420
        
421
        Stash::Beta b = GetTrueCentre().GetBeta() 
422
          + Stash::Beta(2.*radius*(j-steps*0.5)/(steps*0.9),
423
                        Stash::Angle::Degrees);
424
        
425
        Stash::Coordinate test(l,b,GetSystem());
426
        
427
        if (Contains(test)) {
428
          if( fillZenith ){
429
            // zenith
430
            double z = 90. - test.GetCoordinate(*altAzSys).GetBeta().GetDegrees();
431
            hist.Fill(z);}
432
          else{
433
            // azimuth
434
            double a = test.GetCoordinate(*altAzSys).GetLambda().GetDegrees();
435
            hist.Fill(a);
436
          }
437
        }
438
      }
439
    }
440
  }
441

    
442
  if (hist.GetEntries()<5) {
443
    WARN_OUT << "less than 5 entries used to evaluate distribution "
444
             << "for region " << GetName() << endl;
445
  }
446

    
447
  hess->SetCurrentTime( oldtime );
448
}
449

    
450

    
451
/**
452
 * Generates the zenith angle / offset to obs. pos 2D - distribution
453
 * of a given region over the specified time range.
454
 *
455
 * \param hist - histogram to fill 
456
 * \param starttime - starting time of the run
457
 * \param endtime - ending time of the run
458
 * \param psicut - will be ignored, if set to -1.
459
 *
460
 */
461
void 
462
Tools::Region::
463
FillZenithOffsetDistribution(const Stash::Coordinate& refpos,
464
                             TH2 &hist,
465
                             const Sash::Time starttime,
466
                             const Sash::Time endtime,
467
                             double psicut ) const
468
{
469
  DEBUG_OUT << "refpos: " << refpos << std::endl;
470
  DEBUG_OUT << "starttime: " << starttime << std::endl;
471
  DEBUG_OUT << "endtime: " << endtime << std::endl;
472
  DEBUG_OUT << "psicut: " << psicut << std::endl;
473

    
474
  Sash::HESSArray *hess = &Sash::HESSArray::GetHESSArray();
475
  const Stash::System* altAzSys = hess->GetAltAzSystem().GetSystem();
476
  Stash::Coordinate pos = refpos.GetCoordinate(*(GetSystem()));
477
  
478
  double radius = GetRadialBound();
479
  double rl = radius/GetTrueCentre().GetBeta().Cos();
480

    
481
  int steps = GetNumSpaceSteps(radius);
482
  int tsteps = GetNumTimeSteps();
483

    
484
  DEBUG_OUT << "SPACE STEPS:" << steps << endl;
485
  DEBUG_OUT << " TIME STEPS:" << tsteps << endl;
486

    
487
  Sash::TimeDiff ttotal(endtime-starttime);
488
  double totsec = (double) ttotal;
489
  Sash::TimeDiff tdelta( totsec/((double) tsteps) );
490

    
491
  // store the HESS time for later resetting
492
  Sash::Time oldtime = hess->GetCurrentTime();
493

    
494
  DEBUG_OUT << "pos: " << pos << std::endl;
495
  DEBUG_OUT << "radius: " << radius << std::endl;
496
  DEBUG_OUT << "rl: " << rl << std::endl;
497
  DEBUG_OUT << "tdelta: " << tdelta << std::endl;
498

    
499
  for ( int t = 0; t<tsteps; t++) {
500

    
501
    Sash::Time tnow = starttime + Sash::TimeDiff(t*(double)tdelta);
502
    hess->SetCurrentTime( tnow );
503
    DEBUG_OUT_L(10) << "tnow: " << tnow << std::endl;
504

    
505
    for (int i=0; i<steps; ++i) {
506
      
507
      Stash::Lambda l = GetTrueCentre().GetLambda() 
508
        + Stash::Lambda(2.*rl*(i-steps*0.5)/(steps*0.9),Stash::Angle::Degrees);
509
      
510
      for (int j=0; j<steps; ++j) {
511
        
512
        
513
        Stash::Beta b = GetTrueCentre().GetBeta() 
514
          + Stash::Beta(2.*radius*(j-steps*0.5)/(steps*0.9),
515
                        Stash::Angle::Degrees);
516
        
517
        Stash::Coordinate test(l,b,GetSystem());
518
        DEBUG_OUT_L(10) << "test: " << test << std::endl;
519

    
520
        if (Contains(test)) {
521
          double z = 90. - test.GetCoordinate(*altAzSys).GetBeta().GetDegrees();
522
          double d = pos.GetAngularDistance(test).GetDegrees();
523
          DEBUG_OUT_L(10) << "z = " << z << ", d = " << d << std::endl;
524
          // ignore psi, if it is negativ
525
          if (psicut < 0. || d <= psicut) {
526
            DEBUG_OUT_L(10) << "Filling hist" << std::endl;
527
            hist.Fill(z,d);
528
          }
529
                   
530
        }
531
        
532
      }
533
    }
534
  }
535

    
536
  hess->SetCurrentTime( oldtime );
537

    
538
}
539

    
540

    
541
/**
542
 * Fills histogram off 2d offsets in the sky from 
543
 *
544
 * \param psicut - will be ignored, if set to -1.
545
 */
546
void Tools::Region::Fill2AxesDistanceDistribution(const Stash::Coordinate& 
547
                                                  refpos,
548
                                                  TH2 &hist2, 
549
                                                  double psicut ) const
550
{
551
  Stash::Coordinate pos = refpos.GetCoordinate(*(GetCentre().GetSystem() ) );
552

    
553
  DEBUG_OUT_L(2) << "pos " << pos
554
                 << "refpos " << refpos
555
                 << std::endl;
556

    
557

    
558
  double radius = GetRadialBound();
559
  int steps = GetNumSpaceSteps(radius);
560
  double rl = radius/GetTrueCentre().GetBeta().Cos();
561

    
562
  double refL = pos.GetLambda().GetDegrees();
563
  double refB = pos.GetBeta().GetDegrees();
564

    
565
  double cenL = GetTrueCentre().GetLambda().GetDegrees();
566
  double cenB = GetTrueCentre().GetBeta().GetDegrees();
567

    
568
  if (refL < -180.) refL += 360.;
569
  if (refL > 180.) refL -= 360.;
570
  if (refB < -180.) refB += 360.;
571
  if (refB > 180.) refB -= 360.;
572

    
573
  if (cenL < -180.) cenL += 360.;
574
  if (cenL > 180.) cenL -= 360.;
575
  if (cenB < -180.) cenB += 360.;
576
  if (cenB > 180.) cenB -= 360.;
577

    
578
  double DL = cenL - refL;
579
  double DB = cenB - refB;
580

    
581
  for (int i=0; i<steps; ++i) {
582

    
583
    double deltL = 2.*rl*(i-steps*0.5)/(steps*0.9);
584

    
585
    Stash::Lambda l = GetTrueCentre().GetLambda() 
586
      + Stash::Lambda(deltL,Stash::Angle::Degrees);
587

    
588
    for (int j=0; j<steps; ++j) {
589

    
590
      double deltB = 2.*radius*(j-steps*0.5)/(steps*0.9);
591

    
592
      Stash::Beta b = GetTrueCentre().GetBeta() 
593
        + Stash::Beta(deltB, Stash::Angle::Degrees);
594
      Stash::Coordinate test(l,b,GetCentre().GetSystem());
595

    
596
      if (Contains(test)) {
597
        double d = pos.GetAngularDistance(test).GetDegrees();
598
        // ignore psi, if it is negative
599
        if (psicut < 0. || d <= psicut) {
600
          hist2.Fill(deltL+DL, deltB+DB);
601
        }
602
      }
603
    }
604
  }
605

    
606
}
607

    
608

    
609
/**
610
 * Returns a Tools::Box that bounds the region
611
 */
612
Tools::Box*
613
Tools::Region::
614
GetBoundingBox() const {
615
  
616
  return new Tools::Box(GetTrueCentre(), GetBound1(), GetBound2(), 
617
                        Stash::Lambda(0, Stash::Angle::Degrees), 
618
                        string(GetName())+"_bbox");
619
  
620
}
621

    
622
Tools::Circle*
623
Tools::Region::
624
GetBoundingCircle() const {
625

    
626
  return new Tools::Circle(GetTrueCentre(), GetRadialBound(), string(GetName())+"_bcirc");
627

    
628
}
629

    
630
/////////////////////////////////////////////////////////////////////////////
631
// CircleSegment ////////////////////////////////////////////////////////////
632
/////////////////////////////////////////////////////////////////////////////
633

    
634
Tools::CircleSegment::CircleSegment(const Stash::Coordinate& cor,
635
                                    double r1,double r2,
636
                                    double phi1, double phi2,
637
                                    string name):
638
  Region(cor,name,"SEGMENT", Stash::Lambda(0)),
639
  fR1(r1),fR2(r2),
640
  fPhi1(phi1), fPhi2(phi2), fLine(0)
641
{
642
  if( fPhi1 < 0 ) {
643
    WARN_OUT << "please define phi1 within the boundaries [0;360]!" << std::endl;
644
    fPhi1 += 360.;
645
  }
646
  else if( fPhi1 > 360. ) {
647
    WARN_OUT << "please define phi1 within the boundaries [0;360]!" << std::endl;
648
    fPhi1 -= 360.;
649
  }
650

    
651
  if( fPhi2 < 0 ) {
652
    WARN_OUT << "please define phi1 within the boundaries [0;360]!" << std::endl;
653
    fPhi2 += 360.;
654
  }
655
  else if( fPhi2 > 360. ) {
656
    WARN_OUT << "please define phi1 within the boundaries [0;360]!" << std::endl;
657
    fPhi2 -= 360.;
658
  }
659
}
660

    
661
Tools::CircleSegment::~CircleSegment()
662
{
663
  if(fLine) delete fLine;
664
}
665

    
666
double Tools::CircleSegment::GetArea() const {
667

    
668
  double area = 0;
669

    
670
  if( fabs(fPhi2-fPhi1) > 1e-10 ) {
671

    
672
    bool overzero = (fPhi2 < fPhi1) ? (true) : (false);
673
    if( overzero )
674
      area = (360. - fPhi1 + fPhi2) / 360.;
675
    else
676
      area = (fPhi2 - fPhi1) / 360.;
677

    
678
    area *= M_PI * (pow(fR2,2.) - pow(fR1,2.)); 
679
  }
680

    
681
  return area;
682
}
683

    
684
bool Tools::CircleSegment::IsInside(const Stash::Coordinate& dir) const
685
{
686

    
687
  double r = GetCentre().GetAngularDistance(dir).GetDegrees();
688

    
689
  DEBUG_OUT_L(2) << "Pos: (" << dir.GetLambda().GetStringInHours() << "," 
690
                 << dir.GetBeta() << "), Centre: (" 
691
                 << fCentre.GetLambda().GetStringInHours() 
692
                 << "," << GetCentre().GetBeta() << "), r " << r << " [" << fR1 
693
                 << "," << fR2 << "]"  << std::endl;
694

    
695
  if ((r>fR2) || (r<fR1)) {
696
    DEBUG_OUT_L(2) << "--> Not Contained" << endl;
697
    return false;
698
  }
699

    
700
  double dl = dir.GetLambda().GetDegrees() - fCentre.GetLambda().GetDegrees();
701
  double sign = dl/fabs(dl+1e-10); // need epsilon to avoid NaN, which
702
                                   // get marked as Contained!
703
  Stash::Coordinate ldl = dir + Stash::Lambda(dl,Stash::Angle::Degrees);
704
  double dx = sign * dir.GetAngularDistance(ldl).GetDegrees();
705
  double dy = (dir.GetBeta() - fCentre.GetBeta()).GetDegrees();
706
  Stash::Lambda ang(atan2(dx,dy), Stash::Angle::Radians);
707
  double phi = ang.GetDegrees();
708

    
709
  DEBUG_OUT_L(2) << " phi " << phi << " [" << fPhi1 << "," << fPhi2 << "]" 
710
                 << std::endl;
711

    
712
  bool overzero = (fPhi2 < fPhi1) ? (true) : (false);
713
  if( overzero ) {
714
    if( phi > fPhi2 && phi < fPhi1 )
715
      return false;
716
  }
717
  else {
718
    if( phi < fPhi1 || phi > fPhi2 )
719
      return false;
720
  }
721

    
722
  return true;
723
}
724

    
725

    
726
/**
727
 * Grow the boundary of the CircleSegment by a given amount of space angle.
728
 *
729
 * The CircleSegment is grown both towards its inner and outer boundary, and 
730
 * the azmuthal coverage is enlarged as well.
731
 *
732
 */
733
Tools::CircleSegment* 
734
Tools::CircleSegment::
735
GrowBoundary(const double degrees,
736
             const string name) const
737
{
738
  string grownName(GetName());
739
  grownName += name;
740

    
741
  Tools::CircleSegment* grownReg 
742
    = (Tools::CircleSegment*)this->Clone(grownName.c_str());
743

    
744
  if( degrees > 0 ) {
745

    
746
    // grow radially
747
    grownReg->SetR1( ((fR1-degrees) < 0) ? (0) : (fR1-degrees) );
748
    grownReg->SetR2( fR2 + degrees );
749
    
750
    // grow azimuthally (into full ring if necessary)
751
    double newPhi1, newPhi2;
752

    
753
    newPhi1 = fPhi1 - degrees 
754
      / ((fR1 + fR2) / 2 ) * 180./TMath::Pi();
755
    newPhi2 = fPhi2 + degrees 
756
      / ((fR1 + fR2) / 2 ) * 180./TMath::Pi();
757

    
758
    // bring angles back into boundaries
759
    // both angles crossed zero, so full circle
760
    if( newPhi1 < 0 && newPhi2 > 360. ) {
761
      newPhi1 = 0;
762
      newPhi2 = 360.;
763
    }
764
    // ordinary back-to-boundary
765
    else { 
766
      if( newPhi1 < 0 ) {
767
        newPhi1 += 360.;
768
      }
769
      if( newPhi2 > 360.) {
770
        newPhi2 -= 360.;
771
      }
772
    }
773

    
774
    // check if we have grown into a full ring
775
    bool wasOverZero = (fPhi2 < fPhi1) ? (true) : (false);
776
    bool isOverZero  = (newPhi2 < newPhi1) ? (true) : (false);
777

    
778
    if( wasOverZero && newPhi2 > newPhi1 ) {
779
      newPhi1 = 0.;
780
      newPhi2 = 360.;
781
    }
782
    else if( isOverZero && newPhi2 > newPhi1 ) {
783
      newPhi1 = 0.;
784
      newPhi2 = 360.;
785
    }
786

    
787
    grownReg->SetPhi1( newPhi1 );
788
    grownReg->SetPhi2( newPhi2 );
789
  }
790

    
791
  return grownReg;
792
}
793

    
794

    
795
Tools::Region::VertexList 
796
Tools::CircleSegment::
797
AsVertexList() const {
798

    
799
  Tools::Region::VertexList vlist;
800
  vlist.resize(1);
801

    
802
  const int NPOINTS = 50;
803
  Stash::Lambda step(0);
804
  
805
  bool overzero = (fPhi2 < fPhi1) ? (true) : (false);
806
  if( overzero )
807
    step = Stash::Lambda((360.-fPhi1+fPhi2)/NPOINTS, Stash::Angle::Degrees);
808
  else
809
    step = Stash::Lambda((fPhi2-fPhi1)/NPOINTS, Stash::Angle::Degrees);
810

    
811
  DEBUG_OUT << "STEP: " << step.GetDegrees() << " for "<< AsFITSRegionString() << endl;
812
  
813
  Stash::Beta r1(fR1, Stash::Angle::Degrees);
814
  Stash::Beta r2(fR2, Stash::Angle::Degrees);
815
  Stash::Lambda phi1(fPhi1, Stash::Angle::Degrees);
816
  Stash::Lambda phi2(fPhi2, Stash::Angle::Degrees);
817

    
818
  // Inner arc:
819

    
820
  if (fR1 > 1e-10) {
821
    
822
    Stash::Lambda phi = phi1;
823
    for (int ii=0; ii<NPOINTS; ii++) {
824
      Stash::Coordinate point = InverseTransform( GetPointAtRadius(r1, phi) );
825
      vlist[0].push_back( point );
826
      DEBUG_OUT_L(2) << "INNER Added point " << point << endl;
827

    
828
      phi+=step;
829
    }
830

    
831
  }
832

    
833
  // first connector:
834

    
835
  Stash::Coordinate p1 = InverseTransform( GetPointAtRadius( r1,phi2 ) );
836
  Stash::Coordinate p2 = InverseTransform( GetPointAtRadius( r2,phi2 ) );
837
  vlist[0].push_back( p1 );
838
  vlist[0].push_back( p2 );
839

    
840
  // Outer arc:
841
  if (fR2 > 1e-10) {
842
    
843
    Stash::Lambda phi = phi2;
844
    for (int ii=0; ii<NPOINTS; ii++) {
845
      Stash::Coordinate point = InverseTransform( GetPointAtRadius(r2, phi) );
846
      vlist[0].push_back( point );
847
      DEBUG_OUT_L(2) << "OUTTER Added point " << point << endl;
848
      phi-=step;
849
    }
850

    
851
  }
852

    
853
  // second connector:
854
  Stash::Coordinate p3 = InverseTransform( GetPointAtRadius( r2,phi1 ) );
855
  Stash::Coordinate p4 = InverseTransform( GetPointAtRadius( r1,phi1 ) );
856
  vlist[0].push_back( p3 );
857
  vlist[0].push_back( p4 );
858

    
859
  return vlist;
860

    
861
}
862

    
863

    
864
/**
865
 * \returns a point on a circle with given radius from the Centre
866
 * position. Used by CircleSegment::AsVertexList.
867
 *
868
 * \note rad does need to be a Stash::Beta (not Lambda) in order for
869
 * the "RA factor" to be taken into account properly)
870
 */
871
Stash::Coordinate
872
Tools::Region::
873
GetPointAtRadius( Stash::Beta rad, Stash::Lambda phi ) const {
874
  
875
  Stash::Coordinate point = fCentre + rad;
876
  Stash::DirectionVector dv = point.GetDirectionVector();
877
  Stash::DirectionVector cendv =  fCentre.GetDirectionVector();
878
  Stash::DirectionVector newdv = dv.RotateAroundPoint( cendv, phi.GetNegative() );
879
  return Stash::Coordinate(newdv, GetSystem());
880
 
881
}
882

    
883

    
884
/**
885
 * Returns a point on the line between A and B, where t is the parameter in range [0,1]
886
 */
887
// Stash::Coordinate 
888
// Tools::Region::
889
// GetPointOnLine( Stash::Coordinate A, Stash::Coordinate B, double t) {
890
//   //  return (1.0-t)*A + t*B;
891
// }
892

    
893
string
894
Tools::CircleSegment::
895
AsFITSRegionString() const {
896
  char rep[512];
897
  
898
  Stash::Coordinate realcen = GetTrueCentre();
899

    
900
  string sys = GetFITSSystemName();
901
  sprintf( rep, "%s; panda(%.4fd,%.4fd,%.4fd,%.4fd,1,%.4fd,%.4fd,1) # text={%s} ",
902
           sys.c_str(), 
903
           realcen.GetLambda().GetDegrees(), 
904
           realcen.GetBeta().GetDegrees(),
905
           GetPhi1() + GetRotationAngle().GetDegrees(),  
906
           GetPhi2() + GetRotationAngle().GetDegrees(),
907
           GetR1() / realcen.GetBeta().Cos(), GetR2() / realcen.GetBeta().Cos(),
908
           GetName()
909
           );
910
  return string(rep);
911
}
912

    
913

    
914
bool 
915
Tools::Ring::
916
IsInside(const Stash::Coordinate& dir) const {
917
  
918
  double rad=0;
919

    
920
  rad = fCentre.GetAngularDistance( dir ).GetDegrees();
921

    
922
  if (rad < GetR1() || rad > GetR2()) {
923
    return false;
924
  }
925

    
926
  else return true;
927

    
928
}
929

    
930
string
931
Tools::Ring::
932
AsFITSRegionString() const {
933
  char rep[256];
934
  
935
  Stash::Coordinate realcen = GetTrueCentre();
936

    
937
  string sys = GetFITSSystemName();
938
  sprintf( rep, "%s; annulus(%.4f,%.4f,%.4f,%.4f) # text={%s}", sys.c_str(), 
939
           realcen.GetLambda().GetDegrees(), realcen.GetBeta().GetDegrees(),
940
           GetR1(), GetR2(), GetName());
941
  return string(rep);
942
}
943

    
944

    
945

    
946
bool 
947
Tools::Circle::
948
IsInside(const Stash::Coordinate& dir) const {
949
  
950
  double rad=0;
951

    
952
  rad = fCentre.GetAngularDistance( dir ).GetDegrees();
953

    
954
  if (rad > GetR2()) {
955
    return false;
956
  }
957

    
958
  else return true;
959

    
960
}
961

    
962

    
963
string
964
Tools::Circle::
965
AsFITSRegionString() const {
966
  char rep[256];
967
  
968
  Stash::Coordinate realcen = GetTrueCentre();
969

    
970
  string sys = GetFITSSystemName();
971
  sprintf( rep, "%s; circle(%.4fd,%.4fd,%.4fd) # text={%s}", sys.c_str(), 
972
           realcen.GetLambda().GetDegrees(), realcen.GetBeta().GetDegrees(),
973
           GetR2()/realcen.GetBeta().Cos(), GetName());
974
  return string(rep);
975
}
976

    
977

    
978
/////////////////////////////////////////////////////////////////////////////
979
// BOX //////////////////////////////////////////////////////////////////////
980
/////////////////////////////////////////////////////////////////////////////
981

    
982
Tools::Box::~Box()
983
{ }
984

    
985
Tools::Box::Box(const Stash::Coordinate& cor,
986
                double l1,double l2, Stash::Lambda phi,
987
                string name) :
988
  Region(cor,name,"BOX",phi),fL1(l1),fL2(l2)
989
{}
990

    
991
bool Tools::Box::IsInside(const Stash::Coordinate& dir) const
992
{
993

    
994
  double dist = fCentre.GetAngularDistance(dir).GetDegrees();
995

    
996
  if(dist > fL1 && dist > fL2) {
997
    DEBUG_OUT_L(2) << "--> Not Contained" << endl;
998
    return false;
999
  }
1000

    
1001
  double dl=0,db=0;
1002
  GetOffsetOfPointFromCenter( dir, dl, db );
1003

    
1004

    
1005
  if (dl < fL1/2.0 && db <fL2/2.0)
1006
    return true;
1007

    
1008
  return false;
1009

    
1010
}
1011

    
1012

    
1013
double 
1014
Tools::Box::
1015
GetBound1() const
1016
{ 
1017
  double dx,dy;
1018
  Tools::Region::VertexList vlist = AsVertexList();
1019
  std::vector< Stash::Coordinate >::iterator ivert;
1020
  
1021
  double max = -1000;
1022
  for (ivert=vlist[0].begin(); ivert!=vlist[0].end(); ivert++) {
1023
    GetOffsetOfPointFromCenter( *ivert, dx, dy, false );
1024
    if (fabs(dx) >max) max=fabs(dx);
1025
  }
1026
  return 2.0*max;
1027

    
1028
}
1029

    
1030
double 
1031
Tools::Box::
1032
GetBound2() const
1033
{ 
1034
  double dx,dy;
1035
  Tools::Region::VertexList vlist = AsVertexList();
1036
  std::vector< Stash::Coordinate >::iterator ivert;
1037
  
1038
  double max = -1000;
1039
  for (ivert=vlist[0].begin(); ivert!=vlist[0].end(); ivert++) {
1040
    GetOffsetOfPointFromCenter( *ivert, dx, dy, false );
1041
    if (fabs(dy) >max) max=fabs(dy);
1042
  }
1043
  return 2.0*max;
1044

    
1045
}
1046

    
1047

    
1048

    
1049
/**
1050
 * Grow the boundary of the box by a given amount of space angle.
1051
 *
1052
 * Grows both the width and length of the box.
1053
 */
1054
Tools::Box*
1055
Tools::Box::
1056
GrowBoundary(const double degrees, const string name) const
1057
{
1058
  string grownName(GetName());
1059
  grownName += name;
1060

    
1061
  Tools::Box* grownReg = (Tools::Box*)this->Clone(grownName.c_str());
1062

    
1063
  if( degrees > 0 ) {
1064
    grownReg->SetL1( fL1 + 2*degrees );
1065
    grownReg->SetL2( fL2 + 2*degrees );
1066
  }
1067

    
1068
  return grownReg;
1069
}
1070

    
1071

    
1072
string
1073
Tools::Box::
1074
AsFITSRegionString() const {
1075
  char rep[256];
1076
  
1077
  Stash::Coordinate realcen = GetTrueCentre();
1078

    
1079
  string sys = GetFITSSystemName();
1080
  sprintf( rep, "%s; box(%.4fd,%.4fd,%.4fd,%4fd,%4fd) # text={%s}",  sys.c_str(), 
1081
           realcen.GetLambda().GetDegrees(), realcen.GetBeta().GetDegrees(),
1082
           fL1, fL2, GetRotationAngle().GetDegrees(), GetName()
1083
           );
1084
  return string(rep);
1085
}
1086

    
1087

    
1088
/**
1089
 * \todo: also step along each axis and insert multiple points (for
1090
 * better plotting on distorted projections)
1091
 */
1092
Tools::Region::VertexList 
1093
Tools::Box::
1094
AsVertexList() const {
1095
  
1096

    
1097
  Tools::Region::VertexList vlist;
1098
  vlist.resize(1);
1099

    
1100
  Stash::Coordinate p1 = GetPointAtOffsetFromCenter( fL1/2.0, fL2/2.0 );
1101
  Stash::Coordinate p2 = GetPointAtOffsetFromCenter( fL1/2.0, -fL2/2.0 );
1102
  Stash::Coordinate p3 = GetPointAtOffsetFromCenter( -fL1/2.0, -fL2/2.0 );
1103
  Stash::Coordinate p4 = GetPointAtOffsetFromCenter( -fL1/2.0, fL2/2.0 );
1104

    
1105
  vlist[0].push_back( InverseTransform(p1) );
1106
  vlist[0].push_back( InverseTransform(p2) );
1107
  vlist[0].push_back( InverseTransform(p3) );
1108
  vlist[0].push_back( InverseTransform(p4) );
1109

    
1110
  vlist[0].push_back( vlist[0][0] ); // close the curve
1111

    
1112
  return vlist;
1113

    
1114
}
1115

    
1116
/**
1117
 * \returns a point at a given offset in "sky degrees" (e.g. not RA/Dec degrees).
1118
 */
1119
Stash::Coordinate 
1120
Tools::Region::
1121
GetPointAtOffsetFromCenter( double offsX, double offsY ) const {
1122

    
1123
  // could do this simpler with 3D direction vectors instead of a cos(dec)
1124
  // factor, but this works fine (not easy to do it with how Stash
1125
  // is set up though)
1126

    
1127
  Stash::Coordinate cen = fCentre;
1128

    
1129
  double dist = sqrt(pow(offsX,2) + pow(offsY,2));
1130
  Stash::Beta db( offsY, Stash::Angle::Degrees );
1131
  Stash::Lambda dl( offsX/cen.GetBeta().Cos(), Stash::Angle::Degrees );
1132

    
1133
  Stash::Coordinate newcoord = cen + dl + db;
1134

    
1135
  DEBUG_OUT << "Distance Error: " 
1136
            <<newcoord.GetAngularDistance(fCentre).GetDegrees() - dist << endl;
1137

    
1138
  double dx,dy;
1139
  GetOffsetOfPointFromCenter( newcoord, dx, dy, false );
1140
  DEBUG_OUT << "error="<< fabs(offsX)-fabs(dx) << " offsX=" << offsX << " dx="<<dx << endl;
1141
  DEBUG_OUT << "error="<< fabs(offsY)-fabs(dy) << " offsY=" << offsY << " dy="<<dx << endl;
1142

    
1143
  
1144

    
1145
  return newcoord;
1146

    
1147
}
1148

    
1149
/**
1150
 * \returns offset in sky degrees of point from the region center (similar to GetAngularDistance(), but returning both directions)
1151
 *
1152
 * \param untransformed - return the offset from the untransformed center point
1153
 */
1154
void 
1155
Tools::Region::
1156
GetOffsetOfPointFromCenter( Stash::Coordinate point, double &offsX, double &offsY,
1157
                            bool untransformed ) const  {
1158

    
1159
  Stash::Coordinate cen = fCentre;
1160
  if (untransformed)
1161
    cen = GetTrueCentre();
1162

    
1163

    
1164
  Stash::Coordinate px( point.GetLambda(), cen.GetBeta(), cen.GetSystem() );
1165
  Stash::Coordinate py( cen.GetLambda(), point.GetBeta(), cen.GetSystem());
1166

    
1167
  offsX = px.GetAngularDistance( cen ).GetDegrees();
1168
  offsY = py.GetAngularDistance( cen ).GetDegrees();
1169
  
1170
}
1171

    
1172

    
1173

    
1174

    
1175
Stash::Coordinate 
1176
Tools::Box::
1177
GetRandomSample() {
1178
  
1179
  double dx = gRandom->Uniform( -fL1/2.0, fL1/2.0 );
1180
  double dy = gRandom->Uniform( -fL2/2.0, fL2/2.0 );
1181

    
1182
  return InverseTransform(GetPointAtOffsetFromCenter( dx, dy ));
1183
                               
1184
}
1185

    
1186
/////////////////////////////////////////////////////////////////////////////
1187
// Compound /////////////////////////////////////////////////////////////////
1188
/////////////////////////////////////////////////////////////////////////////
1189

    
1190
/**
1191
 * Returns total area of compound region.
1192
 *
1193
 * \note this area is not correct if regions overlap!
1194
 */
1195
double 
1196
Tools::Compound::
1197
GetArea() const {
1198
  
1199
  RegionList::const_iterator region;
1200
  double area=0.0;
1201

    
1202
  for (region = fRegionList.begin(); region != fRegionList.end(); region++) {
1203
    
1204
    area += (*region)->GetArea();
1205
    
1206
  }
1207

    
1208
  return area;
1209

    
1210
}
1211

    
1212

    
1213
/**
1214
 * Creates a clone of the compound, using ROOT's Clone() mechanism.
1215
 * Additionally, clone all Regions inside the compound.
1216
 */
1217
Tools::Compound*
1218
Tools::Compound::
1219
Clone(const char* name) const
1220
{
1221
  Tools::Compound* clone = (Tools::Compound*)Tools::Region::Clone(name);
1222
  clone->ClearRegionList();
1223

    
1224
  // clone each member of the compound
1225
  RegionList::const_iterator it = fRegionList.begin();
1226
  for( ; it != fRegionList.end(); ++it ) {
1227

    
1228
    std::string newName = (*it)->GetName();
1229
    newName.append("_");
1230
    newName += std::string(name);
1231

    
1232
    clone->AddRegion( (Tools::Region*)((*it)->Clone(newName.c_str())) );
1233
  }
1234

    
1235
  return clone;
1236
}
1237

    
1238

    
1239
/**
1240
 * \brief Move all subregions to the new position (mainting relative offsets).
1241
 *
1242
 * \note this function modifies the regions inside the compound! 
1243
 */
1244
void 
1245
Tools::Compound::
1246
MoveTo( Stash::Coordinate newpos ) {
1247

    
1248
  Stash::Lambda dl = newpos.GetLambda() - fCentre.GetLambda();
1249
  Stash::Beta db = newpos.GetBeta() - fCentre.GetBeta();
1250

    
1251
  RegionList::const_iterator it = fRegionList.begin();
1252
  for( ; it != fRegionList.end(); ++it ) {
1253
    
1254
    Stash::Coordinate newcen = (*it)->GetCentre();
1255
    newcen += dl;
1256
    newcen += db;
1257
    const_cast<Tools::Region*>(*it)->MoveTo(newcen);
1258
    
1259
  }
1260

    
1261
  fCentre = newpos;
1262
  
1263
}
1264

    
1265

    
1266
/**
1267
 * Add a region to the list of regions which make up this compound region
1268
 *
1269
 * \note the center of the compound will be the center of the first
1270
 * region added. For speed, it is not updated to a center of
1271
 * gravity. In the case where you would like it to be so, simply call:
1272
 * \code region->SetCentre( region->GetCentreOfGravity() ) \endcode.
1273
 */
1274
void Tools::Compound::AddRegion( const Tools::Region *reg ) {
1275
  
1276
  fRegionList.push_back( reg );
1277

    
1278
  // Ensure that the Center point of the region is not in the
1279
  // DummySystem, if so, use the system of the first region added as
1280
  // the Region system (rotations will be done in that system)
1281

    
1282
  if (fCentre == Stash::Coordinate(0,0,Stash::System::GetDummySystem())) {
1283
    DEBUG_OUT << GetName() << ": Setting the System for this compound to: " 
1284
              << reg->GetSystem()->GetName() << endl;
1285
    fCentre = reg->GetCentre();
1286
    SetRotation(GetRotationAngle()); // updates the rotation center 
1287
  }
1288

    
1289
  // the following is nice, but slow for large compounds. Instead user
1290
  // should explicitly call compound->SetCentre(
1291
  // compound->GetCentreOfGravity() ); else { fCentre =
1292
  // GetCentreOfGravity(); }
1293

    
1294
}
1295

    
1296

    
1297
Tools::Region::VertexList Tools::Compound::AsVertexList() const
1298
{
1299
  VertexList list;
1300

    
1301
  RegionList::const_iterator region;
1302
  for (region = fRegionList.begin(); region != fRegionList.end(); region++) {
1303

    
1304
    DEBUG_OUT_L(2) << "Getting vertex list for: " << (*region)->GetName() << endl;
1305

    
1306
    VertexList regVertexList = (*region)->AsVertexList();
1307

    
1308
    VertexList::iterator it = regVertexList.begin();
1309
    for( ; it != regVertexList.end(); ++it ){
1310
      vector<Stash::Coordinate> clist;
1311
      vector<Stash::Coordinate>::iterator cc;
1312
      for (cc=(*it).begin(); cc != (*it).end(); cc++)
1313
        clist.push_back( InverseTransform( *cc ) ); // apply the compound's transform
1314
      list.push_back( clist );
1315

    
1316
    }
1317

    
1318
  }
1319

    
1320
  return list;
1321
}
1322

    
1323

    
1324
double Tools::Compound::GetBound1() const {
1325

    
1326
  double maxl1=0;
1327

    
1328
  RegionList::const_iterator region;
1329

    
1330
  for (region = fRegionList.begin(); region != fRegionList.end(); region++) {
1331

    
1332
    // find the bounding circle for each region, and calculate the maximum delta X
1333
    Tools::Circle *bcirc = (*region)->GetBoundingCircle();
1334
    double rad = bcirc->GetR2()  / (*region)->GetTrueCentre().GetBeta().Cos();
1335
    delete bcirc;
1336
    
1337
    Stash::Lambda lam(rad, Stash::Angle::Degrees);
1338

    
1339
    Stash::Coordinate point_plus = InverseTransform((*region)->GetTrueCentre()) + lam;
1340
    Stash::Coordinate point_minus =InverseTransform((*region)->GetTrueCentre()) - lam;
1341

    
1342
    double dx, dy;
1343

    
1344
    GetOffsetOfPointFromCenter( point_plus, dx, dy, true );
1345
    dx = fabs(dx)*2.0;
1346

    
1347
    if (dx>maxl1) maxl1=dx;
1348

    
1349
    GetOffsetOfPointFromCenter( point_minus, dx, dy, true);
1350
    dx = fabs(dx)*2.0;
1351

    
1352
    if (dx>maxl1) maxl1=dx;
1353

    
1354
  }
1355

    
1356
  DEBUG_OUT << "maxl1=" << maxl1 << endl;
1357

    
1358

    
1359
  return maxl1;
1360

    
1361
}
1362

    
1363

    
1364
double Tools::Compound::GetBound2() const {
1365

    
1366
  double maxl2=0;
1367
  RegionList::const_iterator region;
1368

    
1369
  for (region = fRegionList.begin(); region != fRegionList.end(); region++) {
1370

    
1371
    Tools::Circle *bcirc = (*region)->GetBoundingCircle();
1372
    DEBUG_OUT << *bcirc << endl;
1373
    double rad = bcirc->GetR2();
1374
    delete bcirc;
1375
    
1376
    Stash::Beta bet(rad, Stash::Angle::Degrees);
1377

    
1378
    Stash::Coordinate point_plus = InverseTransform((*region)->GetTrueCentre()) + bet;
1379
    Stash::Coordinate point_minus = InverseTransform((*region)->GetTrueCentre()) - bet;
1380

    
1381
    double dx, dy;
1382

    
1383
    GetOffsetOfPointFromCenter( point_plus, dx, dy, true );
1384
    dy = fabs(dy) *2.0;
1385
    if (dy>maxl2) maxl2=dy;
1386

    
1387
    GetOffsetOfPointFromCenter( point_minus, dx, dy, true );
1388
    dy = fabs(dy)*2.0;
1389
    if (dy>maxl2) maxl2=dy;
1390

    
1391
  }
1392

    
1393
  DEBUG_OUT << "maxl2=" << maxl2 << endl;
1394

    
1395
  return maxl2;
1396

    
1397

    
1398
}
1399

    
1400

    
1401

    
1402
double 
1403
Tools::Compound::
1404
GetRadialBound() const {
1405
  
1406
  RegionList::const_iterator region;
1407

    
1408
  double maxrad = 0;
1409

    
1410
  for (region = fRegionList.begin(); region != fRegionList.end(); region++) {  
1411

    
1412
    double rad = (*region)->GetRadialBound();
1413
    Stash::Coordinate regcen 
1414
      = (*region)->GetCentre().GetCoordinate( *(GetCentre().GetSystem()) );
1415
    double dist = GetCentre().GetAngularDistance( regcen ).GetDegrees();
1416

    
1417
    if (rad+dist > maxrad) {
1418
      maxrad = rad+dist;
1419
    }
1420
    
1421
  }
1422

    
1423
  return maxrad;
1424

    
1425
}
1426

    
1427
/**
1428
 * \see help for Tools::Compound::FillZenithDistribution()
1429
 */ 
1430
void
1431
Tools::Compound::FillOffsetDistribution(const Stash::Coordinate& pos,
1432
                                        TH1 &dist, double psicut ) const {
1433

    
1434
  RegionList::const_iterator region;
1435

    
1436
  for (region = fRegionList.begin(); region != fRegionList.end(); region++) {  
1437

    
1438
    (*region)->FillOffsetDistribution( pos, dist, psicut );
1439

    
1440
  }
1441

    
1442

    
1443
}
1444

    
1445
/**
1446
 * \brief Check if another region is completely contained in this region.
1447
 */
1448
bool Tools::Compound::IsCompletelyContained(const Tools::Region& reg) const {
1449

    
1450
  RegionList::const_iterator region;
1451
  bool contained = false;
1452

    
1453
  for (region = fRegionList.begin(); region != fRegionList.end(); region++) {
1454
    
1455
    contained &= (*region)->IsCompletelyContained( reg );
1456
    
1457
  }
1458
  return contained;
1459
}
1460

    
1461
/**
1462
 * \bug: if a compound contains a compound, currently this doesn't output a region
1463
 * string that is readable with DS9 (does DS9 support nested groups?)
1464
 */ 
1465
string
1466
Tools::Compound::
1467
AsFITSRegionString() const {
1468
  
1469
  ostringstream ost; 
1470
  RegionList::const_iterator region;
1471

    
1472
  if (GetNumRegions() == 0) {
1473
    return string("# empty compound region") ;
1474
  }
1475

    
1476
  Stash::Coordinate realcen = GetTrueCentre();
1477
  ost << "# composite("
1478
      << realcen.GetLambda().GetDegrees() <<"d,"
1479
      << realcen.GetBeta().GetDegrees() << "d,"
1480
      << GetRotationAngle().GetDegrees() <<"d) || composite=1 text={" 
1481
      << GetName()<<"}"<<endl;
1482

    
1483
  for (region = fRegionList.begin(); region != fRegionList.end(); region++) {
1484
    string str= (*region)->AsFITSRegionString();
1485
    if (fRegionList.size()>1 && region+1 != fRegionList.end()) {
1486
      size_t pos = str.find_first_of("#");
1487
      if (pos == string::npos || pos==0) 
1488
        str.append("||");
1489
      else
1490
        str.insert( pos-1,"||");  // properly 'or' together regions
1491
    }
1492
    ost << str << endl;
1493
  }
1494

    
1495
  return ost.str();
1496
}
1497

    
1498

    
1499

    
1500
/**
1501
 * This just loops over the sub-regions and calls
1502
 * FillZenithDistribution for each. Really it is not needed, since a
1503
 * Compound acts like a single region with its own
1504
 * FillZenithDistribution method, however since
1505
 * Region::FillZenithDistribution chooses a sampling based on the
1506
 * radial bound of the region, this loop ensures that that sampling
1507
 * well matches each subregion.
1508
 */
1509
void
1510
Tools::Compound::
1511
FillZenithOrAzimuthDistribution( TH1 &hist, const Sash::Time starttime, 
1512
                                 const Sash::Time endtime,
1513
                                 bool fillZenith ) const {
1514
  DEBUG_OUT << fillZenith << std::endl;
1515

    
1516
  RegionList::const_iterator region;
1517

    
1518
  for (region = fRegionList.begin(); region != fRegionList.end(); region++) {  
1519

    
1520
    (*region)->FillZenithOrAzimuthDistribution( hist, starttime, endtime, fillZenith );
1521

    
1522
  }
1523
  
1524
}
1525

    
1526
/**
1527
 * \see help for Tools::Compound::FillZenithDistribution()
1528
 */ 
1529
void
1530
Tools::Compound::
1531
FillZenithOffsetDistribution( const Stash::Coordinate& pos,
1532
                              TH2 &hist, const Sash::Time starttime, 
1533
                              const Sash::Time endtime,
1534
                              double psicut ) const {
1535

    
1536
  DEBUG_OUT << std::endl;
1537

    
1538
  RegionList::const_iterator region;
1539

    
1540
  for (region = fRegionList.begin(); region != fRegionList.end(); region++) {  
1541

    
1542
    (*region)->FillZenithOffsetDistribution( pos, hist, starttime, endtime, psicut );
1543

    
1544
  }
1545
  
1546
}
1547

    
1548
bool 
1549
Tools::Compound::
1550
IsInside( const Stash::Coordinate &dir ) const {
1551

    
1552
  RegionList::const_iterator region;
1553
 
1554
  for (region = fRegionList.begin(); region != fRegionList.end(); region++) {  
1555
    
1556
    if ((*region)->Contains( dir ))
1557
      return true;
1558
    
1559
  }
1560

    
1561
  return false;
1562

    
1563
}
1564

    
1565
bool 
1566
Tools::CompoundIntersection::
1567
IsInside( const Stash::Coordinate &dir ) const {
1568

    
1569
  RegionList::const_iterator region;
1570
  unsigned int count=0;
1571

    
1572
  for (region = fRegionList.begin(); region != fRegionList.end(); region++) {  
1573
    
1574
    if ((*region)->Contains( dir ))
1575
      count++;
1576
    else 
1577
      count--;
1578
 
1579
  }
1580

    
1581
  //  return (count > 0); // XORs the regions
1582
  return (count == fRegionList.size()); // ANDs the regions
1583

    
1584
}
1585

    
1586

    
1587

    
1588
/**
1589
 * Resets the Compound
1590
 */
1591
void
1592
Tools::Compound::
1593
Reset() {
1594

    
1595
  ClearRegionList();
1596
  SetRotation(0);
1597
  fCentre = Stash::Coordinate(0,0,Stash::System::GetDummySystem());
1598
  
1599
}
1600

    
1601

    
1602
/**
1603
 * Grow all regions in the compound by a given amount of space angle.
1604
 *
1605
 * A new Compound is returned, containing the grown sub-regions.
1606
 *
1607
 */
1608
Tools::Compound* 
1609
Tools::Compound::
1610
GrowBoundary( const double degrees, const string name ) const
1611
{
1612
  string grownName(GetName());
1613
  grownName += name;
1614
  
1615
  Tools::Compound* grownReg 
1616
    = (Tools::Compound*)Tools::Region::Clone(grownName.c_str());
1617
  grownReg->Reset();
1618
  
1619
  // grow each region of the compound separately
1620
  RegionList::const_iterator it = fRegionList.begin();
1621
  for( ; it != fRegionList.end(); ++it ) 
1622
    grownReg->AddRegion( (*it)->GrowBoundary(degrees, name) );
1623

    
1624
  return grownReg;
1625
}
1626

    
1627

    
1628
/**
1629
 * output Streamer for all Tools;:Regions (uses AsFITSRegionString()
1630
 * to print out each region)
1631
 */
1632
ostream& operator<<( ostream& stream, const Tools::Region &reg) {
1633
  stream << reg.AsFITSRegionString();
1634
  return stream;
1635
}
1636

    
1637

    
1638