/***************************************************************************
* GSkyRegionRing.cpp - Ring sky region class *
* ----------------------------------------------------------------------- *
* copyright (C) 2013 by Maria Krause, Anneli Schulz *
* ----------------------------------------------------------------------- *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see . *
* *
***************************************************************************/
/**
* @file GSkyRegionRing.hpp
* @brief Ring sky region implementation
* @author Maria Krause, Anneli Schulz
*/
/* __ Includes ___________________________________________________________ */
#ifdef HAVE_CONFIG_H
#include
#endif
#include "GSkyRegionRing.hpp"
#include "GSkyRegionCircle.hpp"
#include "GTools.hpp"
/* __ Method name definitions ____________________________________________ */
#define G_RADIUS "GSkyRegionRing::radius(double&)"
#define G_READ "GSkyRegionRing::read(std::string&)"
#define G_CONTAINS "GSkyRegionRing::contains(GSkyRegion&)"
#define G_OVERLAPS "GSkyRegionRing::overlaps(GSkyRegion&)"
/* __ Macros _____________________________________________________________ */
/* __ Coding definitions _________________________________________________ */
/* __ Debug definitions __________________________________________________ */
/* __ Prototype __________________________________________________________ */
/*==========================================================================
= =
= Constructors/destructors =
= =
==========================================================================*/
/***********************************************************************//**
* @brief Void constructor
***************************************************************************/
GSkyRegionRing::GSkyRegionRing(void)
{
// Initialise members
init_members();
// Return
return;
}
/***********************************************************************//**
* @brief Direction constructor
*
* @param[in] centre Centre sky direction.
* @param[in] radius Region radius [deg].
***************************************************************************/
GSkyRegionRing::GSkyRegionRing(GSkyDir& centre, const double& radius1, const double& radius2)
{
// Initialise members
init_members();
// Set members
this->centre(centre);
this->radius1(radius1);
this->radius2(radius2);
// Compute solid angle
compute_solid_angle();
// Return
return;
}
/***********************************************************************//**
* @brief Direction constructor
*
* @param[in] ra Right Ascension of region centre [deg].
* @param[in] dec Declination of region centre [deg].
* @param[in] radius1 inner Region radius [deg].
* @param[in] radius2 outer Region radius [deg].
***************************************************************************/
GSkyRegionRing::GSkyRegionRing(const double& ra, const double& dec,
const double& radius1, const double& radius2)
{
// Initialise members
init_members();
// Set members
this->centre(ra, dec);
this->radius1(radius1);
this->radius2(radius2);
// Compute solid angle
compute_solid_angle();
// Return
return;
}
/***********************************************************************//**
* @brief String constructor
*
* @param[in] line DS9 region file line.
*
* Constructs region from a DS9 region file line.
***************************************************************************/
GSkyRegionRing::GSkyRegionRing(const std::string& line)
{
// Initialise members
init_members();
// Read information from DS9 region file line
read(line);
// Return
return;
}
/***********************************************************************//**
* @brief Copy constructor
*
* @param[in] region ring sky region.
***************************************************************************/
GSkyRegionRing::GSkyRegionRing(const GSkyRegionRing& region)
{
// Initialise members
init_members();
// Copy members
copy_members(region);
// Return
return;
}
/***********************************************************************//**
* @brief Destructor
***************************************************************************/
GSkyRegionRing::~GSkyRegionRing(void)
{
// Free members
free_members();
// Return
return;
}
/*==========================================================================
= =
= Operators =
= =
==========================================================================*/
/***********************************************************************//**
* @brief Assignment operator
*
* @param[in] Ring Circular sky region.
* @return Circular sky region.
***************************************************************************/
GSkyRegionRing& GSkyRegionRing::operator=(const GSkyRegionRing& Ring)
{
// Execute only if object is not identical
if (this != &Ring) {
// Free members
free_members();
// Initialise private members for clean destruction
init_members();
// Copy members
copy_members(Ring);
} // endif: object was not identical
// Return this object
return *this;
}
/*==========================================================================
= =
= Public methods =
= =
==========================================================================*/
/***********************************************************************//**
* @brief Clear instance
***************************************************************************/
void GSkyRegionRing::clear(void)
{
// Free members
free_members();
this->GSkyRegion::free_members();
// Initialise private members
this->GSkyRegion::init_members();
init_members();
// Return
return;
}
/***********************************************************************//**
* @brief Clone circular sky region
*
* @return Deep copy to circular sky region.
***************************************************************************/
GSkyRegionRing* GSkyRegionRing::clone(void) const
{
// Clone circular sky region
return new GSkyRegionRing(*this);
}
/***********************************************************************//**
* @brief Set radius of circular region
*
* @param[in] radius1 inner region Radius [deg].
* @param[in] radius2 outer region Radius [deg].
*
* @exception GException::invalid_argument
* Radius value is less than 0.
*
* Sets the radius of the circular sky region. Only non-negative radii are
* allowed.
***************************************************************************/
void GSkyRegionRing::radius1(const double& radius1)
{
if (radius1 < 0.0) {
std::string msg =
"A radius of "+gammalib::str(radius1)+" degrees has been"
" specified for a ring sky region.\n"
"The radius of a ring sky region can't be less than 0"
" degrees.";
throw GException::invalid_argument(G_RADIUS, msg);
}
// Set radius value
m_radius1 = radius1;
// Compute the solid angle
compute_solid_angle();
// Return
return;
}
void GSkyRegionRing::radius2(const double& radius2)
{
if (radius2 < 0.0 && radius2 < m_radius1) {
std::string msg =
"A radius of "+gammalib::str(radius2)+" degrees has been"
" specified for a ring sky region.\n"
"The radius of a ring sky region can't be less than 0"
" degrees.";
throw GException::invalid_argument(G_RADIUS, msg);
}
// Set radius value
m_radius2 = radius2;
// Compute the solid angle
compute_solid_angle();
// Return
return;
}
/***********************************************************************//**
* @brief Read region from DS9 string
*
* @param[in] line String in DS9 format.
*
* @exception GException::invalid_value
* Invalid value found in DS9 format string.
***************************************************************************/
void GSkyRegionRing::read(const std::string& line)
{
// Clear the current instance
clear();
// Split the string into 2 parts seperated by #
std::vector substrings = gammalib::split(line,"#");
std::string region_def = (substrings.size() > 0) ? substrings[0] : "";
std::string comment = (substrings.size() > 1) ? substrings[1] : "";
// Finding the Ring
if (region_def.find("Annulus") == std::string::npos) {
std::string msg =
"Unable to find the key word \"Annulus\" in provided string"
" \""+line+"\".\n"
"The \"Ring\" key word is mandatory.";
throw GException::invalid_value(G_READ, msg);
}
// Get the the coordinate system of the values
std::string system = gammalib::split(region_def, ";")[0];
// Get the substring of the important values
unsigned pos = region_def.find("Annulus(");
unsigned end = region_def.find(")");
std::string Ringstring = region_def.substr(pos+8, end);
Ringstring.erase(Ringstring.find(")"), 1);
// Get the values of the region x,y,and radius1 and 2
std::vector values = gammalib::split(Ringstring,",");
if (values.size() != 4) {
std::string msg =
"Invalid number of "+gammalib::str(values.size())+" arguments"
" after the \"Annulus\" key word in provided string \""+line+"\".\n"
"Exactly 4 arguments are expected.";
throw GException::invalid_value(G_READ, msg);
}
double x = gammalib::todouble(values[0]);
double y = gammalib::todouble(values[1]);
double radius1 = gammalib::todouble(values[2]);
double radius2 = gammalib::todouble(values[3]);
// Initialise centre direction
GSkyDir centre = GSkyDir();
// Set the values in the correct system
if (system == "fk5") {
centre.radec_deg(x,y);
}
else if (system == "galactic") {
centre.lb_deg(x,y);
}
else {
std::string msg =
"Unsupported coordinate system \""+system+"\" in provided string"
" \""+line+"\".\n"
"Only the following coordinate systems are supported: \"fk5\", "
"\"galactic\".";
throw GException::invalid_value(G_READ, msg);
}
// Set members
this->centre(centre);
this->radius1(radius1);
this->radius2(radius2);
// Compute solid angle
compute_solid_angle();
// Check if there is a given name for the region and set it
std::vectorcomments = gammalib::split(comment, " ");
for (int i = 0; i < comments.size(); i++) {
if (gammalib::contains(comments[i], "text")) {
std::vector attributes = gammalib::split(comments[i], "=");
if (attributes.size() < 2) {
std::string msg =
"Invalid character sequence encountered in provided"
" string \""+line+"\".\n"
"An attribute of the type \"text=Name\" is expected.";
throw GException::invalid_value(G_READ, msg);
}
m_name = attributes[1];
}
}
// Return
return;
}
/***********************************************************************//**
* @brief Write region into a string
*
* @return String to be written in a DS9 region file
*
* Writes a DS9 region into a string. The region name is only written if it
* is defined.
***************************************************************************/
std::string GSkyRegionRing::write(void) const
{
// Allocate string
std::string result;
// Set string
result.append("fk5;Ring(");
result.append(gammalib::str(m_centre.ra_deg()));
result.append(",");
result.append(gammalib::str(m_centre.dec_deg()));
result.append(",");
result.append(gammalib::str(m_radius1));
result.append(",");
result.append(gammalib::str(m_radius2));
result.append(")");
// Optionally add region name
if (m_name.length() > 0) {
result.append(" # text=");
result.append(m_name);
}
// Return string
return result;
}
/***********************************************************************//**
* @brief Print ring region
*
* @param[in] chatter Chattiness (defaults to NORMAL).
* @return String containing region information.
***************************************************************************/
std::string GSkyRegionRing::print(const GChatter& chatter) const
{
// Initialise result string
std::string result;;
// Continue only if chatter is not silent
if (chatter != SILENT) {
// Append string
result.append("=== GSkyRegionRing ===");
result.append("\n(");
result.append(gammalib::str(m_centre.ra_deg()));
result.append(",");
result.append(gammalib::str(m_centre.dec_deg()));
result.append(",");
result.append(gammalib::str(m_radius1));
result.append(",");
result.append(gammalib::str(m_radius2));
result.append(")");
} // endif: chatter was not silent
// Return result
return result;
}
/***********************************************************************//**
* @brief Checks if sky direction lies within region
*
* @param[in] dir Sky direction.
*
* A sky direction lies within a region when its distance to the region
* centre is not larger than the region ring (between inner and outer radius).
***************************************************************************/
bool GSkyRegionRing::contains(const GSkyDir& dir) const
{
// Initialise return value
bool dir_is_in = false;
// calculate distance from dir to centre
double distance = dir.dist_deg(m_centre);
// Check if distance < radius
if (distance <= radius2() && distance >= radius1()) {
dir_is_in = true;
}
// Return bool
return dir_is_in;
}
/***********************************************************************//**
* @brief Checks if region is fully contained within this region
*
* @param[in] reg Sky region.
*
* @exception GException::feature_not_implemented
* Regions differ in type.
***************************************************************************/
bool GSkyRegionRing::contains(const GSkyRegion& reg) const
{
// Initialise return value
bool fully_inside = false;
// If other region is Ring use a simple way to calculate
if (reg.type() == "Ring") {
// Create circular region from reg
const GSkyRegionRing* regring =
dynamic_cast(®);
// Calculate angular distance between the centres
double ang_dist = m_centre.dist_deg(regring->centre());
// Check if the region is contained in this
if ((ang_dist + regring->radius2()) <= m_radius2 && (ang_dist - regring->radius2()) >= m_radius1) {
fully_inside = true;
}
if ( (ang_dist + m_radius1) <= regring->radius1() && (ang_dist + regring->radius2()) <= m_radius2) {
fully_inside = true;
}
}
// ... otherwise throw an exception
else {
throw GException::feature_not_implemented(G_CONTAINS,
"Cannot compare two different region types yet");
}
// Return value
return fully_inside;
}
/***********************************************************************//**
* @brief Checks if region is overlapping with this region
*
* @param[in] reg Sky region.
*
* @exception GException::feature_not_implemented
* Regions differ in type.
***************************************************************************/
bool GSkyRegionRing::overlaps(const GSkyRegion& reg) const
{
// Initialise return value
bool overlap = false;
// If other region is Ring use a simple way to calculate
if (reg.type() == "Ring") {
// Create circular region from reg
const GSkyRegionRing* regring =
dynamic_cast(®);
// Calculate angular distance between the centres
double ang_dist = m_centre.dist_deg(regring->centre());
// Check if the distance is smaller than the sum of both outer radii
if (ang_dist <= (m_radius2 + regring->radius2())) {
overlap = true;
}
// Check if the distance is smaller than the sum of both inner radii
if (ang_dist <= (m_radius1 + regring->radius1())) {
overlap = true;
}
// Check if two regions overlap
if (ang_dist >= (m_radius1 + regring->radius1()) && ang_dist <= (m_radius2 + regring->radius2())) {
overlap = true;
}
}
// If other region is Circle use a simple way to calculate
else if (reg.type() == "Circle") {
// Create circular region from reg
const GSkyRegionCircle* regcirc =
dynamic_cast(®);
// Calculate angular distance between the centres
double ang_dist = m_centre.dist_deg(regcirc->centre());
// Check if the distance is smaller than the sum of the outer radii and the circular radii
if (ang_dist <= (m_radius2 + regcirc->radius())) {
overlap = true;
}
// Check if two regions overlap
if ((ang_dist + m_radius2) < regcirc->radius()) {
overlap = true;
}
}
// ... otherwise throw an exception
else {
throw GException::feature_not_implemented(G_OVERLAPS,
"Cannot compare two different region types yet");
}
// Return value
return overlap;
}
/*==========================================================================
= =
= Private methods =
= =
==========================================================================*/
/***********************************************************************//**
* @brief Initialise class members
***************************************************************************/
void GSkyRegionRing::init_members(void)
{
// Initialise members
m_centre = GSkyDir();
m_radius1 = 0.0;
m_radius2 = 0.0;
m_type = "Ring";
//Return
return;
}
/***********************************************************************//**
* @brief Copy class members
*
* @param[in] region ring sky region.
***************************************************************************/
void GSkyRegionRing::copy_members(const GSkyRegionRing& region)
{
// Copy attributes
m_centre = region.m_centre;
m_radius1 = region.m_radius1;
m_radius2 = region.m_radius2;
// Return
return;
}
/***********************************************************************//**
* @brief Delete class members
***************************************************************************/
void GSkyRegionRing::free_members(void)
{
// Return
return;
}
/***********************************************************************//**
* @brief Compute solid angle
***************************************************************************/
void GSkyRegionRing::compute_solid_angle(void)
{
// Convert inner and outer radius from degrees to radians
double radius1_rad = m_radius1 * gammalib::deg2rad;
double radius2_rad = m_radius2 * gammalib::deg2rad;
// Compute solid angle
m_solid = gammalib::twopi * (1.0 - std::cos(radius2_rad)) - gammalib::twopi * (1.0 - std::cos(radius1_rad));
// Return
return;
}