Commit ca6d6d37239a755eb77055fd1ba8cd78173555d3

Authored by Jürgen Knödlseder
1 parent 1c7cb600

Adjust sky region classes (#3248)

Renamed the GSkyRegionRect::get_corner() method to GSkyRegionRect::corner() and make local coordinate methods provide. Use GSkyPixel to carry local coordinates in units of radians. Extent the GSkyRegionRect::contains() and GSkyRegionRect::overlaps() method to cover all other possible sky region types.

Adjust GSkyRegionCircle and GSkyRegionMap to the new interface and make sure that their contains() and overlaps() method deal with all other regions.

Adjust the GSkyRegionRect unit test accordingly.
include/GSkyRegionRect.hpp
@@ -34,6 +34,7 @@ @@ -34,6 +34,7 @@
34 #include "GSkyRegion.hpp" 34 #include "GSkyRegion.hpp"
35 35
36 /* __ Forward declarations _______________________________________________ */ 36 /* __ Forward declarations _______________________________________________ */
  37 +class GSkyPixel;
37 38
38 39
39 /***********************************************************************//** 40 /***********************************************************************//**
@@ -97,26 +98,26 @@ public: @@ -97,26 +98,26 @@ public:
97 void read(const std::string& line); 98 void read(const std::string& line);
98 std::string write(void) const; 99 std::string write(void) const;
99 bool contains(const GSkyDir& dir) const; 100 bool contains(const GSkyDir& dir) const;
100 - bool contains_local(const GSkyDir& locdir) const;  
101 bool contains(const GSkyRegion& reg) const; 101 bool contains(const GSkyRegion& reg) const;
102 bool overlaps(const GSkyRegion& reg) const; 102 bool overlaps(const GSkyRegion& reg) const;
103 - GSkyDir transform_to_local(const GSkyDir& skydir) const;  
104 - GSkyDir transform_to_global(const GSkyDir& locdir) const;  
105 - GSkyDir get_corner(const int& index) const; 103 + GSkyDir corner(const int& index) const;
106 std::string print(const GChatter& chatter = NORMAL) const; 104 std::string print(const GChatter& chatter = NORMAL) const;
107 105
108 protected: 106 protected:
109 // Protected methods 107 // Protected methods
110 - void init_members(void);  
111 - void copy_members(const GSkyRegionRect& region);  
112 - void free_members(void);  
113 - void compute_solid_angle(void); 108 + void init_members(void);
  109 + void copy_members(const GSkyRegionRect& region);
  110 + void free_members(void);
  111 + void compute_solid_angle(void);
  112 + bool contains(const GSkyPixel& local) const;
  113 + GSkyPixel dir_to_local(const GSkyDir& dir) const;
  114 + GSkyDir local_to_dir(const GSkyPixel& local) const;
114 115
115 // Protected members 116 // Protected members
116 - GSkyDir m_centre; //!< Centre or reference point of the region  
117 - double m_width; //!< Width of the region (degrees)  
118 - double m_height; //!< Height of the region (degrees)  
119 - double m_posang; //!< Position angle, counterclockwise from North (degrees) 117 + GSkyDir m_centre; //!< Centre or reference point of the region
  118 + double m_width; //!< Width of the region (degrees)
  119 + double m_height; //!< Height of the region (degrees)
  120 + double m_posang; //!< Position angle, counterclockwise from North (degrees)
120 }; 121 };
121 122
122 123
pyext/GSkyRegionRect.i
@@ -70,12 +70,9 @@ public: @@ -70,12 +70,9 @@ public:
70 void read(const std::string& line); 70 void read(const std::string& line);
71 std::string write(void) const; 71 std::string write(void) const;
72 bool contains(const GSkyDir& dir) const; 72 bool contains(const GSkyDir& dir) const;
73 - bool contains_local(const GSkyDir& locdir) const;  
74 bool contains(const GSkyRegion& reg) const; 73 bool contains(const GSkyRegion& reg) const;
75 bool overlaps(const GSkyRegion& reg) const; 74 bool overlaps(const GSkyRegion& reg) const;
76 - GSkyDir transform_to_local(const GSkyDir& skydir) const;  
77 - GSkyDir transform_to_global(const GSkyDir& locdir) const;  
78 - GSkyDir get_corner(const int& index) const; 75 + GSkyDir corner(const int& index) const;
79 }; 76 };
80 77
81 78
src/sky/GSkyRegionCircle.cpp
@@ -526,47 +526,78 @@ bool GSkyRegionCircle::contains(const GSkyDir&amp; dir) const @@ -526,47 +526,78 @@ bool GSkyRegionCircle::contains(const GSkyDir&amp; dir) const
526 bool GSkyRegionCircle::contains(const GSkyRegion& reg) const 526 bool GSkyRegionCircle::contains(const GSkyRegion& reg) const
527 { 527 {
528 // Initialise return value 528 // Initialise return value
529 - bool fully_inside = false; 529 + bool contains = false;
530 530
531 - // If other region is circle use a simple way to calculate 531 + // If the other region is a circle then check whether it is fully
  532 + // container in the circle
532 if (reg.type() == "Circle") { 533 if (reg.type() == "Circle") {
533 534
534 // Create circular region from reg 535 // Create circular region from reg
535 - const GSkyRegionCircle* regcirc =  
536 - dynamic_cast<const GSkyRegionCircle*>(&reg); 536 + const GSkyRegionCircle* circle = dynamic_cast<const GSkyRegionCircle*>(&reg);
537 537
538 // Calculate angular distance between the centres 538 // Calculate angular distance between the centres
539 - double ang_dist = m_centre.dist_deg(regcirc->centre()); 539 + double ang_dist = m_centre.dist_deg(circle->centre());
540 540
541 // Check if the region is contained in this 541 // Check if the region is contained in this
542 - if ((ang_dist + regcirc->radius()) <= m_radius) {  
543 - fully_inside = true; 542 + if ((ang_dist + circle->radius()) <= m_radius) {
  543 + contains = true;
544 } 544 }
545 545
546 - } 546 + } // endif: other region was of type "Circle"
547 547
548 - // If other region is rectangle check corners 548 + // ... otherwise, if the other region is a rectangle then check whether
  549 + // all four corners are contained within the circle
549 else if (reg.type() == "Rect") { 550 else if (reg.type() == "Rect") {
550 551
551 // Create rectangular region from reg 552 // Create rectangular region from reg
552 - const GSkyRegionRect* regrect =  
553 - dynamic_cast<const GSkyRegionRect*>(&reg);  
554 -  
555 - // Check containment of all edges  
556 - fully_inside = contains(regrect->get_corner(0)) &&  
557 - contains(regrect->get_corner(1)) &&  
558 - contains(regrect->get_corner(2)) &&  
559 - contains(regrect->get_corner(3)); 553 + const GSkyRegionRect* rect = dynamic_cast<const GSkyRegionRect*>(&reg);
  554 +
  555 + // Check containment of all corners
  556 + contains = this->contains(rect->corner(0)) &&
  557 + this->contains(rect->corner(1)) &&
  558 + this->contains(rect->corner(2)) &&
  559 + this->contains(rect->corner(3));
  560 +
560 } 561 }
561 562
  563 + // ... otherwise, if the other region is a map then check whether it
  564 + // is contained within the rectangle
  565 + else if (reg.type() == "Map") {
  566 +
  567 + // Create map from reg
  568 + const GSkyRegionMap* map = dynamic_cast<const GSkyRegionMap*>(&reg);
  569 +
  570 + // Get non-zero indices
  571 + std::vector<int> indices = map->nonzero_indices();
  572 +
  573 + // Initialise containment flag to true
  574 + contains = true;
  575 +
  576 + // Loop over all indices
  577 + for (int i = 0; i < indices.size(); ++i) {
  578 +
  579 + // Get sky direction of map pixel
  580 + GSkyDir dir = map->map().inx2dir(i);
  581 +
  582 + // If pixel is not contained then set containment flag to false
  583 + // and break
  584 + if (!this->contains(dir)) {
  585 + contains = false;
  586 + break;
  587 + }
  588 +
  589 + } // endfor: looped over non-zero map indices
  590 +
  591 + } // endif: other region was of type "Map"
  592 +
562 // ... otherwise throw an exception 593 // ... otherwise throw an exception
563 else { 594 else {
564 throw GException::feature_not_implemented(G_CONTAINS, 595 throw GException::feature_not_implemented(G_CONTAINS,
565 "Cannot compare to region type \""+reg.type()+"\" yet."); 596 "Cannot compare to region type \""+reg.type()+"\" yet.");
566 } 597 }
567 598
568 - // Return value  
569 - return fully_inside; 599 + // Return containment flag
  600 + return contains;
570 } 601 }
571 602
572 603
@@ -590,14 +621,13 @@ bool GSkyRegionCircle::overlaps(const GSkyRegion&amp; reg) const @@ -590,14 +621,13 @@ bool GSkyRegionCircle::overlaps(const GSkyRegion&amp; reg) const
590 if (reg.type() == "Circle") { 621 if (reg.type() == "Circle") {
591 622
592 // Create circular region from reg 623 // Create circular region from reg
593 - const GSkyRegionCircle* regcirc =  
594 - dynamic_cast<const GSkyRegionCircle*>(&reg); 624 + const GSkyRegionCircle* circle = dynamic_cast<const GSkyRegionCircle*>(&reg);
595 625
596 // Calculate angular distance between the centres 626 // Calculate angular distance between the centres
597 - double ang_dist = m_centre.dist_deg(regcirc->centre()); 627 + double ang_dist = m_centre.dist_deg(circle->centre());
598 628
599 // Check if the distance is smaller than the sum of both radii 629 // Check if the distance is smaller than the sum of both radii
600 - if (ang_dist <= (m_radius + regcirc->radius())) { 630 + if (ang_dist <= (m_radius + circle->radius())) {
601 overlap = true; 631 overlap = true;
602 } 632 }
603 633
@@ -607,11 +637,10 @@ bool GSkyRegionCircle::overlaps(const GSkyRegion&amp; reg) const @@ -607,11 +637,10 @@ bool GSkyRegionCircle::overlaps(const GSkyRegion&amp; reg) const
607 else if (reg.type() == "Rect") { 637 else if (reg.type() == "Rect") {
608 638
609 // Create rectangular region from reg 639 // Create rectangular region from reg
610 - const GSkyRegionRect* regrect =  
611 - dynamic_cast<const GSkyRegionRect*>(&reg); 640 + const GSkyRegionRect* rect = dynamic_cast<const GSkyRegionRect*>(&reg);
612 641
613 // Check overlap with circle 642 // Check overlap with circle
614 - overlap = regrect->overlaps(*this); 643 + overlap = rect->overlaps(*this);
615 644
616 } // endif: region was rectangle 645 } // endif: region was rectangle
617 646
@@ -619,11 +648,10 @@ bool GSkyRegionCircle::overlaps(const GSkyRegion&amp; reg) const @@ -619,11 +648,10 @@ bool GSkyRegionCircle::overlaps(const GSkyRegion&amp; reg) const
619 else if (reg.type() == "Map") { 648 else if (reg.type() == "Map") {
620 649
621 // Create map from reg 650 // Create map from reg
622 - const GSkyRegionMap* regmap =  
623 - dynamic_cast<const GSkyRegionMap*>(&reg); 651 + const GSkyRegionMap* map = dynamic_cast<const GSkyRegionMap*>(&reg);
624 652
625 // Check overlap with circle 653 // Check overlap with circle
626 - overlap = regmap->overlaps(*this); 654 + overlap = map->overlaps(*this);
627 655
628 } // endif: region was map 656 } // endif: region was map
629 657
@@ -638,7 +666,6 @@ bool GSkyRegionCircle::overlaps(const GSkyRegion&amp; reg) const @@ -638,7 +666,6 @@ bool GSkyRegionCircle::overlaps(const GSkyRegion&amp; reg) const
638 } 666 }
639 667
640 668
641 -  
642 /*========================================================================== 669 /*==========================================================================
643 = = 670 = =
644 = Private methods = 671 = Private methods =
src/sky/GSkyRegionMap.cpp
@@ -677,7 +677,7 @@ void GSkyRegionMap::set_region_rect(const GSkyRegionRect* rect) @@ -677,7 +677,7 @@ void GSkyRegionMap::set_region_rect(const GSkyRegionRect* rect)
677 for (int icorner = 0; icorner < 4; ++icorner) { 677 for (int icorner = 0; icorner < 4; ++icorner) {
678 678
679 // Get skydir of current corner 679 // Get skydir of current corner
680 - GSkyDir corner = rect->get_corner(icorner); 680 + GSkyDir corner = rect->corner(icorner);
681 681
682 // Compute polar props in global coords relative to rectangle centre 682 // Compute polar props in global coords relative to rectangle centre
683 double dist = rect->centre().dist(corner); 683 double dist = rect->centre().dist(corner);
src/sky/GSkyRegionRect.cpp
@@ -39,7 +39,7 @@ @@ -39,7 +39,7 @@
39 #define G_READ "GSkyRegionRect::read(std::string&)" 39 #define G_READ "GSkyRegionRect::read(std::string&)"
40 #define G_CONTAINS "GSkyRegionRect::contains(GSkyRegion&)" 40 #define G_CONTAINS "GSkyRegionRect::contains(GSkyRegion&)"
41 #define G_OVERLAPS "GSkyRegionRect::overlaps(GSkyRegion&)" 41 #define G_OVERLAPS "GSkyRegionRect::overlaps(GSkyRegion&)"
42 -#define G_GET_CORNER "GSkyRegionRect::get_corner(int&)" 42 +#define G_CORNER "GSkyRegionRect::corner(int&)"
43 43
44 /* __ Macros _____________________________________________________________ */ 44 /* __ Macros _____________________________________________________________ */
45 45
@@ -102,8 +102,8 @@ GSkyRegionRect::GSkyRegionRect(const GSkyDir&amp; centre, @@ -102,8 +102,8 @@ GSkyRegionRect::GSkyRegionRect(const GSkyDir&amp; centre,
102 /***********************************************************************//** 102 /***********************************************************************//**
103 * @brief Right Ascension and Declination constructor 103 * @brief Right Ascension and Declination constructor
104 * 104 *
105 - * @param[in] ra Right Ascension of region centre [deg].  
106 - * @param[in] dec Declination of region centre [deg]. 105 + * @param[in] ra Right Ascension of region centre (degrees).
  106 + * @param[in] dec Declination of region centre (degrees).
107 * @param[in] width Region width (degrees). 107 * @param[in] width Region width (degrees).
108 * @param[in] height Region height (degrees). 108 * @param[in] height Region height (degrees).
109 * @param[in] posang Position angle (degrees). 109 * @param[in] posang Position angle (degrees).
@@ -258,7 +258,7 @@ GSkyRegionRect* GSkyRegionRect::clone(void) const @@ -258,7 +258,7 @@ GSkyRegionRect* GSkyRegionRect::clone(void) const
258 /***********************************************************************//** 258 /***********************************************************************//**
259 * @brief Set width of rectangular region 259 * @brief Set width of rectangular region
260 * 260 *
261 - * @param[in] width Width (degrees). 261 + * @param[in] width Rectangle width (degrees).
262 * 262 *
263 * @exception GException::invalid_argument 263 * @exception GException::invalid_argument
264 * Width value is less than 0. 264 * Width value is less than 0.
@@ -291,7 +291,7 @@ void GSkyRegionRect::width(const double&amp; width) @@ -291,7 +291,7 @@ void GSkyRegionRect::width(const double&amp; width)
291 /***********************************************************************//** 291 /***********************************************************************//**
292 * @brief Set height of rectangular region 292 * @brief Set height of rectangular region
293 * 293 *
294 - * @param[in] height Height (degrees). 294 + * @param[in] height Rectangle height (degrees).
295 * 295 *
296 * @exception GException::invalid_argument 296 * @exception GException::invalid_argument
297 * Height value is less than 0. 297 * Height value is less than 0.
@@ -488,43 +488,14 @@ std::string GSkyRegionRect::write(void) const @@ -488,43 +488,14 @@ std::string GSkyRegionRect::write(void) const
488 ***************************************************************************/ 488 ***************************************************************************/
489 bool GSkyRegionRect::contains(const GSkyDir& dir) const 489 bool GSkyRegionRect::contains(const GSkyDir& dir) const
490 { 490 {
491 - // Compute sky direction in local coordinate system  
492 - GSkyDir locdir = transform_to_local(dir); 491 + // Transform sky direction to local coordinate system
  492 + GSkyPixel local = dir_to_local(dir);
493 493
494 // Check containment using local coordinate system 494 // Check containment using local coordinate system
495 - bool dir_is_in = contains_local(locdir); 495 + bool contains = this->contains(local);
496 496
497 - // Return bool  
498 - return dir_is_in;  
499 -}  
500 -  
501 -  
502 -/***********************************************************************//**  
503 - * @brief Checks if local direction lies within region  
504 - *  
505 - * @param[in] locdir Sky direction in local coordinate system.  
506 - *  
507 - * A local direction lies within a region when its distance to the region  
508 - * centre is not larger than the region extension in both axes directions.  
509 - ***************************************************************************/  
510 -bool GSkyRegionRect::contains_local(const GSkyDir& locdir) const  
511 -{  
512 - // Initialise return value  
513 - bool dir_is_in = false;  
514 -  
515 - // Check containment: declination axis  
516 - if ((std::abs(locdir.dec_deg()) - 1.0e-10) <= 0.5 * m_height) {  
517 -  
518 - // Check containment: right ascension axis  
519 - if ((std::abs(locdir.ra_deg()) - 1.0e-10) <= 0.5 * m_width) {  
520 -  
521 - // Sky direction is inside the rectangle  
522 - dir_is_in = true;  
523 - }  
524 - }  
525 -  
526 - // Return bool  
527 - return dir_is_in; 497 + // Return containment flag
  498 + return contains;
528 } 499 }
529 500
530 501
@@ -539,60 +510,88 @@ bool GSkyRegionRect::contains_local(const GSkyDir&amp; locdir) const @@ -539,60 +510,88 @@ bool GSkyRegionRect::contains_local(const GSkyDir&amp; locdir) const
539 bool GSkyRegionRect::contains(const GSkyRegion& reg) const 510 bool GSkyRegionRect::contains(const GSkyRegion& reg) const
540 { 511 {
541 // Initialise return value 512 // Initialise return value
542 - bool is_fully_inside = false; 513 + bool contains = false;
543 514
544 - // If other region is circle use a simple way to calculate 515 + // If the other region is a circle then check whether it is fully
  516 + // contained within the rectangle
545 if (reg.type() == "Circle") { 517 if (reg.type() == "Circle") {
546 518
547 // Create circular region from reg 519 // Create circular region from reg
548 - const GSkyRegionCircle* regcirc =  
549 - dynamic_cast<const GSkyRegionCircle*>(&reg); 520 + const GSkyRegionCircle* circle = dynamic_cast<const GSkyRegionCircle*>(&reg);
550 521
551 // Transform circle center to local coordinate system 522 // Transform circle center to local coordinate system
552 - GSkyDir local_centre = transform_to_local(regcirc->centre());  
553 -  
554 - // Get circle radius  
555 - const double circ_rad = regcirc->radius();  
556 -  
557 - // Check extension of circle along declination axis  
558 - if ((std::abs(local_centre.dec_deg()) + circ_rad) <= 0.5 * m_height) { 523 + GSkyPixel local = dir_to_local(circle->centre());
  524 + double x_deg = local.x() * gammalib::rad2deg;
  525 + double y_deg = local.y() * gammalib::rad2deg;
559 526
560 - // Check extension of circle along right ascension axis  
561 - if ((std::abs(local_centre.ra_deg()) + circ_rad) <= 0.5 * m_width) { 527 + // Get circle radius in degrees
  528 + double radius = circle->radius();
562 529
563 - // Circle is fully contained in this rectangle  
564 - is_fully_inside = true; 530 + // Check whether circle is contained within rectangle
  531 + if ((std::abs(y_deg) + radius) <= 0.5 * m_height) {
  532 + if ((std::abs(x_deg) + radius) <= 0.5 * m_width) {
  533 + contains = true;
565 } 534 }
566 } 535 }
567 - } // Region was of type "Circle"  
568 536
569 - // If other region is rectangle use a simple way to calculate 537 + } // endif: other region was of type "Circle"
  538 +
  539 + // ... otherwise, if the other region is a rectangle then check whether
  540 + // all corners fall within the rectangle
570 else if (reg.type() == "Rect") { 541 else if (reg.type() == "Rect") {
571 542
572 // Create rectangular region from reg 543 // Create rectangular region from reg
573 - const GSkyRegionRect* regrect =  
574 - dynamic_cast<const GSkyRegionRect*>(&reg); 544 + const GSkyRegionRect* rect = dynamic_cast<const GSkyRegionRect*>(&reg);
  545 +
  546 + // Initialise containment flag to true
  547 + contains = true;
575 548
576 // Loop over the four corners of the rectangle :regrect: 549 // Loop over the four corners of the rectangle :regrect:
577 - for (int icorner = 0; icorner < 4; ++icorner) { 550 + for (int i = 0; i < 4; ++i) {
578 551
579 - // Get skydir of current corner  
580 - GSkyDir corner = regrect->get_corner(icorner); 552 + // Get sky direction of current corner
  553 + GSkyDir corner = rect->corner(i);
581 554
582 - // Check corner containment  
583 - if (!contains(corner)) { 555 + // If corner is not contained then set containment flag to false
  556 + // and break
  557 + if (!this->contains(corner)) {
  558 + contains = false;
584 break; 559 break;
585 } 560 }
586 - // Did we arrive at the last corner?  
587 - else if (icorner == 3) {  
588 561
589 - // As we reached this point, the three prior corners were inside  
590 - // and the last corner now is also inside:  
591 - // Rectangle is fully inside  
592 - is_fully_inside = true; 562 + } // endfor: looped over the four corners
  563 +
  564 + } // endif: other region was of type "Rect"
  565 +
  566 + // ... otherwise, if the other region is a map then check whether it
  567 + // is contained within the rectangle
  568 + else if (reg.type() == "Map") {
  569 +
  570 + // Create map from reg
  571 + const GSkyRegionMap* map = dynamic_cast<const GSkyRegionMap*>(&reg);
  572 +
  573 + // Get non-zero indices
  574 + std::vector<int> indices = map->nonzero_indices();
  575 +
  576 + // Initialise containment flag to true
  577 + contains = true;
  578 +
  579 + // Loop over all indices
  580 + for (int i = 0; i < indices.size(); ++i) {
  581 +
  582 + // Get sky direction of map pixel
  583 + GSkyDir dir = map->map().inx2dir(i);
  584 +
  585 + // If pixel is not contained then set containment flag to false
  586 + // and break
  587 + if (!this->contains(dir)) {
  588 + contains = false;
  589 + break;
593 } 590 }
594 - } // Looped over the four corners  
595 - } // Region was of type "Rect" 591 +
  592 + } // endfor: looped over non-zero map indices
  593 +
  594 + } // endif: other region was of type "Map"
596 595
597 // ... otherwise throw an exception 596 // ... otherwise throw an exception
598 else { 597 else {
@@ -600,8 +599,8 @@ bool GSkyRegionRect::contains(const GSkyRegion&amp; reg) const @@ -600,8 +599,8 @@ bool GSkyRegionRect::contains(const GSkyRegion&amp; reg) const
600 "Cannot compare to region type \""+reg.type()+"\" yet."); 599 "Cannot compare to region type \""+reg.type()+"\" yet.");
601 } 600 }
602 601
603 - // Return value  
604 - return is_fully_inside; 602 + // Return containment flag
  603 + return contains;
605 } 604 }
606 605
607 606
@@ -614,36 +613,34 @@ bool GSkyRegionRect::contains(const GSkyRegion&amp; reg) const @@ -614,36 +613,34 @@ bool GSkyRegionRect::contains(const GSkyRegion&amp; reg) const
614 * Regions differ in type. 613 * Regions differ in type.
615 * 614 *
616 * @todo Improve implementation for rectangle-rectangle 615 * @todo Improve implementation for rectangle-rectangle
617 - * @todo Implement check for map  
618 ***************************************************************************/ 616 ***************************************************************************/
619 bool GSkyRegionRect::overlaps(const GSkyRegion& reg) const 617 bool GSkyRegionRect::overlaps(const GSkyRegion& reg) const
620 { 618 {
621 // Initialise return value 619 // Initialise return value
622 bool overlap = false; 620 bool overlap = false;
623 621
624 - // If region is circle then check overlap between circle and rectangle 622 + // If the region is a circle then check overlap between circle and
  623 + // rectangle
625 if (reg.type() == "Circle") { 624 if (reg.type() == "Circle") {
626 625
627 // Create circular region from reg 626 // Create circular region from reg
628 - const GSkyRegionCircle* regcirc =  
629 - dynamic_cast<const GSkyRegionCircle*>(&reg); 627 + const GSkyRegionCircle* circle = dynamic_cast<const GSkyRegionCircle*>(&reg);
630 628
631 // Transform circle center to local coordinate system 629 // Transform circle center to local coordinate system
632 - GSkyDir local_centre = transform_to_local(regcirc->centre());  
633 -  
634 - // Get circle radius  
635 - const double circ_rad = regcirc->radius(); 630 + GSkyPixel local = dir_to_local(circle->centre());
  631 + double x_deg = local.x() * gammalib::rad2deg;
  632 + double y_deg = local.y() * gammalib::rad2deg;
636 633
637 - // Check extension of circle along declination axis  
638 - if (std::abs(local_centre.dec_deg()) <= (0.5 * m_height + circ_rad)) { 634 + // Get circle radius in degrees
  635 + double radius = circle->radius();
639 636
640 - // Check extension of circle along right ascension axis  
641 - if (std::abs(local_centre.ra_deg()) <= (0.5 * m_width + circ_rad)) {  
642 -  
643 - // Circle is overlapping with rectangle 637 + // Check whether circle overlaps within rectangle
  638 + if (std::abs(y_deg) <= (0.5 * m_height + radius)) {
  639 + if (std::abs(x_deg) <= (0.5 * m_width + radius)) {
644 overlap = true; 640 overlap = true;
645 } 641 }
646 } 642 }
  643 +
647 } // endif: region was of type "Circle" 644 } // endif: region was of type "Circle"
648 645
649 // ... otherwise if region is a rectangle then check overlap between 646 // ... otherwise if region is a rectangle then check overlap between
@@ -651,12 +648,11 @@ bool GSkyRegionRect::overlaps(const GSkyRegion&amp; reg) const @@ -651,12 +648,11 @@ bool GSkyRegionRect::overlaps(const GSkyRegion&amp; reg) const
651 else if (reg.type() == "Rect") { 648 else if (reg.type() == "Rect") {
652 649
653 // Create rectangular region from reg 650 // Create rectangular region from reg
654 - const GSkyRegionRect* regrect =  
655 - dynamic_cast<const GSkyRegionRect*>(&reg); 651 + const GSkyRegionRect* rect = dynamic_cast<const GSkyRegionRect*>(&reg);
656 652
657 // Dirty kludge: compare with map 653 // Dirty kludge: compare with map
658 - GSkyRegionMap regmap = GSkyRegionMap(regrect);  
659 - overlap = regmap.overlaps(*this); 654 + GSkyRegionMap map = GSkyRegionMap(rect);
  655 + overlap = map.overlaps(*this);
660 656
661 } // endif: region was of type "Rect" 657 } // endif: region was of type "Rect"
662 658
@@ -664,11 +660,10 @@ bool GSkyRegionRect::overlaps(const GSkyRegion&amp; reg) const @@ -664,11 +660,10 @@ bool GSkyRegionRect::overlaps(const GSkyRegion&amp; reg) const
664 else if (reg.type() == "Map") { 660 else if (reg.type() == "Map") {
665 661
666 // Create map from reg 662 // Create map from reg
667 - const GSkyRegionMap* regmap =  
668 - dynamic_cast<const GSkyRegionMap*>(&reg); 663 + const GSkyRegionMap* map = dynamic_cast<const GSkyRegionMap*>(&reg);
669 664
670 // Check overlap with circle 665 // Check overlap with circle
671 - overlap = regmap->overlaps(*this); 666 + overlap = map->overlaps(*this);
672 667
673 } // endif: region was map 668 } // endif: region was map
674 669
@@ -684,99 +679,47 @@ bool GSkyRegionRect::overlaps(const GSkyRegion&amp; reg) const @@ -684,99 +679,47 @@ bool GSkyRegionRect::overlaps(const GSkyRegion&amp; reg) const
684 679
685 680
686 /***********************************************************************//** 681 /***********************************************************************//**
687 - * @brief Transform a sky direction to the local cartesian coordinate system.  
688 - *  
689 - * @param[in] skydir Sky direction in global coordinate system.  
690 - * @return Sky direction in local cartesian region object coordinates.  
691 - *  
692 - * Transform the sky direction @p skydir to the local cartesian coordinate  
693 - * system. The origin of the local coordinate system is fixed to the center  
694 - * of the rectangle and aligned in +ra (width) and +dec (height). Please  
695 - * note, that a GSkyDir object is returned, even if the local coordsys is  
696 - * is cartesian.  
697 - ***************************************************************************/  
698 -GSkyDir GSkyRegionRect::transform_to_local(const GSkyDir& skydir) const  
699 -{  
700 - // Get distance and polar angle relative to rectangle centre  
701 - double dist = m_centre.dist(skydir);  
702 - double pa = m_centre.posang(skydir) - m_posang * gammalib::deg2rad;  
703 -  
704 - // Compute corresponding x and y coordinate from polar coords  
705 - double x = dist * std::sin(pa);  
706 - double y = dist * std::cos(pa);  
707 -  
708 - // Create output sky direction  
709 - GSkyDir transformed;  
710 - transformed.radec(x, y);  
711 -  
712 - // Return  
713 - return transformed;  
714 -}  
715 -  
716 -  
717 -/***********************************************************************//**  
718 - * @brief Transform a local coordinate to the global coordinate system.  
719 - *  
720 - * @param[in] locdir Direction in local coordinate system.  
721 - * @return Sky direction in global coordinate system.  
722 - *  
723 - * Transform the local direction @p locdir to the global coordinate system.  
724 - * The origin of the local coordinate system is fixed to the center of the  
725 - * rectangle and aligned in +ra (width) and +dec (height).  
726 - ***************************************************************************/  
727 -GSkyDir GSkyRegionRect::transform_to_global(const GSkyDir& locdir) const  
728 -{  
729 - // // Initialise centre in local coordinates (maybe make class member?)  
730 - GSkyDir centre_local;  
731 - centre_local.radec(0,0);  
732 -  
733 - // Access x and y in local coordsys  
734 - double x = locdir.ra();  
735 - double y = locdir.dec();  
736 -  
737 - // Compute dist and position angle in polar coords  
738 - double dist = std::sqrt(x*x + y*y);  
739 - double pa = gammalib::pihalf - std::atan2(y, x) +  
740 - m_posang * gammalib::deg2rad;  
741 -  
742 - // Create output global sky direction  
743 - GSkyDir transformed(m_centre);  
744 - transformed.rotate(pa, dist);  
745 -  
746 - // Return  
747 - return transformed;  
748 -}  
749 -  
750 -  
751 -/***********************************************************************//**  
752 - * @brief Get the sky direction of a corner of the rectangle, there are four. 682 + * @brief Return sky direction of one corner of the rectangle
753 * 683 *
754 * @param[in] index Corner index [0,3] 684 * @param[in] index Corner index [0,3]
755 - * @return Sky direction of corner in global coordinate system. 685 + * @return Sky direction of corner.
756 * 686 *
757 * @exception GException::out_of_range 687 * @exception GException::out_of_range
758 * Corner index is not in [0,3]. 688 * Corner index is not in [0,3].
759 * 689 *
760 - * The index counts CCW starting from +RA,+DEC: +R+D, +R-D, -R-D, -R+D. 690 + * Returns the sky direction of one corner of the rectangle. The corner
  691 + * @p index is counted counterclockwise from the positive Right Ascension
  692 + * and Declination directions:
  693 + *
  694 + * * `index = 0` : \f$(\alpha + \frac{\rm width}{2}, \delta + \frac{\rm height}{2})\f$
  695 + * * `index = 1` : \f$(\alpha + \frac{\rm width}{2}, \delta - \frac{\rm height}{2})\f$
  696 + * * `index = 2` : \f$(\alpha - \frac{\rm width}{2}, \delta + \frac{\rm height}{2})\f$
  697 + * * `index = 3` : \f$(\alpha - \frac{\rm width}{2}, \delta - \frac{\rm height}{2})\f$
  698 + *
  699 + * where \f$\alpha\f$ and \f$\delta\f$ are the Right Ascension and
  700 + * Declination of the rectangle centre, and \f${\rm width}\f$ and
  701 + * \f${\rm height}\f$ are the width and height of the rectangle.
761 ***************************************************************************/ 702 ***************************************************************************/
762 -GSkyDir GSkyRegionRect::get_corner(const int& index) const 703 +GSkyDir GSkyRegionRect::corner(const int& index) const
763 { 704 {
764 // Assert index is in [0,3] 705 // Assert index is in [0,3]
765 if ((index < 0) || (index > 3)) { 706 if ((index < 0) || (index > 3)) {
766 - throw GException::out_of_range(G_GET_CORNER, "Corner index", index, 3); 707 + throw GException::out_of_range(G_CORNER, "Corner index", index, 3);
767 } 708 }
768 709
769 - // Compute the offset to the corners  
770 - // Index counts CCW starting from +RA,+DEC: +R+D, +R-D, -R-D, -R+D 710 + // Compute the offset to the corners in degrees
771 double dx = 0.5 * m_width * (1 - 2*int(index >= 2)); 711 double dx = 0.5 * m_width * (1 - 2*int(index >= 2));
772 double dy = 0.5 * m_height * (1 - 2*int((index==1) || (index==2))); 712 double dy = 0.5 * m_height * (1 - 2*int((index==1) || (index==2)));
773 713
774 - // Define local corner sky direction  
775 - GSkyDir corner;  
776 - corner.radec_deg(dx,dy); 714 + // Convert offset into radians
  715 + dx *= gammalib::deg2rad;
  716 + dy *= gammalib::deg2rad;
  717 +
  718 + // Define corner in local coordinates
  719 + GSkyPixel local(dx,dy);
777 720
778 // Transform to global sky direction 721 // Transform to global sky direction
779 - corner = transform_to_global(corner); 722 + GSkyDir corner = local_to_dir(local);
780 723
781 // Return 724 // Return
782 return corner; 725 return corner;
@@ -871,7 +814,19 @@ void GSkyRegionRect::free_members(void) @@ -871,7 +814,19 @@ void GSkyRegionRect::free_members(void)
871 814
872 815
873 /***********************************************************************//** 816 /***********************************************************************//**
874 - * @brief Compute solid angle (sr) 817 + * @brief Compute solid angle of rectangle
  818 + *
  819 + * This method computes the solid angle of the rectangle in steradians and
  820 + * stores the result in the `m_solid` member.
  821 + *
  822 + * The solid angle is computed using
  823 + *
  824 + * \f[
  825 + * \Omega = {\rm width} \times {\rm height}
  826 + * \f]
  827 + *
  828 + * where \f${\rm width}\f$ and \f${\rm height}\f$ are the width and the
  829 + * height of the rectangle expressed in radians.
875 ***************************************************************************/ 830 ***************************************************************************/
876 void GSkyRegionRect::compute_solid_angle(void) 831 void GSkyRegionRect::compute_solid_angle(void)
877 { 832 {
@@ -881,3 +836,110 @@ void GSkyRegionRect::compute_solid_angle(void) @@ -881,3 +836,110 @@ void GSkyRegionRect::compute_solid_angle(void)
881 // Return 836 // Return
882 return; 837 return;
883 } 838 }
  839 +
  840 +
  841 +/***********************************************************************//**
  842 + * @brief Checks if local direction lies within region
  843 + *
  844 + * @param[in] locdir Sky direction in local coordinate system.
  845 + *
  846 + * A local direction lies within a region when its distance to the region
  847 + * centre is not larger than the region extension in both axes directions.
  848 + ***************************************************************************/
  849 +bool GSkyRegionRect::contains(const GSkyPixel& local) const
  850 +{
  851 + // Initialise return value
  852 + bool contains = false;
  853 +
  854 + // Get local y coordinate in degrees
  855 + double y = local.y() * gammalib::rad2deg;
  856 +
  857 + // Continue if the y coordinate is within half of the height
  858 + if ((std::abs(y) - 1.0e-10) <= 0.5 * m_height) {
  859 +
  860 + // Get local x coordinate in degrees
  861 + double x = local.x() * gammalib::rad2deg;
  862 +
  863 + // If the x coordinate is within half of the width the local
  864 + // coordinate is contained within the rectangle
  865 + if ((std::abs(x) - 1.0e-10) <= 0.5 * m_width) {
  866 + contains = true;
  867 + }
  868 +
  869 + } // endif: y coordinate is within half of the height
  870 +
  871 + // Return containment
  872 + return contains;
  873 +}
  874 +
  875 +
  876 +/***********************************************************************//**
  877 + * @brief Transform sky direction to local rectangle coordinates
  878 + *
  879 + * @param[in] dir Sky direction.
  880 + * @return Local cartesian region object coordinates.
  881 + *
  882 + * Transform the sky direction @p dir to the local cartesian coordinate
  883 + * system. The local coordinates \f$(x,y)\f$ are defined by
  884 + *
  885 + * \f[
  886 + * x = d \sin(\phi)
  887 + * \f]
  888 + *
  889 + * and
  890 + *
  891 + * \f[
  892 + * y = d \cos(\phi)
  893 + * \f]
  894 + *
  895 + * where \f$d\f$ is the angular distance between the sky direction @p dir
  896 + * and the centre of the rectangle in radians, and \f$\phi\f$ is the
  897 + * position angle of the sky direction @p dir with respect to the position
  898 + * angle of the rectangle, computed counter clockwise.
  899 + ***************************************************************************/
  900 +GSkyPixel GSkyRegionRect::dir_to_local(const GSkyDir& dir) const
  901 +{
  902 + // Get distance and polar angle relative to rectangle centre
  903 + double dist = m_centre.dist(dir);
  904 + double pa = m_centre.posang(dir) - m_posang * gammalib::deg2rad;
  905 +
  906 + // Compute corresponding x and y coordinate from polar coords
  907 + double x = dist * std::sin(pa);
  908 + double y = dist * std::cos(pa);
  909 +
  910 + // Create local coordinates
  911 + GSkyPixel local(x,y);
  912 +
  913 + // Return local coordinates
  914 + return local;
  915 +}
  916 +
  917 +
  918 +/***********************************************************************//**
  919 + * @brief Transform local rectangle coordinates to sky direction
  920 + *
  921 + * @param[in] local Local rectangle coordinates.
  922 + * @return Sky direction.
  923 + *
  924 + * Transform the local coordinates @p local to a sky direction. See
  925 + * GSkyRegionRect::dir_to_local for the relation between local rectangle
  926 + * coordinates and the sky direction.
  927 + ***************************************************************************/
  928 +GSkyDir GSkyRegionRect::local_to_dir(const GSkyPixel& local) const
  929 +{
  930 + // Get local coordinates (radians)
  931 + double x = local.x();
  932 + double y = local.y();
  933 +
  934 + // Compute dist and position angle in polar coords
  935 + double dist = std::sqrt(x*x + y*y);
  936 + double pa = gammalib::pihalf - std::atan2(y, x) +
  937 + m_posang * gammalib::deg2rad;
  938 +
  939 + // Create output global sky direction
  940 + GSkyDir dir(m_centre);
  941 + dir.rotate(pa, dist);
  942 +
  943 + // Return sky direction
  944 + return dir;
  945 +}
test/test_GSky.cpp
@@ -1983,35 +1983,37 @@ void TestGSky::test_GSkyRegionRect(void) @@ -1983,35 +1983,37 @@ void TestGSky::test_GSkyRegionRect(void)
1983 test_assert(!refregion.contains(test_dir_2),"test for containment"); 1983 test_assert(!refregion.contains(test_dir_2),"test for containment");
1984 1984
1985 // Corners 1985 // Corners
1986 - test_assert(refregion.contains(refregion.get_corner(0)),"test for corner containment");  
1987 - test_assert(refregion.contains(refregion.get_corner(1)),"test for corner containment");  
1988 - test_assert(refregion.contains(refregion.get_corner(2)),"test for corner containment");  
1989 - test_assert(refregion.contains(refregion.get_corner(3)),"test for corner containment"); 1986 + test_assert(refregion.contains(refregion.corner(0)),"test for corner containment");
  1987 + test_assert(refregion.contains(refregion.corner(1)),"test for corner containment");
  1988 + test_assert(refregion.contains(refregion.corner(2)),"test for corner containment");
  1989 + test_assert(refregion.contains(refregion.corner(3)),"test for corner containment");
1990 1990
1991 1991
1992 // Check GSkyDir containment for rotated rectangle 1992 // Check GSkyDir containment for rotated rectangle
1993 GSkyRegionRect refregion_rot(refdir_radeczerozero, 10, 10, 45); 1993 GSkyRegionRect refregion_rot(refdir_radeczerozero, 10, 10, 45);
1994 1994
1995 // Check corner containment 1995 // Check corner containment
1996 - test_assert(refregion_rot.contains(refregion_rot.get_corner(0)),"test for corner containment");  
1997 - test_assert(refregion_rot.contains(refregion_rot.get_corner(1)),"test for corner containment");  
1998 - test_assert(refregion_rot.contains(refregion_rot.get_corner(2)),"test for corner containment");  
1999 - test_assert(refregion_rot.contains(refregion_rot.get_corner(3)),"test for corner containment"); 1996 + test_assert(refregion_rot.contains(refregion_rot.corner(0)),"test for corner containment");
  1997 + test_assert(refregion_rot.contains(refregion_rot.corner(1)),"test for corner containment");
  1998 + test_assert(refregion_rot.contains(refregion_rot.corner(2)),"test for corner containment");
  1999 + test_assert(refregion_rot.contains(refregion_rot.corner(3)),"test for corner containment");
2000 2000
2001 // Check GSkyDir containment 2001 // Check GSkyDir containment
2002 - test_dir_0.radec_deg(refregion_rot.get_corner(0).ra_deg()+1, 0);  
2003 - test_dir_1.radec_deg(refregion_rot.get_corner(0).ra_deg() , 0);  
2004 - test_dir_2.radec_deg(refregion_rot.get_corner(0).ra_deg()-1, 0); 2002 + test_dir_0.radec_deg(refregion_rot.corner(0).ra_deg()+1, 0);
  2003 + test_dir_1.radec_deg(refregion_rot.corner(0).ra_deg() , 0);
  2004 + test_dir_2.radec_deg(refregion_rot.corner(0).ra_deg()-1, 0);
2005 test_assert(!refregion_rot.contains(test_dir_0),"test for containment"); 2005 test_assert(!refregion_rot.contains(test_dir_0),"test for containment");
2006 test_assert(refregion_rot.contains(test_dir_1),"test for containment"); 2006 test_assert(refregion_rot.contains(test_dir_1),"test for containment");
2007 test_assert(refregion_rot.contains(test_dir_2),"test for containment"); 2007 test_assert(refregion_rot.contains(test_dir_2),"test for containment");
2008 2008
2009 // Check local<->global coordinate transformations 2009 // Check local<->global coordinate transformations
  2010 + /*
2010 test_dir_0.radec_deg(50,50); 2011 test_dir_0.radec_deg(50,50);
2011 test_dir_1 = refregion_rot.transform_to_local(test_dir_0); 2012 test_dir_1 = refregion_rot.transform_to_local(test_dir_0);
2012 test_dir_2 = refregion_rot.transform_to_global(test_dir_1); 2013 test_dir_2 = refregion_rot.transform_to_global(test_dir_1);
2013 test_value(test_dir_0.ra(), test_dir_2.ra(), 1.0e-10, "Test coord trafo"); 2014 test_value(test_dir_0.ra(), test_dir_2.ra(), 1.0e-10, "Test coord trafo");
2014 test_value(test_dir_0.dec(), test_dir_2.dec(), 1.0e-10, "Test coord trafo"); 2015 test_value(test_dir_0.dec(), test_dir_2.dec(), 1.0e-10, "Test coord trafo");
  2016 + */
2015 2017
2016 // Check other rects containment 2018 // Check other rects containment
2017 GSkyDir refdir_raoffset = GSkyDir(); 2019 GSkyDir refdir_raoffset = GSkyDir();