/src/proj/src/projections/mod_ster.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* based upon Snyder and Linck, USGS-NMD */ |
2 | | |
3 | | #include "proj.h" |
4 | | #include "proj_internal.h" |
5 | | #include <errno.h> |
6 | | #include <math.h> |
7 | | |
8 | | PROJ_HEAD(mil_os, "Miller Oblated Stereographic") "\n\tAzi(mod)"; |
9 | | PROJ_HEAD(lee_os, "Lee Oblated Stereographic") "\n\tAzi(mod)"; |
10 | | PROJ_HEAD(gs48, "Modified Stereographic of 48 U.S.") "\n\tAzi(mod)"; |
11 | | PROJ_HEAD(alsk, "Modified Stereographic of Alaska") "\n\tAzi(mod)"; |
12 | | PROJ_HEAD(gs50, "Modified Stereographic of 50 U.S.") "\n\tAzi(mod)"; |
13 | | |
14 | 0 | #define EPSLN 1e-12 |
15 | | |
16 | | namespace { // anonymous namespace |
17 | | struct pj_mod_ster_data { |
18 | | const COMPLEX *zcoeff; |
19 | | double cchio, schio; |
20 | | int n; |
21 | | }; |
22 | | } // anonymous namespace |
23 | | |
24 | 0 | static PJ_XY mod_ster_e_forward(PJ_LP lp, PJ *P) { /* Ellipsoidal, forward */ |
25 | 0 | PJ_XY xy = {0.0, 0.0}; |
26 | 0 | struct pj_mod_ster_data *Q = |
27 | 0 | static_cast<struct pj_mod_ster_data *>(P->opaque); |
28 | 0 | double sinlon, coslon, esphi, chi, schi, cchi, s; |
29 | 0 | COMPLEX p; |
30 | |
|
31 | 0 | sinlon = sin(lp.lam); |
32 | 0 | coslon = cos(lp.lam); |
33 | 0 | esphi = P->e * sin(lp.phi); |
34 | 0 | chi = 2. * atan(tan((M_HALFPI + lp.phi) * .5) * |
35 | 0 | pow((1. - esphi) / (1. + esphi), P->e * .5)) - |
36 | 0 | M_HALFPI; |
37 | 0 | schi = sin(chi); |
38 | 0 | cchi = cos(chi); |
39 | 0 | const double denom = 1. + Q->schio * schi + Q->cchio * cchi * coslon; |
40 | 0 | if (denom == 0) { |
41 | 0 | proj_errno_set(P, PROJ_ERR_COORD_TRANSFM_OUTSIDE_PROJECTION_DOMAIN); |
42 | 0 | return xy; |
43 | 0 | } |
44 | 0 | s = 2. / denom; |
45 | 0 | p.r = s * cchi * sinlon; |
46 | 0 | p.i = s * (Q->cchio * schi - Q->schio * cchi * coslon); |
47 | 0 | p = pj_zpoly1(p, Q->zcoeff, Q->n); |
48 | 0 | xy.x = p.r; |
49 | 0 | xy.y = p.i; |
50 | |
|
51 | 0 | return xy; |
52 | 0 | } |
53 | | |
54 | 0 | static PJ_LP mod_ster_e_inverse(PJ_XY xy, PJ *P) { /* Ellipsoidal, inverse */ |
55 | 0 | PJ_LP lp = {0.0, 0.0}; |
56 | 0 | struct pj_mod_ster_data *Q = |
57 | 0 | static_cast<struct pj_mod_ster_data *>(P->opaque); |
58 | 0 | int nn; |
59 | 0 | COMPLEX p, fxy, fpxy, dp; |
60 | 0 | double den, rh = 0.0, z, sinz = 0.0, cosz = 0.0, chi, phi = 0.0, esphi; |
61 | |
|
62 | 0 | p.r = xy.x; |
63 | 0 | p.i = xy.y; |
64 | 0 | for (nn = 20; nn; --nn) { |
65 | 0 | fxy = pj_zpolyd1(p, Q->zcoeff, Q->n, &fpxy); |
66 | 0 | fxy.r -= xy.x; |
67 | 0 | fxy.i -= xy.y; |
68 | 0 | den = fpxy.r * fpxy.r + fpxy.i * fpxy.i; |
69 | 0 | dp.r = -(fxy.r * fpxy.r + fxy.i * fpxy.i) / den; |
70 | 0 | dp.i = -(fxy.i * fpxy.r - fxy.r * fpxy.i) / den; |
71 | 0 | p.r += dp.r; |
72 | 0 | p.i += dp.i; |
73 | 0 | if ((fabs(dp.r) + fabs(dp.i)) <= EPSLN) |
74 | 0 | break; |
75 | 0 | } |
76 | 0 | if (nn) { |
77 | 0 | rh = hypot(p.r, p.i); |
78 | 0 | z = 2. * atan(.5 * rh); |
79 | 0 | sinz = sin(z); |
80 | 0 | cosz = cos(z); |
81 | 0 | if (fabs(rh) <= EPSLN) { |
82 | | /* if we end up here input coordinates were (0,0). |
83 | | * pj_inv() adds P->lam0 to lp.lam, this way we are |
84 | | * sure to get the correct offset */ |
85 | 0 | lp.lam = 0.0; |
86 | 0 | lp.phi = P->phi0; |
87 | 0 | return lp; |
88 | 0 | } |
89 | 0 | chi = aasin(P->ctx, cosz * Q->schio + p.i * sinz * Q->cchio / rh); |
90 | 0 | phi = chi; |
91 | 0 | for (nn = 20; nn; --nn) { |
92 | 0 | double dphi; |
93 | 0 | esphi = P->e * sin(phi); |
94 | 0 | dphi = 2. * atan(tan((M_HALFPI + chi) * .5) * |
95 | 0 | pow((1. + esphi) / (1. - esphi), P->e * .5)) - |
96 | 0 | M_HALFPI - phi; |
97 | 0 | phi += dphi; |
98 | 0 | if (fabs(dphi) <= EPSLN) |
99 | 0 | break; |
100 | 0 | } |
101 | 0 | } |
102 | 0 | if (nn) { |
103 | 0 | lp.phi = phi; |
104 | 0 | lp.lam = |
105 | 0 | atan2(p.r * sinz, rh * Q->cchio * cosz - p.i * Q->schio * sinz); |
106 | 0 | } else |
107 | 0 | lp.lam = lp.phi = HUGE_VAL; |
108 | 0 | return lp; |
109 | 0 | } |
110 | | |
111 | 571 | static PJ *mod_ster_setup(PJ *P) { /* general initialization */ |
112 | 571 | struct pj_mod_ster_data *Q = |
113 | 571 | static_cast<struct pj_mod_ster_data *>(P->opaque); |
114 | 571 | double esphi, chio; |
115 | | |
116 | 571 | if (P->es != 0.0) { |
117 | 195 | esphi = P->e * sin(P->phi0); |
118 | 195 | chio = 2. * atan(tan((M_HALFPI + P->phi0) * .5) * |
119 | 195 | pow((1. - esphi) / (1. + esphi), P->e * .5)) - |
120 | 195 | M_HALFPI; |
121 | 195 | } else |
122 | 376 | chio = P->phi0; |
123 | 571 | Q->schio = sin(chio); |
124 | 571 | Q->cchio = cos(chio); |
125 | 571 | P->inv = mod_ster_e_inverse; |
126 | 571 | P->fwd = mod_ster_e_forward; |
127 | | |
128 | 571 | return P; |
129 | 571 | } |
130 | | |
131 | | /* Miller Oblated Stereographic */ |
132 | 217 | PJ *PJ_PROJECTION(mil_os) { |
133 | 217 | static const COMPLEX AB[] = {{0.924500, 0.}, {0., 0.}, {0.019430, 0.}}; |
134 | | |
135 | 217 | struct pj_mod_ster_data *Q = static_cast<struct pj_mod_ster_data *>( |
136 | 217 | calloc(1, sizeof(struct pj_mod_ster_data))); |
137 | 217 | if (nullptr == Q) |
138 | 0 | return pj_default_destructor(P, PROJ_ERR_OTHER /*ENOMEM*/); |
139 | 217 | P->opaque = Q; |
140 | | |
141 | 217 | Q->n = 2; |
142 | 217 | P->lam0 = DEG_TO_RAD * 20.; |
143 | 217 | P->phi0 = DEG_TO_RAD * 18.; |
144 | 217 | Q->zcoeff = AB; |
145 | 217 | P->es = 0.; |
146 | | |
147 | 217 | return mod_ster_setup(P); |
148 | 217 | } |
149 | | |
150 | | /* Lee Oblated Stereographic */ |
151 | 10 | PJ *PJ_PROJECTION(lee_os) { |
152 | 10 | static const COMPLEX AB[] = { |
153 | 10 | {0.721316, 0.}, {0., 0.}, {-0.0088162, -0.00617325}}; |
154 | | |
155 | 10 | struct pj_mod_ster_data *Q = static_cast<struct pj_mod_ster_data *>( |
156 | 10 | calloc(1, sizeof(struct pj_mod_ster_data))); |
157 | 10 | if (nullptr == Q) |
158 | 0 | return pj_default_destructor(P, PROJ_ERR_OTHER /*ENOMEM*/); |
159 | 10 | P->opaque = Q; |
160 | | |
161 | 10 | Q->n = 2; |
162 | 10 | P->lam0 = DEG_TO_RAD * -165.; |
163 | 10 | P->phi0 = DEG_TO_RAD * -10.; |
164 | 10 | Q->zcoeff = AB; |
165 | 10 | P->es = 0.; |
166 | | |
167 | 10 | return mod_ster_setup(P); |
168 | 10 | } |
169 | | |
170 | 19 | PJ *PJ_PROJECTION(gs48) { |
171 | 19 | static const COMPLEX /* 48 United States */ |
172 | 19 | AB[] = { |
173 | 19 | {0.98879, 0.}, {0., 0.}, {-0.050909, 0.}, {0., 0.}, {0.075528, 0.}}; |
174 | | |
175 | 19 | struct pj_mod_ster_data *Q = static_cast<struct pj_mod_ster_data *>( |
176 | 19 | calloc(1, sizeof(struct pj_mod_ster_data))); |
177 | 19 | if (nullptr == Q) |
178 | 0 | return pj_default_destructor(P, PROJ_ERR_OTHER /*ENOMEM*/); |
179 | 19 | P->opaque = Q; |
180 | | |
181 | 19 | Q->n = 4; |
182 | 19 | P->lam0 = DEG_TO_RAD * -96.; |
183 | 19 | P->phi0 = DEG_TO_RAD * 39.; |
184 | 19 | Q->zcoeff = AB; |
185 | 19 | P->es = 0.; |
186 | 19 | P->a = 6370997.; |
187 | | |
188 | 19 | return mod_ster_setup(P); |
189 | 19 | } |
190 | | |
191 | 174 | PJ *PJ_PROJECTION(alsk) { |
192 | 174 | static const COMPLEX ABe[] = { |
193 | | /* Alaska ellipsoid */ |
194 | 174 | {.9945303, 0.}, {.0052083, -.0027404}, {.0072721, .0048181}, |
195 | 174 | {-.0151089, -.1932526}, {.0642675, -.1381226}, {.3582802, -.2884586}, |
196 | 174 | }; |
197 | | |
198 | 174 | static const COMPLEX ABs[] = {/* Alaska sphere */ |
199 | 174 | {.9972523, 0.}, {.0052513, -.0041175}, |
200 | 174 | {.0074606, .0048125}, {-.0153783, -.1968253}, |
201 | 174 | {.0636871, -.1408027}, {.3660976, -.2937382}}; |
202 | | |
203 | 174 | struct pj_mod_ster_data *Q = static_cast<struct pj_mod_ster_data *>( |
204 | 174 | calloc(1, sizeof(struct pj_mod_ster_data))); |
205 | 174 | if (nullptr == Q) |
206 | 0 | return pj_default_destructor(P, PROJ_ERR_OTHER /*ENOMEM*/); |
207 | 174 | P->opaque = Q; |
208 | | |
209 | 174 | Q->n = 5; |
210 | 174 | P->lam0 = DEG_TO_RAD * -152.; |
211 | 174 | P->phi0 = DEG_TO_RAD * 64.; |
212 | 174 | if (P->es != 0.0) { /* fixed ellipsoid/sphere */ |
213 | 105 | Q->zcoeff = ABe; |
214 | 105 | P->a = 6378206.4; |
215 | 105 | P->e = sqrt(P->es = 0.00676866); |
216 | 105 | } else { |
217 | 69 | Q->zcoeff = ABs; |
218 | 69 | P->a = 6370997.; |
219 | 69 | } |
220 | | |
221 | 174 | return mod_ster_setup(P); |
222 | 174 | } |
223 | | |
224 | 151 | PJ *PJ_PROJECTION(gs50) { |
225 | 151 | static const COMPLEX ABe[] = { |
226 | | /* GS50 ellipsoid */ |
227 | 151 | {.9827497, 0.}, {.0210669, .0053804}, {-.1031415, -.0571664}, |
228 | 151 | {-.0323337, -.0322847}, {.0502303, .1211983}, {.0251805, .0895678}, |
229 | 151 | {-.0012315, -.1416121}, {.0072202, -.1317091}, {-.0194029, .0759677}, |
230 | 151 | {-.0210072, .0834037}}; |
231 | | |
232 | 151 | static const COMPLEX ABs[] = { |
233 | | /* GS50 sphere */ |
234 | 151 | {.9842990, 0.}, {.0211642, .0037608}, {-.1036018, -.0575102}, |
235 | 151 | {-.0329095, -.0320119}, {.0499471, .1223335}, {.0260460, .0899805}, |
236 | 151 | {.0007388, -.1435792}, {.0075848, -.1334108}, {-.0216473, .0776645}, |
237 | 151 | {-.0225161, .0853673}}; |
238 | | |
239 | 151 | struct pj_mod_ster_data *Q = static_cast<struct pj_mod_ster_data *>( |
240 | 151 | calloc(1, sizeof(struct pj_mod_ster_data))); |
241 | 151 | if (nullptr == Q) |
242 | 0 | return pj_default_destructor(P, PROJ_ERR_OTHER /*ENOMEM*/); |
243 | 151 | P->opaque = Q; |
244 | | |
245 | 151 | Q->n = 9; |
246 | 151 | P->lam0 = DEG_TO_RAD * -120.; |
247 | 151 | P->phi0 = DEG_TO_RAD * 45.; |
248 | 151 | if (P->es != 0.0) { /* fixed ellipsoid/sphere */ |
249 | 90 | Q->zcoeff = ABe; |
250 | 90 | P->a = 6378206.4; |
251 | 90 | P->e = sqrt(P->es = 0.00676866); |
252 | 90 | } else { |
253 | 61 | Q->zcoeff = ABs; |
254 | 61 | P->a = 6370997.; |
255 | 61 | } |
256 | | |
257 | 151 | return mod_ster_setup(P); |
258 | 151 | } |
259 | | |
260 | | #undef EPSLN |