/src/PROJ/src/projections/ocea.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(ocea, "Oblique Cylindrical Equal Area") |
10 | | "\n\tCyl, Sph" |
11 | | "lonc= alpha= or\n\tlat_1= lat_2= lon_1= lon_2="; |
12 | | |
13 | | namespace { // anonymous namespace |
14 | | struct pj_ocea { |
15 | | double rok; |
16 | | double rtk; |
17 | | double sinphi; |
18 | | double cosphi; |
19 | | }; |
20 | | } // anonymous namespace |
21 | | |
22 | 2.52k | static PJ_XY ocea_s_forward(PJ_LP lp, PJ *P) { /* Spheroidal, forward */ |
23 | 2.52k | PJ_XY xy = {0.0, 0.0}; |
24 | 2.52k | struct pj_ocea *Q = static_cast<struct pj_ocea *>(P->opaque); |
25 | 2.52k | double t; |
26 | 2.52k | xy.y = sin(lp.lam); |
27 | 2.52k | t = cos(lp.lam); |
28 | 2.52k | xy.x = atan((tan(lp.phi) * Q->cosphi + Q->sinphi * xy.y) / t); |
29 | 2.52k | if (t < 0.) |
30 | 2.43k | xy.x += M_PI; |
31 | 2.52k | xy.x *= Q->rtk; |
32 | 2.52k | xy.y = Q->rok * (Q->sinphi * sin(lp.phi) - Q->cosphi * cos(lp.phi) * xy.y); |
33 | 2.52k | return xy; |
34 | 2.52k | } |
35 | | |
36 | 0 | static PJ_LP ocea_s_inverse(PJ_XY xy, PJ *P) { /* Spheroidal, inverse */ |
37 | 0 | PJ_LP lp = {0.0, 0.0}; |
38 | 0 | struct pj_ocea *Q = static_cast<struct pj_ocea *>(P->opaque); |
39 | |
|
40 | 0 | xy.y /= Q->rok; |
41 | 0 | xy.x /= Q->rtk; |
42 | 0 | const double t = sqrt(1. - xy.y * xy.y); |
43 | 0 | const double s = sin(xy.x); |
44 | 0 | lp.phi = asin(xy.y * Q->sinphi + t * Q->cosphi * s); |
45 | 0 | lp.lam = atan2(t * Q->sinphi * s - xy.y * Q->cosphi, t * cos(xy.x)); |
46 | 0 | return lp; |
47 | 0 | } |
48 | | |
49 | 82 | PJ *PJ_PROJECTION(ocea) { |
50 | 82 | double phi_1, phi_2, lam_1, lam_2, lonz, alpha; |
51 | | |
52 | 82 | struct pj_ocea *Q = |
53 | 82 | static_cast<struct pj_ocea *>(calloc(1, sizeof(struct pj_ocea))); |
54 | 82 | if (nullptr == Q) |
55 | 0 | return pj_default_destructor(P, PROJ_ERR_OTHER /*ENOMEM*/); |
56 | 82 | P->opaque = Q; |
57 | | |
58 | 82 | Q->rok = 1. / P->k0; |
59 | 82 | Q->rtk = P->k0; |
60 | 82 | double lam_p, phi_p; |
61 | | /*If the keyword "alpha" is found in the sentence then use 1point+1azimuth*/ |
62 | 82 | if (pj_param(P->ctx, P->params, "talpha").i) { |
63 | | /*Define Pole of oblique transformation from 1 point & 1 azimuth*/ |
64 | | // ERO: I've added M_PI so that the alpha is the angle from point 1 to |
65 | | // point 2 from the North in a clockwise direction (to be consistent |
66 | | // with omerc behavior) |
67 | 3 | alpha = M_PI + pj_param(P->ctx, P->params, "ralpha").f; |
68 | 3 | lonz = pj_param(P->ctx, P->params, "rlonc").f; |
69 | | /*Equation 9-8 page 80 (http://pubs.usgs.gov/pp/1395/report.pdf)*/ |
70 | | // Actually slightliy modified to use atan2(), as it is suggested by |
71 | | // Snyder for equation 9-1, but this is not mentioned here |
72 | 3 | lam_p = atan2(-cos(alpha), -sin(P->phi0) * sin(alpha)) + lonz; |
73 | | /*Equation 9-7 page 80 (http://pubs.usgs.gov/pp/1395/report.pdf)*/ |
74 | 3 | phi_p = asin(cos(P->phi0) * sin(alpha)); |
75 | | /*If the keyword "alpha" is NOT found in the sentence then use 2points*/ |
76 | 79 | } else { |
77 | | /*Define Pole of oblique transformation from 2 points*/ |
78 | 79 | phi_1 = pj_param(P->ctx, P->params, "rlat_1").f; |
79 | 79 | phi_2 = pj_param(P->ctx, P->params, "rlat_2").f; |
80 | 79 | lam_1 = pj_param(P->ctx, P->params, "rlon_1").f; |
81 | 79 | lam_2 = pj_param(P->ctx, P->params, "rlon_2").f; |
82 | | /*Equation 9-1 page 80 (http://pubs.usgs.gov/pp/1395/report.pdf)*/ |
83 | 79 | lam_p = atan2(cos(phi_1) * sin(phi_2) * cos(lam_1) - |
84 | 79 | sin(phi_1) * cos(phi_2) * cos(lam_2), |
85 | 79 | sin(phi_1) * cos(phi_2) * sin(lam_2) - |
86 | 79 | cos(phi_1) * sin(phi_2) * sin(lam_1)); |
87 | | |
88 | | /* take care of P->lam0 wrap-around when +lam_1=-90*/ |
89 | 79 | if (lam_1 == -M_HALFPI) |
90 | 0 | lam_p = -lam_p; |
91 | | |
92 | | /*Equation 9-2 page 80 (http://pubs.usgs.gov/pp/1395/report.pdf)*/ |
93 | 79 | double cos_lamp_m_minus_lam_1 = cos(lam_p - lam_1); |
94 | 79 | double tan_phi_1 = tan(phi_1); |
95 | 79 | if (tan_phi_1 == 0.0) { |
96 | | // Not sure if we want to support this case, but at least this |
97 | | // avoids a division by zero, and gives the same result as the below |
98 | | // atan() |
99 | 79 | phi_p = (cos_lamp_m_minus_lam_1 >= 0.0) ? -M_HALFPI : M_HALFPI; |
100 | 79 | } else { |
101 | 0 | phi_p = atan(-cos_lamp_m_minus_lam_1 / tan_phi_1); |
102 | 0 | } |
103 | 79 | } |
104 | 82 | P->lam0 = lam_p + M_HALFPI; |
105 | 82 | Q->cosphi = cos(phi_p); |
106 | 82 | Q->sinphi = sin(phi_p); |
107 | 82 | P->inv = ocea_s_inverse; |
108 | 82 | P->fwd = ocea_s_forward; |
109 | 82 | P->es = 0.; |
110 | | |
111 | 82 | return P; |
112 | 82 | } |