/***************************************************************************
* GSkyRegionMap.cpp - Sky region map class *
* ----------------------------------------------------------------------- *
* copyright (C) 2017-2020 by Pierrick Martin *
* ----------------------------------------------------------------------- *
* *
* 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 GSkyRegionMap.cpp
* @brief Sky region map implementation
* @author Pierrick Martin
*/
/* __ Includes ___________________________________________________________ */
#ifdef HAVE_CONFIG_H
#include
#endif
#include "GTools.hpp"
#include "GMath.hpp"
#include "GSkyDir.hpp"
#include "GSkyMap.hpp"
#include "GSkyRegion.hpp"
#include "GSkyRegionCircle.hpp"
#include "GSkyRegionRectangle.hpp"
#include "GSkyRegionMap.hpp"
/* __ Method name definitions ____________________________________________ */
#define G_CONTAINS "GSkyRegionMap::contains(GSkyRegion&)"
/* __ Macros _____________________________________________________________ */
/* __ Coding definitions _________________________________________________ */
/* __ Debug definitions __________________________________________________ */
/* __ Prototype __________________________________________________________ */
/*==========================================================================
= =
= Constructors/destructors =
= =
==========================================================================*/
/***********************************************************************//**
* @brief Void constructor
***************************************************************************/
GSkyRegionMap::GSkyRegionMap(void) : GSkyRegion()
{
// Initialise members
init_members();
// Return
return;
}
/***********************************************************************//**
* @brief FITS filename constructor
*
* @param[in] filename FITS file.
*
* Constructs region from a FITS file.
***************************************************************************/
GSkyRegionMap::GSkyRegionMap(const GFilename& filename) : GSkyRegion()
{
// Initialise members
init_members();
// Load FITS file
load(filename);
// Return
return;
}
/***********************************************************************//**
* @brief Sky map constructor
*
* @param[in] map Sky map.
***************************************************************************/
GSkyRegionMap::GSkyRegionMap(const GSkyMap& map) : GSkyRegion()
{
// Initialise members
init_members();
// Set sky map
this->map(map);
// Return
return;
}
/***********************************************************************//**
* @brief Region conversion constructor
*
* @param[in] region Pointer to sky region.
*
* Constructs a sky region map from any region type.
***************************************************************************/
GSkyRegionMap::GSkyRegionMap(const GSkyRegion* region) : GSkyRegion(*region)
{
// Initialise members
init_members();
// Get dynamic casts
const GSkyRegionMap* map = dynamic_cast(region);
const GSkyRegionCircle* circle = dynamic_cast(region);
const GSkyRegionRectangle* rect = dynamic_cast(region);
// If region is of type GSkyRegionMap then copy class members
if (map != NULL) {
copy_members(*map);
}
// ... otherwise, if region is of type GSkyRegionCircle then set region
// map from region circle
else if (circle != NULL) {
set_region_circle(circle);
}
// If region is of type GSkyRegionRectangle than set region map fron
// region rect
else if (rect != NULL) {
set_region_rectangle(rect);
}
// Return
return;
}
/***********************************************************************//**
* @brief Copy constructor
*
* @param[in] region Sky region map.
***************************************************************************/
GSkyRegionMap::GSkyRegionMap(const GSkyRegionMap& region) : GSkyRegion(region)
{
// Initialise members
init_members();
// Copy members
copy_members(region);
// Return
return;
}
/***********************************************************************//**
* @brief Destructor
***************************************************************************/
GSkyRegionMap::~GSkyRegionMap(void)
{
// Free members
free_members();
// Return
return;
}
/*==========================================================================
= =
= Operators =
= =
==========================================================================*/
/***********************************************************************//**
* @brief Assignment operator
*
* @param[in] region Sky region map.
* @return Sky region map.
***************************************************************************/
GSkyRegionMap& GSkyRegionMap::operator=(const GSkyRegionMap& region)
{
// Execute only if object is not identical
if (this != ®ion) {
// Free members
free_members();
// Initialise private members
init_members();
// Copy members
copy_members(region);
} // endif: object was not identical
// Return this object
return *this;
}
/*==========================================================================
= =
= Public methods =
= =
==========================================================================*/
/***********************************************************************//**
* @brief Clear instance
***************************************************************************/
void GSkyRegionMap::clear(void)
{
// Free members
free_members();
this->GSkyRegion::free_members();
// Initialise private members
this->GSkyRegion::init_members();
init_members();
// Return
return;
}
/***********************************************************************//**
* @brief Clone sky region map
*
* @return Deep copy to sky region map.
***************************************************************************/
GSkyRegionMap* GSkyRegionMap::clone(void) const
{
// Clone sky map region
return new GSkyRegionMap(*this);
}
/***********************************************************************//**
* @brief Set sky map
*
* @param[in] map Sky map
*
* Sets the sky map of a region map.
***************************************************************************/
void GSkyRegionMap::map(const GSkyMap& map)
{
// Set map object
m_map = map;
// Set non-zero pixel indices
set_nonzero_indices();
// Compute solid angle
compute_solid_angle();
// Return
return;
}
/***********************************************************************//**
* @brief Load region map from FITS file
*
* @param[in] filename FITS file name.
***************************************************************************/
void GSkyRegionMap::load(const GFilename& filename)
{
// Free members
free_members();
// Initialise private members
init_members();
// Load map
m_map.load(filename);
// Set non-zero indices
set_nonzero_indices();
// Compute solid angle
compute_solid_angle();
// Return
return;
}
/***********************************************************************//**
* @brief Create region from a string in DS9 format
*
* @param[in] line String describing region in DS9 region format.
*
* Empty implementation of a virtual function because map is not supported
* as DS9 region.
*
* @todo Translate any DS9 region into a map.
***************************************************************************/
void GSkyRegionMap::read(const std::string& line)
{
// Do nothing because map is not supported as DS9 region
return;
}
/***********************************************************************//**
* @brief Write string describing region in DS9 region format
*
* @return DS9 region string
*
* Returns an empty string.
*
* @todo Translate region map into a DS9 polygon.
***************************************************************************/
std::string GSkyRegionMap::write(void) const
{
// Allocate string
std::string result;
// Return string
return result;
}
/***********************************************************************//**
* @brief Print region description
*
* @param[in] chatter Chattiness.
* @return String containing region information.
***************************************************************************/
std::string GSkyRegionMap::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("=== GSkyRegionMap ===");
result.append("\n(");
result.append(m_map.print());
result.append(")");
} // endif: chatter was not silent
// Return result
return result;
}
/***********************************************************************//**
* @brief Check if a given direction is contained in this region
*
* @param[in] dir Sky direction.
* @return True if sky direction is in region, false otherwise.
*
* Checks if sky direction is contained in the region map.
***************************************************************************/
bool GSkyRegionMap::contains(const GSkyDir& dir) const
{
// Initialize
bool contain = false;
// If direction is within map boundaries
if (m_map.contains(dir)) {
// Convert sky direction into pixel index
int i = m_map.dir2inx(dir);
// Set containment flag
contain = (m_map(i,0) != 0.0);
} // endif: map contains direction
// Return value
return contain;
}
/***********************************************************************//**
* @brief Checks if a given region is fully contained within this region
*
* @param[in] reg Sky region.
* @return True if the region is contained within this region, false
* otherwise.
*
* @exception GException::feature_not_implemented
* Specified sky region type not supported by method.
***************************************************************************/
bool GSkyRegionMap::contains(const GSkyRegion& reg) const
{
// Initialise return value
bool inside = true;
// Case of a region map
if (reg.type() == "Map") {
// Cast to map type
const GSkyRegionMap* inreg = static_cast(®);
// Retrieve map data
const GSkyMap& regmap = inreg->map();
const std::vector& regnzvec = inreg->nonzero_indices();
// Loop over input map non-zero pixels and check if they are all in
for (int i = 0; i < regnzvec.size(); ++i) {
if (!contains(regmap.inx2dir(regnzvec[i]))) {
inside = false;
break;
}
}
} // endcase: region map
// Case of a circular region
else if (reg.type() == "Circle") {
// Create circular region from reg
const GSkyRegionCircle* inreg =
static_cast(®);
// Retrieve circle data
const GSkyDir& centre = inreg->centre();
double rad = inreg->radius();
// Test a number of points along the circle
const int numtest = 360;
double rotangle = 360.0 / numtest;
GSkyDir testdir;
for (int i = 0; i < numtest; ++i) {
testdir = centre;
testdir.rotate_deg(i*rotangle, rad);
if (!contains(testdir)) {
inside = false;
break;
}
}
} // endcase: circular region
// Case of a rectangular region
else if (reg.type() == "Rectangle") {
// Create rectangular region from reg
const GSkyRegionRectangle* rect =
static_cast(®);
// Create region map from rectangle
GSkyRegionMap rect_map(rect);
// Retrieve map data
const GSkyMap& regmap = rect_map.map();
const std::vector& regnzvec = rect_map.nonzero_indices();
// Loop over input map non-zero pixels and check if they are all in
for (int i = 0; i < regnzvec.size(); ++i) {
if (!contains(regmap.inx2dir(regnzvec[i]))) {
inside = false;
break;
}
}
} // endcase: rectangular region
// Other cases not implemented
else {
throw GException::feature_not_implemented(G_CONTAINS,
"Method only implemented for cicular, rectangular and map regions.");
}
// Return result
return inside;
}
/***********************************************************************//**
* @brief Checks if a given region is overlapping with this region
*
* @param[in] reg Sky region.
* @return True if the region is overlapping, false otherwise.
***************************************************************************/
bool GSkyRegionMap::overlaps(const GSkyRegion& reg) const
{
// Initialise return value
bool overlap = false;
// Loop over non-zero pixels until direction is contained into region
for (int i = 0; i < m_nonzero_indices.size(); ++i) {
GSkyDir dir = m_map.inx2dir(m_nonzero_indices[i]);
if (reg.contains(dir)) {
overlap = true;
break;
}
}
// Return
return overlap;
}
/*==========================================================================
= =
= Private methods =
= =
==========================================================================*/
/***********************************************************************//**
* @brief Initialise class members
***************************************************************************/
void GSkyRegionMap::init_members(void)
{
// Initialie base class members
m_type = "Map";
// Initialise members
m_map.clear();
m_nonzero_indices.clear();
//Return
return;
}
/***********************************************************************//**
* @brief Copy class members
*
* @param[in] map Sky region map.
***************************************************************************/
void GSkyRegionMap::copy_members(const GSkyRegionMap& map)
{
// Copy members
m_map = map.m_map;
m_nonzero_indices = map.m_nonzero_indices;
// Compute solid angle
compute_solid_angle();
// Return
return;
}
/***********************************************************************//**
* @brief Delete class members
***************************************************************************/
void GSkyRegionMap::free_members(void)
{
// No memory to be freed, return
return;
}
/***********************************************************************//**
* @brief Compute solid angle
***************************************************************************/
void GSkyRegionMap::compute_solid_angle(void)
{
// Initialise solid angle
m_solid = 0.0;
// Loop over all non-zero pixels of map and add up solid angles
for (int i = 0; i < m_nonzero_indices.size(); ++i) {
m_solid += m_map.solidangle(m_nonzero_indices[i]);
}
// Return
return;
}
/***********************************************************************//**
* @brief Create an array of non-zero pixel indices
***************************************************************************/
void GSkyRegionMap::set_nonzero_indices(void)
{
// Clear zero pixel array
m_nonzero_indices.clear();
// Loop over map pixels and find non-zero pixels
for (int i = 0; i < m_map.npix(); ++i) {
if (m_map(i,0) != 0.0) {
m_nonzero_indices.push_back(i);
}
}
// Return
return;
}
/***********************************************************************//**
* @brief Set region map from region circle
*
* @param[in] circle Region circle
*
* Sets a region map from a region circle. The method allocates a sky map
* with a default resolution of 1/20 of the circle radius. In any case, the
* sky map resolution is comprised within [0.0002, 0.1] degrees. The default
* size of the sky map is 2.5 times the circle radius, and in any case, at
* least 10 sky map pixels will be allocated. The sky map will be in
* celestial coordinates and TAN projection.
***************************************************************************/
void GSkyRegionMap::set_region_circle(const GSkyRegionCircle* circle)
{
// Set sky map resolution from circle radius and constrain the
// resolution to [0.0002, 0.1] deg
double resolution = 0.05 * circle->radius();
if (resolution < 0.0002) {
resolution = 0.0002;
}
else if (resolution > 0.1) {
resolution = 0.1;
}
// Set number of sky map pixels
int nbins = int(2.5 * circle->radius() / resolution);
if (nbins < 10) {
nbins = 10;
}
// Set sky map
GSkyMap map("TAN", "CEL", circle->ra(), circle->dec(),
resolution, resolution,
nbins, nbins);
// Set sky map pixels
for (int i = 0; i < map.npix(); ++i) {
// Compute distance to circle centre
double distance = circle->centre().dist_deg(map.inx2dir(i));
// If distance is smaller or equal to radius then set map
// pixel to 1
if (distance <= circle->radius()) {
map(i) = 1.0;
}
} // endfor: looped over sky map pixels
// Set sky map as region map
this->map(map);
// Return
return;
}
/***********************************************************************//**
* @brief Set region map from region rectangle
*
* @param[in] rect Region rectangle
*
* Sets a region map from a region rectangle.
*
* A skymap is allocated with a resolution goal of 0.01 degrees (same for
* both axes). The particular resolution of either axes depends on the width
* over height of the RoI of the rotated rectangle. The map extent is chosen
* dynamically to match the RoI of the rotated rectangle. Three sparse pixels
* are added at either side of the map. The sky map is created in celestial
* coordinates and TAN projection.
***************************************************************************/
void GSkyRegionMap::set_region_rectangle(const GSkyRegionRectangle* rect)
{
// Define desired resolution (in degrees)
const double resolution_goal = 0.01;
// Try to estimate extent of the (rotated) rectangle:
// Loop over the rectangle corners to get the extension
double dxmax = 0.0;
double dymax = 0.0;
for (int icorner = 0; icorner < 4; ++icorner) {
// Get skydir of current corner
GSkyDir corner = rect->corner(icorner);
// Compute polar props in global coords relative to rectangle centre
double dist = rect->centre().dist(corner);
double pa = rect->centre().posang(corner);
// Compute required map extension along RA/Dec for this corner
double dx = dist * std::sin(pa);
double dy = dist * std::cos(pa);
// Select the largest extension
if (dy > dymax) {
dymax = dy;
}
if (dx > dxmax) {
dxmax = dx;
}
} // endfor: looped over all corners
// Compute width of map RoI in degrees
double map_width = 2.0 * dxmax * gammalib::rad2deg;
double map_height = 2.0 * dymax * gammalib::rad2deg;
// Compute num of bins in x direction, round
int nbins_x = int(map_width / resolution_goal + 0.5);
int nbins_y = int(map_height / resolution_goal + 0.5);
// Ensure a minimum num of bins
if (nbins_x < 10) {
nbins_x = 10;
}
if (nbins_y < 10) {
nbins_y = 10;
}
// Compute actual resolution using nbins
double resolution_x = map_width / nbins_x;
double resolution_y = map_height / nbins_y;
// Add leakage pixels
nbins_x += 6;
nbins_y += 6;
// Create fine sky map
GSkyMap map_fine("TAN", "CEL", rect->ra(), rect->dec(),
-resolution_x, resolution_y,
nbins_x, nbins_y);
// Set sky map pixels that are contained in rectangle to 1
for (int ipix = 0; ipix < map_fine.npix(); ++ipix) {
if (rect->contains(map_fine.inx2dir(ipix))) {
map_fine(ipix) = 1.0;
}
}
// Set sky map as region map
this->map(map_fine);
// Return
return;
}