1 /*
2 ** Copyright (c) 2003, 2006   Gerald I. Evenden
3 */
4 /*
5 ** Permission is hereby granted, free of charge, to any person obtaining
6 ** a copy of this software and associated documentation files (the
7 ** "Software"), to deal in the Software without restriction, including
8 ** without limitation the rights to use, copy, modify, merge, publish,
9 ** distribute, sublicense, and/or sell copies of the Software, and to
10 ** permit persons to whom the Software is furnished to do so, subject to
11 ** the following conditions:
12 **
13 ** The above copyright notice and this permission notice shall be
14 ** included in all copies or substantial portions of the Software.
15 **
16 ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 ** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20 ** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21 ** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22 ** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 */
24 #define PJ_LIB__
25 #include <projects.h>
26 
27 PROJ_HEAD(omerc, "Oblique Mercator")
28     "\n\tCyl, Sph&Ell no_rot\n\t"
29     "alpha= [gamma=] [no_off] lonc= or\n\t lon_1= lat_1= lon_2= lat_2=";
30 
31 struct pj_opaque {
32     double  A, B, E, AB, ArB, BrA, rB, singam, cosgam, sinrot, cosrot;
33     double  v_pole_n, v_pole_s, u_0;
34     int no_rot;
35 };
36 
37 #define TOL 1.e-7
38 #define EPS 1.e-10
39 
40 
e_forward(LP lp,PJ * P)41 static XY e_forward (LP lp, PJ *P) {          /* Ellipsoidal, forward */
42     XY xy = {0.0,0.0};
43     struct pj_opaque *Q = P->opaque;
44     double S, T, U, V, W, temp, u, v;
45 
46     if (fabs(fabs(lp.phi) - M_HALFPI) > EPS) {
47         W = Q->E / pow(pj_tsfn(lp.phi, sin(lp.phi), P->e), Q->B);
48         temp = 1. / W;
49         S = .5 * (W - temp);
50         T = .5 * (W + temp);
51         V = sin(Q->B * lp.lam);
52         U = (S * Q->singam - V * Q->cosgam) / T;
53         if (fabs(fabs(U) - 1.0) < EPS)
54             F_ERROR;
55         v = 0.5 * Q->ArB * log((1. - U)/(1. + U));
56         temp = cos(Q->B * lp.lam);
57                 if(fabs(temp) < TOL) {
58                     u = Q->A * lp.lam;
59                 } else {
60                     u = Q->ArB * atan2((S * Q->cosgam + V * Q->singam), temp);
61                 }
62     } else {
63         v = lp.phi > 0 ? Q->v_pole_n : Q->v_pole_s;
64         u = Q->ArB * lp.phi;
65     }
66     if (Q->no_rot) {
67         xy.x = u;
68         xy.y = v;
69     } else {
70         u -= Q->u_0;
71         xy.x = v * Q->cosrot + u * Q->sinrot;
72         xy.y = u * Q->cosrot - v * Q->sinrot;
73     }
74     return xy;
75 }
76 
77 
e_inverse(XY xy,PJ * P)78 static LP e_inverse (XY xy, PJ *P) {          /* Ellipsoidal, inverse */
79     LP lp = {0.0,0.0};
80     struct pj_opaque *Q = P->opaque;
81     double  u, v, Qp, Sp, Tp, Vp, Up;
82 
83     if (Q->no_rot) {
84         v = xy.y;
85         u = xy.x;
86     } else {
87         v = xy.x * Q->cosrot - xy.y * Q->sinrot;
88         u = xy.y * Q->cosrot + xy.x * Q->sinrot + Q->u_0;
89     }
90     Qp = exp(- Q->BrA * v);
91     Sp = .5 * (Qp - 1. / Qp);
92     Tp = .5 * (Qp + 1. / Qp);
93     Vp = sin(Q->BrA * u);
94     Up = (Vp * Q->cosgam + Sp * Q->singam) / Tp;
95     if (fabs(fabs(Up) - 1.) < EPS) {
96         lp.lam = 0.;
97         lp.phi = Up < 0. ? -M_HALFPI : M_HALFPI;
98     } else {
99         lp.phi = Q->E / sqrt((1. + Up) / (1. - Up));
100         if ((lp.phi = pj_phi2(P->ctx, pow(lp.phi, 1. / Q->B), P->e)) == HUGE_VAL)
101             I_ERROR;
102         lp.lam = - Q->rB * atan2((Sp * Q->cosgam -
103             Vp * Q->singam), cos(Q->BrA * u));
104     }
105     return lp;
106 }
107 
108 
freeup_new(PJ * P)109 static void *freeup_new (PJ *P) {                       /* Destructor */
110     if (0==P)
111         return 0;
112     if (0==P->opaque)
113         return pj_dealloc (P);
114 
115     pj_dealloc (P->opaque);
116     return pj_dealloc(P);
117 }
118 
freeup(PJ * P)119 static void freeup (PJ *P) {
120     freeup_new (P);
121     return;
122 }
123 
124 
PROJECTION(omerc)125 PJ *PROJECTION(omerc) {
126     double con, com, cosph0, D, F, H, L, sinph0, p, J, gamma=0,
127         gamma0, lamc=0, lam1=0, lam2=0, phi1=0, phi2=0, alpha_c=0;
128     int alp, gam, no_off = 0;
129 
130     struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque));
131     if (0==Q)
132         return freeup_new (P);
133     P->opaque = Q;
134 
135     Q->no_rot = pj_param(P->ctx, P->params, "tno_rot").i;
136         if ((alp = pj_param(P->ctx, P->params, "talpha").i) != 0)
137         alpha_c = pj_param(P->ctx, P->params, "ralpha").f;
138         if ((gam = pj_param(P->ctx, P->params, "tgamma").i) != 0)
139         gamma = pj_param(P->ctx, P->params, "rgamma").f;
140     if (alp || gam) {
141         lamc    = pj_param(P->ctx, P->params, "rlonc").f;
142         no_off =
143                     /* For libproj4 compatability */
144                     pj_param(P->ctx, P->params, "tno_off").i
145                     /* for backward compatibility */
146                     || pj_param(P->ctx, P->params, "tno_uoff").i;
147         if( no_off )
148         {
149             /* Mark the parameter as used, so that the pj_get_def() return them */
150             pj_param(P->ctx, P->params, "sno_uoff");
151             pj_param(P->ctx, P->params, "sno_off");
152         }
153     } else {
154         lam1 = pj_param(P->ctx, P->params, "rlon_1").f;
155         phi1 = pj_param(P->ctx, P->params, "rlat_1").f;
156         lam2 = pj_param(P->ctx, P->params, "rlon_2").f;
157         phi2 = pj_param(P->ctx, P->params, "rlat_2").f;
158         if (fabs(phi1 - phi2) <= TOL ||
159             (con = fabs(phi1)) <= TOL ||
160             fabs(con - M_HALFPI) <= TOL ||
161             fabs(fabs(P->phi0) - M_HALFPI) <= TOL ||
162             fabs(fabs(phi2) - M_HALFPI) <= TOL) E_ERROR(-33);
163     }
164     com = sqrt(P->one_es);
165     if (fabs(P->phi0) > EPS) {
166         sinph0 = sin(P->phi0);
167         cosph0 = cos(P->phi0);
168         con = 1. - P->es * sinph0 * sinph0;
169         Q->B = cosph0 * cosph0;
170         Q->B = sqrt(1. + P->es * Q->B * Q->B / P->one_es);
171         Q->A = Q->B * P->k0 * com / con;
172         D = Q->B * com / (cosph0 * sqrt(con));
173         if ((F = D * D - 1.) <= 0.)
174             F = 0.;
175         else {
176             F = sqrt(F);
177             if (P->phi0 < 0.)
178                 F = -F;
179         }
180         Q->E = F += D;
181         Q->E *= pow(pj_tsfn(P->phi0, sinph0, P->e), Q->B);
182     } else {
183         Q->B = 1. / com;
184         Q->A = P->k0;
185         Q->E = D = F = 1.;
186     }
187     if (alp || gam) {
188         if (alp) {
189             gamma0 = asin(sin(alpha_c) / D);
190             if (!gam)
191                 gamma = alpha_c;
192         } else
193             alpha_c = asin(D*sin(gamma0 = gamma));
194         if ((con = fabs(alpha_c)) <= TOL ||
195             fabs(con - M_PI) <= TOL ||
196             fabs(fabs(P->phi0) - M_HALFPI) <= TOL)
197             E_ERROR(-32);
198         P->lam0 = lamc - asin(.5 * (F - 1. / F) *
199            tan(gamma0)) / Q->B;
200     } else {
201         H = pow(pj_tsfn(phi1, sin(phi1), P->e), Q->B);
202         L = pow(pj_tsfn(phi2, sin(phi2), P->e), Q->B);
203         F = Q->E / H;
204         p = (L - H) / (L + H);
205         J = Q->E * Q->E;
206         J = (J - L * H) / (J + L * H);
207         if ((con = lam1 - lam2) < -M_PI)
208             lam2 -= M_TWOPI;
209         else if (con > M_PI)
210             lam2 += M_TWOPI;
211         P->lam0 = adjlon(.5 * (lam1 + lam2) - atan(
212            J * tan(.5 * Q->B * (lam1 - lam2)) / p) / Q->B);
213         gamma0 = atan(2. * sin(Q->B * adjlon(lam1 - P->lam0)) /
214            (F - 1. / F));
215         gamma = alpha_c = asin(D * sin(gamma0));
216     }
217     Q->singam = sin(gamma0);
218     Q->cosgam = cos(gamma0);
219     Q->sinrot = sin(gamma);
220     Q->cosrot = cos(gamma);
221     Q->BrA = 1. / (Q->ArB = Q->A * (Q->rB = 1. / Q->B));
222     Q->AB = Q->A * Q->B;
223     if (no_off)
224         Q->u_0 = 0;
225     else {
226         Q->u_0 = fabs(Q->ArB * atan2(sqrt(D * D - 1.), cos(alpha_c)));
227         if (P->phi0 < 0.)
228             Q->u_0 = - Q->u_0;
229     }
230     F = 0.5 * gamma0;
231     Q->v_pole_n = Q->ArB * log(tan(M_FORTPI - F));
232     Q->v_pole_s = Q->ArB * log(tan(M_FORTPI + F));
233     P->inv = e_inverse;
234     P->fwd = e_forward;
235 
236     return P;
237 }
238 
239 
240 #ifndef PJ_SELFTEST
pj_omerc_selftest(void)241 int pj_omerc_selftest (void) {return 0;}
242 #else
243 
pj_omerc_selftest(void)244 int pj_omerc_selftest (void) {
245     double tolerance_lp = 1e-10;
246     double tolerance_xy = 1e-7;
247 
248     char e_args[] = {"+proj=omerc   +ellps=GRS80  +lat_1=0.5 +lat_2=2"};
249 
250     LP fwd_in[] = {
251         { 2, 1},
252         { 2,-1},
253         {-2, 1},
254         {-2,-1}
255     };
256 
257     XY e_fwd_expect[] = {
258         { 222650.796885261341,  110642.229314983808},
259         { 222650.796885261341, -110642.229314983808},
260         {-222650.796885261545,  110642.229314983808},
261         {-222650.796885261545, -110642.229314983808},
262     };
263 
264     XY inv_in[] = {
265         { 200, 100},
266         { 200,-100},
267         {-200, 100},
268         {-200,-100}
269     };
270 
271     LP e_inv_expect[] = {
272         { 0.00179663056816996357,  0.000904369474808157338},
273         { 0.00179663056816996357, -0.000904369474820879583},
274         {-0.0017966305681604536,   0.000904369474808157338},
275         {-0.0017966305681604536,  -0.000904369474820879583},
276     };
277 
278     return pj_generic_selftest (e_args, 0, tolerance_xy, tolerance_lp, 4, 4, fwd_in, e_fwd_expect, 0, inv_in, e_inv_expect, 0);
279 }
280 
281 
282 #endif
283