/src/gdal/proj/src/projections/adams.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Implementation of the Guyou, Pierce Quincuncial, Adams Hemisphere in a |
3 | | * Square, Adams World in a Square I & II projections. |
4 | | * |
5 | | * Based on original code from libproj4 written by Gerald Evenden. Adapted to |
6 | | * modern PROJ by Kristian Evers. Original code found in file src/proj_guyou.c, |
7 | | * see |
8 | | * https://github.com/rouault/libproj4/blob/master/libproject-1.01/src/proj_guyou.c |
9 | | * for reference. |
10 | | * Fix for Peirce Quincuncial projection to diamond or square by Toby C. |
11 | | * Wilkinson to correctly flip out southern hemisphere into the four triangles |
12 | | * of Peirce's quincunx. The fix inspired by a similar rotate and translate |
13 | | * solution applied by Jonathan Feinberg for cartopy, see |
14 | | * https://github.com/jonathf/cartopy/blob/8172cac7fc45cafc86573d408ce85b74258a9c28/lib/cartopy/peircequincuncial.py |
15 | | * Added original code for horizontal and vertical arrangement of hemispheres by |
16 | | * Toby C. Wilkinson to allow creation of lateral quincuncial projections, such |
17 | | * as Grieger's Triptychial, see, e.g.: |
18 | | * - Grieger, B. (2020). “Optimized global map projections for specific |
19 | | * applications: the triptychial projection and the Spilhaus projection”. |
20 | | * EGU2020-9885. https://doi.org/10.5194/egusphere-egu2020-9885 |
21 | | * |
22 | | * Copyright (c) 2005, 2006, 2009 Gerald I. Evenden |
23 | | * Copyright (c) 2020 Kristian Evers |
24 | | * Copyright (c) 2021 Toby C Wilkinson |
25 | | * |
26 | | * Related material |
27 | | * ---------------- |
28 | | * |
29 | | * CONFORMAL PROJECTION OF THE SPHERE WITHIN A SQUARE, 1929, OSCAR S. ADAMS, |
30 | | * U.S. COAST AND GEODETIC SURVEY, Special Publication No.153, |
31 | | * ftp://ftp.library.noaa.gov/docs.lib/htdocs/rescue/cgs_specpubs/QB275U35no1531929.pdf |
32 | | * |
33 | | * https://en.wikipedia.org/wiki/Guyou_hemisphere-in-a-square_projection |
34 | | * https://en.wikipedia.org/wiki/Adams_hemisphere-in-a-square_projection |
35 | | * https://en.wikipedia.org/wiki/Peirce_quincuncial_projection |
36 | | */ |
37 | | |
38 | | #include <errno.h> |
39 | | #include <math.h> |
40 | | |
41 | | #include <algorithm> |
42 | | |
43 | | #include "proj.h" |
44 | | #include "proj_internal.h" |
45 | | |
46 | | PROJ_HEAD(guyou, "Guyou") "\n\tMisc Sph No inv"; |
47 | | PROJ_HEAD(peirce_q, "Peirce Quincuncial") "\n\tMisc Sph No inv"; |
48 | | PROJ_HEAD(adams_hemi, "Adams Hemisphere in a Square") "\n\tMisc Sph No inv"; |
49 | | PROJ_HEAD(adams_ws1, "Adams World in a Square I") "\n\tMisc Sph No inv"; |
50 | | PROJ_HEAD(adams_ws2, "Adams World in a Square II") "\n\tMisc Sph No inv"; |
51 | | |
52 | | namespace { // anonymous namespace |
53 | | |
54 | | enum projection_type { |
55 | | GUYOU, |
56 | | PEIRCE_Q, |
57 | | ADAMS_HEMI, |
58 | | ADAMS_WS1, |
59 | | ADAMS_WS2, |
60 | | }; |
61 | | |
62 | | enum peirce_shape { |
63 | | PEIRCE_Q_SQUARE, |
64 | | PEIRCE_Q_DIAMOND, |
65 | | PEIRCE_Q_NHEMISPHERE, |
66 | | PEIRCE_Q_SHEMISPHERE, |
67 | | PEIRCE_Q_HORIZONTAL, |
68 | | PEIRCE_Q_VERTICAL, |
69 | | }; |
70 | | |
71 | | struct pj_adams_data { |
72 | | projection_type mode; |
73 | | peirce_shape pqshape; |
74 | | double scrollx = 0.0; |
75 | | double scrolly = 0.0; |
76 | | }; |
77 | | |
78 | | } // anonymous namespace |
79 | | |
80 | 0 | #define TOL 1e-9 |
81 | 0 | #define RSQRT2 0.7071067811865475244008443620 |
82 | | |
83 | 0 | static double ell_int_5(double phi) { |
84 | | /* Procedure to compute elliptic integral of the first kind |
85 | | * where k^2=0.5. Precision good to better than 1e-7 |
86 | | * The approximation is performed with an even Chebyshev |
87 | | * series, thus the coefficients below are the even values |
88 | | * and where series evaluation must be multiplied by the argument. */ |
89 | 0 | constexpr double C0 = 2.19174570831038; |
90 | 0 | static const double C[] = { |
91 | 0 | -8.58691003636495e-07, 2.02692115653689e-07, 3.12960480765314e-05, |
92 | 0 | 5.30394739921063e-05, -0.0012804644680613, -0.00575574836830288, |
93 | 0 | 0.0914203033408211, |
94 | 0 | }; |
95 | |
|
96 | 0 | double y = phi * M_2_PI; |
97 | 0 | y = 2. * y * y - 1.; |
98 | 0 | double y2 = 2. * y; |
99 | 0 | double d1 = 0.0; |
100 | 0 | double d2 = 0.0; |
101 | 0 | for (double c : C) { |
102 | 0 | double temp = d1; |
103 | 0 | d1 = y2 * d1 - d2 + c; |
104 | 0 | d2 = temp; |
105 | 0 | } |
106 | |
|
107 | 0 | return phi * (y * d1 - d2 + 0.5 * C0); |
108 | 0 | } |
109 | | |
110 | 0 | static PJ_XY adams_forward(PJ_LP lp, PJ *P) { |
111 | 0 | double a = 0., b = 0.; |
112 | 0 | bool sm = false, sn = false; |
113 | 0 | PJ_XY xy; |
114 | 0 | const struct pj_adams_data *Q = |
115 | 0 | static_cast<const struct pj_adams_data *>(P->opaque); |
116 | |
|
117 | 0 | switch (Q->mode) { |
118 | 0 | case GUYOU: |
119 | 0 | if ((fabs(lp.lam) - TOL) > M_PI_2) { |
120 | 0 | proj_errno_set(P, PROJ_ERR_COORD_TRANSFM_OUTSIDE_PROJECTION_DOMAIN); |
121 | 0 | return proj_coord_error().xy; |
122 | 0 | } |
123 | | |
124 | 0 | if (fabs(fabs(lp.phi) - M_PI_2) < TOL) { |
125 | 0 | xy.x = 0; |
126 | 0 | xy.y = lp.phi < 0 ? -1.85407 : 1.85407; |
127 | 0 | return xy; |
128 | 0 | } else { |
129 | 0 | const double sl = sin(lp.lam); |
130 | 0 | const double sp = sin(lp.phi); |
131 | 0 | const double cp = cos(lp.phi); |
132 | 0 | a = aacos(P->ctx, (cp * sl - sp) * RSQRT2); |
133 | 0 | b = aacos(P->ctx, (cp * sl + sp) * RSQRT2); |
134 | 0 | sm = lp.lam < 0.; |
135 | 0 | sn = lp.phi < 0.; |
136 | 0 | } |
137 | 0 | break; |
138 | 0 | case PEIRCE_Q: { |
139 | | /* lam0 - note that the original Peirce model used a central meridian of |
140 | | * around -70deg, but the default within proj is +lon0=0 */ |
141 | 0 | if (Q->pqshape == PEIRCE_Q_NHEMISPHERE) { |
142 | 0 | if (lp.phi < -TOL) { |
143 | 0 | proj_errno_set( |
144 | 0 | P, PROJ_ERR_COORD_TRANSFM_OUTSIDE_PROJECTION_DOMAIN); |
145 | 0 | return proj_coord_error().xy; |
146 | 0 | } |
147 | 0 | } |
148 | 0 | if (Q->pqshape == PEIRCE_Q_SHEMISPHERE) { |
149 | 0 | if (lp.phi > -TOL) { |
150 | 0 | proj_errno_set( |
151 | 0 | P, PROJ_ERR_COORD_TRANSFM_OUTSIDE_PROJECTION_DOMAIN); |
152 | 0 | return proj_coord_error().xy; |
153 | 0 | } |
154 | 0 | } |
155 | 0 | const double sl = sin(lp.lam); |
156 | 0 | const double cl = cos(lp.lam); |
157 | 0 | const double cp = cos(lp.phi); |
158 | 0 | a = aacos(P->ctx, cp * (sl + cl) * RSQRT2); |
159 | 0 | b = aacos(P->ctx, cp * (sl - cl) * RSQRT2); |
160 | 0 | sm = sl < 0.; |
161 | 0 | sn = cl > 0.; |
162 | 0 | } break; |
163 | 0 | case ADAMS_HEMI: { |
164 | 0 | const double sp = sin(lp.phi); |
165 | 0 | if ((fabs(lp.lam) - TOL) > M_PI_2) { |
166 | 0 | proj_errno_set(P, PROJ_ERR_COORD_TRANSFM_OUTSIDE_PROJECTION_DOMAIN); |
167 | 0 | return proj_coord_error().xy; |
168 | 0 | } |
169 | 0 | a = cos(lp.phi) * sin(lp.lam); |
170 | 0 | sm = (sp + a) < 0.; |
171 | 0 | sn = (sp - a) < 0.; |
172 | 0 | a = aacos(P->ctx, a); |
173 | 0 | b = M_PI_2 - lp.phi; |
174 | 0 | } break; |
175 | 0 | case ADAMS_WS1: { |
176 | 0 | const double sp = tan(0.5 * lp.phi); |
177 | 0 | b = cos(aasin(P->ctx, sp)) * sin(0.5 * lp.lam); |
178 | 0 | a = aacos(P->ctx, (b - sp) * RSQRT2); |
179 | 0 | b = aacos(P->ctx, (b + sp) * RSQRT2); |
180 | 0 | sm = lp.lam < 0.; |
181 | 0 | sn = lp.phi < 0.; |
182 | 0 | } break; |
183 | 0 | case ADAMS_WS2: { |
184 | 0 | const double spp = tan(0.5 * lp.phi); |
185 | 0 | a = cos(aasin(P->ctx, spp)) * sin(0.5 * lp.lam); |
186 | 0 | sm = (spp + a) < 0.; |
187 | 0 | sn = (spp - a) < 0.; |
188 | 0 | b = aacos(P->ctx, spp); |
189 | 0 | a = aacos(P->ctx, a); |
190 | 0 | } break; |
191 | 0 | } |
192 | | |
193 | 0 | double m = aasin(P->ctx, sqrt((1. + std::min(0.0, cos(a + b))))); |
194 | 0 | if (sm) |
195 | 0 | m = -m; |
196 | |
|
197 | 0 | double n = aasin(P->ctx, sqrt(fabs(1. - std::max(0.0, cos(a - b))))); |
198 | 0 | if (sn) |
199 | 0 | n = -n; |
200 | |
|
201 | 0 | xy.x = ell_int_5(m); |
202 | 0 | xy.y = ell_int_5(n); |
203 | |
|
204 | 0 | if (Q->mode == PEIRCE_Q) { |
205 | | /* Constant complete elliptic integral of the first kind with m=0.5, |
206 | | * calculated using |
207 | | * https://docs.scipy.org/doc/scipy/reference/generated/scipy.special.ellipk.html |
208 | | * . Used as basic as scaled shift distance */ |
209 | 0 | constexpr double shd = 1.8540746773013719 * 2; |
210 | | |
211 | | /* For square and diamond Quincuncial projections, spin out southern |
212 | | * hemisphere to triangular segments of quincunx (before rotation for |
213 | | * square)*/ |
214 | 0 | if (Q->pqshape == PEIRCE_Q_SQUARE || (Q->pqshape == PEIRCE_Q_DIAMOND)) { |
215 | 0 | if (lp.phi < 0.) { /* fold out segments */ |
216 | 0 | if (lp.lam < (-0.75 * M_PI)) |
217 | 0 | xy.y = shd - |
218 | 0 | xy.y; /* top left segment, shift up and reflect y */ |
219 | 0 | if ((lp.lam < (-0.25 * M_PI)) && (lp.lam >= (-0.75 * M_PI))) |
220 | 0 | xy.x = -shd - |
221 | 0 | xy.x; /* left segment, shift left and reflect x */ |
222 | 0 | if ((lp.lam < (0.25 * M_PI)) && (lp.lam >= (-0.25 * M_PI))) |
223 | 0 | xy.y = -shd - |
224 | 0 | xy.y; /* bottom segment, shift down and reflect y */ |
225 | 0 | if ((lp.lam < (0.75 * M_PI)) && (lp.lam >= (0.25 * M_PI))) |
226 | 0 | xy.x = shd - |
227 | 0 | xy.x; /* right segment, shift right and reflect x */ |
228 | 0 | if (lp.lam >= (0.75 * M_PI)) |
229 | 0 | xy.y = shd - |
230 | 0 | xy.y; /* top right segment, shift up and reflect y */ |
231 | 0 | } |
232 | 0 | } |
233 | | |
234 | | /* For square types rotate xy by 45 deg */ |
235 | 0 | if (Q->pqshape == PEIRCE_Q_SQUARE) { |
236 | 0 | const double temp = xy.x; |
237 | 0 | xy.x = RSQRT2 * (xy.x - xy.y); |
238 | 0 | xy.y = RSQRT2 * (temp + xy.y); |
239 | 0 | } |
240 | | |
241 | | /* For rectangle Quincuncial projs, spin out southern hemisphere to east |
242 | | * (horizontal) or north (vertical) after rotation */ |
243 | 0 | if (Q->pqshape == PEIRCE_Q_HORIZONTAL) { |
244 | 0 | if (lp.phi < 0.) { |
245 | 0 | xy.x = shd - xy.x; /* reflect x to east */ |
246 | 0 | } |
247 | 0 | xy.x = xy.x - (shd / 2); /* shift everything so origin is in middle |
248 | | of two hemispheres */ |
249 | 0 | } |
250 | 0 | if (Q->pqshape == PEIRCE_Q_VERTICAL) { |
251 | 0 | if (lp.phi < 0.) { |
252 | 0 | xy.y = shd - xy.y; /* reflect y to north */ |
253 | 0 | } |
254 | 0 | xy.y = xy.y - (shd / 2); /* shift everything so origin is in middle |
255 | | of two hemispheres */ |
256 | 0 | } |
257 | | |
258 | | // if o_scrollx param present, scroll x |
259 | 0 | if (!(Q->scrollx == 0.0) && (Q->pqshape == PEIRCE_Q_HORIZONTAL)) { |
260 | 0 | double xscale = 2.0; |
261 | 0 | double xthresh = shd / 2; |
262 | 0 | xy.x = xy.x + |
263 | 0 | (Q->scrollx * |
264 | 0 | (xthresh * 2 * xscale)); /*shift relative to proj width*/ |
265 | 0 | if (xy.x >= (xthresh * xscale)) { |
266 | 0 | xy.x = xy.x - (shd * xscale); |
267 | 0 | } else if (xy.x < -(xthresh * xscale)) { |
268 | 0 | xy.x = xy.x + (shd * xscale); |
269 | 0 | } |
270 | 0 | } |
271 | | |
272 | | // if o_scrolly param present, scroll y |
273 | 0 | if (!(Q->scrolly == 0.0) && (Q->pqshape == PEIRCE_Q_VERTICAL)) { |
274 | 0 | double yscale = 2.0; |
275 | 0 | double ythresh = shd / 2; |
276 | 0 | xy.y = xy.y + |
277 | 0 | (Q->scrolly * |
278 | 0 | (ythresh * 2 * yscale)); /*shift relative to proj height*/ |
279 | 0 | if (xy.y >= (ythresh * yscale)) { |
280 | 0 | xy.y = xy.y - (shd * yscale); |
281 | 0 | } else if (xy.y < -(ythresh * yscale)) { |
282 | 0 | xy.y = xy.y + (shd * yscale); |
283 | 0 | } |
284 | 0 | } |
285 | 0 | } |
286 | |
|
287 | 0 | if (Q->mode == ADAMS_HEMI || Q->mode == ADAMS_WS2) { /* rotate by 45deg. */ |
288 | 0 | const double temp = xy.x; |
289 | 0 | xy.x = RSQRT2 * (xy.x - xy.y); |
290 | 0 | xy.y = RSQRT2 * (temp + xy.y); |
291 | 0 | } |
292 | |
|
293 | 0 | return xy; |
294 | 0 | } |
295 | | |
296 | 0 | static PJ_LP adams_inverse(PJ_XY xy, PJ *P) { |
297 | 0 | PJ_LP lp; |
298 | | |
299 | | // Only implemented for ADAMS_WS2 |
300 | | // Uses Newton-Raphson method on the following pair of functions: |
301 | | // f_x(lam,phi) = adams_forward(lam, phi).x - xy.x |
302 | | // f_y(lam,phi) = adams_forward(lam, phi).y - xy.y |
303 | | |
304 | | // Initial guess (very rough, especially at high northings) |
305 | | // The magic values are got with: |
306 | | // echo 0 90 | src/proj -f "%.8f" +proj=adams_ws2 +R=1 |
307 | | // echo 180 0 | src/proj -f "%.8f" +proj=adams_ws2 +R=1 |
308 | 0 | lp.phi = std::max(std::min(xy.y / 2.62181347, 1.0), -1.0) * M_HALFPI; |
309 | 0 | lp.lam = |
310 | 0 | fabs(lp.phi) >= M_HALFPI |
311 | 0 | ? 0 |
312 | 0 | : std::max(std::min(xy.x / 2.62205760 / cos(lp.phi), 1.0), -1.0) * |
313 | 0 | M_PI; |
314 | |
|
315 | 0 | constexpr double deltaXYTolerance = 1e-10; |
316 | 0 | return pj_generic_inverse_2d(xy, P, lp, deltaXYTolerance); |
317 | 0 | } |
318 | | |
319 | 0 | static PJ_LP peirce_q_square_inverse(PJ_XY xy, PJ *P) { |
320 | | /* Heuristics based on trial and repeat process */ |
321 | 0 | PJ_LP lp; |
322 | 0 | lp.phi = 0; |
323 | 0 | if (xy.x == 0 && xy.y < 0) { |
324 | 0 | lp.lam = -M_PI / 4; |
325 | 0 | if (fabs(xy.y) < 2.622057580396) |
326 | 0 | lp.phi = M_PI / 4; |
327 | 0 | } else if (xy.x > 0 && fabs(xy.y) < 1e-7) |
328 | 0 | lp.lam = M_PI / 4; |
329 | 0 | else if (xy.x < 0 && fabs(xy.y) < 1e-7) { |
330 | 0 | lp.lam = -3 * M_PI / 4; |
331 | 0 | lp.phi = M_PI / 2 / 2.622057574224 * xy.x + M_PI / 2; |
332 | 0 | } else if (fabs(xy.x) < 1e-7 && xy.y > 0) |
333 | 0 | lp.lam = 3 * M_PI / 4; |
334 | 0 | else if (xy.x >= 0 && xy.y <= 0) { |
335 | 0 | lp.lam = 0; |
336 | 0 | if (xy.x == 0 && xy.y == 0) { |
337 | 0 | lp.phi = M_PI / 2; |
338 | 0 | return lp; |
339 | 0 | } |
340 | 0 | } else if (xy.x >= 0 && xy.y >= 0) |
341 | 0 | lp.lam = M_PI / 2; |
342 | 0 | else if (xy.x <= 0 && xy.y >= 0) { |
343 | 0 | if (fabs(xy.x) < fabs(xy.y)) |
344 | 0 | lp.lam = M_PI * 0.9; |
345 | 0 | else |
346 | 0 | lp.lam = -M_PI * 0.9; |
347 | 0 | } else /* if( xy.x <= 0 && xy.y <= 0 ) */ |
348 | 0 | lp.lam = -M_PI / 2; |
349 | | |
350 | 0 | constexpr double deltaXYTolerance = 1e-10; |
351 | 0 | return pj_generic_inverse_2d(xy, P, lp, deltaXYTolerance); |
352 | 0 | } |
353 | | |
354 | 0 | static PJ_LP peirce_q_diamond_inverse(PJ_XY xy, PJ *P) { |
355 | | /* Heuristics based on a trial and repeat process */ |
356 | 0 | PJ_LP lp; |
357 | 0 | lp.phi = 0; |
358 | 0 | if (xy.x >= 0 && xy.y <= 0) { |
359 | 0 | lp.lam = M_PI / 4; |
360 | 0 | if (xy.x > 0 && xy.y == 0) { |
361 | 0 | lp.lam = M_PI / 2; |
362 | 0 | lp.phi = 0; |
363 | 0 | } else if (xy.x == 0 && xy.y == 0) { |
364 | 0 | lp.lam = 0; |
365 | 0 | lp.phi = M_PI / 2; |
366 | 0 | return lp; |
367 | 0 | } else if (xy.x == 0 && xy.y < 0) { |
368 | 0 | lp.lam = 0; |
369 | 0 | lp.phi = M_PI / 4; |
370 | 0 | } |
371 | 0 | } else if (xy.x >= 0 && xy.y >= 0) |
372 | 0 | lp.lam = 3 * M_PI / 4; |
373 | 0 | else if (xy.x <= 0 && xy.y >= 0) { |
374 | 0 | lp.lam = -3 * M_PI / 4; |
375 | 0 | } else /* if( xy.x <= 0 && xy.y <= 0 ) */ |
376 | 0 | lp.lam = -M_PI / 4; |
377 | | |
378 | 0 | if (fabs(xy.x) > 1.8540746773013719 + 1e-3 || |
379 | 0 | fabs(xy.y) > 1.8540746773013719 + 1e-3) { |
380 | 0 | lp.phi = -M_PI / 4; |
381 | 0 | } |
382 | |
|
383 | 0 | constexpr double deltaXYTolerance = 1e-10; |
384 | 0 | return pj_generic_inverse_2d(xy, P, lp, deltaXYTolerance); |
385 | 0 | } |
386 | | |
387 | 15.4k | static PJ *pj_adams_setup(PJ *P, projection_type mode) { |
388 | 15.4k | struct pj_adams_data *Q = static_cast<struct pj_adams_data *>( |
389 | 15.4k | calloc(1, sizeof(struct pj_adams_data))); |
390 | | |
391 | 15.4k | if (nullptr == Q) |
392 | 0 | return pj_default_destructor(P, PROJ_ERR_OTHER /*ENOMEM*/); |
393 | 15.4k | P->opaque = Q; |
394 | | |
395 | 15.4k | P->es = 0; |
396 | 15.4k | P->fwd = adams_forward; |
397 | | |
398 | 15.4k | Q->mode = mode; |
399 | 15.4k | if (mode == ADAMS_WS2) |
400 | 3.20k | P->inv = adams_inverse; |
401 | | |
402 | 15.4k | if (mode == PEIRCE_Q) { |
403 | | // Quincuncial projections shape options: square, diamond, hemisphere, |
404 | | // horizontal (rectangle) or vertical (rectangle) |
405 | 5.62k | const char *pqshape = pj_param(P->ctx, P->params, "sshape").s; |
406 | | |
407 | 5.62k | if (!pqshape) |
408 | 5.23k | pqshape = "diamond"; /* default if shape value not supplied */ |
409 | | |
410 | 5.62k | if (strcmp(pqshape, "square") == 0) { |
411 | 89 | Q->pqshape = PEIRCE_Q_SQUARE; |
412 | 89 | P->inv = peirce_q_square_inverse; |
413 | 5.53k | } else if (strcmp(pqshape, "diamond") == 0) { |
414 | 5.35k | Q->pqshape = PEIRCE_Q_DIAMOND; |
415 | 5.35k | P->inv = peirce_q_diamond_inverse; |
416 | 5.35k | } else if (strcmp(pqshape, "nhemisphere") == 0) { |
417 | 0 | Q->pqshape = PEIRCE_Q_NHEMISPHERE; |
418 | 181 | } else if (strcmp(pqshape, "shemisphere") == 0) { |
419 | 0 | Q->pqshape = PEIRCE_Q_SHEMISPHERE; |
420 | 181 | } else if (strcmp(pqshape, "horizontal") == 0) { |
421 | 10 | Q->pqshape = PEIRCE_Q_HORIZONTAL; |
422 | 10 | if (pj_param(P->ctx, P->params, "tscrollx").i) { |
423 | 0 | double scrollx; |
424 | 0 | scrollx = pj_param(P->ctx, P->params, "dscrollx").f; |
425 | 0 | if (scrollx > 1 || scrollx < -1) { |
426 | 0 | proj_log_error( |
427 | 0 | P, _("Invalid value for scrollx: |scrollx| should " |
428 | 0 | "between -1 and 1")); |
429 | 0 | return pj_default_destructor( |
430 | 0 | P, PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE); |
431 | 0 | } |
432 | 0 | Q->scrollx = scrollx; |
433 | 0 | } |
434 | 171 | } else if (strcmp(pqshape, "vertical") == 0) { |
435 | 28 | Q->pqshape = PEIRCE_Q_VERTICAL; |
436 | 28 | if (pj_param(P->ctx, P->params, "tscrolly").i) { |
437 | 0 | double scrolly; |
438 | 0 | scrolly = pj_param(P->ctx, P->params, "dscrolly").f; |
439 | 0 | if (scrolly > 1 || scrolly < -1) { |
440 | 0 | proj_log_error( |
441 | 0 | P, _("Invalid value for scrolly: |scrolly| should " |
442 | 0 | "between -1 and 1")); |
443 | 0 | return pj_default_destructor( |
444 | 0 | P, PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE); |
445 | 0 | } |
446 | 0 | Q->scrolly = scrolly; |
447 | 0 | } |
448 | 143 | } else { |
449 | 143 | proj_log_error(P, |
450 | 143 | _("peirce_q: invalid value for 'shape' parameter")); |
451 | 143 | return pj_default_destructor(P, |
452 | 143 | PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE); |
453 | 143 | } |
454 | 5.62k | } |
455 | | |
456 | 15.3k | return P; |
457 | 15.4k | } |
458 | | |
459 | 2.37k | PJ *PJ_PROJECTION(guyou) { return pj_adams_setup(P, GUYOU); } |
460 | | |
461 | 5.62k | PJ *PJ_PROJECTION(peirce_q) { return pj_adams_setup(P, PEIRCE_Q); } |
462 | | |
463 | 692 | PJ *PJ_PROJECTION(adams_hemi) { return pj_adams_setup(P, ADAMS_HEMI); } |
464 | | |
465 | 3.55k | PJ *PJ_PROJECTION(adams_ws1) { return pj_adams_setup(P, ADAMS_WS1); } |
466 | | |
467 | 3.20k | PJ *PJ_PROJECTION(adams_ws2) { return pj_adams_setup(P, ADAMS_WS2); } |
468 | | |
469 | | #undef TOL |
470 | | #undef RSQRT2 |