/src/proj/src/projections/imw_p.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | |
2 | | |
3 | | #include <errno.h> |
4 | | #include <math.h> |
5 | | |
6 | | #include "proj.h" |
7 | | #include "proj_internal.h" |
8 | | |
9 | | PROJ_HEAD(imw_p, "International Map of the World Polyconic") |
10 | | "\n\tMod. Polyconic, Ell\n\tlat_1= and lat_2= [lon_1=]"; |
11 | | |
12 | 0 | #define TOL 1e-10 |
13 | 0 | #define EPS 1e-10 |
14 | | |
15 | | namespace { // anonymous namespace |
16 | | enum Mode { |
17 | | NONE_IS_ZERO = 0, /* phi_1 and phi_2 != 0 */ |
18 | | PHI_1_IS_ZERO = 1, /* phi_1 = 0 */ |
19 | | PHI_2_IS_ZERO = -1 /* phi_2 = 0 */ |
20 | | }; |
21 | | } // anonymous namespace |
22 | | |
23 | | namespace { // anonymous namespace |
24 | | struct pj_imw_p_data { |
25 | | double P, Pp, Q, Qp, R_1, R_2, sphi_1, sphi_2, C2; |
26 | | double phi_1, phi_2, lam_1; |
27 | | double *en; |
28 | | enum Mode mode; |
29 | | }; |
30 | | } // anonymous namespace |
31 | | |
32 | 0 | static int phi12(PJ *P, double *del, double *sig) { |
33 | 0 | struct pj_imw_p_data *Q = static_cast<struct pj_imw_p_data *>(P->opaque); |
34 | 0 | int err = 0; |
35 | |
|
36 | 0 | if (!pj_param(P->ctx, P->params, "tlat_1").i) { |
37 | 0 | proj_log_error(P, _("Missing parameter: lat_1 should be specified")); |
38 | 0 | err = PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE; |
39 | 0 | } else if (!pj_param(P->ctx, P->params, "tlat_2").i) { |
40 | 0 | proj_log_error(P, _("Missing parameter: lat_2 should be specified")); |
41 | 0 | err = PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE; |
42 | 0 | } else { |
43 | 0 | Q->phi_1 = pj_param(P->ctx, P->params, "rlat_1").f; |
44 | 0 | Q->phi_2 = pj_param(P->ctx, P->params, "rlat_2").f; |
45 | 0 | *del = 0.5 * (Q->phi_2 - Q->phi_1); |
46 | 0 | *sig = 0.5 * (Q->phi_2 + Q->phi_1); |
47 | 0 | err = (fabs(*del) < EPS || fabs(*sig) < EPS) |
48 | 0 | ? PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE |
49 | 0 | : 0; |
50 | 0 | if (err) { |
51 | 0 | proj_log_error( |
52 | 0 | P, _("Illegal value for lat_1 and lat_2: |lat_1 - lat_2| " |
53 | 0 | "and |lat_1 + lat_2| should be > 0")); |
54 | 0 | } |
55 | 0 | } |
56 | 0 | return err; |
57 | 0 | } |
58 | | |
59 | 0 | static PJ_XY loc_for(PJ_LP lp, PJ *P, double *yc) { |
60 | 0 | struct pj_imw_p_data *Q = static_cast<struct pj_imw_p_data *>(P->opaque); |
61 | 0 | PJ_XY xy; |
62 | |
|
63 | 0 | if (lp.phi == 0.0) { |
64 | 0 | xy.x = lp.lam; |
65 | 0 | xy.y = 0.; |
66 | 0 | } else { |
67 | 0 | double xa, ya, xb, yb, xc, D, B, m, sp, t, R, C; |
68 | |
|
69 | 0 | sp = sin(lp.phi); |
70 | 0 | m = pj_mlfn(lp.phi, sp, cos(lp.phi), Q->en); |
71 | 0 | xa = Q->Pp + Q->Qp * m; |
72 | 0 | ya = Q->P + Q->Q * m; |
73 | 0 | R = 1. / (tan(lp.phi) * sqrt(1. - P->es * sp * sp)); |
74 | 0 | C = sqrt(R * R - xa * xa); |
75 | 0 | if (lp.phi < 0.) |
76 | 0 | C = -C; |
77 | 0 | C += ya - R; |
78 | 0 | if (Q->mode == PHI_2_IS_ZERO) { |
79 | 0 | xb = lp.lam; |
80 | 0 | yb = Q->C2; |
81 | 0 | } else { |
82 | 0 | t = lp.lam * Q->sphi_2; |
83 | 0 | xb = Q->R_2 * sin(t); |
84 | 0 | yb = Q->C2 + Q->R_2 * (1. - cos(t)); |
85 | 0 | } |
86 | 0 | if (Q->mode == PHI_1_IS_ZERO) { |
87 | 0 | xc = lp.lam; |
88 | 0 | *yc = 0.; |
89 | 0 | } else { |
90 | 0 | t = lp.lam * Q->sphi_1; |
91 | 0 | xc = Q->R_1 * sin(t); |
92 | 0 | *yc = Q->R_1 * (1. - cos(t)); |
93 | 0 | } |
94 | 0 | D = (xb - xc) / (yb - *yc); |
95 | 0 | B = xc + D * (C + R - *yc); |
96 | 0 | xy.x = D * sqrt(R * R * (1 + D * D) - B * B); |
97 | 0 | if (lp.phi > 0) |
98 | 0 | xy.x = -xy.x; |
99 | 0 | xy.x = (B + xy.x) / (1. + D * D); |
100 | 0 | xy.y = sqrt(R * R - xy.x * xy.x); |
101 | 0 | if (lp.phi > 0) |
102 | 0 | xy.y = -xy.y; |
103 | 0 | xy.y += C + R; |
104 | 0 | } |
105 | 0 | return xy; |
106 | 0 | } |
107 | | |
108 | 0 | static PJ_XY imw_p_e_forward(PJ_LP lp, PJ *P) { /* Ellipsoidal, forward */ |
109 | 0 | double yc; |
110 | 0 | PJ_XY xy = loc_for(lp, P, &yc); |
111 | 0 | return (xy); |
112 | 0 | } |
113 | | |
114 | 0 | static PJ_LP imw_p_e_inverse(PJ_XY xy, PJ *P) { /* Ellipsoidal, inverse */ |
115 | 0 | PJ_LP lp = {0.0, 0.0}; |
116 | 0 | struct pj_imw_p_data *Q = static_cast<struct pj_imw_p_data *>(P->opaque); |
117 | 0 | PJ_XY t; |
118 | 0 | double yc = 0.0; |
119 | 0 | int i = 0; |
120 | 0 | const int N_MAX_ITER = 1000; /* Arbitrarily chosen number... */ |
121 | |
|
122 | 0 | lp.phi = Q->phi_2; |
123 | 0 | lp.lam = xy.x / cos(lp.phi); |
124 | 0 | do { |
125 | 0 | t = loc_for(lp, P, &yc); |
126 | 0 | const double denom = t.y - yc; |
127 | 0 | if (denom != 0 || fabs(t.y - xy.y) > TOL) { |
128 | 0 | if (denom == 0) { |
129 | 0 | proj_errno_set( |
130 | 0 | P, PROJ_ERR_COORD_TRANSFM_OUTSIDE_PROJECTION_DOMAIN); |
131 | 0 | return proj_coord_error().lp; |
132 | 0 | } |
133 | 0 | lp.phi = ((lp.phi - Q->phi_1) * (xy.y - yc) / denom) + Q->phi_1; |
134 | 0 | } |
135 | 0 | if (t.x != 0 && fabs(t.x - xy.x) > TOL) |
136 | 0 | lp.lam = lp.lam * xy.x / t.x; |
137 | 0 | i++; |
138 | 0 | } while (i < N_MAX_ITER && |
139 | 0 | (fabs(t.x - xy.x) > TOL || fabs(t.y - xy.y) > TOL)); |
140 | | |
141 | 0 | if (i == N_MAX_ITER) { |
142 | 0 | proj_errno_set(P, PROJ_ERR_COORD_TRANSFM_OUTSIDE_PROJECTION_DOMAIN); |
143 | 0 | return proj_coord_error().lp; |
144 | 0 | } |
145 | | |
146 | 0 | return lp; |
147 | 0 | } |
148 | | |
149 | 0 | static void xy(PJ *P, double phi, double *x, double *y, double *sp, double *R) { |
150 | 0 | double F; |
151 | |
|
152 | 0 | *sp = sin(phi); |
153 | 0 | *R = 1. / (tan(phi) * sqrt(1. - P->es * *sp * *sp)); |
154 | 0 | F = static_cast<struct pj_imw_p_data *>(P->opaque)->lam_1 * *sp; |
155 | 0 | *y = *R * (1 - cos(F)); |
156 | 0 | *x = *R * sin(F); |
157 | 0 | } |
158 | | |
159 | 0 | static PJ *pj_imw_p_destructor(PJ *P, int errlev) { |
160 | 0 | if (nullptr == P) |
161 | 0 | return nullptr; |
162 | | |
163 | 0 | if (nullptr == P->opaque) |
164 | 0 | return pj_default_destructor(P, errlev); |
165 | | |
166 | 0 | if (static_cast<struct pj_imw_p_data *>(P->opaque)->en) |
167 | 0 | free(static_cast<struct pj_imw_p_data *>(P->opaque)->en); |
168 | |
|
169 | 0 | return pj_default_destructor(P, errlev); |
170 | 0 | } |
171 | | |
172 | 0 | PJ *PJ_PROJECTION(imw_p) { |
173 | 0 | double del, sig, s, t, x1, x2, T2, y1, m1, m2, y2; |
174 | 0 | int err; |
175 | 0 | struct pj_imw_p_data *Q = static_cast<struct pj_imw_p_data *>( |
176 | 0 | calloc(1, sizeof(struct pj_imw_p_data))); |
177 | 0 | if (nullptr == Q) |
178 | 0 | return pj_default_destructor(P, PROJ_ERR_OTHER /*ENOMEM*/); |
179 | 0 | P->opaque = Q; |
180 | |
|
181 | 0 | if (!(Q->en = pj_enfn(P->n))) |
182 | 0 | return pj_default_destructor(P, PROJ_ERR_OTHER /*ENOMEM*/); |
183 | 0 | if ((err = phi12(P, &del, &sig)) != 0) { |
184 | 0 | return pj_imw_p_destructor(P, err); |
185 | 0 | } |
186 | 0 | if (Q->phi_2 < Q->phi_1) { /* make sure P->phi_1 most southerly */ |
187 | 0 | del = Q->phi_1; |
188 | 0 | Q->phi_1 = Q->phi_2; |
189 | 0 | Q->phi_2 = del; |
190 | 0 | } |
191 | 0 | if (pj_param(P->ctx, P->params, "tlon_1").i) |
192 | 0 | Q->lam_1 = pj_param(P->ctx, P->params, "rlon_1").f; |
193 | 0 | else { /* use predefined based upon latitude */ |
194 | 0 | sig = fabs(sig * RAD_TO_DEG); |
195 | 0 | if (sig <= 60) |
196 | 0 | sig = 2.; |
197 | 0 | else if (sig <= 76) |
198 | 0 | sig = 4.; |
199 | 0 | else |
200 | 0 | sig = 8.; |
201 | 0 | Q->lam_1 = sig * DEG_TO_RAD; |
202 | 0 | } |
203 | 0 | Q->mode = NONE_IS_ZERO; |
204 | 0 | if (Q->phi_1 != 0.0) |
205 | 0 | xy(P, Q->phi_1, &x1, &y1, &Q->sphi_1, &Q->R_1); |
206 | 0 | else { |
207 | 0 | Q->mode = PHI_1_IS_ZERO; |
208 | 0 | y1 = 0.; |
209 | 0 | x1 = Q->lam_1; |
210 | 0 | } |
211 | 0 | if (Q->phi_2 != 0.0) |
212 | 0 | xy(P, Q->phi_2, &x2, &T2, &Q->sphi_2, &Q->R_2); |
213 | 0 | else { |
214 | 0 | Q->mode = PHI_2_IS_ZERO; |
215 | 0 | T2 = 0.; |
216 | 0 | x2 = Q->lam_1; |
217 | 0 | } |
218 | 0 | m1 = pj_mlfn(Q->phi_1, Q->sphi_1, cos(Q->phi_1), Q->en); |
219 | 0 | m2 = pj_mlfn(Q->phi_2, Q->sphi_2, cos(Q->phi_2), Q->en); |
220 | 0 | t = m2 - m1; |
221 | 0 | s = x2 - x1; |
222 | 0 | y2 = sqrt(t * t - s * s) + y1; |
223 | 0 | Q->C2 = y2 - T2; |
224 | 0 | t = 1. / t; |
225 | 0 | Q->P = (m2 * y1 - m1 * y2) * t; |
226 | 0 | Q->Q = (y2 - y1) * t; |
227 | 0 | Q->Pp = (m2 * x1 - m1 * x2) * t; |
228 | 0 | Q->Qp = (x2 - x1) * t; |
229 | |
|
230 | 0 | P->fwd = imw_p_e_forward; |
231 | 0 | P->inv = imw_p_e_inverse; |
232 | 0 | P->destructor = pj_imw_p_destructor; |
233 | |
|
234 | 0 | return P; |
235 | 0 | } |
236 | | |
237 | | #undef TOL |
238 | | #undef EPS |