Coverage Report

Created: 2025-06-13 06:29

/src/proj/src/projections/airocean.cpp
Line
Count
Source (jump to first uncovered line)
1
/* enable predefined math constants M_* for MS Visual Studio */
2
#if defined(_MSC_VER) || defined(_WIN32)
3
#ifndef _USE_MATH_DEFINES
4
#define _USE_MATH_DEFINES
5
#endif
6
#endif
7
8
#include <cmath>
9
#include <cstdint>
10
#include <errno.h>
11
12
#include "proj.h"
13
#include "proj_internal.h"
14
15
PROJ_HEAD(airocean, "Airocean") "\n\tMisc, Sph&Ell";
16
17
namespace { // anonymous namespace
18
struct pj_face {
19
    PJ_XYZ p1;
20
    PJ_XYZ p2;
21
    PJ_XYZ p3;
22
};
23
} // namespace
24
25
/*
26
    The vertices of the faces of the icosahedron are inspired by those used by
27
   Robert W. Gray.
28
29
    Original Reference:
30
    Robert W. Gray (1995) Exact Transformation Equations for
31
        Fuller's World Map, Vol. 32. Autumn, 1995, pp. 17-25.
32
33
    To accommodate for land parts that would be interrupted by using a mere
34
   icosahedron, some faces are split in two (Australia) and 3 (Japan) subfaces.
35
36
    The parameters below were computed using the script located at:
37
    scripts/build_airocean_parameters.py
38
    (relative to the root of the project)
39
*/
40
// Define the 23 faces and subfaces
41
constexpr pj_face base_ico_faces[23] = {
42
    {{0.42015242670871, 0.07814524940278296, 0.9040825506150193},
43
     {0.5188367303273644, 0.8354203803782358, 0.18133183755726245},
44
     {0.9950094394362416, -0.09134779527642793, 0.040147175877166645}},
45
    {{0.42015242670871, 0.07814524940278296, 0.9040825506150193},
46
     {-0.4146822253203352, 0.6559624054348008, 0.6306758078914754},
47
     {0.5188367303273644, 0.8354203803782358, 0.18133183755726245}},
48
    {{0.42015242670871, 0.07814524940278296, 0.9040825506150193},
49
     {-0.5154559599440418, -0.381716898287133, 0.7672009925177475},
50
     {-0.4146822253203352, 0.6559624054348008, 0.6306758078914754}},
51
    {{0.42015242670871, 0.07814524940278296, 0.9040825506150193},
52
     {0.3557814025329447, -0.8435800024661781, 0.40223422660292557},
53
     {-0.5154559599440418, -0.381716898287133, 0.7672009925177475}},
54
    {{0.42015242670871, 0.07814524940278296, 0.9040825506150193},
55
     {0.9950094394362416, -0.09134779527642793, 0.040147175877166645},
56
     {0.3557814025329447, -0.8435800024661781, 0.40223422660292557}},
57
    {{0.9950094394362416, -0.09134779527642793, 0.040147175877166645},
58
     {0.5188367303273644, 0.8354203803782358, 0.18133183755726245},
59
     {0.5154559599440418, 0.381716898287133, -0.7672009925177475}},
60
    {{0.5154559599440418, 0.381716898287133, -0.7672009925177475},
61
     {0.5188367303273644, 0.8354203803782358, 0.18133183755726245},
62
     {-0.3557814025329447, 0.8435800024661781, -0.40223422660292557}},
63
    {{-0.3557814025329447, 0.8435800024661781, -0.40223422660292557},
64
     {0.5188367303273644, 0.8354203803782358, 0.18133183755726245},
65
     {-0.4146822253203352, 0.6559624054348008, 0.6306758078914754}},
66
    {{-0.5154559599440418, -0.381716898287133, 0.7672009925177475},
67
     {-0.9950094394362416, 0.09134779527642793, -0.040147175877166645},
68
     {-0.4146822253203352, 0.6559624054348008, 0.6306758078914754}},
69
    {{-0.5154559599440418, -0.381716898287133, 0.7672009925177475},
70
     {-0.5188367303273644, -0.8354203803782358, -0.18133183755726245},
71
     {-0.9950094394362416, 0.09134779527642793, -0.040147175877166645}},
72
    {{-0.5154559599440418, -0.381716898287133, 0.7672009925177475},
73
     {0.3557814025329447, -0.8435800024661781, 0.40223422660292557},
74
     {-0.5188367303273644, -0.8354203803782358, -0.18133183755726245}},
75
    {{-0.5188367303273644, -0.8354203803782358, -0.18133183755726245},
76
     {0.3557814025329447, -0.8435800024661781, 0.40223422660292557},
77
     {0.4146822253203352, -0.6559624054348008, -0.6306758078914754}},
78
    {{0.4146822253203352, -0.6559624054348008, -0.6306758078914754},
79
     {0.3557814025329447, -0.8435800024661781, 0.40223422660292557},
80
     {0.9950094394362416, -0.09134779527642793, 0.040147175877166645}},
81
    {{0.5154559599440418, 0.381716898287133, -0.7672009925177475},
82
     {0.4146822253203352, -0.6559624054348008, -0.6306758078914754},
83
     {0.9950094394362416, -0.09134779527642793, 0.040147175877166645}},
84
    {{-0.42015242670871, -0.07814524940278296, -0.9040825506150193},
85
     {-0.3557814025329447, 0.8435800024661781, -0.40223422660292557},
86
     {-0.9950094394362416, 0.09134779527642793, -0.040147175877166645}},
87
    {{-0.42015242670871, -0.07814524940278296, -0.9040825506150193},
88
     {-0.9950094394362416, 0.09134779527642793, -0.040147175877166645},
89
     {-0.5188367303273644, -0.8354203803782358, -0.18133183755726245}},
90
    {{-0.42015242670871, -0.07814524940278296, -0.9040825506150193},
91
     {-0.5188367303273644, -0.8354203803782358, -0.18133183755726245},
92
     {0.4146822253203352, -0.6559624054348008, -0.6306758078914754}},
93
    {{-0.42015242670871, -0.07814524940278296, -0.9040825506150193},
94
     {0.4146822253203352, -0.6559624054348008, -0.6306758078914754},
95
     {0.5154559599440418, 0.381716898287133, -0.7672009925177475}},
96
    {{-0.3557814025329447, 0.8435800024661781, -0.40223422660292557},
97
     {-0.38796691462082733, 0.3827173765316976, -0.6531583886089725},
98
     {0.5154559599440418, 0.381716898287133, -0.7672009925177475}},
99
    {{-0.42015242670871, -0.07814524940278296, -0.9040825506150193},
100
     {0.5154559599440418, 0.381716898287133, -0.7672009925177475},
101
     {-0.38796691462082733, 0.3827173765316976, -0.6531583886089725}},
102
    {{-0.9950094394362416, 0.09134779527642793, -0.040147175877166645},
103
     {-0.3557814025329447, 0.8435800024661781, -0.40223422660292557},
104
     {-0.5884910224298405, 0.5302967343924689, 0.06276480180379439}},
105
    {{-0.3557814025329447, 0.8435800024661781, -0.40223422660292557},
106
     {-0.4146822253203352, 0.6559624054348008, 0.6306758078914754},
107
     {-0.5884910224298405, 0.5302967343924689, 0.06276480180379439}},
108
    {{-0.9950094394362416, 0.09134779527642793, -0.040147175877166645},
109
     {-0.5884910224298405, 0.5302967343924689, 0.06276480180379439},
110
     {-0.4146822253203352, 0.6559624054348008, 0.6306758078914754}}};
