1 #define PJ_LIB__
2 
3 #include <errno.h>
4 #include <math.h>
5 
6 #include "proj.h"
7 #include "proj_internal.h"
8 
9 PROJ_HEAD(gn_sinu, "General Sinusoidal Series") "\n\tPCyl, Sph\n\tm= n=";
10 PROJ_HEAD(sinu, "Sinusoidal (Sanson-Flamsteed)") "\n\tPCyl, Sph&Ell";
11 PROJ_HEAD(eck6, "Eckert VI") "\n\tPCyl, Sph";
12 PROJ_HEAD(mbtfps, "McBryde-Thomas Flat-Polar Sinusoidal") "\n\tPCyl, Sph";
13 
14 #define EPS10    1e-10
15 #define MAX_ITER 8
16 #define LOOP_TOL 1e-7
17 
18 namespace { // anonymous namespace
19 struct pj_opaque {
20     double *en;
21     double m, n, C_x, C_y;
22 };
23 } // anonymous namespace
24 
25 
gn_sinu_e_forward(PJ_LP lp,PJ * P)26 static PJ_XY gn_sinu_e_forward (PJ_LP lp, PJ *P) {          /* Ellipsoidal, forward */
27     PJ_XY xy = {0.0,0.0};
28 
29     const double s = sin(lp.phi);
30     const double c = cos(lp.phi);
31     xy.y = pj_mlfn(lp.phi, s, c, static_cast<struct pj_opaque*>(P->opaque)->en);
32     xy.x = lp.lam * c / sqrt(1. - P->es * s * s);
33     return xy;
34 }
35 
36 
gn_sinu_e_inverse(PJ_XY xy,PJ * P)37 static PJ_LP gn_sinu_e_inverse (PJ_XY xy, PJ *P) {          /* Ellipsoidal, inverse */
38     PJ_LP lp = {0.0,0.0};
39     double s;
40 
41     lp.phi = pj_inv_mlfn(P->ctx, xy.y, P->es, static_cast<struct pj_opaque*>(P->opaque)->en);
42     s = fabs(lp.phi);
43     if (s < M_HALFPI) {
44         s = sin(lp.phi);
45         lp.lam = xy.x * sqrt(1. - P->es * s * s) / cos(lp.phi);
46     } else if ((s - EPS10) < M_HALFPI) {
47         lp.lam = 0.;
48     } else {
49         proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
50     }
51 
52     return lp;
53 }
54 
55 
gn_sinu_s_forward(PJ_LP lp,PJ * P)56 static PJ_XY gn_sinu_s_forward (PJ_LP lp, PJ *P) {           /* Spheroidal, forward */
57     PJ_XY xy = {0.0,0.0};
58     struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
59 
60     if (Q->m == 0.0)
61         lp.phi = Q->n != 1. ? aasin(P->ctx,Q->n * sin(lp.phi)): lp.phi;
62     else {
63         int i;
64 
65         const double k = Q->n * sin(lp.phi);
66         for (i = MAX_ITER; i ; --i) {
67             const double V = (Q->m * lp.phi + sin(lp.phi) - k) /
68                              (Q->m + cos(lp.phi));
69             lp.phi -= V;
70             if (fabs(V) < LOOP_TOL)
71                 break;
72         }
73         if (!i) {
74             proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
75             return xy;
76         }
77 
78     }
79     xy.x = Q->C_x * lp.lam * (Q->m + cos(lp.phi));
80     xy.y = Q->C_y * lp.phi;
81 
82     return xy;
83 }
84 
85 
gn_sinu_s_inverse(PJ_XY xy,PJ * P)86 static PJ_LP gn_sinu_s_inverse (PJ_XY xy, PJ *P) {           /* Spheroidal, inverse */
87     PJ_LP lp = {0.0,0.0};
88     struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
89 
90     xy.y /= Q->C_y;
91     lp.phi = (Q->m != 0.0) ? aasin(P->ctx,(Q->m * xy.y + sin(xy.y)) / Q->n) :
92         ( Q->n != 1. ? aasin(P->ctx,sin(xy.y) / Q->n) : xy.y );
93     lp.lam = xy.x / (Q->C_x * (Q->m + cos(xy.y)));
94     return lp;
95 }
96 
97 
destructor(PJ * P,int errlev)98 static PJ *destructor (PJ *P, int errlev) {                        /* Destructor */
99     if (nullptr==P)
100         return nullptr;
101 
102     if (nullptr==P->opaque)
103         return pj_default_destructor (P, errlev);
104 
105     pj_dealloc (static_cast<struct pj_opaque*>(P->opaque)->en);
106     return pj_default_destructor (P, errlev);
107 }
108 
109 
110 
111 /* for spheres, only */
setup(PJ * P)112 static void setup(PJ *P) {
113     struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
114     P->es = 0;
115     P->inv = gn_sinu_s_inverse;
116     P->fwd = gn_sinu_s_forward;
117 
118     Q->C_y = sqrt((Q->m + 1.) / Q->n);
119     Q->C_x = Q->C_y/(Q->m + 1.);
120 }
121 
122 
PROJECTION(sinu)123 PJ *PROJECTION(sinu) {
124     struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
125     if (nullptr==Q)
126         return pj_default_destructor (P, ENOMEM);
127     P->opaque = Q;
128     P->destructor = destructor;
129 
130     if (!(Q->en = pj_enfn(P->es)))
131         return pj_default_destructor (P, ENOMEM);
132 
133     if (P->es != 0.0) {
134         P->inv = gn_sinu_e_inverse;
135         P->fwd = gn_sinu_e_forward;
136     } else {
137         Q->n = 1.;
138         Q->m = 0.;
139         setup(P);
140     }
141     return P;
142 }
143 
144 
PROJECTION(eck6)145 PJ *PROJECTION(eck6) {
146     struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
147     if (nullptr==Q)
148         return pj_default_destructor (P, ENOMEM);
149     P->opaque = Q;
150     P->destructor = destructor;
151 
152     Q->m = 1.;
153     Q->n = 2.570796326794896619231321691;
154     setup(P);
155 
156     return P;
157 }
158 
159 
PROJECTION(mbtfps)160 PJ *PROJECTION(mbtfps) {
161     struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
162     if (nullptr==Q)
163         return pj_default_destructor (P, ENOMEM);
164     P->opaque = Q;
165     P->destructor = destructor;
166 
167     Q->m = 0.5;
168     Q->n = 1.785398163397448309615660845;
169     setup(P);
170 
171     return P;
172 }
173 
174 
PROJECTION(gn_sinu)175 PJ *PROJECTION(gn_sinu) {
176     struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
177     if (nullptr==Q)
178         return pj_default_destructor (P, ENOMEM);
179     P->opaque = Q;
180     P->destructor = destructor;
181 
182     if (pj_param(P->ctx, P->params, "tn").i && pj_param(P->ctx, P->params, "tm").i) {
183         Q->n = pj_param(P->ctx, P->params, "dn").f;
184         Q->m = pj_param(P->ctx, P->params, "dm").f;
185         if (Q->n <= 0 || Q->m < 0)
186             return destructor (P, PJD_ERR_INVALID_M_OR_N);
187     } else
188         return destructor (P, PJD_ERR_INVALID_M_OR_N);
189 
190     setup(P);
191 
192     return P;
193 }
194