/src/proj/src/projections/imoll.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(imoll, "Interrupted Mollweide") "\n\tPCyl, Sph"; |
10 | | |
11 | | /* |
12 | | This projection is a compilation of 6 separate sub-projections. |
13 | | It is related to the Interrupted Goode Homolosine projection, |
14 | | but uses Mollweide at all latitudes. |
15 | | |
16 | | Each sub-projection is assigned an integer label |
17 | | numbered 1 through 6. Most of this code contains logic to assign |
18 | | the labels based on latitude (phi) and longitude (lam) regions. |
19 | | The code is adapted from igh.cpp. |
20 | | |
21 | | Original Reference: |
22 | | J. Paul Goode (1919) STUDIES IN PROJECTIONS: ADAPTING THE |
23 | | HOMOLOGRAPHIC PROJECTION TO THE PORTRAYAL OF THE EARTH'S |
24 | | ENTIRE SURFACE, Bul. Geog. SOC.Phila., Vol. XWIJNO.3. |
25 | | July, 1919, pp. 103-113. |
26 | | */ |
27 | | |
28 | | C_NAMESPACE PJ *pj_moll(PJ *); |
29 | | |
30 | | namespace pj_imoll_ns { |
31 | | struct pj_imoll_data { |
32 | | struct PJconsts *pj[6]; |
33 | | // We need to know the inverse boundary locations of the "seams". |
34 | | double boundary12; |
35 | | double boundary34; |
36 | | double boundary45; |
37 | | double boundary56; |
38 | | }; |
39 | | |
40 | | constexpr double d20 = 20 * DEG_TO_RAD; |
41 | | constexpr double d30 = 30 * DEG_TO_RAD; |
42 | | constexpr double d40 = 40 * DEG_TO_RAD; |
43 | | constexpr double d60 = 60 * DEG_TO_RAD; |
44 | | constexpr double d80 = 80 * DEG_TO_RAD; |
45 | | constexpr double d100 = 100 * DEG_TO_RAD; |
46 | | constexpr double d140 = 140 * DEG_TO_RAD; |
47 | | constexpr double d160 = 160 * DEG_TO_RAD; |
48 | | constexpr double d180 = 180 * DEG_TO_RAD; |
49 | | |
50 | | constexpr double EPSLN = |
51 | | 1.e-10; /* allow a little 'slack' on zone edge positions */ |
52 | | |
53 | | } // namespace pj_imoll_ns |
54 | | |
55 | | /* |
56 | | Assign an integer index representing each of the 6 |
57 | | sub-projection zones based on latitude (phi) and |
58 | | longitude (lam) ranges. |
59 | | */ |
60 | | |
61 | 0 | static PJ_XY imoll_s_forward(PJ_LP lp, PJ *P) { /* Spheroidal, forward */ |
62 | 0 | using namespace pj_imoll_ns; |
63 | |
|
64 | 0 | PJ_XY xy; |
65 | 0 | struct pj_imoll_data *Q = static_cast<struct pj_imoll_data *>(P->opaque); |
66 | 0 | int z; |
67 | |
|
68 | 0 | if (lp.phi >= 0) { /* 1|2 */ |
69 | 0 | z = (lp.lam <= -d40 ? 1 : 2); |
70 | 0 | } else { /* 3|4|5|6 */ |
71 | 0 | if (lp.lam <= -d100) |
72 | 0 | z = 3; /* 3 */ |
73 | 0 | else if (lp.lam <= -d20) |
74 | 0 | z = 4; /* 4 */ |
75 | 0 | else if (lp.lam <= d80) |
76 | 0 | z = 5; /* 5 */ |
77 | 0 | else |
78 | 0 | z = 6; /* 6 */ |
79 | 0 | } |
80 | |
|
81 | 0 | lp.lam -= Q->pj[z - 1]->lam0; |
82 | 0 | xy = Q->pj[z - 1]->fwd(lp, Q->pj[z - 1]); |
83 | 0 | xy.x += Q->pj[z - 1]->x0; |
84 | 0 | xy.y += Q->pj[z - 1]->y0; |
85 | |
|
86 | 0 | return xy; |
87 | 0 | } |
88 | | |
89 | 0 | static PJ_LP imoll_s_inverse(PJ_XY xy, PJ *P) { /* Spheroidal, inverse */ |
90 | 0 | using namespace pj_imoll_ns; |
91 | |
|
92 | 0 | PJ_LP lp = {0.0, 0.0}; |
93 | 0 | struct pj_imoll_data *Q = static_cast<struct pj_imoll_data *>(P->opaque); |
94 | 0 | const double y90 = sqrt(2.0); /* lt=90 corresponds to y=sqrt(2) */ |
95 | |
|
96 | 0 | int z = 0; |
97 | 0 | if (xy.y > y90 + EPSLN || xy.y < -y90 + EPSLN) /* 0 */ |
98 | 0 | z = 0; |
99 | 0 | else if (xy.y >= 0) |
100 | 0 | z = (xy.x <= (Q->boundary12) ? 1 : 2); /* 1|2 */ |
101 | 0 | else { |
102 | 0 | if (xy.x <= Q->boundary34) |
103 | 0 | z = 3; /* 3 */ |
104 | 0 | else if (xy.x <= Q->boundary45) |
105 | 0 | z = 4; /* 4 */ |
106 | 0 | else if (xy.x <= Q->boundary56) |
107 | 0 | z = 5; /* 5 */ |
108 | 0 | else |
109 | 0 | z = 6; /* 6 */ |
110 | 0 | } |
111 | |
|
112 | 0 | if (z) { |
113 | 0 | bool ok = false; |
114 | |
|
115 | 0 | xy.x -= Q->pj[z - 1]->x0; |
116 | 0 | xy.y -= Q->pj[z - 1]->y0; |
117 | 0 | lp = Q->pj[z - 1]->inv(xy, Q->pj[z - 1]); |
118 | 0 | lp.lam += Q->pj[z - 1]->lam0; |
119 | |
|
120 | 0 | switch (z) { |
121 | 0 | case 1: |
122 | 0 | ok = ((lp.lam >= -d180 - EPSLN && lp.lam <= -d40 + EPSLN) && |
123 | 0 | (lp.phi >= 0.0 - EPSLN)); |
124 | 0 | break; |
125 | 0 | case 2: |
126 | 0 | ok = ((lp.lam >= -d40 - EPSLN && lp.lam <= d180 + EPSLN) && |
127 | 0 | (lp.phi >= 0.0 - EPSLN)); |
128 | 0 | break; |
129 | 0 | case 3: |
130 | 0 | ok = ((lp.lam >= -d180 - EPSLN && lp.lam <= -d100 + EPSLN) && |
131 | 0 | (lp.phi <= 0.0 + EPSLN)); |
132 | 0 | break; |
133 | 0 | case 4: |
134 | 0 | ok = ((lp.lam >= -d100 - EPSLN && lp.lam <= -d20 + EPSLN) && |
135 | 0 | (lp.phi <= 0.0 + EPSLN)); |
136 | 0 | break; |
137 | 0 | case 5: |
138 | 0 | ok = ((lp.lam >= -d20 - EPSLN && lp.lam <= d80 + EPSLN) && |
139 | 0 | (lp.phi <= 0.0 + EPSLN)); |
140 | 0 | break; |
141 | 0 | case 6: |
142 | 0 | ok = ((lp.lam >= d80 - EPSLN && lp.lam <= d180 + EPSLN) && |
143 | 0 | (lp.phi <= 0.0 + EPSLN)); |
144 | 0 | break; |
145 | 0 | } |
146 | 0 | z = (!ok ? 0 : z); /* projectable? */ |
147 | 0 | } |
148 | | |
149 | 0 | if (!z) |
150 | 0 | lp.lam = HUGE_VAL; |
151 | 0 | if (!z) |
152 | 0 | lp.phi = HUGE_VAL; |
153 | |
|
154 | 0 | return lp; |
155 | 0 | } |
156 | | |
157 | 0 | static PJ *pj_imoll_destructor(PJ *P, int errlev) { |
158 | 0 | using namespace pj_imoll_ns; |
159 | |
|
160 | 0 | int i; |
161 | 0 | if (nullptr == P) |
162 | 0 | return nullptr; |
163 | | |
164 | 0 | if (nullptr == P->opaque) |
165 | 0 | return pj_default_destructor(P, errlev); |
166 | | |
167 | 0 | struct pj_imoll_data *Q = static_cast<struct pj_imoll_data *>(P->opaque); |
168 | |
|
169 | 0 | for (i = 0; i < 6; ++i) { |
170 | 0 | if (Q->pj[i]) |
171 | 0 | Q->pj[i]->destructor(Q->pj[i], errlev); |
172 | 0 | } |
173 | |
|
174 | 0 | return pj_default_destructor(P, errlev); |
175 | 0 | } |
176 | | |
177 | | /* |
178 | | Zones: |
179 | | |
180 | | -180 -40 180 |
181 | | +--------------+-------------------------+ |
182 | | |1 |2 | |
183 | | | | | |
184 | | | | | |
185 | | | | | |
186 | | | | | |
187 | | 0 +-------+------+-+-----------+-----------+ |
188 | | |3 |4 |5 |6 | |
189 | | | | | | | |
190 | | | | | | | |
191 | | | | | | | |
192 | | | | | | | |
193 | | +-------+--------+-----------+-----------+ |
194 | | -180 -100 -20 80 180 |
195 | | */ |
196 | | |
197 | | static bool setup_zone(PJ *P, struct pj_imoll_ns::pj_imoll_data *Q, int n, |
198 | | PJ *(*proj_ptr)(PJ *), double x_0, double y_0, |
199 | 0 | double lon_0) { |
200 | 0 | if (!(Q->pj[n - 1] = proj_ptr(nullptr))) |
201 | 0 | return false; |
202 | 0 | if (!(Q->pj[n - 1] = proj_ptr(Q->pj[n - 1]))) |
203 | 0 | return false; |
204 | 0 | Q->pj[n - 1]->ctx = P->ctx; |
205 | 0 | Q->pj[n - 1]->x0 = x_0; |
206 | 0 | Q->pj[n - 1]->y0 = y_0; |
207 | 0 | Q->pj[n - 1]->lam0 = lon_0; |
208 | 0 | return true; |
209 | 0 | } |
210 | | |
211 | | static double compute_zone_offset(struct pj_imoll_ns::pj_imoll_data *Q, |
212 | | int zone1, int zone2, double lam, double phi1, |
213 | 0 | double phi2) { |
214 | 0 | PJ_LP lp1, lp2; |
215 | 0 | PJ_XY xy1, xy2; |
216 | |
|
217 | 0 | lp1.lam = lam - (Q->pj[zone1 - 1]->lam0); |
218 | 0 | lp1.phi = phi1; |
219 | 0 | lp2.lam = lam - (Q->pj[zone2 - 1]->lam0); |
220 | 0 | lp2.phi = phi2; |
221 | 0 | xy1 = Q->pj[zone1 - 1]->fwd(lp1, Q->pj[zone1 - 1]); |
222 | 0 | xy2 = Q->pj[zone2 - 1]->fwd(lp2, Q->pj[zone2 - 1]); |
223 | 0 | return (xy2.x + Q->pj[zone2 - 1]->x0) - (xy1.x + Q->pj[zone1 - 1]->x0); |
224 | 0 | } |
225 | | |
226 | 0 | static double compute_zone_x_boundary(PJ *P, double lam, double phi) { |
227 | 0 | PJ_LP lp1, lp2; |
228 | 0 | PJ_XY xy1, xy2; |
229 | |
|
230 | 0 | lp1.lam = lam - pj_imoll_ns::EPSLN; |
231 | 0 | lp1.phi = phi; |
232 | 0 | lp2.lam = lam + pj_imoll_ns::EPSLN; |
233 | 0 | lp2.phi = phi; |
234 | 0 | xy1 = imoll_s_forward(lp1, P); |
235 | 0 | xy2 = imoll_s_forward(lp2, P); |
236 | 0 | return (xy1.x + xy2.x) / 2.; |
237 | 0 | } |
238 | | |
239 | 0 | PJ *PJ_PROJECTION(imoll) { |
240 | 0 | using namespace pj_imoll_ns; |
241 | |
|
242 | 0 | struct pj_imoll_data *Q = static_cast<struct pj_imoll_data *>( |
243 | 0 | calloc(1, sizeof(struct pj_imoll_data))); |
244 | 0 | if (nullptr == Q) |
245 | 0 | return pj_default_destructor(P, PROJ_ERR_OTHER /*ENOMEM*/); |
246 | 0 | P->opaque = Q; |
247 | | |
248 | | /* Setup zones */ |
249 | 0 | if (!setup_zone(P, Q, 1, pj_moll, -d100, 0, -d100) || |
250 | 0 | !setup_zone(P, Q, 2, pj_moll, d30, 0, d30) || |
251 | 0 | !setup_zone(P, Q, 3, pj_moll, -d160, 0, -d160) || |
252 | 0 | !setup_zone(P, Q, 4, pj_moll, -d60, 0, -d60) || |
253 | 0 | !setup_zone(P, Q, 5, pj_moll, d20, 0, d20) || |
254 | 0 | !setup_zone(P, Q, 6, pj_moll, d140, 0, d140)) { |
255 | 0 | return pj_imoll_destructor(P, PROJ_ERR_OTHER /*ENOMEM*/); |
256 | 0 | } |
257 | | |
258 | | /* Adjust zones */ |
259 | | |
260 | | /* Match 3 (south) to 1 (north) */ |
261 | 0 | Q->pj[2]->x0 += |
262 | 0 | compute_zone_offset(Q, 3, 1, -d160, 0.0 - EPSLN, 0.0 + EPSLN); |
263 | | |
264 | | /* Match 2 (north-east) to 1 (north-west) */ |
265 | 0 | Q->pj[1]->x0 += |
266 | 0 | compute_zone_offset(Q, 2, 1, -d40, 0.0 + EPSLN, 0.0 + EPSLN); |
267 | | |
268 | | /* Match 4 (south) to 1 (north) */ |
269 | 0 | Q->pj[3]->x0 += |
270 | 0 | compute_zone_offset(Q, 4, 1, -d100, 0.0 - EPSLN, 0.0 + EPSLN); |
271 | | |
272 | | /* Match 5 (south) to 2 (north) */ |
273 | 0 | Q->pj[4]->x0 += |
274 | 0 | compute_zone_offset(Q, 5, 2, -d20, 0.0 - EPSLN, 0.0 + EPSLN); |
275 | | |
276 | | /* Match 6 (south) to 2 (north) */ |
277 | 0 | Q->pj[5]->x0 += compute_zone_offset(Q, 6, 2, d80, 0.0 - EPSLN, 0.0 + EPSLN); |
278 | | |
279 | | /* |
280 | | The most straightforward way of computing the x locations of the "seams" |
281 | | in the interrupted projection is to compute the forward transform at the |
282 | | seams and record these values. |
283 | | */ |
284 | 0 | Q->boundary12 = compute_zone_x_boundary(P, -d40, 0.0 + EPSLN); |
285 | 0 | Q->boundary34 = compute_zone_x_boundary(P, -d100, 0.0 - EPSLN); |
286 | 0 | Q->boundary45 = compute_zone_x_boundary(P, -d20, 0.0 - EPSLN); |
287 | 0 | Q->boundary56 = compute_zone_x_boundary(P, d80, 0.0 - EPSLN); |
288 | |
|
289 | 0 | P->inv = imoll_s_inverse; |
290 | 0 | P->fwd = imoll_s_forward; |
291 | 0 | P->destructor = pj_imoll_destructor; |
292 | 0 | P->es = 0.; |
293 | |
|
294 | 0 | return P; |
295 | 0 | } |