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 
26 #include <errno.h>
27 #include <math.h>
28 
29 #include "proj.h"
30 #include "proj_internal.h"
31 
32 PROJ_HEAD(omerc, "Oblique Mercator")
33     "\n\tCyl, Sph&Ell no_rot\n\t"
34     "alpha= [gamma=] [no_off] lonc= or\n\t lon_1= lat_1= lon_2= lat_2=";
35 
36 namespace { // anonymous namespace
37 struct pj_opaque {
38     double  A, B, E, AB, ArB, BrA, rB, singam, cosgam, sinrot, cosrot;
39     double  v_pole_n, v_pole_s, u_0;
40     int no_rot;
41 };
42 } // anonymous namespace
43 
44 #define TOL 1.e-7
45 #define EPS 1.e-10
46 
47 
omerc_e_forward(PJ_LP lp,PJ * P)48 static PJ_XY omerc_e_forward (PJ_LP lp, PJ *P) {          /* Ellipsoidal, forward */
49     PJ_XY xy = {0.0,0.0};
50     struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
51     double u, v;
52 
53     if (fabs(fabs(lp.phi) - M_HALFPI) > EPS) {
54         const double W = Q->E / pow(pj_tsfn(lp.phi, sin(lp.phi), P->e), Q->B);
55         const double one_div_W = 1. / W;
56         const double S = .5 * (W - one_div_W);
57         const double T = .5 * (W + one_div_W);
58         const double V = sin(Q->B * lp.lam);
59         const double U = (S * Q->singam - V * Q->cosgam) / T;
60         if (fabs(fabs(U) - 1.0) < EPS) {
61             proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
62             return xy;
63         }
64         v = 0.5 * Q->ArB * log((1. - U)/(1. + U));
65         const double temp = cos(Q->B * lp.lam);
66         if(fabs(temp) < TOL) {
67             u = Q->A * lp.lam;
68         } else {
69             u = Q->ArB * atan2((S * Q->cosgam + V * Q->singam), temp);
70         }
71     } else {
72         v = lp.phi > 0 ? Q->v_pole_n : Q->v_pole_s;
73         u = Q->ArB * lp.phi;
74     }
75     if (Q->no_rot) {
76         xy.x = u;
77         xy.y = v;
78     } else {
79         u -= Q->u_0;
80         xy.x = v * Q->cosrot + u * Q->sinrot;
81         xy.y = u * Q->cosrot - v * Q->sinrot;
82     }
83     return xy;
84 }
85 
86 
omerc_e_inverse(PJ_XY xy,PJ * P)87 static PJ_LP omerc_e_inverse (PJ_XY xy, PJ *P) {          /* Ellipsoidal, inverse */
88     PJ_LP lp = {0.0,0.0};
89     struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
90     double  u, v, Qp, Sp, Tp, Vp, Up;
91 
92     if (Q->no_rot) {
93         v = xy.y;
94         u = xy.x;
95     } else {
96         v = xy.x * Q->cosrot - xy.y * Q->sinrot;
97         u = xy.y * Q->cosrot + xy.x * Q->sinrot + Q->u_0;
98     }
99     Qp = exp(- Q->BrA * v);
100     if( Qp == 0 ) {
101         proj_errno_set(P, PJD_ERR_INVALID_X_OR_Y);
102         return proj_coord_error().lp;
103     }
104     Sp = .5 * (Qp - 1. / Qp);
105     Tp = .5 * (Qp + 1. / Qp);
106     Vp = sin(Q->BrA * u);
107     Up = (Vp * Q->cosgam + Sp * Q->singam) / Tp;
108     if (fabs(fabs(Up) - 1.) < EPS) {
109         lp.lam = 0.;
110         lp.phi = Up < 0. ? -M_HALFPI : M_HALFPI;
111     } else {
112         lp.phi = Q->E / sqrt((1. + Up) / (1. - Up));
113         if ((lp.phi = pj_phi2(P->ctx, pow(lp.phi, 1. / Q->B), P->e)) == HUGE_VAL) {
114             proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
115             return lp;
116         }
117         lp.lam = - Q->rB * atan2((Sp * Q->cosgam -
118             Vp * Q->singam), cos(Q->BrA * u));
119     }
120     return lp;
121 }
122 
123 
PROJECTION(omerc)124 PJ *PROJECTION(omerc) {
125     double con, com, cosph0, D, F, H, L, sinph0, p, J, gamma=0,
126         gamma0, lamc=0, lam1=0, lam2=0, phi1=0, phi2=0, alpha_c=0;
127     int alp, gam, no_off = 0;
128 
129     struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
130     if (nullptr==Q)
131         return pj_default_destructor (P, ENOMEM);
132     P->opaque = Q;
133 
134     Q->no_rot = pj_param(P->ctx, P->params, "bno_rot").i;
135         if ((alp = pj_param(P->ctx, P->params, "talpha").i) != 0)
136             alpha_c = pj_param(P->ctx, P->params, "ralpha").f;
137         if ((gam = pj_param(P->ctx, P->params, "tgamma").i) != 0)
138             gamma = pj_param(P->ctx, P->params, "rgamma").f;
139     if (alp || gam) {
140         lamc    = pj_param(P->ctx, P->params, "rlonc").f;
141         no_off =
142                     /* For libproj4 compatibility */
143                     pj_param(P->ctx, P->params, "tno_off").i
144                     /* for backward compatibility */
145                     || pj_param(P->ctx, P->params, "tno_uoff").i;
146         if( no_off )
147         {
148             /* Mark the parameter as used, so that the pj_get_def() return them */
149             pj_param(P->ctx, P->params, "sno_uoff");
150             pj_param(P->ctx, P->params, "sno_off");
151         }
152     } else {
153         lam1 = pj_param(P->ctx, P->params, "rlon_1").f;
154         phi1 = pj_param(P->ctx, P->params, "rlat_1").f;
155         lam2 = pj_param(P->ctx, P->params, "rlon_2").f;
156         phi2 = pj_param(P->ctx, P->params, "rlat_2").f;
157         if (fabs(phi1) > M_HALFPI || fabs(phi2) > M_HALFPI)
158             return pj_default_destructor(P, PJD_ERR_LAT_LARGER_THAN_90);
159         if (fabs(phi1 - phi2) <= TOL ||
160             (con = fabs(phi1)) <= TOL ||
161             fabs(con - M_HALFPI) <= TOL ||
162             fabs(fabs(P->phi0) - M_HALFPI) <= TOL ||
163             fabs(fabs(phi2) - M_HALFPI) <= TOL)
164                 return pj_default_destructor(P, PJD_ERR_LAT_0_OR_ALPHA_EQ_90);
165     }
166     com = sqrt(P->one_es);
167     if (fabs(P->phi0) > EPS) {
168         sinph0 = sin(P->phi0);
169         cosph0 = cos(P->phi0);
170         con = 1. - P->es * sinph0 * sinph0;
171         Q->B = cosph0 * cosph0;
172         Q->B = sqrt(1. + P->es * Q->B * Q->B / P->one_es);
173         Q->A = Q->B * P->k0 * com / con;
174         D = Q->B * com / (cosph0 * sqrt(con));
175         if ((F = D * D - 1.) <= 0.)
176             F = 0.;
177         else {
178             F = sqrt(F);
179             if (P->phi0 < 0.)
180                 F = -F;
181         }
182         Q->E = F += D;
183         Q->E *= pow(pj_tsfn(P->phi0, sinph0, P->e), Q->B);
184     } else {
185         Q->B = 1. / com;
186         Q->A = P->k0;
187         Q->E = D = F = 1.;
188     }
189     if (alp || gam) {
190         if (alp) {
191             gamma0 = aasin(P->ctx, sin(alpha_c) / D);
192             if (!gam)
193                 gamma = alpha_c;
194         } else
195             alpha_c = aasin(P->ctx, D*sin(gamma0 = gamma));
196         if( fabs(fabs(P->phi0) - M_HALFPI) <= TOL ) {
197             return pj_default_destructor(P, PJD_ERR_LAT_0_OR_ALPHA_EQ_90);
198         }
199         P->lam0 = lamc - aasin(P->ctx, .5 * (F - 1. / F) *
200            tan(gamma0)) / Q->B;
201     } else {
202         H = pow(pj_tsfn(phi1, sin(phi1), P->e), Q->B);
203         L = pow(pj_tsfn(phi2, sin(phi2), P->e), Q->B);
204         F = Q->E / H;
205         p = (L - H) / (L + H);
206         if( p == 0 ) {
207             // Not quite, but es is very close to 1...
208             return pj_default_destructor(P, PJD_ERR_INVALID_ECCENTRICITY);
209         }
210         J = Q->E * Q->E;
211         J = (J - L * H) / (J + L * H);
212         if ((con = lam1 - lam2) < -M_PI)
213             lam2 -= M_TWOPI;
214         else if (con > M_PI)
215             lam2 += M_TWOPI;
216         P->lam0 = adjlon(.5 * (lam1 + lam2) - atan(
217            J * tan(.5 * Q->B * (lam1 - lam2)) / p) / Q->B);
218         const double denom = F - 1. / F;
219         if( denom == 0 ) {
220             return pj_default_destructor(P, PJD_ERR_INVALID_ECCENTRICITY);
221         }
222         gamma0 = atan(2. * sin(Q->B * adjlon(lam1 - P->lam0)) / denom);
223         gamma = alpha_c = aasin(P->ctx, D * sin(gamma0));
224     }
225     Q->singam = sin(gamma0);
226     Q->cosgam = cos(gamma0);
227     Q->sinrot = sin(gamma);
228     Q->cosrot = cos(gamma);
229     Q->BrA = 1. / (Q->ArB = Q->A * (Q->rB = 1. / Q->B));
230     Q->AB = Q->A * Q->B;
231     if (no_off)
232         Q->u_0 = 0;
233     else {
234         Q->u_0 = fabs(Q->ArB * atan(sqrt(D * D - 1.) / cos(alpha_c)));
235         if (P->phi0 < 0.)
236             Q->u_0 = - Q->u_0;
237     }
238     F = 0.5 * gamma0;
239     Q->v_pole_n = Q->ArB * log(tan(M_FORTPI - F));
240     Q->v_pole_s = Q->ArB * log(tan(M_FORTPI + F));
241     P->inv = omerc_e_inverse;
242     P->fwd = omerc_e_forward;
243 
244     return P;
245 }
246