111
// // Define the centers for each face or subface
112
constexpr PJ_XYZ base_ico_centers[23] = {
113
    {0.6446661988241054, 0.27407261150153034, 0.37518718801648276},
114
    {0.17476897723857973, 0.5231760117386065, 0.5720300653545857},
115
    {-0.16999525285188902, 0.1174635855168169, 0.7673197836747474},
116
    {0.08682595643253761, -0.3823838837835094, 0.6911725899118975},
117
    {0.5903144228926321, -0.28559418277994103, 0.44882131769837047},
118
    {0.6764340432358825, 0.375263161129647, -0.18190732636110615},
119
    {0.22617042924615385, 0.6869057603771823, -0.3293677938544702},
120
    {-0.0838756325086385, 0.778320929426405, 0.13659113961527075},
121
    {-0.6417158749002062, 0.12186443414136523, 0.45257654151068544},
122
    {-0.6764340432358825, -0.375263161129647, 0.18190732636110615},
123
    {-0.22617042924615385, -0.6869057603771823, 0.32936779385447024},
124
    {0.0838756325086385, -0.778320929426405, -0.13659113961527075},
125
    {0.5884910224298405, -0.5302967343924689, -0.06276480180379439},
126
    {0.6417158749002062, -0.12186443414136523, -0.4525765415106855},
127
    {-0.5903144228926321, 0.28559418277994103, -0.44882131769837047},
128
    {-0.6446661988241054, -0.2740726115015303, -0.37518718801648276},
129
    {-0.17476897723857973, -0.5231760117386065, -0.5720300653545857},
130
    {0.16999525285188902, -0.11746358551681692, -0.7673197836747474},
131
    {-0.07609745240324339, 0.5360047590950029, -0.6075312025765486},
132
    {-0.09755446046183185, 0.2287630084720159, -0.7748139772472463},
133
    {-0.646427288133009, 0.48840817737835834, -0.12653886689209928},
134
    {-0.4529848834277068, 0.6766130474311494, 0.09706879436411474},
135
    {-0.6660608957288058, 0.4258689783678992, 0.2177644779393677}};
136
// // Define the normals for each face and subface
137
constexpr PJ_XYZ base_ico_normals[23] = {
138
    {0.8112534709140969, 0.34489532376393844, 0.47213877364139306},
139
    {0.21993077914046083, 0.6583691780274996, 0.7198475378926182},
140
    {-0.21392348345014195, 0.1478171829550702, 0.9656017935214206},
141
    {0.10926252787847963, -0.4811951572873208, 0.8697775121287253},
142
    {0.7428567301586793, -0.35939416782780276, 0.5648005936517034},
143
    {0.8512303986474292, 0.4722343788582682, -0.2289137388687808},
144
    {0.2846148069787909, 0.8644080972654203, -0.4144792552473538},
145
    {-0.10554981496139187, 0.9794457296411412, 0.17188746100093646},
146
    {-0.8075407579970092, 0.15335524858988167, 0.5695261994882688},
147
    {-0.8512303986474292, -0.4722343788582682, 0.22891373886878083},
148
    {-0.2846148069787909, -0.8644080972654203, 0.4144792552473538},
149
    {0.10554981496139185, -0.9794457296411412, -0.17188746100093638},
150
    {0.7405621473854482, -0.6673299564565524, -0.07898376463267347},
151
    {0.8075407579970092, -0.15335524858988167, -0.5695261994882688},
152
    {-0.7428567301586793, 0.35939416782780276, -0.5648005936517034},
153
    {-0.8112534709140969, -0.34489532376393844, -0.47213877364139306},
154
    {-0.21993077914046083, -0.6583691780274996, -0.7198475378926182},
155
    {0.21392348345014195, -0.1478171829550702, -0.9656017935214206},
156
    {-0.10926252787847963, 0.4811951572873209, -0.8697775121287253},
157
    {-0.10926252787847968, 0.4811951572873209, -0.8697775121287253},
158
    {-0.740562147385448, 0.6673299564565524, 0.07898376463267354},
159
    {-0.7405621473854481, 0.6673299564565524, 0.07898376463267347},
160
    {-0.7405621473854481, 0.6673299564565525, 0.07898376463267329}};
