From f8630e06efcdc97f59968e63a311bfdc19f2ae62 Mon Sep 17 00:00:00 2001 From: Frank Warmerdam Date: Fri, 19 Jul 2013 16:57:36 +0000 Subject: major healpix update, fix polar scaling (#219) git-svn-id: http://svn.osgeo.org/metacrs/proj/trunk@2378 4e78687f-474d-0410-85f9-8d5e500ac6b2 --- ChangeLog | 4 + src/PJ_healpix.c | 966 ++++++++++++++++++++++++------------------------------- 2 files changed, 424 insertions(+), 546 deletions(-) diff --git a/ChangeLog b/ChangeLog index 73859021..8b42efed 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2013-07-19 Frank Warmerdam + + * src/PJ_healpix.c: major update for polar scaling and parms (#219) + 2013-07-12 Frank Warmerdam * src/geodesic.{c,h}: allow polygon vertices to be specified diff --git a/src/PJ_healpix.c b/src/PJ_healpix.c index 359c9995..5c07700d 100644 --- a/src/PJ_healpix.c +++ b/src/PJ_healpix.c @@ -1,11 +1,13 @@ /****************************************************************************** * $Id: PJ_healpix.c 1504 2011-10-18 14:58:57Z landcare $ * - * Project: PROJ.4 - * Purpose: Implementation of the healpix projection. - * Definition: http://code.scenzgrid.org/index.php/p/scenzgrid-py/source/tree/master/docs/scenzgrid.pdf - * Author: Alex Raichev & Michael Speth , spethm@landcareresearch.co.nz - * + * Project: PROJ.4 + * Purpose: Implementation of the HEALPix and rHEALPix projections. + * For background see . + * Authors: Alex Raichev (raichev@cs.auckland.ac.nz) + * Michael Speth (spethm@landcareresearch.co.nz) + * Notes: Raichev implemented these projections in Python and + * Speth translated them into C here. ****************************************************************************** * Copyright (c) 2001, Thomas Flemming, tf@ttqv.com * @@ -17,7 +19,7 @@ * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. + * in all copies or substcounteral portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF @@ -28,122 +30,52 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. *****************************************************************************/ - -#define PROJ_PARMS__ \ - int npole;\ - int spole; - -#define PJ_LIB__ +# define PROJ_PARMS__ \ + int north_square; \ + int south_square; \ + double qp; \ + double *apa; +# define PJ_LIB__ # include PROJ_HEAD(healpix, "HEALPix") "\n\tSph., Ellps."; -PROJ_HEAD(rhealpix, "rHEALPix") "\n\tSph., Ellps.\n\tnpole= spole="; +PROJ_HEAD(rhealpix, "rHEALPix") "\n\tSph., Ellps.\n\tnorth_square= south_square="; # include -# define R1 {{ 0,-1},{ 1, 0}} /** Matrix for anticlockwise rotation by pi/2 **/ -# define R2 {{-1, 0},{ 0,-1}} /** Matrix for anticlockwise rotation by pi (R1 X R1) X = dot product **/ -# define R3 {{ 0, 1},{-1, 0}} /** Matrix for anticlockwise rotation by 3*pi/2 (R2 X R1) **/ -# define IDENT {{1,0},{0,1}} -/** - * 0 - Identity matrix
- * 1 - Counter-clockwise rotation by PI/2
- * 2 - Counter-clockwise rotation by PI
- * 3 - Counter-clockwise rotation by 3*PI/2
- * 4 - Counter-clockwise rotation by 3*PI/2
- * 5 - Counter-clockwise rotation by PI
- * 6 - Counter-clockwise rotation by PI/2
- **/ -# define ROT { IDENT, R1, R2, R3, R3, R2, R1} -# define RFACTOR 3 /** Used for returning the rotation matrix **/ -/** Used for calculating if a point is within the HEALPix projection for sphere. **/ -# define EPS 1e-12 +/* Matrix for counterclockwise rotation by pi/2: */ +# define R1 {{ 0,-1},{ 1, 0}} +/* Matrix for counterclockwise rotation by pi: */ +# define R2 {{-1, 0},{ 0,-1}} +/* Matrix for counterclockwise rotation by 3*pi/2: */ +# define R3 {{ 0, 1},{-1, 0}} +/* Identity matrix */ +# define IDENT {{1, 0},{0, 1}} +/* IDENT, R1, R2, R3, R1 inverse, R2 inverse, R3 inverse:*/ +# define ROT {IDENT, R1, R2, R3, R3, R2, R1} +/* Fuzz to handle rounding errors: */ +# define EPS 1e-15 typedef struct { - int cn; // the number 0 -> 4 indicating the position of the polar cap. - double x,y; // the coordinates of the pole points (point of most extreme latitude on the polar caps). - enum Region { north, south, equatorial } region; + int cn; /* An integer 0--3 indicating the position of the polar cap. */ + double x, y; /* Coordinates of the pole point (point of most extreme latitude on the polar caps). */ + enum Region {north, south, equatorial} region; } CapMap; typedef struct { - double x,y; + double x, y; } Point; double rot[7][2][2] = ROT; -/** - NOTES: Alex Raichev implemented the math in python and this is a port of his work. - The healpix projection is a Lambert cylindrical equal-area projection for - equaltorial latitudes and an interrupted Colignon projection for polar - latitudes. - **/ - /** * Returns the sign of the double. * @param v the parameter whose sign is returned. * @return 1 for positive number, -1 for negative, and 0 for zero. **/ -static double pj_sign (double v) { +double pj_sign (double v) { return v > 0 ? 1 : (v < 0 ? -1 : 0); } /** - * Scales the number by a factor. - * @param num the number to be scaled. - * @param factor the factor to scale the number by. - * @param isInverse 1 for scaling the number by 1 / factor and 0 for scaling by the factor. - * @return the scaled number. - **/ -double scale_number(double num, double factor, int isInverse){ - if(isInverse == 1){ - return num * 1.0/factor; - } - return num * factor; -} -/** - * Scales all the items of the array by a factor. - * @param xy - **/ -void scale_array(XY *array, double k, int inverse){ - double c = 0; - if (inverse == 1) { - c = 1.0/k; - }else{ - c = k; - } - array->x *= c; - array->y *= c; -} -/** - * Given an angle return its equivalent angle. - * @param x the angle to convert - * @return the equivalent angle such that -PI <= the angle returend <= PI - **/ -double standardize_lon(double x){ - if(x < -1*PI || x >= PI){ - x = x - 2*PI*floor(x/(2*PI)); - if(x >= PI){ - x = x - 2*PI; - } - } - return x; -} -/** - * Given an angle, return its unit-circle equivalent angle. - * @param x the angel to convert. - * @return the equivalent angle such that -PI/2 <= the angle returned <= PI/2. - **/ -double standardize_lat(double x){ - if( x < -PI/2.0 || x > PI/2){ - x = x-2.0*PI*floor(x/(2.0*PI)); - if(x > PI/2.0 && x <= 3.0*PI/2){ - x = PI - x; - }else{ - x = x - 2*PI; - } - } - return x; -} -/** - * Returns the index of the 2d array in rot. - * @param index range from -3 to 3. - * @return the index into the rot 3d array. + * Return the index of the matrix in ROT. + * @param index ranges from -3 to 3. */ -static int get_rotate_index(int index){ - switch(index){ +static int get_rotate_index(int index) { + switch(index) { case 0: return 0; case 1: @@ -162,537 +94,479 @@ static int get_rotate_index(int index){ return 0; } /** - * Calculates if the point lies on or within the polygon. - * Very good explination of how this works: http://paulbourke.net/geometry/insidepoly/ + * Return 1 if point (testx, testy) lies in the interior of the polygon + * determined by the vertices in vert, and return 0 otherwise. + * See http://paulbourke.net/geometry/polygonmesh/ for more details. * @param nvert the number of vertices in the polygon. - * @param vert the x,y-coordinates of the polygon's vertices - * @param testx the x-coordinate of the test point. - * @param testy the y-coordinate of the test point. - * @return 1 if on or within the bounds of the polygon, and 0 otherwise. + * @param vert the (x, y)-coordinates of the polygon's vertices **/ -static -int pnpoly(int nvert, double vert[][2], double testx, double testy){ - - int i,c = 0; +static int pnpoly(int nvert, double vert[][2], double testx, double testy) { + int i, c = 0; int counter = 0; double xinters; - Point p1,p2; - - // check for boundrary cases - for(i = 0; i < nvert; i++){ - if(testx == vert[i][0] && testy == vert[i][1]){ - return 1; - } + Point p1, p2; + /* Check for boundrary cases */ + for (i = 0; i < nvert; i++) { + if (testx == vert[i][0] && testy == vert[i][1]) { + return 1; + } } - - // initialize p1 p1.x = vert[0][0]; p1.y = vert[0][1]; - - for(i = 1; i < nvert; i++){ - p2.x = vert[i % nvert][0]; - p2.y = vert[i % nvert][1]; - - if(testy > MIN(p1.y,p2.y)){ - if (testy <= MAX(p1.y,p2.y)) { - if (testx <= MAX(p1.x,p2.x)) { - if (p1.y != p2.y) { - xinters = (testy-p1.y)*(p2.x-p1.x)/(p2.y-p1.y)+p1.x; - if (p1.x == p2.x || testx <= xinters){ - counter++; - } - } - } - } - } - p1 = p2; + for (i = 1; i < nvert; i++) { + p2.x = vert[i % nvert][0]; + p2.y = vert[i % nvert][1]; + if (testy > MIN(p1.y, p2.y)) { + if (testy <= MAX(p1.y, p2.y)) { + if (testx <= MAX(p1.x, p2.x)) { + if (p1.y != p2.y) { + xinters = (testy-p1.y)*(p2.x-p1.x)/(p2.y-p1.y)+p1.x; + if (p1.x == p2.x || testx <= xinters) { + counter++; + } + } + } + } + } + p1 = p2; } - if(counter % 2 == 0){ - return 0; - }else{ - return 1; + if (counter % 2 == 0) { + return 0; + } else { + return 1; } return c; } /** - * Calculates if the coordinates are within the image of projection. - * @param x the x-coordinate to check. - * @param y the y-coordinate to check. - * @param proj 0 for healpix and 1 for rhealpix. - * @param npole the positions of the polar squares, only used for rhealpix. - * @param spole the positions of the polar squares, only used for rhealpix. - * @return 1 if the coordinate is within the projection and 0 otherwise. + * Return 1 if (x, y) lies in (the interior or boundary of) the image of the + * HEALPix projection (in case proj=0) or in the image the rHEALPix projection + * (in case proj=1), and return 0 otherwise. + * @param north_square the position of the north polar square (rHEALPix only) + * @param south_square the position of the south polar square (rHEALPix only) **/ -int in_image(double x, double y, int proj, int npole, int spole){ - if(proj == 0){ - double healpixVertsJit[][2] = { - {-1.0*PI-EPS ,PI/4.0}, - {-3.0*PI/4.0 ,PI/2.0+EPS}, - {-1.0*PI/2.0 ,PI/4.0+EPS}, - {-1.0*PI/4.0 ,PI/2.0+EPS}, - {0.0 ,PI/4.0+EPS}, - {PI/4.0 ,PI/2.0+EPS}, - {PI/2.0 ,PI/4.0+EPS}, - {3.0*PI/4.0 ,PI/2.0+EPS}, - {PI+EPS ,PI/4.0}, - {PI+EPS ,-1.0*PI/4.0}, - {3.0*PI/4.0 ,-1.0*PI/2.0-EPS}, - {PI/2.0 ,-1.0*PI/4.0-EPS}, - {PI/4.0 ,-1.0*PI/2.0-EPS}, - {0.0 ,-1.0*PI/4.0-EPS}, - {-1.0*PI/4.0 ,-1.0*PI/2.0-EPS}, - {-1.0*PI/2.0 ,-1.0*PI/4.0-EPS}, - {-3.0*PI/4.0 ,-1.0*PI/2.0-EPS}, - {-1.0*PI-EPS ,-1.0*PI/4.0}}; - return pnpoly((int)sizeof(healpixVertsJit)/sizeof(healpixVertsJit[0]), - healpixVertsJit,x,y); - }else{ - // Used for calculating if a point is within the rHEALPix projection for sphere. - double rhealpixVertsJit[][2] = { - {-1.0*PI-EPS ,PI/4.0+EPS}, - {-1.0*PI + npole*PI/2.0-EPS ,PI/4.0+EPS}, - {-1.0*PI + npole*PI/2.0-EPS ,3*PI/4.0+EPS}, - {-1.0*PI + (npole + 1.0)*PI/2.0+EPS ,3*PI/4.0+EPS}, - {-1.0*PI + (npole + 1.0)*PI/2.0+EPS ,PI/4.0+EPS}, - {PI+EPS ,PI/4.0+EPS}, - {PI+EPS ,-1.0*PI/4.0-EPS}, - {-1.0*PI + (spole + 1.0)*PI/2.0+EPS ,-1.0*PI/4.0-EPS}, - {-1.0*PI + (spole + 1.0)*PI/2.0+EPS ,-3.0*PI/4.0-EPS}, - {-1.0*PI + spole*PI/2.0-EPS ,-3.0*PI/4.0-EPS}, - {-1.0*PI + spole*PI/2.0-EPS ,-1.0*PI/4.0-EPS}, - {-1.0*PI-EPS ,-1.0*PI/4.0-EPS}}; - return pnpoly((int)sizeof(rhealpixVertsJit)/sizeof(rhealpixVertsJit[0]), - rhealpixVertsJit,x,y); +int in_image(double x, double y, int proj, int north_square, int south_square) { + if (proj == 0) { + double healpixVertsJit[][2] = { + {-1.0*PI- EPS, PI/4.0}, + {-3.0*PI/4.0, PI/2.0 + EPS}, + {-1.0*PI/2.0, PI/4.0 + EPS}, + {-1.0*PI/4.0, PI/2.0 + EPS}, + {0.0, PI/4.0 + EPS}, + {PI/4.0, PI/2.0 + EPS}, + {PI/2.0, PI/4.0 + EPS}, + {3.0*PI/4.0, PI/2.0 + EPS}, + {PI+ EPS, PI/4.0}, + {PI+ EPS, -1.0*PI/4.0}, + {3.0*PI/4.0, -1.0*PI/2.0 - EPS}, + {PI/2.0, -1.0*PI/4.0 - EPS}, + {PI/4.0, -1.0*PI/2.0 - EPS}, + {0.0, -1.0*PI/4.0 - EPS}, + {-1.0*PI/4.0, -1.0*PI/2.0 - EPS}, + {-1.0*PI/2.0, -1.0*PI/4.0 - EPS}, + {-3.0*PI/4.0, -1.0*PI/2.0 - EPS}, + {-1.0*PI - EPS, -1.0*PI/4.0} + }; + return pnpoly((int)sizeof(healpixVertsJit)/ + sizeof(healpixVertsJit[0]), healpixVertsJit, x, y); + } else { + double rhealpixVertsJit[][2] = { + {-1.0*PI - EPS, PI/4.0 + EPS}, + {-1.0*PI + north_square*PI/2.0- EPS, PI/4.0 + EPS}, + {-1.0*PI + north_square*PI/2.0- EPS, 3*PI/4.0 + EPS}, + {-1.0*PI + (north_square + 1.0)*PI/2.0 + EPS, 3*PI/4.0 + EPS}, + {-1.0*PI + (north_square + 1.0)*PI/2.0 + EPS, PI/4.0 + EPS}, + {PI + EPS, PI/4.0 + EPS}, + {PI + EPS, -1.0*PI/4.0 - EPS}, + {-1.0*PI + (south_square + 1.0)*PI/2.0 + EPS, -1.0*PI/4.0 - EPS}, + {-1.0*PI + (south_square + 1.0)*PI/2.0 + EPS, -3.0*PI/4.0 - EPS}, + {-1.0*PI + south_square*PI/2.0 - EPS, -3.0*PI/4.0 - EPS}, + {-1.0*PI + south_square*PI/2.0 - EPS, -1.0*PI/4.0 - EPS}, + {-1.0*PI - EPS, -1.0*PI/4.0 - EPS}}; + return pnpoly((int)sizeof(rhealpixVertsJit)/ + sizeof(rhealpixVertsJit[0]), rhealpixVertsJit, x, y); } } /** - * Returns an authalic latitude of the point given a point of geographic - * latitude phi on an ellipse of eccentricity e. - * pj_authlat is the inverse of the alex's auth_lat. - * @param phi - * @param e - * @param inverse 1 for inverse or 0 otherwise. - * @return the authalic latitude of the point. + * Return the authalic latitude of latitude alpha (if inverse=0) or + * return the approximate latitude of authalic latitude alpha (if inverse=1). + * P contains the relavent ellipsoid parameters. **/ -double auth_lat(double phi, double e, int inverse){ - if(inverse == 0){ - double q = ((1.0 - pow(e,2.0)) * sin(phi)) / (1.0 - (pow(e*sin(phi),2.0))) - - (1.0 - pow(e,2.0)) / (2.0*e) * log((1.0 - e*sin(phi)) / (1.0+e*sin(phi))); - - double qp = 1.0 - (1.0-pow(e,2.0)) / (2.0*e)*log((1.0 - e) / (1.0 + e)); - double ratio = q/qp; - // Rounding errors - if( fabsl(ratio) > 1){ - ratio = pj_sign(ratio); - } - return asin(ratio); +double auth_lat(PJ *P, double alpha, int inverse) { + if (inverse == 0) { + /* Authalic latitude. */ + double q = pj_qsfn(sin(alpha), P->e, 1.0 - P->es); + double qp = P->qp; + double ratio = q/qp; + if (fabsl(ratio) > 1) { + /* Rounding error. */ + ratio = pj_sign(ratio); + } + return asin(ratio); + } else { + /* Approximation to inverse authalic latitude. */ + return pj_authlat(alpha, P->apa); } - return phi + (pow(e,2) / 3.0 + 31*pow(e,4) / 180.0 + 517.0*pow(e,6)/5040.0) * sin(2.0*phi) - + (23.0*pow(e,4)/360.0 + 251.0*pow(e,6)/3780.0)*sin(4.0*phi) - + 761.0*pow(e,6)/45360.0 * sin(6.0*phi); } /** - * Compute the forward signature functions of the HEALPix - * projection of a sphere with radius `R` and central meridian `lon0`. + * Return the HEALPix projection of the longitude-latitude point lp on + * the unit sphere. **/ -XY healpix_sphere(LP lp, PJ *P){ - double lam = standardize_lon(lp.lam); - double phi = standardize_lat(lp.phi); - double phi0 = aasin(P->ctx, 2.0/3.0); +XY healpix_sphere(LP lp) { + double lam = lp.lam; + double phi = lp.phi; + double phi0 = asin(2.0/3.0); XY xy; - // equatorial region - if( fabsl(phi) <= phi0) { - xy.x = lam; - xy.y = 3.0*PI/8.0*sin(phi); + /* equatorial region */ + if ( fabsl(phi) <= phi0) { + xy.x = lam; + xy.y = 3.0*PI/8.0*sin(phi); } else { - double lamc; - double sigma = sqrt(3.0 * (1 - fabsl(sin(phi)))); - double cn = floor(2 * lam / PI + 2); - if (cn >= 4) { - cn = 3; - } - lamc = -3*PI/4 + (PI/2)*cn; - xy.x = lamc + (lam - lamc) * sigma; - xy.y = pj_sign(phi)*PI/4 * (2 - sigma); + double lamc; + double sigma = sqrt(3.0*(1 - fabsl(sin(phi)))); + double cn = floor(2*lam / PI + 2); + if (cn >= 4) { + cn = 3; + } + lamc = -3*PI/4 + (PI/2)*cn; + xy.x = lamc + (lam - lamc)*sigma; + xy.y = pj_sign(phi)*PI/4*(2 - sigma); } - xy.x = scale_number(xy.x,P->a,0); - xy.y = scale_number(xy.y,P->a,0); return xy; } /** - * Compute the inverse signature functions of the HEALPix - * projection of a sphere with radius `R` and central meridian `lon0`. + * Return the inverse of healpix_sphere(). **/ -LP healpix_sphere_inv(XY xy, PJ *P){ - double x,y,y0; - double cn; - double xc; - double tau; +LP healpix_sphere_inverse(XY xy) { LP lp; - // Scale down to radius 1 sphere - x = scale_number(xy.x,P->a,1); - y = scale_number(xy.y,P->a,1); - y0 = PI/4.0; - // Equatorial region. - if(fabsl(y) <= y0){ - lp.lam = x; - lp.phi = asin(8.0*y/(3.0*PI)); - } else if(fabsl(y) < PI/2.0){ - cn = floor(2.0 * x/PI + 2.0); - if(cn >= 4){ - cn = 3; - } - xc = -3.0 * PI/4.0 + (PI/2.0)*cn; - tau = 2.0 - 4.0*fabsl(y)/PI; - lp.lam = xc + (x - xc)/tau; - lp.phi = pj_sign(y)*asin(1.0 - pow(tau , 2.0)/3.0); + double x = xy.x; + double y = xy.y; + double y0 = PI/4.0; + /* Equatorial region. */ + if (fabsl(y) <= y0) { + lp.lam = x; + lp.phi = asin(8.0*y/(3.0*PI)); + } else if (fabsl(y) < PI/2.0) { + double cn = floor(2.0*x/PI + 2.0); + if (cn >= 4) { + cn = 3; + } + double xc = -3.0*PI/4.0 + (PI/2.0)*cn; + double tau = 2.0 - 4.0*fabsl(y)/PI; + lp.lam = xc + (x - xc)/tau; + lp.phi = pj_sign(y)*asin(1.0 - pow(tau , 2.0)/3.0); } else { - lp.lam = -1.0*PI - P->lam0; - lp.phi = pj_sign(y)*PI/2.0; + lp.lam = -1.0*PI; + lp.phi = pj_sign(y)*PI/2.0; } return (lp); } /** - * Adds one vector to another of length 2. - * @param a the first term. - * @param b the second term. - * @param ret holds the summation of the vectors. + * Return the vector sum a + b, where a and b are 2-dimensional vectors. + * @param ret holds a + b. **/ -static void vector_add(double a[], double b[],double * ret){ +static void vector_add(double a[2], double b[2], double *ret) { int i; - for(i = 0; i < 2; i++){ - ret[i] = a[i] + b[i]; + for(i = 0; i < 2; i++) { + ret[i] = a[i] + b[i]; } } /** - * Subs tracts one vector from another of length 2. - * @param a the minuend. - * @param b the subtrahend. - * @param ret the difference of the vectors where the difference is the result of a minus b. + * Return the vector difference a - b, where a and b are 2-dimensional vectors. + * @param ret holds a - b. **/ -static void vector_sub(double a[], double b[], double * ret){ +static void vector_sub(double a[2], double b[2], double*ret) { int i; - for(i = 0; i < 2; i++){ - ret[i] = a[i] - b[i]; + for(i = 0; i < 2; i++) { + ret[i] = a[i] - b[i]; } } /** - * Calculates the dot product of the arrays. - * @param a the array that will be used to calculate the dot product. - * Must contain the same number of columns as b's rows. Must be a matrix with equal lengthed rows and columns. - * @param b the array that will be used to calculate the dot product; must contain the same number of rows as a's columns. - * @param length the size of the b array. Note, a's column size must equal b's length. - * @param ret the dot product of a and b. + * Return the 2 x 1 matrix product a*b, where a is a 2 x 2 matrix and + * b is a 2 x 1 matrix. + * @param ret holds a*b. **/ -static void dot_product(double a[2][2], double b[], double * ret){ - int i,j; +static void dot_product(double a[2][2], double b[2], double *ret) { + int i, j; int length = 2; - for(i = 0; i < length; i++){ - ret[i] = 0; - for(j = 0; j < length; j++){ - ret[i] += a[i][j]*b[j]; - } + for(i = 0; i < length; i++) { + ret[i] = 0; + for(j = 0; j < length; j++) { + ret[i] += a[i][j]*b[j]; + } } } /** - * Returns the polar cap number, pole point coordinates, and region - * for x,y in the HEALPix projection of the sphere of radius R. - * @param x coordinate in the HEALPix or rHEALPix. - * @param y coordinate in the HEALPix or rHEALPix. - * @param npole integer between 0 and 3 indicating the position of the north pole. - * @param spole integer between 0 and 3 indicating teh position of the south pole. - * @param inverse 1 computes the rHEALPix projection and 0 computes forward. - * @return a structure containing the cap poles. + * Return the number of the polar cap, the pole point coordinates, and + * the region that (x, y) lies in. + * If inverse=0, then assume (x,y) lies in the image of the HEALPix + * projection of the unit sphere. + * If inverse=1, then assume (x,y) lies in the image of the + * (north_square, south_square)-rHEALPix projection of the unit sphere. **/ -static CapMap get_cap(double x, double y, double R, int npole, int spole, int inverse){ +static CapMap get_cap(double x, double y, int north_square, int south_square, + int inverse) { CapMap capmap; double c; - capmap.x = x; capmap.y = y; - - if(inverse == 0){ - if(y > R*PI/4.0){ - capmap.region = north; - c = R*PI/2.0; - }else if(y < -1*R*PI/4.0){ - capmap.region = south; - c = -1*R*PI/2.0; - }else{ - capmap.region = equatorial; - capmap.cn = 0; - return capmap; - } - // polar region - if(x < -1*R*PI/2.0){ - capmap.cn = 0; - capmap.x = (-1*R*3.0*PI/4.0); - capmap.y = c; - }else if(x >= -1*R*PI/2.0 && x < 0){ - capmap.cn = 1; - capmap.x = -1*R*PI/4.0; - capmap.y = c; - }else if(x >= 0 && x < R*PI/2.0){ - capmap.cn = 2; - capmap.x = R*PI/4.0; - capmap.y = c; - }else{ - capmap.cn = 3; - capmap.x = R*3.0*PI/4.0; - capmap.y = c; - } - return capmap; - }else{ - double eps; - if(y > R*PI/4.0){ - capmap.region = north; - capmap.x = R*(-3.0*PI/4.0 + npole*PI/2.0); - capmap.y = R*PI/2.0; - x = x - npole*R*PI/2.0; - }else if(y < -1*R*PI/4.0){ - capmap.region = south; - capmap.x = R*(-3.0*PI/4.0 + spole*PI/2); - capmap.y = -1*R*PI/2.0; - x = x - spole*R*PI/2.0; - }else{ - capmap.region = equatorial; - capmap.cn = 0; - return capmap; - } - // Polar Region, find # of HEALPix polar cap number that - // x,y moves to when rHEALPix polar square is disassembled. - eps = R*1e-15; // Kludge. Fuzz to avoid some rounding errors. - if(capmap.region == north){ - if(y >= -1*x - R*PI/4.0 - eps && y < x + R*5.0*PI/4.0 - eps){ - capmap.cn = (npole + 1) % 4; - }else if(y > -1*x -1*R*PI/4.0 + eps && y >= x + R*5.0*PI/4.0 - eps){ - capmap.cn = (npole + 2) % 4; - }else if(y <= -1*x -1*R*PI/4.0 + eps && y > x + R*5.0*PI/4.0 + eps){ - capmap.cn = (npole + 3) % 4; - }else{ - capmap.cn = npole; - } - }else if(capmap.region == south){ - if(y <= x + R*PI/4.0 + eps && y > -1*x - R*5.0*PI/4 + eps){ - capmap.cn = (spole + 1) % 4; - }else if(y < x + R*PI/4.0 - eps && y <= -1*x - R*5.0*PI/4.0 + eps){ - capmap.cn = (spole + 2) % 4; - }else if(y >= x + R*PI/4.0 - eps && y < -1*x - R*5.0*PI/4.0 - eps){ - capmap.cn = (spole + 3) % 4; - }else { - capmap.cn = spole; - } - } - return capmap; + if (inverse == 0) { + if (y > PI/4.0) { + capmap.region = north; + c = PI/2.0; + } else if (y < -1*PI/4.0) { + capmap.region = south; + c = -1*PI/2.0; + } else { + capmap.region = equatorial; + capmap.cn = 0; + return capmap; + } + /* polar region */ + if (x < -1*PI/2.0) { + capmap.cn = 0; + capmap.x = (-1*3.0*PI/4.0); + capmap.y = c; + } else if (x >= -1*PI/2.0 && x < 0) { + capmap.cn = 1; + capmap.x = -1*PI/4.0; + capmap.y = c; + } else if (x >= 0 && x < PI/2.0) { + capmap.cn = 2; + capmap.x = PI/4.0; + capmap.y = c; + } else { + capmap.cn = 3; + capmap.x = 3.0*PI/4.0; + capmap.y = c; + } + return capmap; + } else { + double eps; + if (y > PI/4.0) { + capmap.region = north; + capmap.x = (-3.0*PI/4.0 + north_square*PI/2.0); + capmap.y = PI/2.0; + x = x - north_square*PI/2.0; + } else if (y < -1*PI/4.0) { + capmap.region = south; + capmap.x = (-3.0*PI/4.0 + south_square*PI/2); + capmap.y = -1*PI/2.0; + x = x - south_square*PI/2.0; + } else { + capmap.region = equatorial; + capmap.cn = 0; + return capmap; + } + /* Polar Region, find the HEALPix polar cap number that + x, y moves to when rHEALPix polar square is disassembled. */ + eps = 1e-15; /* Kludge. Fuzz to avoid some rounding errors. */ + if (capmap.region == north) { + if (y >= -1*x - PI/4.0 - eps && y < x + 5.0*PI/4.0 - eps) { + capmap.cn = (north_square + 1) % 4; + } else if (y > -1*x -1*PI/4.0 + eps && y >= x + 5.0*PI/4.0 - eps) { + capmap.cn = (north_square + 2) % 4; + } else if (y <= -1*x -1*PI/4.0 + eps && y > x + 5.0*PI/4.0 + eps) { + capmap.cn = (north_square + 3) % 4; + } else { + capmap.cn = north_square; + } + } else if (capmap.region == south) { + if (y <= x + PI/4.0 + eps && y > -1*x - 5.0*PI/4 + eps) { + capmap.cn = (south_square + 1) % 4; + } else if (y < x + PI/4.0 - eps && y <= -1*x - 5.0*PI/4.0 + eps) { + capmap.cn = (south_square + 2) % 4; + } else if (y >= x + PI/4.0 - eps && y < -1*x - 5.0*PI/4.0 - eps) { + capmap.cn = (south_square + 3) % 4; + } else { + capmap.cn = south_square; + } + } + return capmap; } } /** - * Rearrange point x,y in the HEALPix projection by + * Rearrange point (x, y) in the HEALPix projection by * combining the polar caps into two polar squares. - * Put the north polar square in position npole and - * the south polar square in position spole. - * @param x coordinate in the HEALPix projection of the sphere. - * @param y coordinate in the HEALPix projection of the sphere. - * @param R - the Sphere's radius. - * @param npole integer between 0 and 3 indicating the position - * of the north polar square. - * @param spole integer between 0 and 3 indicating the position - * of the south polar square. - * @param inverse 1 to uncombine the polar caps and 0 to combine. + * Put the north polar square in position north_square and + * the south polar square in position south_square. + * If inverse=1, then uncombine the polar caps. + * @param north_square integer between 0 and 3. + * @param south_square integer between 0 and 3. **/ -static XY combine_caps(double x, double y, double R, int npole, int spole, int inverse){ +static XY combine_caps(double x, double y, int north_square, int south_square, + int inverse) { XY xy; double v[2]; double a[2]; double vector[2]; double v_min_c[2]; double ret_dot[2]; - CapMap capmap = get_cap(x,y,R,npole,spole,inverse); - - if(capmap.region == equatorial){ - xy.x = capmap.x; - xy.y = capmap.y; - return xy; + CapMap capmap = get_cap(x, y, north_square, south_square, inverse); + if (capmap.region == equatorial) { + xy.x = capmap.x; + xy.y = capmap.y; + return xy; } v[0] = x; v[1] = y; - if(inverse == 0){ - // compute forward function by rotating, translating, and shifting xy. - int pole = 0; - double (*tmpRot)[2]; - double c[2] = {capmap.x,capmap.y}; - if(capmap.region == north){ - pole = npole; - a[0] = R * (-3.0*PI/4.0 + pole * PI/2); - a[1] = R * (PI/2.0 + pole * 0); - - tmpRot = rot[get_rotate_index(capmap.cn - pole)]; - vector_sub(v,c,v_min_c); - dot_product(tmpRot,v_min_c,ret_dot); - vector_add(ret_dot, a, vector); - - }else { - pole = spole; - a[0] = R * (-3.0*PI/4.0 + pole * PI/2); - a[1] = R * (PI/-2.0 + pole * 0); - - tmpRot = rot[get_rotate_index(-1*(capmap.cn - pole))]; - vector_sub(v,c,v_min_c); - dot_product(tmpRot,v_min_c,ret_dot); - - vector_add(ret_dot, a, vector); - } - - xy.x = vector[0]; - xy.y = vector[1]; - return xy; - }else{ - // compute inverse function. - // get the current position of rHEALPix polar squares - int pole = 0; - double (*tmpRot)[2]; - double c[2] = {capmap.x,capmap.y}; - // disassemble - if(capmap.region == north){ - pole = npole; - a[0] = R * (-3.0*PI/4.0 + capmap.cn * PI/2); - a[1] = R * (PI/2.0 + capmap.cn * 0); - - tmpRot = rot[get_rotate_index(-1*(capmap.cn - pole))]; - vector_sub(v,c,v_min_c); - dot_product(tmpRot,v_min_c,ret_dot); - vector_add(ret_dot, a, vector); - - }else{ - pole = spole; - a[0] = R * (-3.0*PI/4.0 + capmap.cn * PI/2); - a[1] = R * (PI/-2.0 + capmap.cn * 0); - - tmpRot = rot[get_rotate_index(capmap.cn - pole)]; - vector_sub(v,c,v_min_c); - dot_product(tmpRot,v_min_c,ret_dot); - vector_add(ret_dot, a, vector); - } - xy.x = vector[0]; - xy.y = vector[1]; - return xy; + if (inverse == 0) { + /* Rotate (x, y) about its polar cap tip and then translate it to + north_square or south_square. */ + int pole = 0; + double (*tmpRot)[2]; + double c[2] = {capmap.x, capmap.y}; + if (capmap.region == north) { + pole = north_square; + a[0] = (-3.0*PI/4.0 + pole*PI/2); + a[1] = (PI/2.0 + pole*0); + tmpRot = rot[get_rotate_index(capmap.cn - pole)]; + vector_sub(v, c, v_min_c); + dot_product(tmpRot, v_min_c, ret_dot); + vector_add(ret_dot, a, vector); + } else { + pole = south_square; + a[0] = (-3.0*PI/4.0 + pole*PI/2); + a[1] = (PI/-2.0 + pole*0); + tmpRot = rot[get_rotate_index(-1*(capmap.cn - pole))]; + vector_sub(v, c, v_min_c); + dot_product(tmpRot, v_min_c, ret_dot); + vector_add(ret_dot, a, vector); + } + xy.x = vector[0]; + xy.y = vector[1]; + return xy; + } else { + /* Inverse function. + Unrotate (x, y) and then translate it back. */ + int pole = 0; + double (*tmpRot)[2]; + double c[2] = {capmap.x, capmap.y}; + /* disassemble */ + if (capmap.region == north) { + pole = north_square; + a[0] = (-3.0*PI/4.0 + capmap.cn*PI/2); + a[1] = (PI/2.0 + capmap.cn*0); + tmpRot = rot[get_rotate_index(-1*(capmap.cn - pole))]; + vector_sub(v, c, v_min_c); + dot_product(tmpRot, v_min_c, ret_dot); + vector_add(ret_dot, a, vector); + } else { + pole = south_square; + a[0] = (-3.0*PI/4.0 + capmap.cn*PI/2); + a[1] = (PI/-2.0 + capmap.cn*0); + tmpRot = rot[get_rotate_index(capmap.cn - pole)]; + vector_sub(v, c, v_min_c); + dot_product(tmpRot, v_min_c, ret_dot); + vector_add(ret_dot, a, vector); + } + xy.x = vector[0]; + xy.y = vector[1]; + return xy; } } -FORWARD(e_healpix_forward); /* ellipsoidal */ - //int r1[][2] = R1; - double bet = auth_lat(lp.phi, P->e, 0); +FORWARD(s_healpix_forward); /* sphere */ (void) xy; - lp.phi = bet; - P->a = P->ra; - return healpix_sphere(lp,P); + return healpix_sphere(lp); } -FORWARD(s_healpix_forward); /* spheroid */ +FORWARD(e_healpix_forward); /* ellipsoid */ (void) xy; - return healpix_sphere(lp, P); + lp.phi = auth_lat(P, lp.phi, 0); + return healpix_sphere(lp); } -INVERSE(e_healpix_inverse); /* ellipsoidal */ - double x, y; - P->a = P->ra; - - // Scale down to radius 1 sphere before checking x,y - x = scale_number(xy.x,P->a,1); - y = scale_number(xy.y,P->a,1); - // check if the point is in the image - if(in_image(x,y,0,0,0) == 0){ - lp.lam = HUGE_VAL; - lp.phi = HUGE_VAL; - pj_ctx_set_errno( P->ctx, -15); - return lp; +INVERSE(s_healpix_inverse); /* sphere */ + /* Check whether (x, y) lies in the HEALPix image */ + if (in_image(xy.x, xy.y, 0, 0, 0) == 0) { + lp.lam = HUGE_VAL; + lp.phi = HUGE_VAL; + pj_ctx_set_errno(P->ctx, -15); + return lp; } - - lp = healpix_sphere_inv(xy, P); - - lp.phi = auth_lat(lp.phi,P->e,1); - - return (lp); + return healpix_sphere_inverse(xy); } -INVERSE(s_healpix_inverse); /* spheroid */ - double x = xy.x; - double y = xy.y; - // Scale down to radius 1 sphere before checking x,y - x = scale_number(x,P->a,1); - y = scale_number(y,P->a,1); - // check if the point is in the image - if(in_image(x,y,0,0,0) == 0){ - lp.lam = HUGE_VAL; - lp.phi = HUGE_VAL; - pj_ctx_set_errno( P->ctx, -15); - return lp; +INVERSE(e_healpix_inverse); /* ellipsoid */ + /* Check whether (x, y) lies in the HEALPix image. */ + if (in_image(xy.x, xy.y, 0, 0, 0) == 0) { + lp.lam = HUGE_VAL; + lp.phi = HUGE_VAL; + pj_ctx_set_errno(P->ctx, -15); + return lp; } - return healpix_sphere_inv(xy, P); + lp = healpix_sphere_inverse(xy); + lp.phi = auth_lat(P, lp.phi, 1); + return (lp); } -FORWARD(e_rhealpix_forward); /* ellipsoidal */ - double bet = auth_lat(lp.phi,P->e,0); - lp.phi = bet; - xy = healpix_sphere(lp,P); - return combine_caps(xy.x, xy.y, P->a, P->npole, P->spole, 0); +FORWARD(s_rhealpix_forward); /* sphere */ + xy = healpix_sphere(lp); + return combine_caps(xy.x, xy.y, P->north_square, P->south_square, 0); } -FORWARD(s_rhealpix_forward); /* spheroid */ - // Compute forward function. - xy = healpix_sphere(lp,P); - return combine_caps(xy.x, xy.y, P->a, P->npole, P->spole, 0); +FORWARD(e_rhealpix_forward); /* ellipsoid */ + lp.phi = auth_lat(P, lp.phi, 0); + xy = healpix_sphere(lp); + return combine_caps(xy.x, xy.y, P->north_square, P->south_square, 0); } -INVERSE(e_rhealpix_inverse); /* ellipsoidal */ - double x = scale_number(xy.x,P->a,1); - double y = scale_number(xy.y,P->a,1); - // check for out of bounds coordinates - if(in_image(x,y,1,P->npole,P->spole) == 0){ - lp.lam = HUGE_VAL; - lp.phi = HUGE_VAL; - pj_ctx_set_errno( P->ctx, -15); - return lp; +INVERSE(s_rhealpix_inverse); /* sphere */ + /* Check whether (x, y) lies in the rHEALPix image. */ + if (in_image(xy.x, xy.y, 1, P->north_square, P->south_square) == 0) { + lp.lam = HUGE_VAL; + lp.phi = HUGE_VAL; + pj_ctx_set_errno(P->ctx, -15); + return lp; } - - xy = combine_caps(xy.x,xy.y,P->a,P->npole,P->spole,1); - lp = healpix_sphere_inv(xy, P); - lp.phi = auth_lat(lp.phi,P->e,1); - return lp; + xy = combine_caps(xy.x, xy.y, P->north_square, P->south_square, 1); + return healpix_sphere_inverse(xy); } -INVERSE(s_rhealpix_inverse); /* spheroid */ - double x = scale_number(xy.x,P->a,1); - double y = scale_number(xy.y,P->a,1); - // check for out of bounds coordinates - if(in_image(x,y,1,P->npole,P->spole) == 0){ - lp.lam = HUGE_VAL; - lp.phi = HUGE_VAL; - pj_ctx_set_errno( P->ctx, -15); - return lp; +INVERSE(e_rhealpix_inverse); /* ellipsoid */ + /* Check whether (x, y) lies in the rHEALPix image. */ + if (in_image(xy.x, xy.y, 1, P->north_square, P->south_square) == 0) { + lp.lam = HUGE_VAL; + lp.phi = HUGE_VAL; + pj_ctx_set_errno(P->ctx, -15); + return lp; } - xy = combine_caps(xy.x,xy.y,P->a,P->npole,P->spole,1); - return healpix_sphere_inv(xy, P); + xy = combine_caps(xy.x, xy.y, P->north_square, P->south_square, 1); + lp = healpix_sphere_inverse(xy); + lp.phi = auth_lat(P, lp.phi, 1); + return lp; } FREEUP; - if (P) { - pj_dalloc(P); - } + if (P) { + if (P->apa) + pj_dalloc(P->apa); + pj_dalloc(P); + } } -ENTRY0(healpix) - if(P->es){ - P->inv = e_healpix_inverse; P->fwd = e_healpix_forward; - }else{ - P->inv = s_healpix_inverse; P->fwd = s_healpix_forward; +ENTRY1(healpix, apa) + if (P->es) { + P->apa = pj_authset(P->es); /* For auth_lat(). */ + P->qp = pj_qsfn(1.0, P->e, P->one_es); /* For auth_lat(). */ + P->a = P->a*sqrt(0.5*P->qp); /* Set P->a to authalic radius. */ + P->ra = 1.0/P->a; + P->fwd = e_healpix_forward; + P->inv = e_healpix_inverse; + } else { + P->fwd = s_healpix_forward; + P->inv = s_healpix_inverse; } ENDENTRY(P) -ENTRY0(rhealpix) - P->npole = pj_param(P->ctx, P->params,"inpole").i; - P->spole = pj_param(P->ctx,P->params,"ispole").i; - - // check for valid npole and spole inputs - if(P->npole < 0 || P->npole > 3){ - E_ERROR(-47); +ENTRY1(rhealpix, apa) + P->north_square = pj_param(P->ctx, P->params,"inorth_square").i; + P->south_square = pj_param(P->ctx, P->params,"isouth_square").i; + /* Check for valid north_square and south_square inputs. */ + if (P->north_square < 0 || P->north_square > 3) { + E_ERROR(-47); } - if(P->spole < 0 || P->spole > 3){ - E_ERROR(-47); + if (P->south_square < 0 || P->south_square > 3) { + E_ERROR(-47); } - - if(P->es){ - P->inv = e_rhealpix_inverse; P->fwd = e_rhealpix_forward; - }else{ - P->inv = s_rhealpix_inverse; P->fwd = s_rhealpix_forward; + if (P->es) { + P->apa = pj_authset(P->es); /* For auth_lat(). */ + P->qp = pj_qsfn(1.0, P->e, P->one_es); /* For auth_lat(). */ + P->a = P->a*sqrt(0.5*P->qp); /* Set P->a to authalic radius. */ + P->ra = 1.0/P->a; + P->fwd = e_rhealpix_forward; + P->inv = e_rhealpix_inverse; + } else { + P->fwd = s_rhealpix_forward; + P->inv = s_rhealpix_inverse; } ENDENTRY(P) -- cgit v1.2.3