/*************************************************************************** * GHealpix.cpp - Healpix projection class * * ----------------------------------------------------------------------- * * copyright (C) 2010-2015 by Juergen Knoedlseder * * ----------------------------------------------------------------------- * * * * 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 GHealpix.cpp * @brief HealPix projection class implementation * @author Juergen Knoedlseder */ /* __ Includes ___________________________________________________________ */ #ifdef HAVE_CONFIG_H #include #endif #include #include "GException.hpp" #include "GTools.hpp" #include "GMath.hpp" #include "GVector.hpp" #include "GHealpix.hpp" /* __ Method name definitions ____________________________________________ */ #define G_CONSTRUCT "GHealpix::GHealpix(int& ,std::string& ,std::string&)" #define G_READ "GHealpix::read(GFitsHDU&)" #define G_XY2DIR "GHealpix::xy2dir(GSkyPixel&)" #define G_DIR2XY2 "GHealpix::dir2xy(GSkyDir&)" #define G_NEST2RING "GHealpix::nest2ring(int&)" #define G_RING2NEST "GHealpix::ring2nest(int&)" #define G_PIX2ANG_RING "GHealpix::pix2ang_ring(int, double*, double*)" #define G_PIX2ANG_NEST "GHealpix::pix2ang_nest(int, double*, double*)" #define G_ORDERING_SET "GHealpix::ordering(std::string&)" #define G_INTERPOLATOR "GHealpix::interpolator(double&, double&)" #define G_NEIGHBOURS "GHealpix::neighbours(GSkyPixel&)" #define G_BOUNDARIES "GHealpix::boundaries(GSkyPixel&, int&)" /* __ Macros _____________________________________________________________ */ /* __ Coding definitions _________________________________________________ */ /* __ Debug definitions __________________________________________________ */ /* __ Local prototypes ___________________________________________________ */ /* __ Constants __________________________________________________________ */ const int jrll[12] = {2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4}; const int jpll[12] = {1, 3, 5, 7, 0, 2, 4, 6, 1, 3, 5, 7}; const int nb_xoffset[] = {-1,-1, 0, 1, 1, 1, 0,-1}; const int nb_yoffset[] = { 0, 1, 1, 1, 0,-1,-1,-1}; const int nb_facearray[][12] = {{ 8, 9,10,11,-1,-1,-1,-1,10,11, 8, 9 }, // S { 5, 6, 7, 4, 8, 9,10,11, 9,10,11, 8 }, // SE { -1,-1,-1,-1, 5, 6, 7, 4,-1,-1,-1,-1 }, // E { 4, 5, 6, 7,11, 8, 9,10,11, 8, 9,10 }, // SW { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 }, // center { 1, 2, 3, 0, 0, 1, 2, 3, 5, 6, 7, 4 }, // NE { -1,-1,-1,-1, 7, 4, 5, 6,-1,-1,-1,-1 }, // W { 3, 0, 1, 2, 3, 0, 1, 2, 4, 5, 6, 7 }, // NW { 2, 3, 0, 1,-1,-1,-1,-1, 0, 1, 2, 3 }}; // N const int nb_swaparray[][3] = {{ 0,0,3 }, // S { 0,0,6 }, // SE { 0,0,0 }, // E { 0,0,5 }, // SW { 0,0,0 }, // center { 5,0,0 }, // NE { 0,0,0 }, // W { 6,0,0 }, // NW { 3,0,0 }}; // N const int order_max = 13; const int ns_max = 1 << order_max; /* __ Static conversion arrays ___________________________________________ */ static short ctab[0x100]; static short utab[0x100]; /* __ Globals ____________________________________________________________ */ /*========================================================================== = = = Constructors/destructors = = = ==========================================================================*/ /***********************************************************************//** * @brief Void constructor ***************************************************************************/ GHealpix::GHealpix(void) : GSkyProjection() { // Initialise class members init_members(); // Return return; } /***********************************************************************//** * @brief Constructor * * @param[in] nside Number of sides. * @param[in] order Pixel ordering ('RING' or 'NESTED'). * @param[in] coords Coordinate system ('EQU' or 'GAL'). * * @exception GException::wcs_hpx_bad_nside * Invalid nside parameter. * @exception GException::wcs_bad_coords * Invalid coordsys parameter. * @exception GException::wcs_hpx_bad_ordering * Invalid ordering parameter. ***************************************************************************/ GHealpix::GHealpix(const int& nside, const std::string& order, const std::string& coords) : GSkyProjection() { // Initialise class members init_members(); // Check nside parameter (power of 2 between 1 and 8192) if (nside2order(nside) == -1) { throw GException::wcs_hpx_bad_nside(G_CONSTRUCT, nside); } // Set coordinate system coordsys(coords); // Set pixel ordering ordering(order); // Set Healpix parameters m_nside = nside; m_npface = m_nside * m_nside; m_ncap = 2 * (m_npface - m_nside); m_num_pixels = 12 * m_npface; m_fact2 = 4.0 / m_num_pixels; m_fact1 = 2 * m_nside * m_fact2; m_solidangle = gammalib::fourpi / m_num_pixels; m_order = nside2order(m_nside); // Return return; } /***********************************************************************//** * @brief Constructor from FITS HDU table * * @param[in] hdu FITS HDU. ***************************************************************************/ GHealpix::GHealpix(const GFitsHDU& hdu) : GSkyProjection() { // Initialise class members init_members(); // Read Healpix definition from FITS table read(hdu); // Return return; } /***********************************************************************//** * @brief Copy constructor * * @param[in] proj Healpix projection. ***************************************************************************/ GHealpix::GHealpix(const GHealpix& proj) : GSkyProjection(proj) { // Initialise class members for clean destruction init_members(); // Copy members copy_members(proj); // Return return; } /***********************************************************************//** * @brief Destructor ***************************************************************************/ GHealpix::~GHealpix() { // Free members free_members(); // Return return; } /*========================================================================== = = = Operators = = = ==========================================================================*/ /***********************************************************************//** * @brief Assignment operator * * @param[in] proj Healpix projection. * @return Healpix projection. ***************************************************************************/ GHealpix& GHealpix::operator=(const GHealpix& proj) { // Execute only if object is not identical if (this != &proj) { // Copy base class members this->GSkyProjection::operator=(proj); // Free members free_members(); // Initialise private members for clean destruction init_members(); // Copy members copy_members(proj); } // endif: object was not identical // Return this object return *this; } /*========================================================================== = = = Public methods = = = ==========================================================================*/ /***********************************************************************//** * @brief Clear object * * This method properly resets the object to an initial state. ***************************************************************************/ void GHealpix::clear(void) { // Free class members (base and derived classes, derived class first) free_members(); this->GSkyProjection::free_members(); // Initialise members this->GSkyProjection::init_members(); init_members(); // Return return; } /***********************************************************************//** * @brief Clone instance ***************************************************************************/ GHealpix* GHealpix::clone(void) const { return new GHealpix(*this); } /***********************************************************************//** * @brief Read Healpix definition from FITS header * * @param[in] hdu FITS HDU. * * @exception GException::wcs * Unable to load Healpix definition from HDU. * @exception GException::wcs_bad_coords * Invalid coordsys parameter. * @exception GException::wcs_hpx_bad_ordering * Invalid ordering parameter. ***************************************************************************/ void GHealpix::read(const GFitsHDU& hdu) { // Clear object clear(); // Check if we have a healpix representation if (hdu.string("PIXTYPE") != "HEALPIX") { throw GException::wcs(G_READ, "HDU does not contain Healpix data"); } // Get pixel ordering. First search for the ORDERING keyword, then // search for the ORDER keyword std::string ordering; if (hdu.has_card("ORDERING")) { ordering = hdu.string("ORDERING"); } else if (hdu.has_card("ORDER")) { ordering = hdu.string("ORDER"); } // Get coordinate system. First search for HIER_CRD keyword (this has been // used in older versions of LAT exposure cubes). If not found then search // for standard COORDSYS keyword. std::string coordsys; if (hdu.has_card("HIER_CRD")) { coordsys = hdu.string("HIER_CRD"); } else if (hdu.has_card("COORDSYS")) { coordsys = hdu.string("COORDSYS"); } // Set coordinate system this->coordsys(coordsys); // Set pixel ordering this->ordering(ordering); // Get Healpix resolution and determine number of pixels and solid angle m_nside = hdu.integer("NSIDE"); m_npface = m_nside * m_nside; m_ncap = 2 * (m_npface - m_nside); m_num_pixels = 12 * m_npface; m_fact2 = 4.0 / m_num_pixels; m_fact1 = 2 * m_nside * m_fact2; m_solidangle = gammalib::fourpi / m_num_pixels; m_order = nside2order(m_nside); // Return return; } /***********************************************************************//** * @brief Write Healpix definition into FITS HDU * * @param[in] hdu FITS HDU. * * Writes the following keywords in the FITS HDU: * EXTNAME = HEALPIX * PIXTYPE = HEALPIX * NSIDE = nside() = m_nside * FIRSTPIX = 0 * LASTPIX = npix()-1 = m_num_pixels-1 * ORDERING = ordering() * COORDSYS = coordsys() ***************************************************************************/ void GHealpix::write(GFitsHDU& hdu) const { // Set extension name hdu.extname("HEALPIX"); // Set keywords hdu.card("PIXTYPE", "HEALPIX", "HEALPix pixelisation"); hdu.card("NSIDE", nside(), "HEALPix resolution parameter"); hdu.card("FIRSTPIX", 0, "Index of first pixel"); hdu.card("LASTPIX", npix()-1, "Index of last pixel"); hdu.card("ORDERING", ordering(), "Pixel ordering scheme, either RING or NESTED"); hdu.card("COORDSYS", coordsys(), "Coordinate system, either EQU or GAL"); // Return return; } /***********************************************************************//** * @brief Returns sky direction of pixel * * @param[in] pixel Sky map pixel. ***************************************************************************/ GSkyDir GHealpix::pix2dir(const GSkyPixel& pixel) const { // Throw an exception if sky map pixel is not 1D if (!pixel.is_1D()) { std::string msg = "Sky map pixel "+pixel.print()+" is not" " 1-dimensional.\n" "Only 1-dimensional pixels are supported by the" " Healpix projection."; throw GException::invalid_argument(G_XY2DIR, msg); } // Perform ordering dependent conversion double theta = 0.0; double phi = 0.0; switch (m_ordering) { case 0: pix2ang_ring(int(pixel), &theta, &phi); break; case 1: pix2ang_nest(int(pixel), &theta, &phi); break; default: break; } // Store coordinate system-dependent result GSkyDir result; switch (m_coordsys) { case 0: result.radec(phi, gammalib::pihalf - theta); break; case 1: result.lb(phi, gammalib::pihalf - theta); break; default: break; } // Return return result; } /***********************************************************************//** * @brief Returns sky map pixel of sky coordinate * * @param[in] dir Sky coordinate. * @return Sky map pixel. ***************************************************************************/ GSkyPixel GHealpix::dir2pix(const GSkyDir& dir) const { // Compute coordinate system dependent (z,phi) double z = 0; double phi = 0; switch (m_coordsys) { case 0: z = cos(gammalib::pihalf - dir.dec()); phi = dir.ra(); break; case 1: z = cos(gammalib::pihalf - dir.b()); phi = dir.l(); break; default: break; } // Perform ordering dependent conversion int index; switch (m_ordering) { case 0: index = ang2pix_z_phi_ring(z, phi); break; case 1: index = ang2pix_z_phi_nest(z, phi); break; default: break; } // Return sky map pixel return (GSkyPixel(index)); } /***********************************************************************//** * @brief Return interpolator for given sky direction * * @param[in] dir Sky direction ***************************************************************************/ GBilinear GHealpix::interpolator(const GSkyDir& dir) const { // Compute coordinate system dependent theta and phi (in radians) double theta = 0; double phi = 0; switch (m_coordsys) { case 0: theta = gammalib::pihalf - dir.dec(); phi = dir.ra(); break; case 1: theta = gammalib::pihalf - dir.b(); phi = dir.l(); break; default: break; } // Normalize theta and phi value theta = gammalib::modulo(theta, gammalib::twopi); if (theta > gammalib::pi) { phi += gammalib::pi; theta = gammalib::twopi - theta; } phi = gammalib::modulo(phi, gammalib::twopi); // Get interpolator GBilinear interpolator = this->interpolator(theta, phi); // Return interpolator return (interpolator); } /***********************************************************************//** * @brief Returns ordering parameter. ***************************************************************************/ std::string GHealpix::ordering(void) const { // Set pixel ordering type std::string ordering; switch (m_ordering) { case 0: ordering = "RING"; break; case 1: ordering = "NESTED"; break; default: ordering = "UNKNOWN"; break; } // Return ordering return ordering; } /***********************************************************************//** * @brief Set pixel ordering. * * @param[in] ordering Pixel ordering (RING or NEST/NESTED). * * @exception GException::wcs_hpx_bad_ordering * Invalid ordering parameter. ***************************************************************************/ void GHealpix::ordering(const std::string& ordering) { // Convert argument to upper case std::string uordering = gammalib::toupper(ordering); // Set pixel ordering if (uordering == "RING") { m_ordering = 0; } else if (uordering == "NESTED" || uordering == "NEST") { m_ordering = 1; } else { throw GException::wcs_hpx_bad_ordering(G_ORDERING_SET, ordering); } // Return return; } /***********************************************************************//** * @brief Return neighbouring pixels of a pixel * * @param[in] pixel Pixel index. * @return Array of neighbouring pixels. * * Returns the 8 neighbours of a given pixel. The method returns a vector * with contains the pixel indices of the SW, W, NW, N, NE, E, SE and S * neighbours of @p pix (in the given order). If a neighbour does not exist * (this can only be the case for the W, N, E and S neighbors), its entry is * set to -1. * * This method has been adapted from the neighbors() function located in the * file healpix_base.cc in Healpix version 3.20. ***************************************************************************/ std::vector GHealpix::neighbours(const GSkyPixel& pixel) const { // Throw an exception if sky map pixel is not 1D if (!pixel.is_1D()) { std::string msg = "Sky map pixel "+pixel.print()+" is not" " 1-dimensional.\n" "Only 1-dimensional pixels are supported by the" " Healpix projection."; throw GException::invalid_argument(G_NEIGHBOURS, msg); } // Initialise result array std::vector result; // Determine pixel index and face number int ix; int iy; int face_num; pix2xyf(int(pixel), &ix, &iy, &face_num); // ... const int nsm1 = m_nside - 1; // ... if ((ix > 0) && (ix < nsm1) && (iy > 0) && (iy < nsm1)) { // Ring ordering scheme ... if (m_ordering == 0) { // Ring? for (int m = 0; m < 8; ++m) { int index = xyf2ring(ix+nb_xoffset[m],iy+nb_yoffset[m],face_num); result.push_back(index); } } // ... or nested scheme? else { int fpix = int(face_num) << (2*m_order); int px0 = spread_bits(ix); int py0 = spread_bits(iy) << 1; int pxp = spread_bits(ix+1); int pyp = spread_bits(iy+1) << 1; int pxm = spread_bits(ix-1); int pym = spread_bits(iy-1) << 1; result.push_back(fpix + pxm + py0); result.push_back(fpix + pxm + pyp); result.push_back(fpix + px0 + pyp); result.push_back(fpix + pxp + pyp); result.push_back(fpix + pxp + py0); result.push_back(fpix + pxp + pym); result.push_back(fpix + px0 + pym); result.push_back(fpix + pxm + pym); } } // endif // ... else { for (int i = 0; i < 8; ++i) { int x = ix + nb_xoffset[i]; int y = iy + nb_yoffset[i]; int nbnum = 4; if (x < 0) { x += m_nside; nbnum -= 1; } else if (x >= m_nside) { x -= m_nside; nbnum += 1; } if (y < 0) { y += m_nside; nbnum -= 3; } else if (y >= m_nside) { y -= m_nside; nbnum += 3; } // Compute face int f = nb_facearray[nbnum][face_num]; // If face is valid then compute index if (f >= 0) { int bits = nb_swaparray[nbnum][face_num>>2]; if (bits & 1) { x = m_nside - x - 1; } if (bits & 2) { y = m_nside - y - 1; } if (bits & 4) { std::swap(x,y); } if (m_ordering == 0) { // Ring? result.push_back(xyf2ring(x, y, f)); } else { result.push_back(xyf2nest(x, y, f)); } } // ... otherwise push back an invalid pixel else { result.push_back(-1); } } // endfor } // endelse // Return indices return result; } /***********************************************************************//** * @brief Return pixel boundaries * * @param[in] pixel Sky pixel. * @param[in] step Number of returned points (4*step, defaults to 4). * @return Array of pixel boundaries. * * The method returns a vector with sky directions along the boundary of a * HealPix pixel. By default, the 4 corners of HealPix pixel will be * returned. The first point corresponds to the northernmost corner, the * subsequent points follow the pixel boundary through west, south and east * corners. If step>1 more intermediate points will be added after the 4th * corner of the pixel. * * This method has been adapted from the boundaries() function located in the * file healpix_base.cc in Healpix version 3.20. ***************************************************************************/ std::vector GHealpix::boundaries(const GSkyPixel& pixel, const int& step) const { // Throw an exception if sky map pixel is not 1D if (!pixel.is_1D()) { std::string msg = "Sky map pixel "+pixel.print()+" is not" " 1-dimensional.\n" "Only 1-dimensional pixels are supported by the" " Healpix projection."; throw GException::invalid_argument(G_BOUNDARIES, msg); } // Allocate boundaries std::vector boundaries; // Determine pixel index and face number int ix; int iy; int face; pix2xyf(int(pixel), &ix, &iy, &face); // ... double dc = 0.5 / m_nside; double xc = (ix + 0.5) / m_nside; double yc = (iy + 0.5) / m_nside; double d = 1.0 / (step*m_nside); // for (int i = 0; i < step; ++i) { // Declare local coordinates double z; double phi; // First coordinate xyf2loc(xc+dc-i*d, yc+dc, face, &z, &phi); boundaries.push_back(loc2dir(z, phi)); // Second coordinate xyf2loc(xc-dc, yc+dc-i*d, face, &z, &phi); boundaries.push_back(loc2dir(z, phi)); // Third coordinate xyf2loc(xc-dc+i*d, yc-dc, face, &z, &phi); boundaries.push_back(loc2dir(z, phi)); // Forth coordinate xyf2loc(xc+dc, yc-dc+i*d, face, &z, &phi); boundaries.push_back(loc2dir(z, phi)); } // endfor: looped over step size // Return boundaries return boundaries; } /***********************************************************************//** * @brief Return maximum angular distance between pixel centre and corners * * @return Maximum angular distance between pixel centre and corners (radians). * * Returns the maximum angular distance (in radians) between any pixel * centre and its corners. * * This method has been adapted from the max_pixrad() function located in the * file healpix_base.cc in Healpix version 3.20. ***************************************************************************/ double GHealpix::max_pixrad(void) const { // Compute ... double t1 = 1.0 - 1.0/m_nside; t1 *= t1; // Get vectors GVector va = set_z_phi(2.0/3.0, gammalib::pi/(4*m_nside)); GVector vb = set_z_phi(1.0 - t1/3, 0.0); // Get angle double angle = std::atan2(norm(cross(va, vb)), va * vb); // Return angle return angle; } /***********************************************************************//** * @brief Print WCS information * * @param[in] chatter Chattiness (defaults to NORMAL). * @return String containing WCS information. ***************************************************************************/ std::string GHealpix::print(const GChatter& chatter) const { // Initialise result string std::string result; // Continue only if chatter is not silent if (chatter != SILENT) { // Append header result.append("=== GHealpix ==="); // Append information result.append("\n"+gammalib::parformat("Coordinate system")); result.append(coordsys()); result.append("\n"+gammalib::parformat("Nside (# of divisions)")); result.append(gammalib::str(m_nside)); result.append("\n"+gammalib::parformat("Npface (pixels per face)")); result.append(gammalib::str(m_npface)); result.append("\n"+gammalib::parformat("Ncap (# of cap pixels)")); result.append(gammalib::str(m_ncap)); result.append("\n"+gammalib::parformat("Npix (# of pixels)")); result.append(gammalib::str(m_num_pixels)); result.append("\n"+gammalib::parformat("Order")); result.append(gammalib::str(m_order)); result.append("\n"+gammalib::parformat("Solid angle per pixel")); result.append(gammalib::str(m_solidangle)+" sr"); result.append("\n"+gammalib::parformat("Ordering")+ordering()); } // endif: chatter was not silent // Return result return result; } /*========================================================================== = = = Private methods = = = ==========================================================================*/ /***********************************************************************//** * @brief Initialise class members ***************************************************************************/ void GHealpix::init_members(void) { // Initialise members m_nside = 0; m_npface = 0; m_ncap = 0; m_order = 0; m_ordering = 0; m_num_pixels = 0; m_fact1 = 0.0; m_fact2 = 0.0; m_solidangle = 0.0; // Construct conversion arrays for (int m = 0; m < 0x100; ++m) { ctab[m] = (m&0x1 ) | ((m&0x2 ) << 7) | ((m&0x4 ) >> 1) | ((m&0x8 ) << 6) | ((m&0x10) >> 2) | ((m&0x20) << 5) | ((m&0x40) >> 3) | ((m&0x80) << 4); utab[m] = (m&0x1 ) | ((m&0x2 ) << 1) | ((m&0x4 ) << 2) | ((m&0x8 ) << 3) | ((m&0x10) << 4) | ((m&0x20) << 5) | ((m&0x40) << 6) | ((m&0x80) << 7); } // Return return; } /***********************************************************************//** * @brief Copy class members * * @param[in] proj Healpix projection. ***************************************************************************/ void GHealpix::copy_members(const GHealpix& proj) { // Copy attributes m_nside = proj.m_nside; m_npface = proj.m_npface; m_ncap = proj.m_ncap; m_order = proj.m_order; m_ordering = proj.m_ordering; m_num_pixels = proj.m_num_pixels; m_fact1 = proj.m_fact1; m_fact2 = proj.m_fact2; m_solidangle = proj.m_solidangle; // Return return; } /***********************************************************************//** * @brief Delete class members ***************************************************************************/ void GHealpix::free_members(void) { // Return return; } /***********************************************************************//** * @brief Returns true if argument is identical * * @param[in] proj Sky projection. * * This method is a helper for the sky projection friends. ***************************************************************************/ bool GHealpix::compare(const GSkyProjection& proj) const { // Initialise result bool result = false; // Continue only we compare to a GHealpix object const GHealpix* ptr = dynamic_cast(&proj); if (ptr != NULL) { result = ((m_coordsys == ptr->m_coordsys) && (m_nside == ptr->m_nside) && (m_npface == ptr->m_npface) && (m_ncap == ptr->m_ncap) && (m_order == ptr->m_order) && (m_ordering == ptr->m_ordering) && (m_num_pixels == ptr->m_num_pixels)); } // Return result return result; } /*========================================================================== = = = Low-level Healpix methods = = = ==========================================================================*/ /***********************************************************************//** * @brief Compress Bits * * @param[in] value Value. * @return Compressed Bits. * * This method has been adapted from the compress_bits() function located in * the file healpix_base.cc in Healpix version 3.20. ***************************************************************************/ int GHealpix::compress_bits(const int& value) const { // Compress Bits int raw = (value & 0x5555) | ((value & 0x55550000) >> 15); int compressed = ctab[raw & 0xff] | (ctab[raw >> 8] << 4); // Return compressed value return compressed; } /***********************************************************************//** * @brief Spread Bits * * @param[in] value Compressed value. * @return Spread Bits. * * This method has been adapted from the spread_bits() function located in * the file healpix_base.cc in Healpix version 3.20. ***************************************************************************/ int GHealpix::spread_bits(const int& value) const { // Spread bits int spread = utab[value & 0xff] | (utab[(value >> 8) & 0xff] << 16); // Return spread value return spread; } /***********************************************************************//** * @brief Convert pixel number in to (x,y,face) tuple * * @param[in] pix Pixel number. * @param[out] ix X index. * @param[out] iy Y index. * @param[out] face Face number. * * This method has been adapted from the pix2xyf() function located in * the file healpix_base.cc in Healpix version 3.20. ***************************************************************************/ void GHealpix::pix2xyf(const int& pix, int* ix, int* iy, int* face) const { // Handle ring scheme ... if (m_ordering == 0) { ring2xyf(pix, ix, iy, face); } // ... or nested scheme else { nest2xyf(pix, ix, iy, face); } // Return return; } /***********************************************************************//** * @brief Convert pixel number in nested scheme to (x,y,face) tuple * * @param[in] pix Pixel number in nested scheme. * @param[out] ix X index. * @param[out] iy Y index. * @param[out] face Face number. * * This method has been adapted from the nest2xyf() function located in * the file healpix_base.cc in Healpix version 3.20. ***************************************************************************/ void GHealpix::nest2xyf(const int& pix, int* ix, int* iy, int* face) const { // Compute face number *face = pix >> (2 * m_order); // Compute pixel int pixel = pix & (m_npface - 1); // Compute (x,y) *ix = compress_bits(pixel); *iy = compress_bits(pixel >> 1); // Return return; } /***********************************************************************//** * @brief Convert pixel number in ring scheme to (x,y,face) tuple * * @param[in] pix Pixel number in ring scheme * @param[out] ix X index. * @param[out] iy Y index. * @param[out] face Face number. * * This method has been adapted from the ring2xyf() function located in * the file healpix_base.cc in Healpix version 3.20. ***************************************************************************/ void GHealpix::ring2xyf(const int& pix, int* ix, int* iy, int* face) const { // Declare some variables int nl2 = 2*m_nside; int iring; int iphi; int kshift; int nr; // Handle pixel in the North Polar cap if (pix < m_ncap) { iring = (1+isqrt(1+2*pix)) >> 1; // Counted from North pole iphi = (pix+1) - 2*iring*(iring-1); kshift = 0; nr = iring; *face = (iphi-1)/nr; } // Handle pixel in equatorial region else if (pix < (m_num_pixels-m_ncap)) { int ip = pix - m_ncap; int tmp = (m_order>=0) ? ip >> (m_order+2) : ip/(4*m_nside); iring = tmp + m_nside; iphi = ip - tmp * 4 * m_nside + 1; kshift = (iring + m_nside) & 1; nr = m_nside; int ire = iring - m_nside + 1; int irm = nl2 + 2 - ire; int ifm = iphi - ire/2 + m_nside - 1; int ifp = iphi - irm/2 + m_nside - 1; if (m_order >= 0) { ifm >>= m_order; ifp >>= m_order; } else { ifm /= m_nside; ifp /= m_nside; } *face = (ifp==ifm) ? (ifp|4) : ((ifp>1; // Counted from South pole iphi = 4 * iring + 1 - (ip - 2*iring*(iring-1)); kshift = 0; nr = iring; iring = 2 * nl2 - iring; *face = 8 + (iphi-1)/nr; } // Now compute the (ix,iy) values int irt = iring - (jrll[*face] * m_nside) + 1; int ipt = 2 * iphi - jpll[*face] * nr - kshift -1; if (ipt >= nl2) { ipt -= 8*m_nside; } *ix = (ipt-irt) >> 1; *iy = (-ipt-irt) >> 1; // Return return; } /***********************************************************************//** * @brief Convert (x,y,face) tuple to pixel number in nested scheme * * @param[in] ix X index * @param[in] iy Y index * @param[in] face Face number * @return Pixel number if nested scheme * * This method has been adapted from the xyf2nest() function located in * the file healpix_base.cc in Healpix version 3.20. ***************************************************************************/ int GHealpix::xyf2nest(const int& ix, const int& iy, const int& face) const { // Computed pixel number int pix = (int(face) << (2 * m_order)) + spread_bits(ix) + (spread_bits(iy) << 1); // Return pixel number return pix; } /***********************************************************************//** * @brief Convert (x,y,face) tuple to pixel number in ring scheme * * @param[in] ix X index * @param[in] iy Y index * @param[in] face Face number * @return Pixel number if ring scheme * * This method has been adapted from the xyf2ring() function located in * the file healpix_base.cc in Healpix version 3.20. ***************************************************************************/ int GHealpix::xyf2ring(const int& ix, const int& iy, const int& face) const { // Compute ring number int nl4 = 4 * m_nside; int jr = (jrll[face]*m_nside) - ix - iy - 1; // Get information about that ring int n_before; int nr; bool shifted; get_ring_info(jr, &n_before, &nr, &shifted); // Compute pixel number nr >>= 2; int kshift = 1-shifted; int jp = (jpll[face]*nr + ix - iy + 1 + kshift) / 2; //planck_assert(jp<=4*nr,"must not happen"); // Assumption: if this triggers, then nl4==4*nr if (jp < 1) { jp += nl4; } // Return pixel number return (n_before + jp - 1); } /***********************************************************************//** * @brief Convert (x,y,f) tuple into local coordinates * * @param[in] x X value. * @param[in] y Y value. * @param[in] face Face number. * @param[out] z Z value. * @param[out] phi Phi value. * * This method has been adapted from the xyf2loc() function located in the * file healpix_base.cc in Healpix version 3.20. ***************************************************************************/ void GHealpix::xyf2loc(const double& x, const double& y, const int& face, double* z, double* phi) const { // ... double jr = jrll[face] - x - y; double nr; // Compute z if (jr < 1) { nr = jr; double tmp = nr*nr / 3.0; *z = 1 - tmp; } else if (jr > 3) { nr = 4 - jr; double tmp = nr*nr / 3.0; *z = tmp - 1; } else { nr = 1; *z = (2.0-jr) * 2.0/3.0; } // Compute Phi double tmp = jpll[face] * nr + x - y; if (tmp < 0) { tmp += 8; } if (tmp >= 8) { tmp -= 8; } *phi = (nr < 1.0e-15) ? 0.0 : (0.5 * gammalib::pihalf * tmp) / nr; // Return return; } /***********************************************************************//** * @brief Convert local coordinate into sky direction * * @param[in] z Z value. * @param[in] phi Phi value. * @return Sky direction. * * This method has been adapted from the locToVec3() function located in * the file healpix_base.cc in Healpix version 3.20. ***************************************************************************/ GSkyDir GHealpix::loc2dir(const double& z, const double& phi) const { // Compute longitude and latitude double sintheta = std::sqrt((1.0 - z) * (1.0 + z)); double longitude = std::atan2(sintheta * std::sin(phi), sintheta * std::cos(phi)); double latitude = std::asin(z); // Set sky direction GSkyDir dir; switch (m_coordsys) { case 0: dir.radec(longitude, latitude); break; case 1: dir.lb(longitude, latitude); break; default: break; } // Return sky direction return dir; } /***********************************************************************//** * @brief Convert nside to order * * @param[in] nside Number of sides. * @return Order of HealPix projection. ***************************************************************************/ int GHealpix::nside2order(const int& nside) const { // Initialise order int order = -1; // Determine order for (int m = 0; m <= order_max; ++m) { int nstest = 1 << m; if (nside == nstest) { order = m; break; } if (nside < nstest) break; } // Return order return order; } /***********************************************************************//** * @brief Converts pixel number in nested indexing scheme to ring scheme * * @param[in] pix Pixel number in nested indexing scheme. * @return Pixel number in ring indexing scheme. * * @exception GException::invalid_value * Healpix projection does not represent a hiearachical map. * * This method has been adapted from the nest2ring() function located in * the file healpix_base.cc in Healpix version 3.20. ***************************************************************************/ int GHealpix::nest2ring(const int& pix) const { // Throw an exception if map is not a hierachical map if (m_order < 0) { std::string msg = "A hierarchical map projection is required."; throw GException::invalid_value(G_NEST2RING, msg); } // Convert nested index to (x,y,face) tuple int ix; int iy; int face; nest2xyf(pix, &ix, &iy, &face); // Convert (x,y,face) tuple to ring index int iring = xyf2ring(ix, iy, face); // Return ring index return iring; } /***********************************************************************//** * @brief Converts pixel number in ring indexing scheme to nested scheme * * @param[in] pix Pixel number in ring indexing scheme. * @return Pixel number in nested indexing scheme. * * @exception GException::invalid_value * Healpix projection does not represent a hiearachical map. * * This method has been adapted from the ring2nest() function located in * the file healpix_base.cc in Healpix version 3.20. ***************************************************************************/ int GHealpix::ring2nest(const int& pix) const { // Throw an exception if map is not a hierachical map if (m_order < 0) { std::string msg = "A hierarchical map projection is required."; throw GException::invalid_value(G_RING2NEST, msg); } // Convert nested index to (x,y,face) tuple int ix; int iy; int face; ring2xyf(pix, &ix, &iy, &face); // Convert (x,y,face) tuple to ring index int iring = xyf2nest(ix, iy, face); // Return ring index return iring; } /***********************************************************************//** * @brief Convert pixel index to (x,y) coordinate * * @param[in] ipix Pixel index for which (x,y) are to be computed. * @param[out] x Pointer to x coordinate. * @param[out] y Pointer to y coordinate. ***************************************************************************/ void GHealpix::pix2xy(const int& ipix, int* x, int* y) const { // Set x coordinate int raw = (ipix & 0x5555) | ((ipix & 0x55550000) >> 15); *x = ctab[raw & 0xff] | (ctab[raw >> 8] << 4); // Set y coordinate raw = ((ipix & 0xaaaa) >> 1) | ((ipix & 0xaaaa0000) >> 16); *y = ctab[raw & 0xff] | (ctab[raw >> 8] << 4); // Return return; } /***********************************************************************//** * @brief Convert (x,y) coordinate to pixel * * @param[in] x x coordinate. * @param[in] y y coordinate. ***************************************************************************/ int GHealpix::xy2pix(int x, int y) const { // Return pixel return utab[x&0xff] | (utab[x>>8]<<16) | (utab[y&0xff]<<1) | (utab[y>>8]<<17); } /***********************************************************************//** * @brief Convert pixel index to (theta,phi) angles for ring ordering * * @param[in] ipix Pixel index for which (theta,phi) are to be computed. * @param[out] theta Pointer to result zenith angle in radians. * @param[out] phi Pointer to result azimuth angle in radians. * * @exception GException::out_of_range * Pixel index is out of range. ***************************************************************************/ void GHealpix::pix2ang_ring(int ipix, double* theta, double* phi) const { // Check if ipix is in range if (ipix < 0 || ipix >= m_num_pixels) { throw GException::out_of_range(G_PIX2ANG_RING, ipix, 0, m_num_pixels-1); } // Handle North Polar cap if (ipix < m_ncap) { int iring = int(0.5*(1+isqrt(1+2*ipix))); // counted from North pole int iphi = (ipix+1) - 2*iring*(iring-1); *theta = std::acos(1.0 - (iring*iring) * m_fact2); *phi = (iphi - 0.5) * gammalib::pi/(2.0*iring); } // Handle Equatorial region else if (ipix < (m_num_pixels - m_ncap)) { int ip = ipix - m_ncap; int iring = ip/(4*m_nside) + m_nside; // counted from North pole int iphi = ip%(4*m_nside) + 1; double fodd = ((iring+m_nside)&1) ? 1 : 0.5; int nl2 = 2*m_nside; *theta = std::acos((nl2 - iring) * m_fact1); *phi = (iphi - fodd) * gammalib::pi/nl2; } // Handle South Polar cap else { int ip = m_num_pixels - ipix; int iring = int(0.5*(1+isqrt(2*ip-1))); // Counted from South pole int iphi = 4*iring + 1 - (ip - 2*iring*(iring-1)); *theta = std::acos(-1.0 + (iring*iring) * m_fact2); *phi = (iphi - 0.5) * gammalib::pi/(2.*iring); } // Return return; } /***********************************************************************//** * @brief Convert pixel index to (theta,phi) angles for nested ordering * * @param[in] ipix Pixel index for which (theta,phi) are to be computed. * @param[out] theta Pointer to result zenith angle in radians. * @param[out] phi Pointer to result azimuth angle in radians. * * @exception GException::out_of_range * Pixel index is out of range. ***************************************************************************/ void GHealpix::pix2ang_nest(int ipix, double* theta, double* phi) const { // Check if ipix is in range if (ipix < 0 || ipix >= m_num_pixels) { throw GException::out_of_range(G_PIX2ANG_NEST, ipix, 0, m_num_pixels-1); } // Get face number and index in face int nl4 = 4 * m_nside; int face_num = ipix >> (2*m_order); // Face number in {0,11} int ipf = ipix & (m_npface - 1); // Get pixel coordinates int ix; int iy; pix2xy(ipf, &ix, &iy); // Computes the z coordinate on the sphere int jr = (jrll[face_num] << m_order) - ix - iy - 1; // Declare result variables int nr; double z; int kshift; // North pole region if (jr < m_nside) { nr = jr; z = 1. - nr*nr*m_fact2; kshift = 0; } // South pole region else if (jr > 3*m_nside) { nr = nl4 - jr; z = nr*nr*m_fact2 - 1; kshift = 0; } // Equatorial region else { nr = m_nside; z = (2*m_nside-jr) * m_fact1; kshift = (jr-m_nside) & 1; } // Computes the phi coordinate on the sphere, in [0,2Pi] int jp = (jpll[face_num]*nr + ix - iy + 1 + kshift) / 2; if (jp > nl4) jp -= nl4; if (jp < 1) jp += nl4; // Computes Theta and Phi *theta = std::acos(z); *phi = (jp - (kshift+1)*0.5) * (gammalib::pihalf / nr); // Return return; } /***********************************************************************//** * @brief Returns pixel which contains angular coordinates (z,phi) * * @param[in] z Cosine of zenith angle - cos(theta). * @param[in] phi Azimuth angle in radians. ***************************************************************************/ int GHealpix::ang2pix_z_phi_ring(double z, double phi) const { // Initialise pixel int ipix = 0; // Setup double za = fabs(z); double tt = gammalib::modulo(phi, gammalib::twopi) * gammalib::inv_pihalf; // in [0,4) // Equatorial region if (za <= gammalib::twothird) { double temp1 = m_nside*(0.5+tt); double temp2 = m_nside*z*0.75; int jp = int(temp1-temp2); // index of ascending edge line int jm = int(temp1+temp2); // index of descending edge line int ir = m_nside + 1 + jp - jm; // in {1,2n+1} int kshift = 1 - (ir & 1); // kshift=1 if ir even, 0 otherwise int ip = (jp+jm-m_nside+kshift+1)/2; // in {0,4n-1} ip = int(gammalib::modulo(ip,4*m_nside)); ipix = m_ncap + (ir-1)*4*m_nside + ip; } // North & South polar caps else { double tp = tt - int(tt); double tmp = m_nside * std::sqrt(3*(1-za)); int jp = int(tp*tmp); // increasing edge line index int jm = int((1.0-tp)*tmp); // decreasing edge line index int ir = jp + jm + 1; // ring number counted from the closest pole int ip = int(tt*ir); // in {0,4*ir-1} ip = int(gammalib::modulo(ip,4*ir)); if (z>0) ipix = 2*ir*(ir-1) + ip; else ipix = m_num_pixels - 2*ir*(ir+1) + ip; } // Return pixel return ipix; } /***********************************************************************//** * @brief Returns pixel which contains angular coordinates (z,phi) * * @param[in] z Cosine of zenith angle - cos(theta). * @param[in] phi Azimuth angle in radians. ***************************************************************************/ int GHealpix::ang2pix_z_phi_nest(double z, double phi) const { // Initialise face and pixel numbers int face_num; int ix; int iy; // Setup double za = fabs(z); double tt = gammalib::modulo(phi, gammalib::twopi) * gammalib::inv_pihalf; // in [0,4) // Equatorial region if (za <= gammalib::twothird) { double temp1 = ns_max*(0.5+tt); double temp2 = ns_max*z*0.75; int jp = int(temp1-temp2); // index of ascending edge line int jm = int(temp1+temp2); // index of descending edge line int ifp = jp >> order_max; // in {0,4} int ifm = jm >> order_max; if (ifp == ifm) // faces 4 to 7 face_num = (ifp==4) ? 4: ifp+4; else if (ifp < ifm) // (half-)faces 0 to 3 face_num = ifp; else // (half-)faces 8 to 11 face_num = ifm + 8; ix = jm & (ns_max-1); iy = ns_max - (jp & (ns_max-1)) - 1; } // Polar region, za > 2/3 else { int ntt = int(tt); double tp = tt-ntt; double tmp = ns_max * std::sqrt(3*(1-za)); int jp = int(tp*tmp); // increasing edge line index int jm = int((1.0-tp)*tmp); // decreasing edge line index if (jp >= ns_max) jp = ns_max-1; // for points too close to the boundary if (jm >= ns_max) jm = ns_max-1; if (z >= 0) { face_num = ntt; // in {0,3} ix = ns_max - jm - 1; iy = ns_max - jp - 1; } else { face_num = ntt + 8; // in {8,11} ix = jp; iy = jm; } } // Get pixel int ipf = xy2pix(ix, iy); ipf >>= (2*(order_max - m_order)); // in {0, nside**2 - 1} return ipf + (face_num<<(2*m_order)); // in {0, 12*nside**2 - 1} } /***********************************************************************//** * @brief Get ring index north of cos(theta) * * @param[in] costheta Cosine of colatitude theta * * Returns the number of the next ring to the north of @a cos(theta). It may * return 0; in this case @a costheta lies north of all rings. * * This method has been adapted from the ring_above() function located in * the file healpix_base.cc in Healpix version 3.20. ***************************************************************************/ int GHealpix::ring_above(const double& costheta) const { // Initialise ring index int iring; // Get absolute cosine double acostheta = std::abs(costheta); // Are we in the equatorial region if (acostheta <= gammalib::twothird) { iring = int(m_nside * (2.0 - 1.5 * costheta)); } // ... otherwise we're in the pole region else { iring = int(m_nside * std::sqrt(3.0 * (1.0 - acostheta))); if (costheta <= 0.0) { iring = 4 * m_nside - iring - 1; } } // Return return iring; } /***********************************************************************//** * @brief Returns useful information about a given ring of the projection * * @param[in] ring The ring number (the number of the first ring is 1) * @param[out] startpix The number of the first pixel in the ring * @param[out] ringpix The number of pixels in the ring * @param[out] shifted If @a true, the center of the first pixel is not at * @a phi=0 * * This method has been adapted from the get_ring_info_small() function * located in the file healpix_base.cc in Healpix version 3.20. ***************************************************************************/ void GHealpix::get_ring_info(const int& ring, int* startpix, int* ringpix, bool* shifted) const { // Handle ring in North polar cap if (ring < m_nside) { *shifted = true; *ringpix = 4 * ring; *startpix = 2 * ring * (ring-1); } // Handle ring in equatorial region else if (ring < 3*m_nside) { *shifted = ((ring-m_nside) & 1) == 0; *ringpix = 4 * m_nside; *startpix = m_ncap + (ring-m_nside) * *ringpix; } // Handle ring in South polar cap else { int nr = 4 * m_nside - ring; *shifted = true; *ringpix = 4 * nr; *startpix = m_num_pixels - 2 * nr * (nr+1); } // Return return; } /***********************************************************************//** * @brief Returns useful information about a given ring of the projection * * @param[in] ring The ring number (the number of the first ring is 1) * @param[out] startpix The number of the first pixel in the ring * @param[out] ringpix The number of pixels in the ring * @param[out] theta (radians) * @param[out] shifted If @a true, the center of the first pixel is not at * @a phi=0 * * This method has been adapted from the get_ring_info2() function * located in the file healpix_base.cc in Healpix version 3.20. ***************************************************************************/ void GHealpix::get_ring_info(const int& ring, int* startpix, int* ringpix, double* theta, bool* shifted) const { // int northring = (ring > 2 * m_nside) ? 4 * m_nside - ring : ring; // Are we in the North? if (northring < m_nside) { double tmp = northring * northring * m_fact2; double costheta = 1.0 - tmp; double sintheta = std::sqrt(tmp * (2.0-tmp)); *startpix = 2 * northring * (northring - 1); *ringpix = 4 * northring; *theta = std::atan2(sintheta, costheta); *shifted = true; } else { *theta = std::acos((2.0 * m_nside-northring) * m_fact1); *ringpix = 4 * m_nside; *shifted = ((northring - m_nside) & 1) == 0; *startpix = m_ncap + (northring - m_nside) * *ringpix; } // Are we in the southern hemisphere? if (northring != ring) { *theta = gammalib::pi - *theta; *startpix = m_num_pixels - *startpix - *ringpix; } // Return return; } /***********************************************************************//** * @brief Return interpolator * * @param[in] theta Colatitude of direction (radian, the North pole is at theta=0) * @param[in] phi Longitude of direction (radians) * * @exception GException::invalid_argument * Invalid @p theta argument * * Returns a bilinear pixel interpolator for a given sky direction. * * This method has been adapted from the get_interpol() function * located in the file healpix_base.cc in Healpix version 3.20. ***************************************************************************/ GBilinear GHealpix::interpolator(const double& theta, const double& phi) const { // Allocate interpolator GBilinear interpolator; // Check that theta is valid if (theta < 0.0 || theta > gammalib::pi) { std::string msg = "Colatitude "+gammalib::str(theta)+" is outside " "valid range [0,pi]."; throw GException::invalid_argument(G_INTERPOLATOR, msg); } // Prepare computation double costheta = std::cos(theta); int ir1 = ring_above(costheta); // Ring above actual colatitude int ir2 = ir1 + 1; // Ring below actual colatitude int sp; // Start pixel in ring int nr; // Number of pixels in ring double theta1; // Colatitude of ring above double theta2; // Colatitude of ring below bool shift; // Compute interpolating pixels and phi weights if the colatitude is not // North of all rings (if we're North of all rings, ir1=0) if (ir1 > 0) { get_ring_info(ir1, &sp, &nr, &theta1, &shift); double dphi = gammalib::twopi / nr; // Phi spacing of pixels double tmp = (phi/dphi - 0.5*shift); int i1 = (tmp < 0.0) ? int(tmp)-1 : int(tmp); double w1 = (phi - (i1+0.5*shift)*dphi) / dphi; int i2 = i1 + 1; if (i1 < 0) { i1 += nr; } if (i2 >= nr) { i2 -= nr; } interpolator.index1() = sp + i1; interpolator.index2() = sp + i2; interpolator.weight1() = 1.0 - w1; interpolator.weight2() = w1; } // Compute interpolating pixels and phi weights is the colatitude is not // South of all rings (if we're South of all rings, ir2=4*m_nside) if (ir2 < (4*m_nside)) { get_ring_info(ir2, &sp, &nr, &theta2, &shift); double dphi = gammalib::twopi / nr; // Phi spacing of pixels double tmp = (phi/dphi - 0.5*shift); int i1 = (tmp < 0.0) ? int(tmp)-1 : int(tmp); double w1 = (phi - (i1+0.5*shift)*dphi) / dphi; int i2 = i1 + 1; if (i1 < 0) { i1 += nr; } if (i2 >= nr) { i2 -= nr; } interpolator.index3() = sp + i1; interpolator.index4() = sp + i2; interpolator.weight3() = 1.0 - w1; interpolator.weight4() = w1; } // Now handle the special case that the colatitude is North of all // rings if (ir1 == 0) { double wtheta = theta/theta2; interpolator.weight3() *= wtheta; interpolator.weight4() *= wtheta; double fac = (1.0-wtheta)*0.25; interpolator.weight1() = fac; interpolator.weight2() = fac; interpolator.weight3() += fac; interpolator.weight4() += fac; interpolator.index1() = (interpolator.index3() + 2) & 3; interpolator.index2() = (interpolator.index4() + 2) & 3; } // ... and now the case that the colatitude is South of all rings else if (ir2 == 4*m_nside) { double wtheta = (theta-theta1) / (gammalib::pi-theta1); interpolator.weight1() *= (1.0 - wtheta); interpolator.weight2() *= (1.0 - wtheta); double fac = wtheta*0.25; interpolator.weight1() += fac; interpolator.weight2() += fac; interpolator.weight3() = fac; interpolator.weight4() = fac; interpolator.index3() = ((interpolator.index1() + 2) & 3) + m_num_pixels - 4; interpolator.index4() = ((interpolator.index2() + 2) & 3) + m_num_pixels - 4; } // ... and now multiply-in the theta weights for the general case else { double wtheta = (theta-theta1) / (theta2-theta1); interpolator.weight1() *= (1.0 - wtheta); interpolator.weight2() *= (1.0 - wtheta); interpolator.weight3() *= wtheta; interpolator.weight4() *= wtheta; } // If we have a nested pixel scheme then convert now the ring // indices into nested indices if (m_ordering == 1) { interpolator.index1() = ring2nest(interpolator.index1()); interpolator.index2() = ring2nest(interpolator.index2()); interpolator.index3() = ring2nest(interpolator.index3()); interpolator.index4() = ring2nest(interpolator.index4()); } // Return interpolator return interpolator; } /***********************************************************************//** * @brief Integer n that fulfills n*n <= arg < (n+1)*(n+1) * * @param[in] arg Argument. * * Returns the integer @a n, which fulfills @a n*n <= arg < (n+1)*(n+1). ***************************************************************************/ unsigned int GHealpix::isqrt(unsigned int arg) const { // Return return unsigned(std::sqrt(arg+0.5)); } /***********************************************************************//** * @brief Return 3D vector * * @param[in] z Z value. * @param[in] phi Phi value. * @return 3D vector * * This method has been adapted from the set_z_phi() function located in the * file vec3.h in Healpix version 3.20. ***************************************************************************/ GVector GHealpix::set_z_phi(const double& z, const double& phi) const { // Initialise 3D vector GVector vector(3); // Assign elements double sintheta = std::sqrt((1.0 - z) * (1.0 + z)); vector[0] = sintheta * std::cos(phi); vector[1] = sintheta * std::sin(phi); vector[2] = z; // Return vector return vector; }