161
162
// /*
163
//     The points of the Airocean projection map are deduced from the unfolded
164
//     net of the altered icosahedron defined above. The distances in the
165
//     projected 2d space are expressed in meter.
166
// */
167
// // Define the 23 unfolded surfaces used (from icosahedron + split faces)
168
constexpr pj_face base_airocean_faces[23] = {
169
    {{1.8211859946200586, 3.1543866727148018, 1.0},
170
     {1.8211859946200586, 4.205848896953069, 1.0},
171
     {2.7317789919300877, 3.6801177848339353, 1.0}},
172
    {{1.8211859946200586, 3.1543866727148018, 1.0},
173
     {0.9105929973100293, 3.6801177848339353, 1.0},
174
     {1.8211859946200586, 4.205848896953069, 1.0}},
175
    {{1.8211859946200586, 3.1543866727148018, 1.0},
176
     {0.9105929973100293, 2.628655560595668, 1.0},
177
     {0.9105929973100293, 3.6801177848339353, 1.0}},
178
    {{1.8211859946200586, 3.1543866727148018, 1.0},
179
     {1.8211859946200586, 2.1029244484765344, 1.0},
180
     {0.9105929973100293, 2.628655560595668, 1.0}},
181
    {{1.8211859946200586, 3.1543866727148018, 1.0},
182
     {2.7317789919300877, 3.6801177848339353, 1.0},
183
     {2.7317789919300877, 2.628655560595668, 1.0}},
184
    {{2.7317789919300877, 3.6801177848339353, 1.0},
185
     {1.8211859946200586, 4.205848896953069, 1.0},
186
     {2.7317789919300877, 4.731580009072203, 1.0}},
187
    {{1.8211859946200586, 5.257311121191336, 1.0},
188
     {1.8211859946200586, 4.205848896953069, 1.0},
189
     {0.9105929973100293, 4.731580009072203, 1.0}},
190
    {{0.9105929973100293, 4.731580009072203, 1.0},
191
     {1.8211859946200586, 4.205848896953069, 1.0},
192
     {0.9105929973100293, 3.6801177848339353, 1.0}},
193
    {{0.9105929973100293, 2.628655560595668, 1.0},
194
     {0.0, 3.1543866727148018, 1.0},
195
     {0.9105929973100293, 3.6801177848339353, 1.0}},
196
    {{0.9105929973100293, 2.628655560595668, 1.0},
197
     {0.9105929973100293, 1.5771933363574009, 1.0},
198
     {0.0, 2.1029244484765344, 1.0}},
199
    {{0.9105929973100293, 2.628655560595668, 1.0},
200
     {1.8211859946200586, 2.1029244484765344, 1.0},
201
     {0.9105929973100293, 1.5771933363574009, 1.0}},
202
    {{0.9105929973100293, 1.5771933363574009, 1.0},
203
     {1.8211859946200586, 2.1029244484765344, 1.0},
204
     {1.8211859946200586, 1.0514622242382672, 1.0}},
205
    {{1.8211859946200586, 1.0514622242382672, 1.0},
206
     {1.8211859946200586, 2.1029244484765344, 1.0},
207
     {2.7317789919300877, 1.5771933363574009, 1.0}},
208
    {{1.8211859946200586, 0.0, 1.0},
209
     {1.8211859946200586, 1.0514622242382672, 1.0},
210
     {2.7317789919300877, 0.5257311121191336, 1.0}},
211
    {{0.0, 5.257311121191336, 1.0},
212
     {0.9105929973100293, 4.731580009072203, 1.0},
213
     {0.0, 4.205848896953069, 1.0}},
214
    {{0.0, 1.0514622242382672, 1.0},
215
     {0.0, 2.1029244484765344, 1.0},
216
     {0.9105929973100293, 1.5771933363574009, 1.0}},
217
    {{0.9105929973100293, 0.5257311121191336, 1.0},
218
     {0.9105929973100293, 1.5771933363574009, 1.0},
219
     {1.8211859946200586, 1.0514622242382672, 1.0}},
220
    {{0.9105929973100293, 0.5257311121191336, 1.0},
221
     {1.8211859946200586, 1.0514622242382672, 1.0},
222
     {1.8211859946200586, 0.0, 1.0}},
223
    {{0.9105929973100293, 4.731580009072203, 1.0},
224
     {0.45529649865501465, 4.994445565131769, 1.0},
225
     {0.9105929973100293, 5.78304223331047, 1.0}},
226
    {{0.9105929973100293, 0.5257311121191336, 1.0},
227
     {1.8211859946200586, 0.0, 1.0},
228
     {0.9105929973100293, 0.0, 1.0}},
229
    {{0.0, 4.205848896953069, 1.0},
230
     {0.9105929973100293, 4.731580009072203, 1.0},
231
     {0.6070619982066862, 4.205848896953069, 1.0}},
232
    {{0.9105929973100293, 4.731580009072203, 1.0},
233
     {0.9105929973100293, 3.6801177848339353, 1.0},
234
     {0.6070619982066862, 4.205848896953069, 1.0}},
235
    {{0.0, 3.1543866727148018, 1.0},
236
     {0.3035309991033431, 3.6801177848339353, 1.0},
237
     {0.9105929973100293, 3.6801177848339353, 1.0}}};
