1 /*
2  *  This file is part of Healpix_cxx.
3  *
4  *  Healpix_cxx is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  Healpix_cxx is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with Healpix_cxx; if not, write to the Free Software
16  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17  *
18  *  For more information about HEALPix, see http://healpix.sourceforge.net
19  */
20 
21 /*
22  *  Healpix_cxx is being developed at the Max-Planck-Institut fuer Astrophysik
23  *  and financially supported by the Deutsches Zentrum fuer Luft- und Raumfahrt
24  *  (DLR).
25  */
26 
27 /*! \file healpix_base.h
28  *
29  *  \copyright Copyright (C) 2003-2020 Max-Planck-Society
30  *  \author Martin Reinecke
31  */
32 
33 #ifndef HEALPIX_BASE_H
34 #define HEALPIX_BASE_H
35 
36 #include <array>
37 #include <cmath>
38 #include <vector>
39 #include "ducc0/infra/error_handling.h"
40 #include "ducc0/math/math_utils.h"
41 #include "ducc0/math/vec3.h"
42 #include "ducc0/healpix/healpix_tables.h"
43 #include "ducc0/math/pointing.h"
44 #include "ducc0/math/rangeset.h"
45 
46 namespace ducc0 {
47 
48 namespace detail_healpix {
49 
50 template<typename I> struct Orderhelper__ {};
51 template<> struct Orderhelper__<int> {enum{omax=13};};
52 template<> struct Orderhelper__<int64_t> {enum{omax=29};};
53 
54 /*! Functionality related to the HEALPix pixelisation. */
55 template<typename I> class T_Healpix_Base: public Healpix_Tables
56   {
57   protected:
58     /*! The order of the map; -1 for nonhierarchical map. */
59     int order_;
60     /*! The N_side parameter of the map; 0 if not allocated. */
61     I nside_;
62     I npface_, ncap_, npix_;
63     double fact1_, fact2_;
64     /*! The map's ordering scheme. */
65     Ordering_Scheme scheme_;
66 
67     /*! Returns the number of the next ring to the north of \a z=cos(theta).
68         It may return 0; in this case \a z lies north of all rings. */
69     inline I ring_above (double z) const;
70     void in_ring (I iz, double phi0, double dphi, rangeset<I> &pixset) const;
71 
72     template<typename I2> void query_multidisc (const std::vector<vec3> &norm,
73       const std::vector<double> &rad, int fact, rangeset<I2> &pixset) const;
74 
75     void query_multidisc_general (const std::vector<vec3> &norm, const std::vector<double> &rad,
76       bool inclusive, const std::vector<int> &cmds, rangeset<I> &pixset) const;
77 
78     void query_strip_internal (double theta1, double theta2, bool inclusive,
79       rangeset<I> &pixset) const;
80 
81     I xyf2nest(int ix, int iy, int face_num) const;
82     void nest2xyf(I pix, int &ix, int &iy, int &face_num) const;
83     I xyf2ring(int ix, int iy, int face_num) const;
84     void ring2xyf(I pix, int &ix, int &iy, int &face_num) const;
85 
86     I loc2pix (double z, double phi, double sth, bool have_sth) const;
87     void pix2loc (I pix, double &z, double &phi, double &sth, bool &have_sth)
88       const;
89 
90     void xyf2loc(double x, double y, int face, double &z, double &ph,
91       double &sth, bool &have_sth) const;
92 
93     I nest_peano_helper (I pix, int dir) const;
94 
95     typedef I (T_Healpix_Base::*swapfunc)(I pix) const;
96 //    using swapfunc = I(I) const;
97 
98   public:
99     enum {order_max=Orderhelper__<I>::omax};
100 
101     /*! Calculates the map order from its \a N_side parameter.
102         Returns -1 if \a nside is not a power of 2.
103         \param nside the \a N_side parameter */
104     static int nside2order (I nside);
105     /*! Calculates the \a N_side parameter from the number of pixels.
106         \param npix the number of pixels */
107     static I npix2nside (I npix);
108     /*! Constructs an unallocated object. */
109     T_Healpix_Base ();
110     /*! Constructs an object with a given \a order and the ordering
111         scheme \a scheme. */
112     T_Healpix_Base (int order, Ordering_Scheme scheme)
113       { Set (order, scheme); }
114     /*! Constructs an object with a given \a nside and the ordering
115         scheme \a scheme. The \a nside_dummy parameter must be set to
116         SET_NSIDE. */
117     T_Healpix_Base (I nside, Ordering_Scheme scheme, const nside_dummy)
118       { SetNside (nside, scheme); }
119 
120     /*! Adjusts the object to \a order and \a scheme. */
121     void Set (int order, Ordering_Scheme scheme);
122     /*! Adjusts the object to \a nside and \a scheme. */
123     void SetNside (I nside, Ordering_Scheme scheme);
124 
125     /*! Returns the z-coordinate of the ring \a ring. This also works
126         for the (not really existing) rings 0 and 4*nside. */
127     double ring2z (I ring) const;
128     /*! Returns the number of the ring in which \a pix lies. */
129     I pix2ring (I pix) const;
130 
131     I xyf2pix(int ix, int iy, int face_num) const
132       {
133       return (scheme_==RING) ?
134         xyf2ring(ix,iy,face_num) : xyf2nest(ix,iy,face_num);
135       }
136     void pix2xyf(I pix, int &ix, int &iy, int &face_num) const
137       {
138       (scheme_==RING) ?
139         ring2xyf(pix,ix,iy,face_num) : nest2xyf(pix,ix,iy,face_num);
140       }
141 
142     /*! Translates a pixel number from NEST to RING. */
143     I nest2ring (I pix) const;
144     /*! Translates a pixel number from RING to NEST. */
145     I ring2nest (I pix) const;
146     /*! Translates a pixel number from NEST to its Peano index. */
147     I nest2peano (I pix) const;
148     /*! Translates a pixel number from its Peano index to NEST. */
149     I peano2nest (I pix) const;
150 
151     /*! Returns the number of the pixel which contains the angular coordinates
152         (\a z:=cos(theta), \a phi).
153         \note This method is inaccurate near the poles at high resolutions. */
154     I zphi2pix (double z, double phi) const
155       { return loc2pix(z,phi,0.,false); }
156 
157     /*! Returns the number of the pixel which contains the angular coordinates
158         \a ang. */
159     I ang2pix (const pointing &ang) const
160       {
161       const double pi_=3.141592653589793238462643383279502884197;
162       MR_assert((ang.theta>=0)&&(ang.theta<=pi_),"invalid theta value");
163       return ((ang.theta<0.01) || (ang.theta > 3.14159-0.01)) ?
164         loc2pix(cos(ang.theta),ang.phi,sin(ang.theta),true) :
165         loc2pix(cos(ang.theta),ang.phi,0.,false);
166       }
167     /*! Returns the number of the pixel which contains the vector \a vec
168         (\a vec is normalized if necessary). */
169     I vec2pix (const vec3 &vec) const
170       {
171       double xl = 1./vec.Length();
172       double phi = safe_atan2(vec.y,vec.x);
173       double nz = vec.z*xl;
174       if (std::abs(nz)>0.99)
175         return loc2pix (nz,phi,std::sqrt(vec.x*vec.x+vec.y*vec.y)*xl,true);
176       else
177         return loc2pix (nz,phi,0,false);
178       }
179 
180     /*! Returns the angular coordinates (\a z:=cos(theta), \a phi) of the center
181         of the pixel with number \a pix.
182         \note This method is inaccurate near the poles at high resolutions. */
183     void pix2zphi (I pix, double &z, double &phi) const
184       {
185       bool dum_b;
186       double dum_d;
187       pix2loc(pix,z,phi,dum_d,dum_b);
188       }
189 
190     /*! Returns the angular coordinates of the center of the pixel with
191         number \a pix. */
192     pointing pix2ang (I pix) const
193       {
194       double z, phi, sth;
195       bool have_sth;
196       pix2loc (pix,z,phi,sth,have_sth);
197       return have_sth ? pointing(atan2(sth,z),phi) : pointing(acos(z),phi);
198       }
199     /*! Returns the vector to the center of the pixel with number \a pix. */
200     vec3 pix2vec (I pix) const
201       {
202       double z, phi, sth;
203       bool have_sth;
204       pix2loc (pix,z,phi,sth,have_sth);
205       if (have_sth)
206         return vec3(sth*cos(phi),sth*sin(phi),z);
207       else
208         {
209         vec3 res;
210         res.set_z_phi (z, phi);
211         return res;
212         }
213       }
214     /*! Returns the pixel number for this T_Healpix_Base corresponding to the
215         pixel number \a pix in \a b.
216         \note \a b.Nside()\%Nside() must be 0. */
217     I pixel_import (I pix, const T_Healpix_Base &b) const
218       {
219       I ratio = b.nside_/nside_;
220       MR_assert(nside_*ratio==b.nside_,"bad nside ratio");
221       int x, y, f;
222       b.pix2xyf(pix, x, y, f);
223       x/=ratio; y/=ratio;
224       return xyf2pix(x, y, f);
225       }
226 
227     template<typename I2> void query_disc_internal (pointing ptg, double radius,
228       int fact, rangeset<I2> &pixset) const;
229 
230     /*! Returns the range set of all pixels whose centers lie within the disk
231         defined by \a dir and \a radius.
232         \param dir the angular coordinates of the disk center
233         \param radius the radius (in radians) of the disk
234         \param pixset a \a rangeset object containing the indices of all pixels
235            whose centers lie inside the disk
236         \note This method is more efficient in the RING scheme. */
237     void query_disc (pointing ptg, double radius, rangeset<I> &pixset) const;
238     /*! Returns the range set of all pixels whose centers lie within the disk
239         defined by \a dir and \a radius.
240         \param dir the angular coordinates of the disk center
241         \param radius the radius (in radians) of the disk
242         \note This method is more efficient in the RING scheme. */
243     rangeset<I> query_disc (pointing ptg, double radius) const
244       {
245       rangeset<I> res;
246       query_disc(ptg, radius, res);
247       return res;
248       }
249     /*! Returns the range set of all pixels which overlap with the disk
250         defined by \a dir and \a radius.
251         \param dir the angular coordinates of the disk center
252         \param radius the radius (in radians) of the disk
253         \param pixset a \a rangeset object containing the indices of all pixels
254            overlapping with the disk.
255         \param fact The overlapping test will be done at the resolution
256            \a fact*nside. For NESTED ordering, \a fact must be a power of 2,
257            else it can be any positive integer. A typical choice would be 4.
258         \note This method may return some pixels which don't overlap with
259            the disk at all. The higher \a fact is chosen, the fewer false
260            positives are returned, at the cost of increased run time.
261         \note This method is more efficient in the RING scheme. */
262     void query_disc_inclusive (pointing ptg, double radius, rangeset<I> &pixset,
263       int fact=1) const;
264     /*! Returns the range set of all pixels which overlap with the disk
265         defined by \a dir and \a radius.
266         \param dir the angular coordinates of the disk center
267         \param radius the radius (in radians) of the disk
268         \param fact The overlapping test will be done at the resolution
269            \a fact*nside. For NESTED ordering, \a fact must be a power of 2,
270            else it can be any positive integer. A typical choice would be 4.
271         \note This method may return some pixels which don't overlap with
272            the disk at all. The higher \a fact is chosen, the fewer false
273            positives are returned, at the cost of increased run time.
274         \note This method is more efficient in the RING scheme. */
275     rangeset<I> query_disc_inclusive (pointing ptg, double radius,
276       int fact=1) const
277       {
278       rangeset<I> res;
279       query_disc_inclusive(ptg, radius, res, fact);
280       return res;
281       }
282 
283     /*! \deprecated Please use the version based on \a rangeset */
284     void query_disc (const pointing &dir, double radius,
285       std::vector<I> &listpix) const
286       {
287       rangeset<I> pixset;
288       query_disc(dir,radius,pixset);
289       pixset.toVector(listpix);
290       }
291     /*! \deprecated Please use the version based on \a rangeset */
292     void query_disc_inclusive (const pointing &dir, double radius,
293       std::vector<I> &listpix, int fact=1) const
294       {
295       rangeset<I> pixset;
296       query_disc_inclusive(dir,radius,pixset,fact);
297       pixset.toVector(listpix);
298       }
299 
300     template<typename I2> void query_polygon_internal
301       (const std::vector<pointing> &vertex, int fact,
302       rangeset<I2> &pixset) const;
303 
304     /*! Returns a range set of pixels whose centers lie within the convex
305         polygon defined by the \a vertex array.
306         \param vertex array containing the vertices of the polygon.
307         \param pixset a \a rangeset object containing the indices of all pixels
308            whose centers lie inside the polygon
309         \note This method is more efficient in the RING scheme. */
310     void query_polygon (const std::vector<pointing> &vertex,
311       rangeset<I> &pixset) const;
312     /*! Returns a range set of pixels whose centers lie within the convex
313         polygon defined by the \a vertex array.
314         \param vertex array containing the vertices of the polygon.
315         \note This method is more efficient in the RING scheme. */
316     rangeset<I> query_polygon (const std::vector<pointing> &vertex) const
317       {
318       rangeset<I> res;
319       query_polygon(vertex, res);
320       return res;
321       }
322 
323     /*! Returns a range set of pixels which overlap with the convex
324         polygon defined by the \a vertex array.
325         \param vertex array containing the vertices of the polygon.
326         \param pixset a \a rangeset object containing the indices of all pixels
327            overlapping with the polygon.
328         \param fact The overlapping test will be done at the resolution
329            \a fact*nside. For NESTED ordering, \a fact must be a power of 2,
330            else it can be any positive integer. A typical choice would be 4.
331         \note This method may return some pixels which don't overlap with
332            the polygon at all. The higher \a fact is chosen, the fewer false
333            positives are returned, at the cost of increased run time.
334         \note This method is more efficient in the RING scheme. */
335     void query_polygon_inclusive (const std::vector<pointing> &vertex,
336       rangeset<I> &pixset, int fact=1) const;
337     /*! Returns a range set of pixels which overlap with the convex
338         polygon defined by the \a vertex array.
339         \param vertex array containing the vertices of the polygon.
340         \param fact The overlapping test will be done at the resolution
341            \a fact*nside. For NESTED ordering, \a fact must be a power of 2,
342            else it can be any positive integer. A typical choice would be 4.
343         \note This method may return some pixels which don't overlap with
344            the polygon at all. The higher \a fact is chosen, the fewer false
345            positives are returned, at the cost of increased run time.
346         \note This method is more efficient in the RING scheme. */
347     rangeset<I> query_polygon_inclusive (const std::vector<pointing> &vertex,
348       int fact=1) const
349       {
350       rangeset<I> res;
351       query_polygon_inclusive(vertex, res, fact);
352       return res;
353       }
354 
355     /*! Returns a range set of pixels whose centers lie within the colatitude
356         range defined by \a theta1 and \a theta2 (if \a inclusive==false), or
357         which overlap with this region (if \a inclusive==true). If
358         \a theta1<theta2, the region between both angles is considered,
359         otherwise the regions \a 0<theta<theta2 and \a theta1<theta<pi.
360         \param theta1 first colatitude
361         \param theta2 second colatitude
362         \param inclusive if \a false, return the exact set of pixels whose
363            pixels centers lie within the region; if \a true, return all pixels
364            that overlap with the region. */
365     void query_strip (double theta1, double theta2, bool inclusive,
366       rangeset<I> &pixset) const;
367     /*! Returns a range set of pixels whose centers lie within the colatitude
368         range defined by \a theta1 and \a theta2 (if \a inclusive==false), or
369         which overlap with this region (if \a inclusive==true). If
370         \a theta1<theta2, the region between both angles is considered,
371         otherwise the regions \a 0<theta<theta2 and \a theta1<theta<pi.
372         \param theta1 first colatitude
373         \param theta2 second colatitude
374         \param inclusive if \a false, return the exact set of pixels whose
375            pixels centers lie within the region; if \a true, return all pixels
376            that overlap with the region. */
377     rangeset<I> query_strip (double theta1, double theta2, bool inclusive) const
378       {
379       rangeset<I> res;
380       query_strip(theta1, theta2, inclusive, res);
381       return res;
382       }
383 
384     /*! Returns useful information about a given ring of the map.
385         \param ring the ring number (the number of the first ring is 1)
386         \param startpix the number of the first pixel in the ring
387                (NOTE: this is always given in the RING numbering scheme!)
388         \param ringpix the number of pixels in the ring
389         \param costheta the cosine of the colatitude of the ring
390         \param sintheta the sine of the colatitude of the ring
391         \param shifted if \a true, the center of the first pixel is not at
392                \a phi=0 */
393     void get_ring_info (I ring, I &startpix, I &ringpix,
394       double &costheta, double &sintheta, bool &shifted) const;
395     /*! Returns useful information about a given ring of the map.
396         \param ring the ring number (the number of the first ring is 1)
397         \param startpix the number of the first pixel in the ring
398                (NOTE: this is always given in the RING numbering scheme!)
399         \param ringpix the number of pixels in the ring
400         \param theta the colatitude (in radians) of the ring
401         \param shifted if \a true, the center of the first pixel is not at
402                \a phi=0 */
403     void get_ring_info2 (I ring, I &startpix, I &ringpix,
404       double &theta, bool &shifted) const;
405     /*! Returns useful information about a given ring of the map.
406         \param ring the ring number (the number of the first ring is 1)
407         \param startpix the number of the first pixel in the ring
408                (NOTE: this is always given in the RING numbering scheme!)
409         \param ringpix the number of pixels in the ring
410         \param shifted if \a true, the center of the first pixel is not at
411                \a phi=0 */
412     void get_ring_info_small (I ring, I &startpix, I &ringpix,
413         bool &shifted) const;
414     /*! Returns the neighboring pixels of \a pix in \a result.
415         On exit, \a result contains (in this order)
416         the pixel numbers of the SW, W, NW, N, NE, E, SE and S neighbor
417         of \a pix. If a neighbor does not exist (this can only be the case
418         for the W, N, E and S neighbors), its entry is set to -1.
419 
420         \note This method works in both RING and NEST schemes, but is
421           considerably faster in the NEST scheme. */
422     void neighbors (I pix, std::array<I,8> &result) const;
423     /*! Returns interpolation information for the direction \a ptg.
424         The surrounding pixels are returned in \a pix, their corresponding
425         weights in \a wgt.
426         \note This method works in both RING and NEST schemes, but is
427           considerably faster in the RING scheme. */
428     void get_interpol (const pointing &ptg, std::array<I,4> &pix,
429                        std::array<double,4> &wgt) const;
430 
431     /*! Returns the order parameter of the object. */
432     int Order() const { return order_; }
433     /*! Returns the \a N_side parameter of the object. */
434     I Nside() const { return nside_; }
435     /*! Returns the number of pixels of the object. */
436     I Npix() const { return npix_; }
437     /*! Returns the ordering scheme of the object. */
438     Ordering_Scheme Scheme() const { return scheme_; }
439 
440     /*! Returns \a true, if both objects have the same nside and scheme,
441         else \a false. */
442     bool conformable (const T_Healpix_Base &other) const
443       { return ((nside_==other.nside_) && (scheme_==other.scheme_)); }
444 
445     /*! Swaps the contents of two Healpix_Base objects. */
446     void swap (T_Healpix_Base &other);
447 
448     /*! Returns the maximum angular distance (in radian) between any pixel
449         center and its corners. */
450     double max_pixrad() const;
451 
452     /*! Returns the maximum angular distance (in radian) between any pixel
453         center and its corners in a given ring. */
454     double max_pixrad(I ring) const;
455 
456     /*! Returns a set of points along the boundary of the given pixel.
457         \a step=1 gives 4 points on the corners. The first point corresponds
458         to the northernmost corner, the subsequent points follow the pixel
459         boundary through west, south and east corners.
460         \param pix pixel index number
461         \param step the number of returned points is 4*step. */
462     void boundaries (I pix, size_t step, std::vector<vec3> &out) const;
463 
464     std::vector<int> swap_cycles() const;
465   };
466 
467 /*! T_Healpix_Base for Nside up to 2^13. */
468 typedef T_Healpix_Base<int> Healpix_Base;
469 /*! T_Healpix_Base for Nside up to 2^29. */
470 typedef T_Healpix_Base<int64_t> Healpix_Base2;
471 
472 }
473 
474 using detail_healpix::Healpix_Base;
475 using detail_healpix::Healpix_Base2;
476 
477 }
478 
479 #endif
480