/src/proj/src/projections/healpix.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /****************************************************************************** |
2 | | * Project: PROJ.4 |
3 | | * Purpose: Implementation of the HEALPix and rHEALPix projections. |
4 | | * For background see |
5 | | *<http://code.scenzgrid.org/index.php/p/scenzgrid-py/source/tree/master/docs/rhealpix_dggs.pdf>. |
6 | | * Authors: Alex Raichev (raichev@cs.auckland.ac.nz) |
7 | | * Michael Speth (spethm@landcareresearch.co.nz) |
8 | | * Notes: Raichev implemented these projections in Python and |
9 | | * Speth translated them into C here. |
10 | | ****************************************************************************** |
11 | | * Copyright (c) 2001, Thomas Flemming, tf@ttqv.com |
12 | | * |
13 | | * Permission is hereby granted, free of charge, to any person obtaining a |
14 | | * copy of this software and associated documentation files (the "Software"), |
15 | | * to deal in the Software without restriction, including without limitation |
16 | | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
17 | | * and/or sell copies of the Software, and to permit persons to whom the |
18 | | * Software is furnished to do so, subject to the following conditions: |
19 | | * |
20 | | * The above copyright notice and this permission notice shall be included |
21 | | * in all copies or substcounteral portions of the Software. |
22 | | * |
23 | | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
24 | | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
25 | | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
26 | | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS |
27 | | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
28 | | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
29 | | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
30 | | * SOFTWARE. |
31 | | *****************************************************************************/ |
32 | | |
33 | | #include <errno.h> |
34 | | #include <math.h> |
35 | | |
36 | | #include "proj.h" |
37 | | #include "proj_internal.h" |
38 | | |
39 | | PROJ_HEAD(healpix, "HEALPix") "\n\tSph&Ell\n\trot_xy="; |
40 | | PROJ_HEAD(rhealpix, "rHEALPix") "\n\tSph&Ell\n\tnorth_square= south_square="; |
41 | | |
42 | | /* Matrix for counterclockwise rotation by pi/2: */ |
43 | | #define R1 \ |
44 | | { \ |
45 | | {0, -1}, { 1, 0 } \ |
46 | | } |
47 | | /* Matrix for counterclockwise rotation by pi: */ |
48 | | #define R2 \ |
49 | | { \ |
50 | | {-1, 0}, { 0, -1 } \ |
51 | | } |
52 | | /* Matrix for counterclockwise rotation by 3*pi/2: */ |
53 | | #define R3 \ |
54 | | { \ |
55 | | {0, 1}, { -1, 0 } \ |
56 | | } |
57 | | /* Identity matrix */ |
58 | | #define IDENT \ |
59 | | { \ |
60 | | {1, 0}, { 0, 1 } \ |
61 | | } |
62 | | /* IDENT, R1, R2, R3, R1 inverse, R2 inverse, R3 inverse:*/ |
63 | | #define ROT \ |
64 | | { IDENT, R1, R2, R3, R3, R2, R1 } |
65 | | /* Fuzz to handle rounding errors: */ |
66 | 0 | #define EPS 1e-15 |
67 | | |
68 | | namespace { // anonymous namespace |
69 | | struct pj_healpix_data { |
70 | | int north_square; |
71 | | int south_square; |
72 | | double rot_xy; |
73 | | double qp; |
74 | | double *apa; |
75 | | }; |
76 | | } // anonymous namespace |
77 | | |
78 | | typedef struct { |
79 | | int cn; /* An integer 0--3 indicating the position of the polar cap. */ |
80 | | double x, y; /* Coordinates of the pole point (point of most extreme |
81 | | latitude on the polar caps). */ |
82 | | enum Region { north, south, equatorial } region; |
83 | | } CapMap; |
84 | | |
85 | | static const double rot[7][2][2] = ROT; |
86 | | |
87 | | /** |
88 | | * Returns the sign of the double. |
89 | | * @param v the parameter whose sign is returned. |
90 | | * @return 1 for positive number, -1 for negative, and 0 for zero. |
91 | | **/ |
92 | 0 | static double sign(double v) { return v > 0 ? 1 : (v < 0 ? -1 : 0); } |
93 | | |
94 | 0 | static PJ_XY rotate(PJ_XY p, double angle) { |
95 | 0 | PJ_XY result; |
96 | 0 | result.x = p.x * cos(angle) - p.y * sin(angle); |
97 | 0 | result.y = p.y * cos(angle) + p.x * sin(angle); |
98 | 0 | return result; |
99 | 0 | } |
100 | | |
101 | | /** |
102 | | * Return the index of the matrix in ROT. |
103 | | * @param index ranges from -3 to 3. |
104 | | */ |
105 | 0 | static int get_rotate_index(int index) { |
106 | 0 | switch (index) { |
107 | 0 | case 0: |
108 | 0 | return 0; |
109 | 0 | case 1: |
110 | 0 | return 1; |
111 | 0 | case 2: |
112 | 0 | return 2; |
113 | 0 | case 3: |
114 | 0 | return 3; |
115 | 0 | case -1: |
116 | 0 | return 4; |
117 | 0 | case -2: |
118 | 0 | return 5; |
119 | 0 | case -3: |
120 | 0 | return 6; |
121 | 0 | } |
122 | 0 | return 0; |
123 | 0 | } |
124 | | |
125 | | /** |
126 | | * Return 1 if point (testx, testy) lies in the interior of the polygon |
127 | | * determined by the vertices in vert, and return 0 otherwise. |
128 | | * See http://paulbourke.net/geometry/polygonmesh/ for more details. |
129 | | * @param nvert the number of vertices in the polygon. |
130 | | * @param vert the (x, y)-coordinates of the polygon's vertices |
131 | | **/ |
132 | 0 | static int pnpoly(int nvert, double vert[][2], double testx, double testy) { |
133 | 0 | int i; |
134 | 0 | int counter = 0; |
135 | 0 | double xinters; |
136 | 0 | PJ_XY p1, p2; |
137 | | |
138 | | /* Check for boundary cases */ |
139 | 0 | for (i = 0; i < nvert; i++) { |
140 | 0 | if (testx == vert[i][0] && testy == vert[i][1]) { |
141 | 0 | return 1; |
142 | 0 | } |
143 | 0 | } |
144 | | |
145 | 0 | p1.x = vert[0][0]; |
146 | 0 | p1.y = vert[0][1]; |
147 | |
|
148 | 0 | for (i = 1; i < nvert; i++) { |
149 | 0 | p2.x = vert[i % nvert][0]; |
150 | 0 | p2.y = vert[i % nvert][1]; |
151 | 0 | if (testy > MIN(p1.y, p2.y) && testy <= MAX(p1.y, p2.y) && |
152 | 0 | testx <= MAX(p1.x, p2.x) && p1.y != p2.y) { |
153 | 0 | xinters = (testy - p1.y) * (p2.x - p1.x) / (p2.y - p1.y) + p1.x; |
154 | 0 | if (p1.x == p2.x || testx <= xinters) |
155 | 0 | counter++; |
156 | 0 | } |
157 | 0 | p1 = p2; |
158 | 0 | } |
159 | |
|
160 | 0 | if (counter % 2 == 0) { |
161 | 0 | return 0; |
162 | 0 | } else { |
163 | 0 | return 1; |
164 | 0 | } |
165 | 0 | } |
166 | | |
167 | | /** |
168 | | * Return 1 if (x, y) lies in (the interior or boundary of) the image of the |
169 | | * HEALPix projection (in case proj=0) or in the image the rHEALPix projection |
170 | | * (in case proj=1), and return 0 otherwise. |
171 | | * @param north_square the position of the north polar square (rHEALPix only) |
172 | | * @param south_square the position of the south polar square (rHEALPix only) |
173 | | **/ |
174 | | static int in_image(double x, double y, int proj, int north_square, |
175 | 0 | int south_square) { |
176 | 0 | if (proj == 0) { |
177 | 0 | double healpixVertsJit[][2] = {{-M_PI - EPS, M_FORTPI}, |
178 | 0 | {-3 * M_FORTPI, M_HALFPI + EPS}, |
179 | 0 | {-M_HALFPI, M_FORTPI + EPS}, |
180 | 0 | {-M_FORTPI, M_HALFPI + EPS}, |
181 | 0 | {0.0, M_FORTPI + EPS}, |
182 | 0 | {M_FORTPI, M_HALFPI + EPS}, |
183 | 0 | {M_HALFPI, M_FORTPI + EPS}, |
184 | 0 | {3 * M_FORTPI, M_HALFPI + EPS}, |
185 | 0 | {M_PI + EPS, M_FORTPI}, |
186 | 0 | {M_PI + EPS, -M_FORTPI}, |
187 | 0 | {3 * M_FORTPI, -M_HALFPI - EPS}, |
188 | 0 | {M_HALFPI, -M_FORTPI - EPS}, |
189 | 0 | {M_FORTPI, -M_HALFPI - EPS}, |
190 | 0 | {0.0, -M_FORTPI - EPS}, |
191 | 0 | {-M_FORTPI, -M_HALFPI - EPS}, |
192 | 0 | {-M_HALFPI, -M_FORTPI - EPS}, |
193 | 0 | {-3 * M_FORTPI, -M_HALFPI - EPS}, |
194 | 0 | {-M_PI - EPS, -M_FORTPI}, |
195 | 0 | {-M_PI - EPS, M_FORTPI}}; |
196 | 0 | return pnpoly((int)sizeof(healpixVertsJit) / sizeof(healpixVertsJit[0]), |
197 | 0 | healpixVertsJit, x, y); |
198 | 0 | } else { |
199 | | /** |
200 | | * We need to assign the array this way because the input is |
201 | | * dynamic (north_square and south_square vars are unknown at |
202 | | * compile time). |
203 | | **/ |
204 | 0 | double rhealpixVertsJit[][2] = { |
205 | 0 | {-M_PI - EPS, M_FORTPI + EPS}, |
206 | 0 | {-M_PI + north_square * M_HALFPI - EPS, M_FORTPI + EPS}, |
207 | 0 | {-M_PI + north_square * M_HALFPI - EPS, 3 * M_FORTPI + EPS}, |
208 | 0 | {-M_PI + (north_square + 1.0) * M_HALFPI + EPS, 3 * M_FORTPI + EPS}, |
209 | 0 | {-M_PI + (north_square + 1.0) * M_HALFPI + EPS, M_FORTPI + EPS}, |
210 | 0 | {M_PI + EPS, M_FORTPI + EPS}, |
211 | 0 | {M_PI + EPS, -M_FORTPI - EPS}, |
212 | 0 | {-M_PI + (south_square + 1.0) * M_HALFPI + EPS, -M_FORTPI - EPS}, |
213 | 0 | {-M_PI + (south_square + 1.0) * M_HALFPI + EPS, |
214 | 0 | -3 * M_FORTPI - EPS}, |
215 | 0 | {-M_PI + south_square * M_HALFPI - EPS, -3 * M_FORTPI - EPS}, |
216 | 0 | {-M_PI + south_square * M_HALFPI - EPS, -M_FORTPI - EPS}, |
217 | 0 | {-M_PI - EPS, -M_FORTPI - EPS}}; |
218 | |
|
219 | 0 | return pnpoly((int)sizeof(rhealpixVertsJit) / |
220 | 0 | sizeof(rhealpixVertsJit[0]), |
221 | 0 | rhealpixVertsJit, x, y); |
222 | 0 | } |
223 | 0 | } |
224 | | |
225 | | /** |
226 | | * Return the authalic latitude of latitude alpha (if inverse=0) or |
227 | | * return the latitude of authalic latitude alpha (if inverse=1). |
228 | | * P contains the relevant ellipsoid parameters. |
229 | | **/ |
230 | 0 | static double auth_lat(PJ *P, double alpha, int inverse) { |
231 | 0 | const struct pj_healpix_data *Q = |
232 | 0 | static_cast<const struct pj_healpix_data *>(P->opaque); |
233 | 0 | if (inverse == 0) { |
234 | | /* Authalic latitude from geographic latitude. */ |
235 | 0 | return pj_authalic_lat(alpha, sin(alpha), cos(alpha), Q->apa, P, Q->qp); |
236 | 0 | } else { |
237 | | /* Geographic latitude from authalic latitude. */ |
238 | 0 | return pj_authalic_lat_inverse(alpha, Q->apa, P, Q->qp); |
239 | 0 | } |
240 | 0 | } |
241 | | |
242 | | /** |
243 | | * Return the HEALPix projection of the longitude-latitude point lp on |
244 | | * the unit sphere. |
245 | | **/ |
246 | 0 | static PJ_XY healpix_sphere(PJ_LP lp) { |
247 | 0 | double lam = lp.lam; |
248 | 0 | double phi = lp.phi; |
249 | 0 | double phi0 = asin(2.0 / 3.0); |
250 | 0 | PJ_XY xy; |
251 | | |
252 | | /* equatorial region */ |
253 | 0 | if (fabs(phi) <= phi0) { |
254 | 0 | xy.x = lam; |
255 | 0 | xy.y = 3 * M_PI / 8 * sin(phi); |
256 | 0 | } else { |
257 | 0 | double lamc; |
258 | 0 | double sigma = sqrt(3 * (1 - fabs(sin(phi)))); |
259 | 0 | double cn = floor(2 * lam / M_PI + 2); |
260 | 0 | if (cn >= 4) { |
261 | 0 | cn = 3; |
262 | 0 | } |
263 | 0 | lamc = -3 * M_FORTPI + M_HALFPI * cn; |
264 | 0 | xy.x = lamc + (lam - lamc) * sigma; |
265 | 0 | xy.y = sign(phi) * M_FORTPI * (2 - sigma); |
266 | 0 | } |
267 | 0 | return xy; |
268 | 0 | } |
269 | | |
270 | | /** |
271 | | * Return the inverse of healpix_sphere(). |
272 | | **/ |
273 | 0 | static PJ_LP healpix_spherhealpix_e_inverse(PJ_XY xy) { |
274 | 0 | PJ_LP lp; |
275 | 0 | double x = xy.x; |
276 | 0 | double y = xy.y; |
277 | 0 | double y0 = M_FORTPI; |
278 | | |
279 | | /* Equatorial region. */ |
280 | 0 | if (fabs(y) <= y0) { |
281 | 0 | lp.lam = x; |
282 | 0 | lp.phi = asin(8 * y / (3 * M_PI)); |
283 | 0 | } else if (fabs(y) < M_HALFPI) { |
284 | 0 | double cn = floor(2 * x / M_PI + 2); |
285 | 0 | double xc, tau; |
286 | 0 | if (cn >= 4) { |
287 | 0 | cn = 3; |
288 | 0 | } |
289 | 0 | xc = -3 * M_FORTPI + M_HALFPI * cn; |
290 | 0 | tau = 2.0 - 4 * fabs(y) / M_PI; |
291 | 0 | lp.lam = xc + (x - xc) / tau; |
292 | 0 | lp.phi = sign(y) * asin(1.0 - pow(tau, 2) / 3.0); |
293 | 0 | } else { |
294 | 0 | lp.lam = -M_PI; |
295 | 0 | lp.phi = sign(y) * M_HALFPI; |
296 | 0 | } |
297 | 0 | return (lp); |
298 | 0 | } |
299 | | |
300 | | /** |
301 | | * Return the vector sum a + b, where a and b are 2-dimensional vectors. |
302 | | * @param ret holds a + b. |
303 | | **/ |
304 | 0 | static void vector_add(const double a[2], const double b[2], double *ret) { |
305 | 0 | int i; |
306 | 0 | for (i = 0; i < 2; i++) { |
307 | 0 | ret[i] = a[i] + b[i]; |
308 | 0 | } |
309 | 0 | } |
310 | | |
311 | | /** |
312 | | * Return the vector difference a - b, where a and b are 2-dimensional vectors. |
313 | | * @param ret holds a - b. |
314 | | **/ |
315 | 0 | static void vector_sub(const double a[2], const double b[2], double *ret) { |
316 | 0 | int i; |
317 | 0 | for (i = 0; i < 2; i++) { |
318 | 0 | ret[i] = a[i] - b[i]; |
319 | 0 | } |
320 | 0 | } |
321 | | |
322 | | /** |
323 | | * Return the 2 x 1 matrix product a*b, where a is a 2 x 2 matrix and |
324 | | * b is a 2 x 1 matrix. |
325 | | * @param ret holds a*b. |
326 | | **/ |
327 | 0 | static void dot_product(const double a[2][2], const double b[2], double *ret) { |
328 | 0 | int i, j; |
329 | 0 | int length = 2; |
330 | 0 | for (i = 0; i < length; i++) { |
331 | 0 | ret[i] = 0; |
332 | 0 | for (j = 0; j < length; j++) { |
333 | 0 | ret[i] += a[i][j] * b[j]; |
334 | 0 | } |
335 | 0 | } |
336 | 0 | } |
337 | | |
338 | | /** |
339 | | * Return the number of the polar cap, the pole point coordinates, and |
340 | | * the region that (x, y) lies in. |
341 | | * If inverse=0, then assume (x,y) lies in the image of the HEALPix |
342 | | * projection of the unit sphere. |
343 | | * If inverse=1, then assume (x,y) lies in the image of the |
344 | | * (north_square, south_square)-rHEALPix projection of the unit sphere. |
345 | | **/ |
346 | | static CapMap get_cap(double x, double y, int north_square, int south_square, |
347 | 0 | int inverse) { |
348 | 0 | CapMap capmap; |
349 | 0 | double c; |
350 | |
|
351 | 0 | capmap.x = x; |
352 | 0 | capmap.y = y; |
353 | 0 | if (inverse == 0) { |
354 | 0 | if (y > M_FORTPI) { |
355 | 0 | capmap.region = CapMap::north; |
356 | 0 | c = M_HALFPI; |
357 | 0 | } else if (y < -M_FORTPI) { |
358 | 0 | capmap.region = CapMap::south; |
359 | 0 | c = -M_HALFPI; |
360 | 0 | } else { |
361 | 0 | capmap.region = CapMap::equatorial; |
362 | 0 | capmap.cn = 0; |
363 | 0 | return capmap; |
364 | 0 | } |
365 | | /* polar region */ |
366 | 0 | if (x < -M_HALFPI) { |
367 | 0 | capmap.cn = 0; |
368 | 0 | capmap.x = (-3 * M_FORTPI); |
369 | 0 | capmap.y = c; |
370 | 0 | } else if (x >= -M_HALFPI && x < 0) { |
371 | 0 | capmap.cn = 1; |
372 | 0 | capmap.x = -M_FORTPI; |
373 | 0 | capmap.y = c; |
374 | 0 | } else if (x >= 0 && x < M_HALFPI) { |
375 | 0 | capmap.cn = 2; |
376 | 0 | capmap.x = M_FORTPI; |
377 | 0 | capmap.y = c; |
378 | 0 | } else { |
379 | 0 | capmap.cn = 3; |
380 | 0 | capmap.x = 3 * M_FORTPI; |
381 | 0 | capmap.y = c; |
382 | 0 | } |
383 | 0 | } else { |
384 | 0 | if (y > M_FORTPI) { |
385 | 0 | capmap.region = CapMap::north; |
386 | 0 | capmap.x = -3 * M_FORTPI + north_square * M_HALFPI; |
387 | 0 | capmap.y = M_HALFPI; |
388 | 0 | x = x - north_square * M_HALFPI; |
389 | 0 | } else if (y < -M_FORTPI) { |
390 | 0 | capmap.region = CapMap::south; |
391 | 0 | capmap.x = -3 * M_FORTPI + south_square * M_HALFPI; |
392 | 0 | capmap.y = -M_HALFPI; |
393 | 0 | x = x - south_square * M_HALFPI; |
394 | 0 | } else { |
395 | 0 | capmap.region = CapMap::equatorial; |
396 | 0 | capmap.cn = 0; |
397 | 0 | return capmap; |
398 | 0 | } |
399 | | /* Polar Region, find the HEALPix polar cap number that |
400 | | x, y moves to when rHEALPix polar square is disassembled. */ |
401 | 0 | if (capmap.region == CapMap::north) { |
402 | 0 | if (y >= -x - M_FORTPI - EPS && y < x + 5 * M_FORTPI - EPS) { |
403 | 0 | capmap.cn = (north_square + 1) % 4; |
404 | 0 | } else if (y > -x - M_FORTPI + EPS && y >= x + 5 * M_FORTPI - EPS) { |
405 | 0 | capmap.cn = (north_square + 2) % 4; |
406 | 0 | } else if (y <= -x - M_FORTPI + EPS && y > x + 5 * M_FORTPI + EPS) { |
407 | 0 | capmap.cn = (north_square + 3) % 4; |
408 | 0 | } else { |
409 | 0 | capmap.cn = north_square; |
410 | 0 | } |
411 | 0 | } else /* if (capmap.region == CapMap::south) */ { |
412 | 0 | if (y <= x + M_FORTPI + EPS && y > -x - 5 * M_FORTPI + EPS) { |
413 | 0 | capmap.cn = (south_square + 1) % 4; |
414 | 0 | } else if (y < x + M_FORTPI - EPS && y <= -x - 5 * M_FORTPI + EPS) { |
415 | 0 | capmap.cn = (south_square + 2) % 4; |
416 | 0 | } else if (y >= x + M_FORTPI - EPS && y < -x - 5 * M_FORTPI - EPS) { |
417 | 0 | capmap.cn = (south_square + 3) % 4; |
418 | 0 | } else { |
419 | 0 | capmap.cn = south_square; |
420 | 0 | } |
421 | 0 | } |
422 | 0 | } |
423 | 0 | return capmap; |
424 | 0 | } |
425 | | |
426 | | /** |
427 | | * Rearrange point (x, y) in the HEALPix projection by |
428 | | * combining the polar caps into two polar squares. |
429 | | * Put the north polar square in position north_square and |
430 | | * the south polar square in position south_square. |
431 | | * If inverse=1, then uncombine the polar caps. |
432 | | * @param north_square integer between 0 and 3. |
433 | | * @param south_square integer between 0 and 3. |
434 | | **/ |
435 | | static PJ_XY combine_caps(double x, double y, int north_square, |
436 | 0 | int south_square, int inverse) { |
437 | 0 | PJ_XY xy; |
438 | 0 | double vector[2]; |
439 | 0 | double v_min_c[2]; |
440 | 0 | double ret_dot[2]; |
441 | 0 | const double(*tmpRot)[2]; |
442 | 0 | int pole = 0; |
443 | |
|
444 | 0 | CapMap capmap = get_cap(x, y, north_square, south_square, inverse); |
445 | 0 | if (capmap.region == CapMap::equatorial) { |
446 | 0 | xy.x = capmap.x; |
447 | 0 | xy.y = capmap.y; |
448 | 0 | return xy; |
449 | 0 | } |
450 | | |
451 | 0 | double v[] = {x, y}; |
452 | 0 | double c[] = {capmap.x, capmap.y}; |
453 | |
|
454 | 0 | if (inverse == 0) { |
455 | | /* Rotate (x, y) about its polar cap tip and then translate it to |
456 | | north_square or south_square. */ |
457 | |
|
458 | 0 | if (capmap.region == CapMap::north) { |
459 | 0 | pole = north_square; |
460 | 0 | tmpRot = rot[get_rotate_index(capmap.cn - pole)]; |
461 | 0 | } else { |
462 | 0 | pole = south_square; |
463 | 0 | tmpRot = rot[get_rotate_index(-1 * (capmap.cn - pole))]; |
464 | 0 | } |
465 | 0 | } else { |
466 | | /* Inverse function. |
467 | | Unrotate (x, y) and then translate it back. */ |
468 | | |
469 | | /* disassemble */ |
470 | 0 | if (capmap.region == CapMap::north) { |
471 | 0 | pole = north_square; |
472 | 0 | tmpRot = rot[get_rotate_index(-1 * (capmap.cn - pole))]; |
473 | 0 | } else { |
474 | 0 | pole = south_square; |
475 | 0 | tmpRot = rot[get_rotate_index(capmap.cn - pole)]; |
476 | 0 | } |
477 | 0 | } |
478 | |
|
479 | 0 | vector_sub(v, c, v_min_c); |
480 | 0 | dot_product(tmpRot, v_min_c, ret_dot); |
481 | 0 | { |
482 | 0 | double a[] = {-3 * M_FORTPI + |
483 | 0 | ((inverse == 0) ? pole : capmap.cn) * M_HALFPI, |
484 | 0 | ((capmap.region == CapMap::north) ? 1 : -1) * M_HALFPI}; |
485 | 0 | vector_add(ret_dot, a, vector); |
486 | 0 | } |
487 | |
|
488 | 0 | xy.x = vector[0]; |
489 | 0 | xy.y = vector[1]; |
490 | 0 | return xy; |
491 | 0 | } |
492 | | |
493 | 0 | static PJ_XY s_healpix_forward(PJ_LP lp, PJ *P) { /* sphere */ |
494 | 0 | (void)P; |
495 | 0 | struct pj_healpix_data *Q = |
496 | 0 | static_cast<struct pj_healpix_data *>(P->opaque); |
497 | 0 | return rotate(healpix_sphere(lp), -Q->rot_xy); |
498 | 0 | } |
499 | | |
500 | 0 | static PJ_XY e_healpix_forward(PJ_LP lp, PJ *P) { /* ellipsoid */ |
501 | 0 | lp.phi = auth_lat(P, lp.phi, 0); |
502 | 0 | struct pj_healpix_data *Q = |
503 | 0 | static_cast<struct pj_healpix_data *>(P->opaque); |
504 | 0 | return rotate(healpix_sphere(lp), -Q->rot_xy); |
505 | 0 | } |
506 | | |
507 | 0 | static PJ_LP s_healpix_inverse(PJ_XY xy, PJ *P) { /* sphere */ |
508 | 0 | struct pj_healpix_data *Q = |
509 | 0 | static_cast<struct pj_healpix_data *>(P->opaque); |
510 | 0 | xy = rotate(xy, Q->rot_xy); |
511 | | |
512 | | /* Check whether (x, y) lies in the HEALPix image */ |
513 | 0 | if (in_image(xy.x, xy.y, 0, 0, 0) == 0) { |
514 | 0 | PJ_LP lp; |
515 | 0 | lp.lam = HUGE_VAL; |
516 | 0 | lp.phi = HUGE_VAL; |
517 | 0 | proj_context_errno_set( |
518 | 0 | P->ctx, PROJ_ERR_COORD_TRANSFM_OUTSIDE_PROJECTION_DOMAIN); |
519 | 0 | return lp; |
520 | 0 | } |
521 | 0 | return healpix_spherhealpix_e_inverse(xy); |
522 | 0 | } |
523 | | |
524 | 0 | static PJ_LP e_healpix_inverse(PJ_XY xy, PJ *P) { /* ellipsoid */ |
525 | 0 | PJ_LP lp = {0.0, 0.0}; |
526 | 0 | struct pj_healpix_data *Q = |
527 | 0 | static_cast<struct pj_healpix_data *>(P->opaque); |
528 | 0 | xy = rotate(xy, Q->rot_xy); |
529 | | |
530 | | /* Check whether (x, y) lies in the HEALPix image. */ |
531 | 0 | if (in_image(xy.x, xy.y, 0, 0, 0) == 0) { |
532 | 0 | lp.lam = HUGE_VAL; |
533 | 0 | lp.phi = HUGE_VAL; |
534 | 0 | proj_context_errno_set( |
535 | 0 | P->ctx, PROJ_ERR_COORD_TRANSFM_OUTSIDE_PROJECTION_DOMAIN); |
536 | 0 | return lp; |
537 | 0 | } |
538 | 0 | lp = healpix_spherhealpix_e_inverse(xy); |
539 | 0 | lp.phi = auth_lat(P, lp.phi, 1); |
540 | 0 | return lp; |
541 | 0 | } |
542 | | |
543 | 0 | static PJ_XY s_rhealpix_forward(PJ_LP lp, PJ *P) { /* sphere */ |
544 | 0 | struct pj_healpix_data *Q = |
545 | 0 | static_cast<struct pj_healpix_data *>(P->opaque); |
546 | |
|
547 | 0 | PJ_XY xy = healpix_sphere(lp); |
548 | 0 | return combine_caps(xy.x, xy.y, Q->north_square, Q->south_square, 0); |
549 | 0 | } |
550 | | |
551 | 0 | static PJ_XY e_rhealpix_forward(PJ_LP lp, PJ *P) { /* ellipsoid */ |
552 | 0 | struct pj_healpix_data *Q = |
553 | 0 | static_cast<struct pj_healpix_data *>(P->opaque); |
554 | 0 | PJ_XY xy; |
555 | 0 | lp.phi = auth_lat(P, lp.phi, 0); |
556 | 0 | xy = healpix_sphere(lp); |
557 | 0 | return combine_caps(xy.x, xy.y, Q->north_square, Q->south_square, 0); |
558 | 0 | } |
559 | | |
560 | 0 | static PJ_LP s_rhealpix_inverse(PJ_XY xy, PJ *P) { /* sphere */ |
561 | 0 | struct pj_healpix_data *Q = |
562 | 0 | static_cast<struct pj_healpix_data *>(P->opaque); |
563 | | |
564 | | /* Check whether (x, y) lies in the rHEALPix image. */ |
565 | 0 | if (in_image(xy.x, xy.y, 1, Q->north_square, Q->south_square) == 0) { |
566 | 0 | PJ_LP lp; |
567 | 0 | lp.lam = HUGE_VAL; |
568 | 0 | lp.phi = HUGE_VAL; |
569 | 0 | proj_context_errno_set( |
570 | 0 | P->ctx, PROJ_ERR_COORD_TRANSFM_OUTSIDE_PROJECTION_DOMAIN); |
571 | 0 | return lp; |
572 | 0 | } |
573 | 0 | xy = combine_caps(xy.x, xy.y, Q->north_square, Q->south_square, 1); |
574 | 0 | return healpix_spherhealpix_e_inverse(xy); |
575 | 0 | } |
576 | | |
577 | 0 | static PJ_LP e_rhealpix_inverse(PJ_XY xy, PJ *P) { /* ellipsoid */ |
578 | 0 | struct pj_healpix_data *Q = |
579 | 0 | static_cast<struct pj_healpix_data *>(P->opaque); |
580 | 0 | PJ_LP lp = {0.0, 0.0}; |
581 | | |
582 | | /* Check whether (x, y) lies in the rHEALPix image. */ |
583 | 0 | if (in_image(xy.x, xy.y, 1, Q->north_square, Q->south_square) == 0) { |
584 | 0 | lp.lam = HUGE_VAL; |
585 | 0 | lp.phi = HUGE_VAL; |
586 | 0 | proj_context_errno_set( |
587 | 0 | P->ctx, PROJ_ERR_COORD_TRANSFM_OUTSIDE_PROJECTION_DOMAIN); |
588 | 0 | return lp; |
589 | 0 | } |
590 | 0 | xy = combine_caps(xy.x, xy.y, Q->north_square, Q->south_square, 1); |
591 | 0 | lp = healpix_spherhealpix_e_inverse(xy); |
592 | 0 | lp.phi = auth_lat(P, lp.phi, 1); |
593 | 0 | return lp; |
594 | 0 | } |
595 | | |
596 | 0 | static PJ *pj_healpix_data_destructor(PJ *P, int errlev) { /* Destructor */ |
597 | 0 | if (nullptr == P) |
598 | 0 | return nullptr; |
599 | | |
600 | 0 | if (nullptr == P->opaque) |
601 | 0 | return pj_default_destructor(P, errlev); |
602 | | |
603 | 0 | free(static_cast<struct pj_healpix_data *>(P->opaque)->apa); |
604 | 0 | return pj_default_destructor(P, errlev); |
605 | 0 | } |
606 | | |
607 | 0 | PJ *PJ_PROJECTION(healpix) { |
608 | 0 | struct pj_healpix_data *Q = static_cast<struct pj_healpix_data *>( |
609 | 0 | calloc(1, sizeof(struct pj_healpix_data))); |
610 | 0 | if (nullptr == Q) |
611 | 0 | return pj_default_destructor(P, PROJ_ERR_OTHER /*ENOMEM*/); |
612 | 0 | P->opaque = Q; |
613 | 0 | P->destructor = pj_healpix_data_destructor; |
614 | |
|
615 | 0 | double angle = pj_param(P->ctx, P->params, "drot_xy").f; |
616 | 0 | Q->rot_xy = PJ_TORAD(angle); |
617 | |
|
618 | 0 | if (P->es != 0.0) { |
619 | 0 | Q->apa = pj_authalic_lat_compute_coeffs(P->n); /* For auth_lat(). */ |
620 | 0 | if (nullptr == Q->apa) |
621 | 0 | return pj_healpix_data_destructor(P, PROJ_ERR_OTHER /*ENOMEM*/); |
622 | 0 | Q->qp = |
623 | 0 | pj_authalic_lat_q(1.0, P); /* For auth_lat(). */ |
624 | 0 | P->a = P->a * sqrt(0.5 * Q->qp); /* Set P->a to authalic radius. */ |
625 | 0 | pj_calc_ellipsoid_params( |
626 | 0 | P, P->a, P->es); /* Ensure we have a consistent parameter set */ |
627 | 0 | P->fwd = e_healpix_forward; |
628 | 0 | P->inv = e_healpix_inverse; |
629 | 0 | } else { |
630 | 0 | P->fwd = s_healpix_forward; |
631 | 0 | P->inv = s_healpix_inverse; |
632 | 0 | } |
633 | | |
634 | 0 | return P; |
635 | 0 | } |
636 | | |
637 | 0 | PJ *PJ_PROJECTION(rhealpix) { |
638 | 0 | struct pj_healpix_data *Q = static_cast<struct pj_healpix_data *>( |
639 | 0 | calloc(1, sizeof(struct pj_healpix_data))); |
640 | 0 | if (nullptr == Q) |
641 | 0 | return pj_default_destructor(P, PROJ_ERR_OTHER /*ENOMEM*/); |
642 | 0 | P->opaque = Q; |
643 | 0 | P->destructor = pj_healpix_data_destructor; |
644 | |
|
645 | 0 | Q->north_square = pj_param(P->ctx, P->params, "inorth_square").i; |
646 | 0 | Q->south_square = pj_param(P->ctx, P->params, "isouth_square").i; |
647 | | |
648 | | /* Check for valid north_square and south_square inputs. */ |
649 | 0 | if (Q->north_square < 0 || Q->north_square > 3) { |
650 | 0 | proj_log_error( |
651 | 0 | P, |
652 | 0 | _("Invalid value for north_square: it should be in [0,3] range.")); |
653 | 0 | return pj_healpix_data_destructor( |
654 | 0 | P, PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE); |
655 | 0 | } |
656 | 0 | if (Q->south_square < 0 || Q->south_square > 3) { |
657 | 0 | proj_log_error( |
658 | 0 | P, |
659 | 0 | _("Invalid value for south_square: it should be in [0,3] range.")); |
660 | 0 | return pj_healpix_data_destructor( |
661 | 0 | P, PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE); |
662 | 0 | } |
663 | 0 | if (P->es != 0.0) { |
664 | 0 | Q->apa = pj_authalic_lat_compute_coeffs(P->n); /* For auth_lat(). */ |
665 | 0 | if (nullptr == Q->apa) |
666 | 0 | return pj_healpix_data_destructor(P, PROJ_ERR_OTHER /*ENOMEM*/); |
667 | 0 | Q->qp = |
668 | 0 | pj_authalic_lat_q(1.0, P); /* For auth_lat(). */ |
669 | 0 | P->a = P->a * sqrt(0.5 * Q->qp); /* Set P->a to authalic radius. */ |
670 | 0 | P->ra = 1.0 / P->a; |
671 | 0 | P->fwd = e_rhealpix_forward; |
672 | 0 | P->inv = e_rhealpix_inverse; |
673 | 0 | } else { |
674 | 0 | P->fwd = s_rhealpix_forward; |
675 | 0 | P->inv = s_rhealpix_inverse; |
676 | 0 | } |
677 | | |
678 | 0 | return P; |
679 | 0 | } |
680 | | |
681 | | #undef R1 |
682 | | #undef R2 |
683 | | #undef R3 |
684 | | #undef IDENT |
685 | | #undef ROT |
686 | | #undef EPS |