238
239
// /*
240
//     The parameters here are extracted from the transition matrices
241
//     that allow converting a icosahedron face or subface to its
242
//     corresponding face in the Airocean projected space.
243
//     Since only a few parameters of those matrices are relevant,
244
//     the irrelevant ones has been discarded.
245
// */
246
// // Icosahedron to Airocean (forward)
247
constexpr double base_ico_air_trans[23][4][4] = {
248
    {{0.5771127852625935, -0.6019490725122667, -0.5519041105011566,
249
      2.1247169937234016},
250
     {0.09385435001257117, 0.7202114479424703, -0.6873767753105484,
251
      3.6801177848339357},
252
     {0.8112534709140967, 0.3448953237639384, 0.4721387736413929,
253
      -0.7946544722917659},
254
     {0.0, 0.0, 0.0, 1.0}},
255
    {{0.9709901201198636, -0.2187361325341673, -0.09660585361978567,
256
      1.5176549955167156},
257
     {0.09385435001257089, 0.7202114479424708, -0.687376775310548,
258
      3.6801177848339353},
259
     {0.21993077914046077, 0.6583691780274995, 0.7198475378926181,
260
      -0.7946544722917659},
261
     {0.0, 0.0, 0.0, 1.0}},
262
    {{0.9721374115064793, -0.06476823821979226, 0.2252863255224028,
263
      1.2141239964133725},
264
     {0.09584151698527507, 0.9868916636293633, -0.12984316647721492,
265
      3.1543866727148013},
266
     {-0.21392348345014195, 0.1478171829550702, 0.9656017935214207,
267
      -0.7946544722917663},
268
     {0.0, 0.0, 0.0, 1.0}},
269
    {{0.9921258753731454, -0.0010987106726278763, -0.1252399307326839,
270
      1.5176549955167151},
271
     {0.06122048200295415, 0.8766128070237673, 0.47728611874350335,
272
      2.6286555605956674},
273
     {0.10926252787847969, -0.4811951572873209, 0.8697775121287256,
274
      -0.7946544722917663},
275
     {0.0, 0.0, 0.0, 1.0}},
276
    {{0.28030414798915965, -0.5991800396948614, -0.7499419075177327,
277
      2.428247992826745},
278
     {0.6079419898954396, 0.7154153424148981, -0.34436524905882676,
279
      3.1543866727148013},
280
     {0.742856730158679, -0.3593941678278027, 0.5648005936517032,
281
      -0.794654472291766},
282
     {0.0, 0.0, 0.0, 1.0}},
283
    {{0.25960661905056537, -0.7580069591045613, -0.5983559586852888,
284
      2.428247992826744},
285
     {-0.4560824615830708, 0.4499112594427941, -0.7678337364709403,
286
      4.205848896953069},
287
     {0.8512303986474292, 0.47223437885826813, -0.22891373886878083,
288
      -0.7946544722917661},
289
     {0.0, 0.0, 0.0, 1.0}},
290
    {{0.958636570067365, -0.258086064605963, 0.12003128669511368,
291
      1.5176549955167158},
292
     {-0.003215303703157293, -0.43149765310854393, -0.9021083289627215,
293
      4.731580009072202},
294
     {0.2846148069787908, 0.8644080972654206, -0.41447925524735385,
295
      -0.7946544722917662},
296
     {0.0, 0.0, 0.0, 1.0}},
297
    {{0.992834940445074, 0.09405868118990741, 0.07370037668995856,
298
      1.2141239964133723},
299
     {0.056018011327093935, 0.17843493822832973, -0.982355819052552,
300
      4.205848896953068},
301
     {-0.10554981496139189, 0.9794457296411413, 0.17188746100093644,
302
      -0.7946544722917662},
303
     {0.0, 0.0, 0.0, 1.0}},
304
    {{0.5819727895662967, 0.05026939415592827, 0.8116530417706931,
305
      0.6070619982066865},
306
     {0.09584151698527442, 0.9868916636293634, -0.1298431664772149,
307
      3.1543866727148013},
308
     {-0.8075407579970093, 0.1533552485898817, 0.5695261994882689,
309
      -0.7946544722917663},
310
     {0.0, 0.0, 0.0, 1.0}},
311
    {{0.5247823074767625, -0.7686380596783918, 0.36578554232391575,
312
      0.6070619982066867},
313
     {0.0032153037031566203, 0.4314976531085445, 0.9021083289627214,
314
      2.102924448476535},
315
     {-0.8512303986474292, -0.47223437885826813, 0.22891373886878078,
316
      -0.7946544722917661},
317
     {0.0, 0.0, 0.0, 1.0}},
318
    {{0.9586365700673652, -0.2580860646059632, 0.12003128669511379,
319
      1.2141239964133719},
320
     {0.003215303703156878, 0.43149765310854465, 0.9021083289627218,
321
      2.1029244484765344},
322
     {-0.2846148069787909, -0.8644080972654204, 0.4144792552473538,
323
      -0.7946544722917662},
324
     {0.0, 0.0, 0.0, 1.0}},
325
    {{0.9928349404450738, 0.0940586811899076, 0.07370037668995869,
326
      1.5176549955167153},
327
     {-0.05601801132709388, -0.17843493822832968, 0.9823558190525513,
328
      1.5771933363574009},
329
     {0.10554981496139189, -0.9794457296411413, -0.17188746100093644,
330
      -0.7946544722917662},
331
     {0.0, 0.0, 0.0, 1.0}},
332
    {{0.6696489291164518, 0.7230710214322986, 0.1695246580826539,
333
      2.1247169937234016},
334
     {-0.05601801132709365, -0.17843493822832943, 0.9823558190525516,
335
      1.5771933363574009},
336
     {0.7405621473854482, -0.6673299564565524, -0.07898376463267347,
337
      -0.7946544722917662},
338
     {0.0, 0.0, 0.0, 1.0}},
339
    {{0.5819727895662965, 0.050269394155928314, 0.811653041770693,
340
      2.1247169937234016},
341
     {-0.09584151698527484, -0.9868916636293626, 0.129843166477215,
342
      0.5257311121191333},
343
     {0.8075407579970093, -0.1533552485898817, -0.5695261994882689,
344
      -0.7946544722917663},
345
     {0.0, 0.0, 0.0, 1.0}},
346
    {{0.3863411332821331, 0.9191578806358752, 0.07674189989336716,
347
      0.30353099910334314},
348
     {0.5467215078924839, -0.16119746460886833, -0.8216513678023304,
349
      4.731580009072203},
350
     {-0.742856730158679, 0.35939416782780265, -0.5648005936517032,
351
      -0.794654472291766},
352
     {0.0, 0.0, 0.0, 1.0}},
353
    {{0.20727614126473407, -0.9246959462706865, 0.31933369413978446,
354
      0.303530999103343},
355
     {-0.5467215078924849, 0.1611974646088691, 0.8216513678023303,
356
      1.5771933363574007},
357
     {-0.8112534709140967, -0.34489532376393833, -0.47213877364139295,
358
      -0.794654472291766},
359
     {0.0, 0.0, 0.0, 1.0}},
360
    {{0.9709901201198639, -0.21873613253416718, -0.09660585361978535,
361
      1.2141239964133725},
362
     {-0.09385435001257073, -0.7202114479424704, 0.6873767753105484,
363
      1.0514622242382674},
364
     {-0.21993077914046086, -0.6583691780274995, -0.719847537892618,
365
      -0.794654472291766},
366
     {0.0, 0.0, 0.0, 1.0}},
367
    {{0.9721374115064794, -0.0647682382197923, 0.2252863255224031,
368
      1.5176549955167156},
369
     {-0.09584151698527477, -0.9868916636293626, 0.12984316647721514,
370
      0.5257311121191336},
371
     {0.21392348345014198, -0.1478171829550702, -0.9656017935214205,
372
      -0.7946544722917661},
373
     {0.0, 0.0, 0.0, 1.0}},
374
    {{0.5490814303330593, 0.7586196048290541, 0.350721938339208,
375
      0.6070619982066862},
376
     {0.8285959708235409, -0.43925791486578636, -0.3471040209544599,
377
      5.257311121191335},
378
     {-0.10926252787847968, 0.48119515728732093, -0.8697775121287254,
379
      -0.7946544722917663},
380
     {0.0, 0.0, 0.0, 1.0}},
381
    {{0.9921258753731453, -0.0010987106726278503, -0.125239930732684,
382
      1.2141239964133725},
383
     {-0.061220482002954366, -0.8766128070237673, -0.4772861187435034, 0.0},
384
     {-0.10926252787847965, 0.48119515728732093, -0.8697775121287254,
385
      -0.7946544722917663},
386
     {0.0, 0.0, 0.0, 1.0}},
387
    {{0.6696489291164521, 0.7230710214322988, 0.169524658082654,
388
      0.607061998206686},
389
     {0.05601801132709396, 0.17843493822832968, -0.9823558190525518,
390
      4.205848896953069},
391
     {-0.7405621473854482, 0.6673299564565525, 0.07898376463267334,
392
      -0.7946544722917662},
393
     {0.0, 0.0, 0.0, 1.0}},
394
    {{0.6696489291164525, 0.7230710214322987, 0.1695246580826538,
395
      0.6070619982066863},
396
     {0.05601801132709517, 0.1784349382283307, -0.9823558190525518,
397
      4.205848896953069},
398
     {-0.7405621473854483, 0.6673299564565526, 0.07898376463267348,
399
      -0.7946544722917663},
400
     {0.0, 0.0, 0.0, 1.0}},
401
    {{0.28631144367947836, 0.20700632128770896, 0.9355074238963061,
402
      0.3035309991033428},
403
     {0.6079419898954391, 0.7154153424148978, -0.3443652490588263,
404
      3.6801177848339357},
405
     {-0.7405621473854481, 0.6673299564565525, 0.07898376463267341,
406
      -0.7946544722917661},
407
     {0.0, 0.0, 0.0, 1.0}}};
