/src/proj/src/projections/cass.cpp
Line | Count | Source |
1 | | |
2 | | |
3 | | #include <errno.h> |
4 | | #include <math.h> |
5 | | |
6 | | #include "proj.h" |
7 | | #include "proj_internal.h" |
8 | | |
9 | | PROJ_HEAD(cass, "Cassini") "\n\tCyl, Sph&Ell"; |
10 | | |
11 | 0 | #define C1 .16666666666666666666 |
12 | 0 | #define C2 .00833333333333333333 |
13 | 0 | #define C3 .04166666666666666666 |
14 | 0 | #define C4 .33333333333333333333 |
15 | 0 | #define C5 .06666666666666666666 |
16 | | |
17 | | namespace { // anonymous namespace |
18 | | struct cass_data { |
19 | | double *en; |
20 | | double m0; |
21 | | bool hyperbolic; |
22 | | }; |
23 | | } // anonymous namespace |
24 | | |
25 | 0 | static PJ_XY cass_e_forward(PJ_LP lp, PJ *P) { /* Ellipsoidal, forward */ |
26 | 0 | PJ_XY xy = {0.0, 0.0}; |
27 | 0 | struct cass_data *Q = static_cast<struct cass_data *>(P->opaque); |
28 | |
|
29 | 0 | const double sinphi = sin(lp.phi); |
30 | 0 | const double cosphi = cos(lp.phi); |
31 | 0 | const double M = pj_mlfn(lp.phi, sinphi, cosphi, Q->en); |
32 | |
|
33 | 0 | const double nu_square = 1. / (1. - P->es * sinphi * sinphi); |
34 | 0 | const double nu = sqrt(nu_square); |
35 | 0 | const double tanphi = tan(lp.phi); |
36 | 0 | const double T = tanphi * tanphi; |
37 | 0 | const double A = lp.lam * cosphi; |
38 | 0 | const double C = P->es * (cosphi * cosphi) / (1 - P->es); |
39 | 0 | const double A2 = A * A; |
40 | |
|
41 | 0 | xy.x = nu * A * (1. - A2 * T * (C1 + (8. - T + 8. * C) * A2 * C2)); |
42 | 0 | xy.y = M - Q->m0 + nu * tanphi * A2 * (.5 + (5. - T + 6. * C) * A2 * C3); |
43 | 0 | if (Q->hyperbolic) { |
44 | 0 | const double rho = nu_square * (1. - P->es) * nu; |
45 | 0 | xy.y -= xy.y * xy.y * xy.y / (6 * rho * nu); |
46 | 0 | } |
47 | |
|
48 | 0 | return xy; |
49 | 0 | } |
50 | | |
51 | 0 | static PJ_XY cass_s_forward(PJ_LP lp, PJ *P) { /* Spheroidal, forward */ |
52 | 0 | PJ_XY xy = {0.0, 0.0}; |
53 | 0 | xy.x = asin(cos(lp.phi) * sin(lp.lam)); |
54 | 0 | xy.y = atan2(tan(lp.phi), cos(lp.lam)) - P->phi0; |
55 | 0 | return xy; |
56 | 0 | } |
57 | | |
58 | 0 | static PJ_LP cass_e_inverse(PJ_XY xy, PJ *P) { /* Ellipsoidal, inverse */ |
59 | 0 | PJ_LP lp = {0.0, 0.0}; |
60 | 0 | struct cass_data *Q = static_cast<struct cass_data *>(P->opaque); |
61 | |
|
62 | 0 | const double phi1 = pj_inv_mlfn(Q->m0 + xy.y, Q->en); |
63 | 0 | const double tanphi1 = tan(phi1); |
64 | 0 | const double T1 = tanphi1 * tanphi1; |
65 | 0 | const double sinphi1 = sin(phi1); |
66 | 0 | const double nu1_square = 1. / (1. - P->es * sinphi1 * sinphi1); |
67 | 0 | const double nu1 = sqrt(nu1_square); |
68 | 0 | const double rho1 = nu1_square * (1. - P->es) * nu1; |
69 | 0 | const double D = xy.x / nu1; |
70 | 0 | const double D2 = D * D; |
71 | 0 | lp.phi = |
72 | 0 | phi1 - (nu1 * tanphi1 / rho1) * D2 * (.5 - (1. + 3. * T1) * D2 * C3); |
73 | 0 | lp.lam = D * (1. + T1 * D2 * (-C4 + (1. + 3. * T1) * D2 * C5)) / cos(phi1); |
74 | | |
75 | | // EPSG guidance note 7-2 suggests a custom approximation for the |
76 | | // 'Vanua Levu 1915 / Vanua Levu Grid' case, but better use the |
77 | | // generic inversion method |
78 | | // Actually use it in the non-hyperbolic case. It enables to make the |
79 | | // 5108.gie roundtripping tests to success, with at most 2 iterations. |
80 | 0 | constexpr double deltaXYTolerance = 1e-12; |
81 | 0 | lp = pj_generic_inverse_2d(xy, P, lp, deltaXYTolerance); |
82 | |
|
83 | 0 | return lp; |
84 | 0 | } |
85 | | |
86 | 0 | static PJ_LP cass_s_inverse(PJ_XY xy, PJ *P) { /* Spheroidal, inverse */ |
87 | 0 | PJ_LP lp = {0.0, 0.0}; |
88 | 0 | double dd; |
89 | 0 | lp.phi = asin(sin(dd = xy.y + P->phi0) * cos(xy.x)); |
90 | 0 | lp.lam = atan2(tan(xy.x), cos(dd)); |
91 | 0 | return lp; |
92 | 0 | } |
93 | | |
94 | 11 | static PJ *pj_cass_destructor(PJ *P, int errlev) { /* Destructor */ |
95 | 11 | if (nullptr == P) |
96 | 0 | return nullptr; |
97 | | |
98 | 11 | if (nullptr == P->opaque) |
99 | 0 | return pj_default_destructor(P, errlev); |
100 | | |
101 | 11 | free(static_cast<struct cass_data *>(P->opaque)->en); |
102 | 11 | return pj_default_destructor(P, errlev); |
103 | 11 | } |
104 | | |
105 | 14 | PJ *PJ_PROJECTION(cass) { |
106 | | |
107 | | /* Spheroidal? */ |
108 | 14 | if (0 == P->es) { |
109 | 3 | P->inv = cass_s_inverse; |
110 | 3 | P->fwd = cass_s_forward; |
111 | 3 | return P; |
112 | 3 | } |
113 | | |
114 | | /* otherwise it's ellipsoidal */ |
115 | 11 | auto Q = |
116 | 11 | static_cast<struct cass_data *>(calloc(1, sizeof(struct cass_data))); |
117 | 11 | P->opaque = Q; |
118 | 11 | if (nullptr == P->opaque) |
119 | 0 | return pj_default_destructor(P, PROJ_ERR_OTHER /*ENOMEM*/); |
120 | 11 | P->destructor = pj_cass_destructor; |
121 | | |
122 | 11 | Q->en = pj_enfn(P->n); |
123 | 11 | if (nullptr == Q->en) |
124 | 0 | return pj_default_destructor(P, PROJ_ERR_OTHER /*ENOMEM*/); |
125 | | |
126 | 11 | Q->m0 = pj_mlfn(P->phi0, sin(P->phi0), cos(P->phi0), Q->en); |
127 | 11 | if (pj_param_exists(P->params, "hyperbolic")) |
128 | 5 | Q->hyperbolic = true; |
129 | 11 | P->inv = cass_e_inverse; |
130 | 11 | P->fwd = cass_e_forward; |
131 | | |
132 | 11 | return P; |
133 | 11 | } |
134 | | |
135 | | #undef C1 |
136 | | #undef C2 |
137 | | #undef C3 |
138 | | #undef C4 |
139 | | #undef C5 |