/src/PROJ/src/projections/lagrng.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | |
2 | | #include <errno.h> |
3 | | #include <math.h> |
4 | | |
5 | | #include "proj.h" |
6 | | #include "proj_internal.h" |
7 | | |
8 | | PROJ_HEAD(lagrng, "Lagrange") "\n\tMisc Sph\n\tW="; |
9 | | |
10 | 30.4k | #define TOL 1e-10 |
11 | | |
12 | | namespace { // anonymous namespace |
13 | | struct pj_lagrng { |
14 | | double a1; |
15 | | double a2; |
16 | | double hrw; |
17 | | double hw; |
18 | | double rw; |
19 | | double w; |
20 | | }; |
21 | | } // anonymous namespace |
22 | | |
23 | 15.1k | static PJ_XY lagrng_s_forward(PJ_LP lp, PJ *P) { /* Spheroidal, forward */ |
24 | 15.1k | PJ_XY xy = {0.0, 0.0}; |
25 | 15.1k | struct pj_lagrng *Q = static_cast<struct pj_lagrng *>(P->opaque); |
26 | 15.1k | double v, c; |
27 | | |
28 | 15.1k | const double sin_phi = sin(lp.phi); |
29 | 15.1k | if (fabs(fabs(sin_phi) - 1) < TOL) { |
30 | 0 | xy.x = 0; |
31 | 0 | xy.y = lp.phi < 0 ? -2. : 2.; |
32 | 15.1k | } else { |
33 | 15.1k | v = Q->a1 * pow((1. + sin_phi) / (1. - sin_phi), Q->hrw); |
34 | 15.1k | lp.lam *= Q->rw; |
35 | 15.1k | c = 0.5 * (v + 1. / v) + cos(lp.lam); |
36 | 15.1k | if (c < TOL) { |
37 | 0 | proj_errno_set(P, PROJ_ERR_COORD_TRANSFM_OUTSIDE_PROJECTION_DOMAIN); |
38 | 0 | return xy; |
39 | 0 | } |
40 | 15.1k | xy.x = 2. * sin(lp.lam) / c; |
41 | 15.1k | xy.y = (v - 1. / v) / c; |
42 | 15.1k | } |
43 | 15.1k | return xy; |
44 | 15.1k | } |
45 | | |
46 | 0 | static PJ_LP lagrng_s_inverse(PJ_XY xy, PJ *P) { /* Spheroidal, inverse */ |
47 | 0 | PJ_LP lp = {0.0, 0.0}; |
48 | 0 | struct pj_lagrng *Q = static_cast<struct pj_lagrng *>(P->opaque); |
49 | 0 | double c, x2, y2p, y2m; |
50 | |
|
51 | 0 | if (fabs(fabs(xy.y) - 2.) < TOL) { |
52 | 0 | lp.phi = xy.y < 0 ? -M_HALFPI : M_HALFPI; |
53 | 0 | lp.lam = 0; |
54 | 0 | } else { |
55 | 0 | x2 = xy.x * xy.x; |
56 | 0 | y2p = 2. + xy.y; |
57 | 0 | y2m = 2. - xy.y; |
58 | 0 | c = y2p * y2m - x2; |
59 | 0 | if (fabs(c) < TOL) { |
60 | 0 | proj_errno_set(P, PROJ_ERR_COORD_TRANSFM_OUTSIDE_PROJECTION_DOMAIN); |
61 | 0 | return lp; |
62 | 0 | } |
63 | 0 | lp.phi = 2. * atan(pow((y2p * y2p + x2) / (Q->a2 * (y2m * y2m + x2)), |
64 | 0 | Q->hw)) - |
65 | 0 | M_HALFPI; |
66 | 0 | lp.lam = Q->w * atan2(4. * xy.x, c); |
67 | 0 | } |
68 | 0 | return lp; |
69 | 0 | } |
70 | | |
71 | 214 | PJ *PJ_PROJECTION(lagrng) { |
72 | 214 | double sin_phi1; |
73 | 214 | struct pj_lagrng *Q = |
74 | 214 | static_cast<struct pj_lagrng *>(calloc(1, sizeof(struct pj_lagrng))); |
75 | 214 | if (nullptr == Q) |
76 | 0 | return pj_default_destructor(P, PROJ_ERR_OTHER /*ENOMEM*/); |
77 | 214 | P->opaque = Q; |
78 | | |
79 | 214 | if (pj_param(P->ctx, P->params, "tW").i) |
80 | 0 | Q->w = pj_param(P->ctx, P->params, "dW").f; |
81 | 214 | else |
82 | 214 | Q->w = 2; |
83 | 214 | if (Q->w <= 0) { |
84 | 0 | proj_log_error(P, _("Invalid value for W: it should be > 0")); |
85 | 0 | return pj_default_destructor(P, PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE); |
86 | 0 | } |
87 | 214 | Q->hw = 0.5 * Q->w; |
88 | 214 | Q->rw = 1. / Q->w; |
89 | 214 | Q->hrw = 0.5 * Q->rw; |
90 | 214 | sin_phi1 = sin(pj_param(P->ctx, P->params, "rlat_1").f); |
91 | 214 | if (fabs(fabs(sin_phi1) - 1.) < TOL) { |
92 | 0 | proj_log_error(P, |
93 | 0 | _("Invalid value for lat_1: |lat_1| should be < 90°")); |
94 | 0 | return pj_default_destructor(P, PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE); |
95 | 0 | } |
96 | | |
97 | 214 | Q->a1 = pow((1. - sin_phi1) / (1. + sin_phi1), Q->hrw); |
98 | 214 | Q->a2 = Q->a1 * Q->a1; |
99 | | |
100 | 214 | P->es = 0.; |
101 | 214 | P->inv = lagrng_s_inverse; |
102 | 214 | P->fwd = lagrng_s_forward; |
103 | | |
104 | 214 | return P; |
105 | 214 | } |