408
// // Airocean to Icosahedron (inverse)
409
constexpr double base_air_ico_trans[23][4][4] = {
410
    {{0.577112785262594, 0.09385435001257074, 0.8112534709140972,
411
      -0.9269302059836626},
412
     {-0.6019490725122669, 0.7202114479424705, 0.3448953237639385,
413
      -1.0974189231897016},
414
     {-0.5519041105011576, -0.6873767753105482, 0.47213877364139284,
415
      4.0774547262062395},
416
     {0.0, 0.0, 0.0, 1.0}},
417
    {{0.970990120119864, 0.09385435001257075, 0.21993077914046097,
418
      -1.6442540918239978},
419
     {-0.21873613253416777, 0.7202114479424705, 0.6583691780274992,
420
      -1.7953209624349933},
421
     {-0.09660585361978517, -0.6873767753105485, 0.7198475378926187,
422
      3.248271917398959},
423
     {0.0, 0.0, 0.0, 1.0}},
424
    {{0.9721374115064793, 0.09584151698527468, -0.21392348345014173,
425
      -1.6526118158442071},
426
     {-0.06476823821979319, 0.9868916636293628, 0.14781718295507035,
427
      -2.916937653420916},
428
     {0.22528632552240307, -0.12984316647721503, 0.9656017935214205,
429
      0.9033698036730199},
430
     {0.0, 0.0, 0.0, 1.0}},
431
    {{0.9921258753731454, 0.061220482002954296, 0.10926252787847969,
432
      -1.5798063949483236},
433
     {-0.0010987106726281115, 0.8766128070237668, -0.48119515728732043,
434
      -2.68502954971497},
435
     {-0.12523993073268413, 0.4772861187435032, 0.8697775121287253,
436
      -0.3733772136037109},
437
     {0.0, 0.0, 0.0, 1.0}},
438
    {{0.2803041479891603, 0.6079419898954391, 0.7428567301586791,
439
      -2.0080176725529477},
440
     {-0.5991800396948611, 0.7154153424148979, -0.35939416782780287,
441
      -1.0873330756182955},
442
     {-0.7499419075177335, -0.3443652490588265, 0.5648005936517035,
443
      3.3561274015422433},
444
     {0.0, 0.0, 0.0, 1.0}},
445
    {{0.2596066190505654, -0.4560824615830712, 0.8512303986474292,
446
      1.9642587095706099},
447
     {-0.7580069591045617, 0.4499112594427949, 0.47223437885826836,
448
      0.3236332638697585},
449
     {-0.5983559586852887, -0.7678337364709401, -0.22891373886878078,
450
      4.500442002892026},
451
     {0.0, 0.0, 0.0, 1.0}},
452
    {{0.958636570067365, -0.003215303703156967, 0.28461480697879094,
453
      -1.2134956834766393},
454
     {-0.2580860646059631, -0.43149765310854504, 0.8644080972654203,
455
      3.1202570350096352},
456
     {0.12003128669511316, -0.9021083289627222, -0.4144792552473535,
457
      3.756863859611938},
458
     {0.0, 0.0, 0.0, 1.0}},
459
    {{0.992834940445074, 0.05601801132709367, -0.10554981496139178,
460
      -1.5249036493302057},
461
     {0.09405868118990754, 0.17843493822832954, 0.9794457296411412,
462
      -0.08634836060276646},
463
     {0.07370037668995799, -0.9823558190525513, 0.1718874610009362,
464
      4.17874988170889},
465
     {0.0, 0.0, 0.0, 1.0}},
466
    {{0.581972789566297, 0.09584151698527493, -0.8075407579970092,
467
      -1.297330643307362},
468
     {0.05026939415592872, 0.986891663629363, 0.15335524858988142,
469
      -3.0216901158893736},
470
     {0.8116530417706934, -0.12984316647721506, 0.5695261994882689,
471
      0.3694283780016493},
472
     {0.0, 0.0, 0.0, 1.0}},
473
    {{0.5247823074767624, 0.0032153037031565812, -0.8512303986474291,
474
      -1.0017709802028867},
475
     {-0.7686380596783923, 0.431497653108545, -0.47223437885826824,
476
      -0.8160591689057779},
477
     {0.36578554232391597, 0.9021083289627221, 0.22891373886878089,
478
      -1.937212836027187},
479
     {0.0, 0.0, 0.0, 1.0}},
480
    {{0.9586365700673654, 0.0032153037031565886, -0.2846148069787907,
481
      -1.3968356335709964},
482
     {-0.258086064605963, 0.43149765310854504, -0.8644080972654202,
483
      -1.2809642403813966},
484
     {0.12003128669511362, 0.9021083289627224, 0.41447925524735396,
485
      -1.7134307317924613},
486
     {0.0, 0.0, 0.0, 1.0}},
487
    {{0.9928349404450739, -0.05601801132709361, 0.10554981496139221,
488
      -1.3345540404002831},
489
     {0.09405868118990741, -0.17843493822832956, -0.9794457296411411,
490
      -0.639643161258916},
491
     {0.07370037668995856, 0.982355819052552, -0.17188746100093616,
492
      -1.7978079362118518},
493
     {0.0, 0.0, 0.0, 1.0}},
494
    {{0.6696489291164524, -0.056018011327093886, 0.7405621473854481,
495
      -0.7459722029114777},
496
     {0.723071021432299, -0.1784349382283297, -0.6673299564565522,
497
      -1.7851916257515466},
498
     {0.16952465808265363, 0.9823558190525522, -0.07898376463267373,
499
      -1.9723217754287594},
500
     {0.0, 0.0, 0.0, 1.0}},
501
    {{0.5819727895662968, -0.09584151698527477, 0.8075407579970091,
502
      -0.5444247336640644},
503
     {0.05026939415592821, -0.9868916636293631, -0.15335524858988164,
504
      0.29016698169232114},
505
     {0.8116530417706935, 0.1298431664772151, -0.569526199488269,
506
      -2.2453721446813035},
507
     {0.0, 0.0, 0.0, 1.0}},
508
    {{0.3863411332821329, 0.5467215078924852, -0.7428567301586795,
509
      -3.2944374903463687},
510
     {0.9191578806358753, -0.16119746460886916, 0.3593941678278029,
511
      0.7693199739932717},
512
     {0.07674189989336772, -0.8216513678023304, -0.564800593651703,
513
      3.4155943230742447},
514
     {0.0, 0.0, 0.0, 1.0}},
515
    {{0.20727614126473443, -0.546721507892485, -0.8112534709140969,
516
      0.15470458601882164},
517
     {-0.9246959462706867, 0.16119746460886913, -0.3448953237639384,
518
      -0.24763829408199384},
519
     {0.31933369413978435, 0.8216513678023302, -0.4721387736413931,
520
      -1.768017925352872},
521
     {0.0, 0.0, 0.0, 1.0}},
522
    {{0.9709901201198642, -0.09385435001257068, -0.21993077914046077,
523
      -1.2549870787377553},
524
     {-0.2187361325341676, -0.7202114479424703, -0.6583691780274997,
525
      0.4996719066292349},
526
     {-0.09660585361978546, 0.6873767753105482, -0.7198475378926181,
527
      -1.1774892933385632},
528
     {0.0, 0.0, 0.0, 1.0}},
529
    {{0.9721374115064794, -0.09584151698527459, 0.21392348345014212,
530
      -1.2549870787377553},
531
     {-0.06476823821979266, -0.9868916636293628, -0.14781718295507024,
532
      0.49967190662923483},
533
     {0.2252863255224028, 0.12984316647721506, -0.9656017935214204,
534
      -1.1774892933385632},
535
     {0.0, 0.0, 0.0, 1.0}},
536
    {{0.5490814303330579, 0.8285959708235412, -0.10926252787847955,
537
      -4.776339239093644},
538
     {0.7586196048290552, -0.4392579148657884, 0.4811951572873209,
539
      2.231170271492442},
540
     {0.3507219383392087, -0.3471040209544594, -0.8697775121287253,
541
      0.9207512789590909},
542
     {0.0, 0.0, 0.0, 1.0}},
543
    {{0.9921258753731456, -0.061220482002954546, -0.10926252787847962,
544
      -1.2913897891856965},
545
     {-0.00109871067262767, -0.8766128070237672, 0.48119515728732093,
546
      0.38371785477626236},
547
     {-0.12523993073268386, -0.47728611874350324, -0.8697775121287252,
548
      -0.5391157847001973},
549
     {0.0, 0.0, 0.0, 1.0}},
550
    {{0.6696489291164526, 0.0560180113270932, -0.7405621473854482,
551
      -1.2306127305858023},
552
     {0.723071021432299, 0.17843493822832968, 0.6673299564565522,
553
      -0.6591225928490807},
554
     {0.16952465808265377, -0.9823558190525503, 0.07898376463267326,
555
      4.09149296210043},
556
     {0.0, 0.0, 0.0, 1.0}},
557
    {{0.669648929116452, 0.056018011327093706, -0.740562147385448,
558
      -1.230612730585803},
559
     {0.7230710214322988, 0.1784349382283296, 0.6673299564565524,
560
      -0.6591225928490807},
561
     {0.1695246580826554, -0.9823558190525514, 0.0789837646326731,
562
      4.091492962100434},
563
     {0.0, 0.0, 0.0, 1.0}},
564
    {{0.2863114436794785, 0.6079419898954399, -0.7405621473854486,
565
      -2.9126935501461353},
566
     {0.2070063212877089, 0.7154153424148983, 0.6673299564565521,
567
      -2.165348826292825},
568
     {0.935507423896306, -0.3443652490588271, 0.07898376463267351,
569
      1.046113976300111},
570
     {0.0, 0.0, 0.0, 1.0}}};
