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