571
572
// By default the resulting orientation of the projection is vertical
573
// the following transforms are used to alter the projection data
574
// so that the resulting orientation is horizontal instead
575
constexpr double orient_horizontal_trans[4][4] = {
576
    {0.0, -1.0, 0.0, 5.78304223331047},
577
    {1.0, 0.0, 0.0, 0.0},
578
    {0.0, 0.0, 1.0, 0.0},
579
    {0.0, 0.0, 0.0, 1.0}};
580
constexpr double orient_horizontal_inv_trans[4][4] = {
581
    {0.0, 1.0, 0.0, 0.0},
582
    {-1.0, -0.0, -0.0, 5.78304223331047},
583
    {0.0, 0.0, 1.0, 0.0},
584
    {0.0, 0.0, 0.0, 1.0}};
585
586
namespace { // anonymous namespace
587
588
struct pj_airocean_data {
589
    pj_face ico_faces[23] = {};
590
    PJ_XYZ ico_centers[23] = {};
591
    PJ_XYZ ico_normals[23] = {};
592
    pj_face airocean_faces[23] = {};
593
    double ico_air_trans[23][4][4] = {};
594
    double air_ico_trans[23][4][4] = {};
595
596
0
    void initialize() {
597
0
        memcpy(this->ico_faces, base_ico_faces, sizeof(pj_face[23]));
598
0
        memcpy(this->airocean_faces, base_airocean_faces, sizeof(pj_face[23]));
599
0
        memcpy(this->ico_centers, base_ico_centers, sizeof(PJ_XYZ[23]));
600
0
        memcpy(this->ico_normals, base_ico_normals, sizeof(PJ_XYZ[23]));
601
0
        memcpy(this->ico_air_trans, base_ico_air_trans,
602
0
               sizeof(double[23][4][4]));
603
0
        memcpy(this->air_ico_trans, base_air_ico_trans,
604
0
               sizeof(double[23][4][4]));
605
0
    }
606
607
    static void mat_mult(const double m1[4][4], const double m2[4][4],
608
0
                         double res[4][4]) {
609
0
        for (unsigned char i = 0; i < 4; ++i)
610
0
            for (unsigned char j = 0; j < 4; ++j)
611
0
                res[i][j] = (m1[i][0] * m2[0][j]) + (m1[i][1] * m2[1][j]) +
612
0
                            (m1[i][2] * m2[2][j]) + (m1[i][3] * m2[3][j]);
613
0
    }
614
615
0
    static PJ_XYZ vec_mult(const double m[4][4], const PJ_XYZ *v) {
616
0
        double x = m[0][0] * v->x + m[0][1] * v->y + m[0][2] * v->z + m[0][3];
617
0
        double y = m[1][0] * v->x + m[1][1] * v->y + m[1][2] * v->z + m[1][3];
618
0
        double z = m[2][0] * v->x + m[2][1] * v->y + m[2][2] * v->z + m[2][3];
619
0
        return {x, y, z};
620
0
    }
621
622
0
    void transform(const double m[4][4], const double inv_m[4][4]) {
623
0
        for (unsigned char i = 0; i < 23; i++) {
624
0
            mat_mult(m, base_ico_air_trans[i], this->ico_air_trans[i]);
625
0
            mat_mult(base_air_ico_trans[i], inv_m, this->air_ico_trans[i]);
626
0
            this->airocean_faces[i] = {
627
0
                vec_mult(m, &base_airocean_faces[i].p1),
628
0
                vec_mult(m, &base_airocean_faces[i].p2),
629
0
                vec_mult(m, &base_airocean_faces[i].p3),
630
0
            };
631
0
        }
632
0
    }
633
};
634
635
} // anonymous namespace
636
637
0
inline double det(const PJ_XYZ *u, const PJ_XYZ *v, const PJ_XYZ *w) {
638
0
    return (u->x * (v->y * w->z - v->z * w->y) -
639
0
            v->x * (u->y * w->z - u->z * w->y) +
640
0
            w->x * (u->y * v->z - u->z * v->y));
641
0
}
642
643
0
inline bool is_point_in_face(const PJ_XYZ *p, const pj_face *face) {
644
0
    return (det(p, &face->p2, &face->p3) <= 0 &&
645
0
            det(&face->p1, p, &face->p3) <= 0 &&
646
0
            det(&face->p1, &face->p2, p) <= 0);
647
0
}
648
649
inline unsigned char get_ico_face_index(const pj_airocean_data *pj_data,
650
0
                                        const PJ_XYZ *p) {
651
0
    for (unsigned char i = 0; i < 23; i++) {
652
0
        if (is_point_in_face(p, &pj_data->ico_faces[i])) {
653
0
            return i;
654
0
        }
655
0
    }
656
657
0
    return 23;
658
0
}
659
660
inline unsigned char get_dym_face_index(const pj_airocean_data *pj_data,
661
0
                                        const PJ_XY *p) {
662
0
    const PJ_XYZ pp{p->x, p->y, 1.0};
663
0
    for (unsigned char i = 0; i < 23; i++) {
664
0
        if (is_point_in_face(&pp, &pj_data->airocean_faces[i])) {
665
0
            return i;
666
0
        }
667
0
    }
668
669
0
    return 23;
670
0
}
671
672
inline PJ_XY ico_to_dym(const pj_airocean_data *pj_data, const PJ_XYZ *p,
673
0
                        unsigned char face_id) {
674
0
    return PJ_XY{
675
0
        pj_data->ico_air_trans[face_id][0][0] * p->x +     // * -1
676
0
            pj_data->ico_air_trans[face_id][0][1] * p->y + //
677
0
            pj_data->ico_air_trans[face_id][0][2] * p->z + //
678
0
            pj_data->ico_air_trans[face_id][0][3],         // +1000
679
0
        pj_data->ico_air_trans[face_id][1][0] * p->x +
680
0
            pj_data->ico_air_trans[face_id][1][1] * p->y +
681
0
            pj_data->ico_air_trans[face_id][1][2] * p->z +
682
0
            pj_data->ico_air_trans[face_id][1][3],
683
0
    };
684
0
}
685
686
inline PJ_XYZ dym_to_ico(const pj_airocean_data *pj_data, const PJ_XY *p,
687
0
                         unsigned char face_id) {
688
0
    return PJ_XYZ{
689
0
        pj_data->air_ico_trans[face_id][0][0] * p->x +     // * -1
690
0
            pj_data->air_ico_trans[face_id][0][1] * p->y + //
691
0
            pj_data->air_ico_trans[face_id][0][3], // + [face_id][0][0] * 1000
692
0
        pj_data->air_ico_trans[face_id][1][0] * p->x +
693
0
            pj_data->air_ico_trans[face_id][1][1] * p->y +
694
0
            pj_data->air_ico_trans[face_id][1][3],
695
0
        pj_data->air_ico_trans[face_id][2][0] * p->x +
696
0
            pj_data->air_ico_trans[face_id][2][1] * p->y +
697
0
            pj_data->air_ico_trans[face_id][2][3],
698
0
    };
699
0
}
700
701
inline PJ_XYZ cartesian_to_ico(const pj_airocean_data *pj_data, const PJ_XYZ *p,
702
0
                               unsigned char face_id) {
703
0
    const PJ_XYZ *center = &pj_data->ico_centers[face_id];
704
0
    const PJ_XYZ *normal = &pj_data->ico_normals[face_id];
705
706
    // cppcheck-suppress unreadVariable
707
0
    double a =
708
0
        1.0 - (center->x * normal->x + center->y * normal->y +
709
0
               center->z * normal->z) /
710
0
                  (p->x * normal->x + p->y * normal->y + p->z * normal->z);
711
712
0
    return PJ_XYZ{
713
0
        p->x - a * p->x,
714
0
        p->y - a * p->y,
715
0
        p->z - a * p->z,
716
0
    };
717
0
}
718
719
// ============================================
720
//
721
//      The Forward and Inverse Functions
722
//
723
// ============================================
724
0
static PJ_XY airocean_forward(PJ_LP lp, PJ *P) {
725
0
    const struct pj_airocean_data *Q =
726
0
        static_cast<struct pj_airocean_data *>(P->opaque);
727
728
0
    double lat;
729
730
    /* Convert the geodetic latitude to a geocentric latitude.
731
     * This corresponds to the shift from the ellipsoid to the sphere
732
     * described in [LK12]. */
733
0
    if (P->es != 0.0) {
734
0
        double one_minus_f = 1.0 - (P->a - P->b) / P->a;
735
0
        double one_minus_f_squared = one_minus_f * one_minus_f;
736
0
        lat = atan(one_minus_f_squared * tan(lp.phi));
737
0
    } else {
738
0
        lat = lp.phi;
739
0
    }
740
741
    // Convert the lat/long to x,y,z on the unit sphere
742
0
    double x, y, z;
743
0
    double sinlat, coslat;
744
0
    double sinlon, coslon;
745
746
0
    sinlat = sin(lat);
747
0
    coslat = cos(lat);
748
0
    sinlon = sin(lp.lam);
749
0
    coslon = cos(lp.lam);
750
0
    x = coslat * coslon;
751
0
    y = coslat * sinlon;
752
0
    z = sinlat;
753
754
0
    PJ_XYZ cartesianPoint{x, y, z};
755
756
0
    unsigned char face_id = get_ico_face_index(Q, &cartesianPoint);
757
0
    if (face_id == 23) {
758
        // not sure this can happen
759
0
        proj_errno_set(P, PROJ_ERR_COORD_TRANSFM_OUTSIDE_PROJECTION_DOMAIN);
760
0
        PJ_XY xy;
761
0
        xy.x = HUGE_VAL;
762
0
        xy.y = HUGE_VAL;
763
0
        return xy;
764
0
    }
765
766
0
    PJ_XYZ icoPoint = cartesian_to_ico(Q, &cartesianPoint, face_id);
767
768
0
    PJ_XY airoceanPoint = ico_to_dym(Q, &icoPoint, face_id);
769
770
0
    return airoceanPoint;
771
0
}
772
773
0
static PJ_LP airocean_inverse(PJ_XY xy, PJ *P) {
774
0
    const struct pj_airocean_data *Q =
775
0
        static_cast<struct pj_airocean_data *>(P->opaque);
776
777
0
    PJ_LP lp = {0.0, 0.0};
778
779
0
    unsigned char face_id = get_dym_face_index(Q, &xy);
780
781
0
    if (face_id == 23) {
782
        // Point lies outside icosahedron net faces
783
0
        proj_errno_set(P, PROJ_ERR_COORD_TRANSFM_OUTSIDE_PROJECTION_DOMAIN);
784
0
        lp.lam = HUGE_VAL;
785
0
        lp.phi = HUGE_VAL;
786
0
        return lp;
787
0
    }
788
789
0
    PJ_XYZ sphereCoords = dym_to_ico(Q, &xy, face_id);
790
791
0
    double norm = sqrt((sphereCoords.x * sphereCoords.x) +
792
0
                       (sphereCoords.y * sphereCoords.y) +
793
0
                       (sphereCoords.z * sphereCoords.z));
794
0
    double q = sphereCoords.x / norm;
795
0
    double r = sphereCoords.y / norm;
796
0
    double s = sphereCoords.z / norm;
797
798
    // Get the spherical angles from the x y z
799
0
    lp.phi = acos(-s) - M_HALFPI;
800
0
    lp.lam = atan2(r, q);
801
802
    /* Apply the shift from the sphere to the ellipsoid as described
803
     * in [LK12]. */
804
0
    if (P->es != 0.0) {
805
0
        int invert_sign;
806
0
        volatile double tanphi, xa;
807
0
        invert_sign = (lp.phi < 0.0 ? 1 : 0);
808
0
        tanphi = tan(lp.phi);
809
0
        double one_minus_f = 1.0 - (P->a - P->b) / P->a;
810
0
        double a_squared = P->a * P->a;
811
0
        xa = P->b / sqrt(tanphi * tanphi + one_minus_f * one_minus_f);
812
0
        lp.phi = atan(sqrt(a_squared - xa * xa) / (one_minus_f * xa));
813
0
        if (invert_sign) {
814
0
            lp.phi = -lp.phi;
815
0
        }
816
0
    }
817
818
0
    return lp;
819
0
}
820
821
0
PJ *PJ_PROJECTION(airocean) {
822
0
    char *opt;
823
0
    struct pj_airocean_data *Q = static_cast<struct pj_airocean_data *>(
824
0
        calloc(1, sizeof(struct pj_airocean_data)));
825
0
    if (nullptr == Q)
826
0
        return pj_default_destructor(P, PROJ_ERR_OTHER /*ENOMEM*/);
827
0
    Q->initialize();
828
0
    P->opaque = Q;
829
0
    opt = pj_param(P->ctx, P->params, "sorient").s;
830
0
    if (opt) {
831
0
        if (!strcmp(opt, "horizontal")) {
832
0
            Q->transform(orient_horizontal_trans, orient_horizontal_inv_trans);
833
0
        } else if (!strcmp(opt, "vertical")) {
834
            // the orientation is vertical by default.
835
0
        } else {
836
0
            proj_log_error(P, _("Invalid value for orient: only vertical or "
837
0
                                "horizontal are supported"));
838
0
            return pj_default_destructor(P,
839
0
                                         PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE);
840
0
        }
841
0
    }
842
843
0
    P->inv = airocean_inverse;
844
0
    P->fwd = airocean_forward;
845
846
0
    return P;
847
0
}