Coverage Report

Created: 2025-02-12 06:06

/src/h3/src/h3lib/lib/faceijk.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright 2016-2023 Uber Technologies, Inc.
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *         http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
/** @file faceijk.c
17
 * @brief   Functions for working with icosahedral face-centered hex IJK
18
 *  coordinate systems.
19
 */
20
21
#include "faceijk.h"
22
23
#include <assert.h>
24
#include <math.h>
25
#include <stdio.h>
26
#include <stdlib.h>
27
#include <string.h>
28
29
#include "constants.h"
30
#include "coordijk.h"
31
#include "h3Index.h"
32
#include "latLng.h"
33
#include "vec3d.h"
34
35
/** square root of 7 and inverse square root of 7 */
36
0
#define M_SQRT7 2.6457513110645905905016157536392604257102
37
0
#define M_RSQRT7 0.37796447300922722721451653623418006081576
38
39
/** @brief icosahedron face centers in lat/lng radians */
40
const LatLng faceCenterGeo[NUM_ICOSA_FACES] = {
41
    {0.803582649718989942, 1.248397419617396099},    // face  0
42
    {1.307747883455638156, 2.536945009877921159},    // face  1
43
    {1.054751253523952054, -1.347517358900396623},   // face  2
44
    {0.600191595538186799, -0.450603909469755746},   // face  3
45
    {0.491715428198773866, 0.401988202911306943},    // face  4
46
    {0.172745327415618701, 1.678146885280433686},    // face  5
47
    {0.605929321571350690, 2.953923329812411617},    // face  6
48
    {0.427370518328979641, -1.888876200336285401},   // face  7
49
    {-0.079066118549212831, -0.733429513380867741},  // face  8
50
    {-0.230961644455383637, 0.506495587332349035},   // face  9
51
    {0.079066118549212831, 2.408163140208925497},    // face 10
52
    {0.230961644455383637, -2.635097066257444203},   // face 11
53
    {-0.172745327415618701, -1.463445768309359553},  // face 12
54
    {-0.605929321571350690, -0.187669323777381622},  // face 13
55
    {-0.427370518328979641, 1.252716453253507838},   // face 14
56
    {-0.600191595538186799, 2.690988744120037492},   // face 15
57
    {-0.491715428198773866, -2.739604450678486295},  // face 16
58
    {-0.803582649718989942, -1.893195233972397139},  // face 17
59
    {-1.307747883455638156, -0.604647643711872080},  // face 18
60
    {-1.054751253523952054, 1.794075294689396615},   // face 19
61
};
62
63
/** @brief icosahedron face centers in x/y/z on the unit sphere */
64
static const Vec3d faceCenterPoint[NUM_ICOSA_FACES] = {
65
    {0.2199307791404606, 0.6583691780274996, 0.7198475378926182},     // face  0
66
    {-0.2139234834501421, 0.1478171829550703, 0.9656017935214205},    // face  1
67
    {0.1092625278784797, -0.4811951572873210, 0.8697775121287253},    // face  2
68
    {0.7428567301586791, -0.3593941678278028, 0.5648005936517033},    // face  3
69
    {0.8112534709140969, 0.3448953237639384, 0.4721387736413930},     // face  4
70
    {-0.1055498149613921, 0.9794457296411413, 0.1718874610009365},    // face  5
71
    {-0.8075407579970092, 0.1533552485898818, 0.5695261994882688},    // face  6
72
    {-0.2846148069787907, -0.8644080972654206, 0.4144792552473539},   // face  7
73
    {0.7405621473854482, -0.6673299564565524, -0.0789837646326737},   // face  8
74
    {0.8512303986474293, 0.4722343788582681, -0.2289137388687808},    // face  9
75
    {-0.7405621473854481, 0.6673299564565524, 0.0789837646326737},    // face 10
76
    {-0.8512303986474292, -0.4722343788582682, 0.2289137388687808},   // face 11
77
    {0.1055498149613919, -0.9794457296411413, -0.1718874610009365},   // face 12
78
    {0.8075407579970092, -0.1533552485898819, -0.5695261994882688},   // face 13
79
    {0.2846148069787908, 0.8644080972654204, -0.4144792552473539},    // face 14
80
    {-0.7428567301586791, 0.3593941678278027, -0.5648005936517033},   // face 15
81
    {-0.8112534709140971, -0.3448953237639382, -0.4721387736413930},  // face 16
82
    {-0.2199307791404607, -0.6583691780274996, -0.7198475378926182},  // face 17
83
    {0.2139234834501420, -0.1478171829550704, -0.9656017935214205},   // face 18
84
    {-0.1092625278784796, 0.4811951572873210, -0.8697775121287253},   // face 19
85
};
86
87
/** @brief icosahedron face ijk axes as azimuth in radians from face center to
88
 * vertex 0/1/2 respectively
89
 */
90
static const double faceAxesAzRadsCII[NUM_ICOSA_FACES][3] = {
91
    {5.619958268523939882, 3.525563166130744542,
92
     1.431168063737548730},  // face  0
93
    {5.760339081714187279, 3.665943979320991689,
94
     1.571548876927796127},  // face  1
95
    {0.780213654393430055, 4.969003859179821079,
96
     2.874608756786625655},  // face  2
97
    {0.430469363979999913, 4.619259568766391033,
98
     2.524864466373195467},  // face  3
99
    {6.130269123335111400, 4.035874020941915804,
100
     1.941478918548720291},  // face  4
101
    {2.692877706530642877, 0.598482604137447119,
102
     4.787272808923838195},  // face  5
103
    {2.982963003477243874, 0.888567901084048369,
104
     5.077358105870439581},  // face  6
105
    {3.532912002790141181, 1.438516900396945656,
106
     5.627307105183336758},  // face  7
107
    {3.494305004259568154, 1.399909901866372864,
108
     5.588700106652763840},  // face  8
109
    {3.003214169499538391, 0.908819067106342928,
110
     5.097609271892733906},  // face  9
111
    {5.930472956509811562, 3.836077854116615875,
112
     1.741682751723420374},  // face 10
113
    {0.138378484090254847, 4.327168688876645809,
114
     2.232773586483450311},  // face 11
115
    {0.448714947059150361, 4.637505151845541521,
116
     2.543110049452346120},  // face 12
117
    {0.158629650112549365, 4.347419854898940135,
118
     2.253024752505744869},  // face 13
119
    {5.891865957979238535, 3.797470855586042958,
120
     1.703075753192847583},  // face 14
121
    {2.711123289609793325, 0.616728187216597771,
122
     4.805518392002988683},  // face 15
123
    {3.294508837434268316, 1.200113735041072948,
124
     5.388903939827463911},  // face 16
125
    {3.804819692245439833, 1.710424589852244509,
126
     5.899214794638635174},  // face 17
127
    {3.664438879055192436, 1.570043776661997111,
128
     5.758833981448388027},  // face 18
129
    {2.361378999196363184, 0.266983896803167583,
130
     4.455774101589558636},  // face 19
131
};
132
133
/** @brief Definition of which faces neighbor each other. */
134
static const FaceOrientIJK faceNeighbors[NUM_ICOSA_FACES][4] = {
135
    {
136
        // face 0
137
        {0, {0, 0, 0}, 0},  // central face
138
        {4, {2, 0, 2}, 1},  // ij quadrant
139
        {1, {2, 2, 0}, 5},  // ki quadrant
140
        {5, {0, 2, 2}, 3}   // jk quadrant
141
    },
142
    {
143
        // face 1
144
        {1, {0, 0, 0}, 0},  // central face
145
        {0, {2, 0, 2}, 1},  // ij quadrant
146
        {2, {2, 2, 0}, 5},  // ki quadrant
147
        {6, {0, 2, 2}, 3}   // jk quadrant
148
    },
149
    {
150
        // face 2
151
        {2, {0, 0, 0}, 0},  // central face
152
        {1, {2, 0, 2}, 1},  // ij quadrant
153
        {3, {2, 2, 0}, 5},  // ki quadrant
154
        {7, {0, 2, 2}, 3}   // jk quadrant
155
    },
156
    {
157
        // face 3
158
        {3, {0, 0, 0}, 0},  // central face
159
        {2, {2, 0, 2}, 1},  // ij quadrant
160
        {4, {2, 2, 0}, 5},  // ki quadrant
161
        {8, {0, 2, 2}, 3}   // jk quadrant
162
    },
163
    {
164
        // face 4
165
        {4, {0, 0, 0}, 0},  // central face
166
        {3, {2, 0, 2}, 1},  // ij quadrant
167
        {0, {2, 2, 0}, 5},  // ki quadrant
168
        {9, {0, 2, 2}, 3}   // jk quadrant
169
    },
170
    {
171
        // face 5
172
        {5, {0, 0, 0}, 0},   // central face
173
        {10, {2, 2, 0}, 3},  // ij quadrant
174
        {14, {2, 0, 2}, 3},  // ki quadrant
175
        {0, {0, 2, 2}, 3}    // jk quadrant
176
    },
177
    {
178
        // face 6
179
        {6, {0, 0, 0}, 0},   // central face
180
        {11, {2, 2, 0}, 3},  // ij quadrant
181
        {10, {2, 0, 2}, 3},  // ki quadrant
182
        {1, {0, 2, 2}, 3}    // jk quadrant
183
    },
184
    {
185
        // face 7
186
        {7, {0, 0, 0}, 0},   // central face
187
        {12, {2, 2, 0}, 3},  // ij quadrant
188
        {11, {2, 0, 2}, 3},  // ki quadrant
189
        {2, {0, 2, 2}, 3}    // jk quadrant
190
    },
191
    {
192
        // face 8
193
        {8, {0, 0, 0}, 0},   // central face
194
        {13, {2, 2, 0}, 3},  // ij quadrant
195
        {12, {2, 0, 2}, 3},  // ki quadrant
196
        {3, {0, 2, 2}, 3}    // jk quadrant
197
    },
198
    {
199
        // face 9
200
        {9, {0, 0, 0}, 0},   // central face
201
        {14, {2, 2, 0}, 3},  // ij quadrant
202
        {13, {2, 0, 2}, 3},  // ki quadrant
203
        {4, {0, 2, 2}, 3}    // jk quadrant
204
    },
205
    {
206
        // face 10
207
        {10, {0, 0, 0}, 0},  // central face
208
        {5, {2, 2, 0}, 3},   // ij quadrant
209
        {6, {2, 0, 2}, 3},   // ki quadrant
210
        {15, {0, 2, 2}, 3}   // jk quadrant
211
    },
212
    {
213
        // face 11
214
        {11, {0, 0, 0}, 0},  // central face
215
        {6, {2, 2, 0}, 3},   // ij quadrant
216
        {7, {2, 0, 2}, 3},   // ki quadrant
217
        {16, {0, 2, 2}, 3}   // jk quadrant
218
    },
219
    {
220
        // face 12
221
        {12, {0, 0, 0}, 0},  // central face
222
        {7, {2, 2, 0}, 3},   // ij quadrant
223
        {8, {2, 0, 2}, 3},   // ki quadrant
224
        {17, {0, 2, 2}, 3}   // jk quadrant
225
    },
226
    {
227
        // face 13
228
        {13, {0, 0, 0}, 0},  // central face
229
        {8, {2, 2, 0}, 3},   // ij quadrant
230
        {9, {2, 0, 2}, 3},   // ki quadrant
231
        {18, {0, 2, 2}, 3}   // jk quadrant
232
    },
233
    {
234
        // face 14
235
        {14, {0, 0, 0}, 0},  // central face
236
        {9, {2, 2, 0}, 3},   // ij quadrant
237
        {5, {2, 0, 2}, 3},   // ki quadrant
238
        {19, {0, 2, 2}, 3}   // jk quadrant
239
    },
240
    {
241
        // face 15
242
        {15, {0, 0, 0}, 0},  // central face
243
        {16, {2, 0, 2}, 1},  // ij quadrant
244
        {19, {2, 2, 0}, 5},  // ki quadrant
245
        {10, {0, 2, 2}, 3}   // jk quadrant
246
    },
247
    {
248
        // face 16
249
        {16, {0, 0, 0}, 0},  // central face
250
        {17, {2, 0, 2}, 1},  // ij quadrant
251
        {15, {2, 2, 0}, 5},  // ki quadrant
252
        {11, {0, 2, 2}, 3}   // jk quadrant
253
    },
254
    {
255
        // face 17
256
        {17, {0, 0, 0}, 0},  // central face
257
        {18, {2, 0, 2}, 1},  // ij quadrant
258
        {16, {2, 2, 0}, 5},  // ki quadrant
259
        {12, {0, 2, 2}, 3}   // jk quadrant
260
    },
261
    {
262
        // face 18
263
        {18, {0, 0, 0}, 0},  // central face
264
        {19, {2, 0, 2}, 1},  // ij quadrant
265
        {17, {2, 2, 0}, 5},  // ki quadrant
266
        {13, {0, 2, 2}, 3}   // jk quadrant
267
    },
268
    {
269
        // face 19
270
        {19, {0, 0, 0}, 0},  // central face
271
        {15, {2, 0, 2}, 1},  // ij quadrant
272
        {18, {2, 2, 0}, 5},  // ki quadrant
273
        {14, {0, 2, 2}, 3}   // jk quadrant
274
    }};
275
276
/** @brief direction from the origin face to the destination face, relative to
277
 * the origin face's coordinate system, or -1 if not adjacent.
278
 */
279
static const int adjacentFaceDir[NUM_ICOSA_FACES][NUM_ICOSA_FACES] = {
280
    {0,  KI, -1, -1, IJ, JK, -1, -1, -1, -1,
281
     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},  // face 0
282
    {IJ, 0,  KI, -1, -1, -1, JK, -1, -1, -1,
283
     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},  // face 1
284
    {-1, IJ, 0,  KI, -1, -1, -1, JK, -1, -1,
285
     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},  // face 2
286
    {-1, -1, IJ, 0,  KI, -1, -1, -1, JK, -1,
287
     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},  // face 3
288
    {KI, -1, -1, IJ, 0,  -1, -1, -1, -1, JK,
289
     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},  // face 4
290
    {JK, -1, -1, -1, -1, 0,  -1, -1, -1, -1,
291
     IJ, -1, -1, -1, KI, -1, -1, -1, -1, -1},  // face 5
292
    {-1, JK, -1, -1, -1, -1, 0,  -1, -1, -1,
293
     KI, IJ, -1, -1, -1, -1, -1, -1, -1, -1},  // face 6
294
    {-1, -1, JK, -1, -1, -1, -1, 0,  -1, -1,
295
     -1, KI, IJ, -1, -1, -1, -1, -1, -1, -1},  // face 7
296
    {-1, -1, -1, JK, -1, -1, -1, -1, 0,  -1,
297
     -1, -1, KI, IJ, -1, -1, -1, -1, -1, -1},  // face 8
298
    {-1, -1, -1, -1, JK, -1, -1, -1, -1, 0,
299
     -1, -1, -1, KI, IJ, -1, -1, -1, -1, -1},  // face 9
300
    {-1, -1, -1, -1, -1, IJ, KI, -1, -1, -1,
301
     0,  -1, -1, -1, -1, JK, -1, -1, -1, -1},  // face 10
302
    {-1, -1, -1, -1, -1, -1, IJ, KI, -1, -1,
303
     -1, 0,  -1, -1, -1, -1, JK, -1, -1, -1},  // face 11
304
    {-1, -1, -1, -1, -1, -1, -1, IJ, KI, -1,
305
     -1, -1, 0,  -1, -1, -1, -1, JK, -1, -1},  // face 12
306
    {-1, -1, -1, -1, -1, -1, -1, -1, IJ, KI,
307
     -1, -1, -1, 0,  -1, -1, -1, -1, JK, -1},  // face 13
308
    {-1, -1, -1, -1, -1, KI, -1, -1, -1, IJ,
309
     -1, -1, -1, -1, 0,  -1, -1, -1, -1, JK},  // face 14
310
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
311
     JK, -1, -1, -1, -1, 0,  IJ, -1, -1, KI},  // face 15
312
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
313
     -1, JK, -1, -1, -1, KI, 0,  IJ, -1, -1},  // face 16
314
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
315
     -1, -1, JK, -1, -1, -1, KI, 0,  IJ, -1},  // face 17
316
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
317
     -1, -1, -1, JK, -1, -1, -1, KI, 0,  IJ},  // face 18
318
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
319
     -1, -1, -1, -1, JK, IJ, -1, -1, KI, 0}  // face 19
320
};
321
322
/** @brief overage distance table */
323
static const int maxDimByCIIres[] = {
324
    2,        // res  0
325
    -1,       // res  1
326
    14,       // res  2
327
    -1,       // res  3
328
    98,       // res  4
329
    -1,       // res  5
330
    686,      // res  6
331
    -1,       // res  7
332
    4802,     // res  8
333
    -1,       // res  9
334
    33614,    // res 10
335
    -1,       // res 11
336
    235298,   // res 12
337
    -1,       // res 13
338
    1647086,  // res 14
339
    -1,       // res 15
340
    11529602  // res 16
341
};
342
343
/** @brief unit scale distance table */
344
static const int unitScaleByCIIres[] = {
345
    1,       // res  0
346
    -1,      // res  1
347
    7,       // res  2
348
    -1,      // res  3
349
    49,      // res  4
350
    -1,      // res  5
351
    343,     // res  6
352
    -1,      // res  7
353
    2401,    // res  8
354
    -1,      // res  9
355
    16807,   // res 10
356
    -1,      // res 11
357
    117649,  // res 12
358
    -1,      // res 13
359
    823543,  // res 14
360
    -1,      // res 15
361
    5764801  // res 16
362
};
363
364
/**
365
 * Encodes a coordinate on the sphere to the FaceIJK address of the containing
366
 * cell at the specified resolution.
367
 *
368
 * @param g The spherical coordinates to encode.
369
 * @param res The desired H3 resolution for the encoding.
370
 * @param h The FaceIJK address of the containing cell at resolution res.
371
 */
372
0
void _geoToFaceIjk(const LatLng *g, int res, FaceIJK *h) {
373
    // first convert to hex2d
374
0
    Vec2d v;
375
0
    _geoToHex2d(g, res, &h->face, &v);
376
377
    // then convert to ijk+
378
0
    _hex2dToCoordIJK(&v, &h->coord);
379
0
}
380
381
/**
382
 * Encodes a coordinate on the sphere to the corresponding icosahedral face and
383
 * containing 2D hex coordinates relative to that face center.
384
 *
385
 * @param g The spherical coordinates to encode.
386
 * @param res The desired H3 resolution for the encoding.
387
 * @param face The icosahedral face containing the spherical coordinates.
388
 * @param v The 2D hex coordinates of the cell containing the point.
389
 */
390
0
void _geoToHex2d(const LatLng *g, int res, int *face, Vec2d *v) {
391
    // determine the icosahedron face
392
0
    double sqd;
393
0
    _geoToClosestFace(g, face, &sqd);
394
395
    // cos(r) = 1 - 2 * sin^2(r/2) = 1 - 2 * (sqd / 4) = 1 - sqd/2
396
0
    double r = acos(1 - sqd * 0.5);
397
398
0
    if (r < EPSILON) {
399
0
        v->x = v->y = 0.0;
400
0
        return;
401
0
    }
402
403
    // now have face and r, now find CCW theta from CII i-axis
404
0
    double theta =
405
0
        _posAngleRads(faceAxesAzRadsCII[*face][0] -
406
0
                      _posAngleRads(_geoAzimuthRads(&faceCenterGeo[*face], g)));
407
408
    // adjust theta for Class III (odd resolutions)
409
0
    if (isResolutionClassIII(res))
410
0
        theta = _posAngleRads(theta - M_AP7_ROT_RADS);
411
412
    // perform gnomonic scaling of r
413
0
    r = tan(r);
414
415
    // scale for current resolution length u
416
0
    r *= INV_RES0_U_GNOMONIC;
417
0
    for (int i = 0; i < res; i++) r *= M_SQRT7;
418
419
    // we now have (r, theta) in hex2d with theta ccw from x-axes
420
421
    // convert to local x,y
422
0
    v->x = r * cos(theta);
423
0
    v->y = r * sin(theta);
424
0
}
425
426
/**
427
 * Determines the center point in spherical coordinates of a cell given by 2D
428
 * hex coordinates on a particular icosahedral face.
429
 *
430
 * @param v The 2D hex coordinates of the cell.
431
 * @param face The icosahedral face upon which the 2D hex coordinate system is
432
 *             centered.
433
 * @param res The H3 resolution of the cell.
434
 * @param substrate Indicates whether or not this grid is actually a substrate
435
 *        grid relative to the specified resolution.
436
 * @param g The spherical coordinates of the cell center point.
437
 */
438
0
void _hex2dToGeo(const Vec2d *v, int face, int res, int substrate, LatLng *g) {
439
    // calculate (r, theta) in hex2d
440
0
    double r = _v2dMag(v);
441
442
0
    if (r < EPSILON) {
443
0
        *g = faceCenterGeo[face];
444
0
        return;
445
0
    }
446
447
0
    double theta = atan2(v->y, v->x);
448
449
    // scale for current resolution length u
450
0
    for (int i = 0; i < res; i++) r *= M_RSQRT7;
451
452
    // scale accordingly if this is a substrate grid
453
0
    if (substrate) {
454
0
        r *= M_ONETHIRD;
455
0
        if (isResolutionClassIII(res)) r *= M_RSQRT7;
456
0
    }
457
458
0
    r *= RES0_U_GNOMONIC;
459
460
    // perform inverse gnomonic scaling of r
461
0
    r = atan(r);
462
463
    // adjust theta for Class III
464
    // if a substrate grid, then it's already been adjusted for Class III
465
0
    if (!substrate && isResolutionClassIII(res))
466
0
        theta = _posAngleRads(theta + M_AP7_ROT_RADS);
467
468
    // find theta as an azimuth
469
0
    theta = _posAngleRads(faceAxesAzRadsCII[face][0] - theta);
470
471
    // now find the point at (r,theta) from the face center
472
0
    _geoAzDistanceRads(&faceCenterGeo[face], theta, r, g);
473
0
}
474
475
/**
476
 * Determines the center point in spherical coordinates of a cell given by
477
 * a FaceIJK address at a specified resolution.
478
 *
479
 * @param h The FaceIJK address of the cell.
480
 * @param res The H3 resolution of the cell.
481
 * @param g The spherical coordinates of the cell center point.
482
 */
483
0
void _faceIjkToGeo(const FaceIJK *h, int res, LatLng *g) {
484
0
    Vec2d v;
485
0
    _ijkToHex2d(&h->coord, &v);
486
0
    _hex2dToGeo(&v, h->face, res, 0, g);
487
0
}
488
489
/**
490
 * Generates the cell boundary in spherical coordinates for a pentagonal cell
491
 * given by a FaceIJK address at a specified resolution.
492
 *
493
 * @param h The FaceIJK address of the pentagonal cell.
494
 * @param res The H3 resolution of the cell.
495
 * @param start The first topological vertex to return.
496
 * @param length The number of topological vertexes to return.
497
 * @param g The spherical coordinates of the cell boundary.
498
 */
499
void _faceIjkPentToCellBoundary(const FaceIJK *h, int res, int start,
500
0
                                int length, CellBoundary *g) {
501
0
    int adjRes = res;
502
0
    FaceIJK centerIJK = *h;
503
0
    FaceIJK fijkVerts[NUM_PENT_VERTS];
504
0
    _faceIjkPentToVerts(&centerIJK, &adjRes, fijkVerts);
505
506
    // If we're returning the entire loop, we need one more iteration in case
507
    // of a distortion vertex on the last edge
508
0
    int additionalIteration = length == NUM_PENT_VERTS ? 1 : 0;
509
510
    // convert each vertex to lat/lng
511
    // adjust the face of each vertex as appropriate and introduce
512
    // edge-crossing vertices as needed
513
0
    g->numVerts = 0;
514
0
    FaceIJK lastFijk;
515
0
    for (int vert = start; vert < start + length + additionalIteration;
516
0
         vert++) {
517
0
        int v = vert % NUM_PENT_VERTS;
518
519
0
        FaceIJK fijk = fijkVerts[v];
520
521
0
        _adjustPentVertOverage(&fijk, adjRes);
522
523
        // all Class III pentagon edges cross icosa edges
524
        // note that Class II pentagons have vertices on the edge,
525
        // not edge intersections
526
0
        if (isResolutionClassIII(res) && vert > start) {
527
            // find hex2d of the two vertexes on the last face
528
529
0
            FaceIJK tmpFijk = fijk;
530
531
0
            Vec2d orig2d0;
532
0
            _ijkToHex2d(&lastFijk.coord, &orig2d0);
533
534
0
            int currentToLastDir = adjacentFaceDir[tmpFijk.face][lastFijk.face];
535
536
0
            const FaceOrientIJK *fijkOrient =
537
0
                &faceNeighbors[tmpFijk.face][currentToLastDir];
538
539
0
            tmpFijk.face = fijkOrient->face;
540
0
            CoordIJK *ijk = &tmpFijk.coord;
541
542
            // rotate and translate for adjacent face
543
0
            for (int i = 0; i < fijkOrient->ccwRot60; i++) _ijkRotate60ccw(ijk);
544
545
0
            CoordIJK transVec = fijkOrient->translate;
546
0
            _ijkScale(&transVec, unitScaleByCIIres[adjRes] * 3);
547
0
            _ijkAdd(ijk, &transVec, ijk);
548
0
            _ijkNormalize(ijk);
549
550
0
            Vec2d orig2d1;
551
0
            _ijkToHex2d(ijk, &orig2d1);
552
553
            // find the appropriate icosa face edge vertexes
554
0
            int maxDim = maxDimByCIIres[adjRes];
555
0
            Vec2d v0 = {3.0 * maxDim, 0.0};
556
0
            Vec2d v1 = {-1.5 * maxDim, 3.0 * M_SQRT3_2 * maxDim};
557
0
            Vec2d v2 = {-1.5 * maxDim, -3.0 * M_SQRT3_2 * maxDim};
558
559
0
            Vec2d *edge0;
560
0
            Vec2d *edge1;
561
0
            switch (adjacentFaceDir[tmpFijk.face][fijk.face]) {
562
0
                case IJ:
563
0
                    edge0 = &v0;
564
0
                    edge1 = &v1;
565
0
                    break;
566
0
                case JK:
567
0
                    edge0 = &v1;
568
0
                    edge1 = &v2;
569
0
                    break;
570
0
                case KI:
571
0
                default:
572
0
                    assert(adjacentFaceDir[tmpFijk.face][fijk.face] == KI);
573
0
                    edge0 = &v2;
574
0
                    edge1 = &v0;
575
0
                    break;
576
0
            }
577
578
            // find the intersection and add the lat/lng point to the result
579
0
            Vec2d inter;
580
0
            _v2dIntersect(&orig2d0, &orig2d1, edge0, edge1, &inter);
581
0
            _hex2dToGeo(&inter, tmpFijk.face, adjRes, 1,
582
0
                        &g->verts[g->numVerts]);
583
0
            g->numVerts++;
584
0
        }
585
586
        // convert vertex to lat/lng and add to the result
587
        // vert == start + NUM_PENT_VERTS is only used to test for possible
588
        // intersection on last edge
589
0
        if (vert < start + NUM_PENT_VERTS) {
590
0
            Vec2d vec;
591
0
            _ijkToHex2d(&fijk.coord, &vec);
592
0
            _hex2dToGeo(&vec, fijk.face, adjRes, 1, &g->verts[g->numVerts]);
593
0
            g->numVerts++;
594
0
        }
595
596
0
        lastFijk = fijk;
597
0
    }
598
0
}
599
600
/**
601
 * Get the vertices of a pentagon cell as substrate FaceIJK addresses
602
 *
603
 * @param fijk The FaceIJK address of the cell.
604
 * @param res The H3 resolution of the cell. This may be adjusted if
605
 *            necessary for the substrate grid resolution.
606
 * @param fijkVerts Output array for the vertices
607
 */
608
0
void _faceIjkPentToVerts(FaceIJK *fijk, int *res, FaceIJK *fijkVerts) {
609
    // the vertexes of an origin-centered pentagon in a Class II resolution on a
610
    // substrate grid with aperture sequence 33r. The aperture 3 gets us the
611
    // vertices, and the 3r gets us back to Class II.
612
    // vertices listed ccw from the i-axes
613
0
    CoordIJK vertsCII[NUM_PENT_VERTS] = {
614
0
        {2, 1, 0},  // 0
615
0
        {1, 2, 0},  // 1
616
0
        {0, 2, 1},  // 2
617
0
        {0, 1, 2},  // 3
618
0
        {1, 0, 2},  // 4
619
0
    };
620
621
    // the vertexes of an origin-centered pentagon in a Class III resolution on
622
    // a substrate grid with aperture sequence 33r7r. The aperture 3 gets us the
623
    // vertices, and the 3r7r gets us to Class II. vertices listed ccw from the
624
    // i-axes
625
0
    CoordIJK vertsCIII[NUM_PENT_VERTS] = {
626
0
        {5, 4, 0},  // 0
627
0
        {1, 5, 0},  // 1
628
0
        {0, 5, 4},  // 2
629
0
        {0, 1, 5},  // 3
630
0
        {4, 0, 5},  // 4
631
0
    };
632
633
    // get the correct set of substrate vertices for this resolution
634
0
    CoordIJK *verts;
635
0
    if (isResolutionClassIII(*res))
636
0
        verts = vertsCIII;
637
0
    else
638
0
        verts = vertsCII;
639
640
    // adjust the center point to be in an aperture 33r substrate grid
641
    // these should be composed for speed
642
0
    _downAp3(&fijk->coord);
643
0
    _downAp3r(&fijk->coord);
644
645
    // if res is Class III we need to add a cw aperture 7 to get to
646
    // icosahedral Class II
647
0
    if (isResolutionClassIII(*res)) {
648
0
        _downAp7r(&fijk->coord);
649
0
        *res += 1;
650
0
    }
651
652
    // The center point is now in the same substrate grid as the origin
653
    // cell vertices. Add the center point substate coordinates
654
    // to each vertex to translate the vertices to that cell.
655
0
    for (int v = 0; v < NUM_PENT_VERTS; v++) {
656
0
        fijkVerts[v].face = fijk->face;
657
0
        _ijkAdd(&fijk->coord, &verts[v], &fijkVerts[v].coord);
658
0
        _ijkNormalize(&fijkVerts[v].coord);
659
0
    }
660
0
}
661
662
/**
663
 * Generates the cell boundary in spherical coordinates for a cell given by a
664
 * FaceIJK address at a specified resolution.
665
 *
666
 * @param h The FaceIJK address of the cell.
667
 * @param res The H3 resolution of the cell.
668
 * @param start The first topological vertex to return.
669
 * @param length The number of topological vertexes to return.
670
 * @param g The spherical coordinates of the cell boundary.
671
 */
672
void _faceIjkToCellBoundary(const FaceIJK *h, int res, int start, int length,
673
0
                            CellBoundary *g) {
674
0
    int adjRes = res;
675
0
    FaceIJK centerIJK = *h;
676
0
    FaceIJK fijkVerts[NUM_HEX_VERTS];
677
0
    _faceIjkToVerts(&centerIJK, &adjRes, fijkVerts);
678
679
    // If we're returning the entire loop, we need one more iteration in case
680
    // of a distortion vertex on the last edge
681
0
    int additionalIteration = length == NUM_HEX_VERTS ? 1 : 0;
682
683
    // convert each vertex to lat/lng
684
    // adjust the face of each vertex as appropriate and introduce
685
    // edge-crossing vertices as needed
686
0
    g->numVerts = 0;
687
0
    int lastFace = -1;
688
0
    Overage lastOverage = NO_OVERAGE;
689
0
    for (int vert = start; vert < start + length + additionalIteration;
690
0
         vert++) {
691
0
        int v = vert % NUM_HEX_VERTS;
692
693
0
        FaceIJK fijk = fijkVerts[v];
694
695
0
        const int pentLeading4 = 0;
696
0
        Overage overage = _adjustOverageClassII(&fijk, adjRes, pentLeading4, 1);
697
698
        /*
699
        Check for edge-crossing. Each face of the underlying icosahedron is a
700
        different projection plane. So if an edge of the hexagon crosses an
701
        icosahedron edge, an additional vertex must be introduced at that
702
        intersection point. Then each half of the cell edge can be projected
703
        to geographic coordinates using the appropriate icosahedron face
704
        projection. Note that Class II cell edges have vertices on the face
705
        edge, with no edge line intersections.
706
        */
707
0
        if (isResolutionClassIII(res) && vert > start &&
708
0
            fijk.face != lastFace && lastOverage != FACE_EDGE) {
709
            // find hex2d of the two vertexes on original face
710
0
            int lastV = (v + 5) % NUM_HEX_VERTS;
711
0
            Vec2d orig2d0;
712
0
            _ijkToHex2d(&fijkVerts[lastV].coord, &orig2d0);
713
714
0
            Vec2d orig2d1;
715
0
            _ijkToHex2d(&fijkVerts[v].coord, &orig2d1);
716
717
            // find the appropriate icosa face edge vertexes
718
0
            int maxDim = maxDimByCIIres[adjRes];
719
0
            Vec2d v0 = {3.0 * maxDim, 0.0};
720
0
            Vec2d v1 = {-1.5 * maxDim, 3.0 * M_SQRT3_2 * maxDim};
721
0
            Vec2d v2 = {-1.5 * maxDim, -3.0 * M_SQRT3_2 * maxDim};
722
723
0
            int face2 = ((lastFace == centerIJK.face) ? fijk.face : lastFace);
724
0
            Vec2d *edge0;
725
0
            Vec2d *edge1;
726
0
            switch (adjacentFaceDir[centerIJK.face][face2]) {
727
0
                case IJ:
728
0
                    edge0 = &v0;
729
0
                    edge1 = &v1;
730
0
                    break;
731
0
                case JK:
732
0
                    edge0 = &v1;
733
0
                    edge1 = &v2;
734
0
                    break;
735
                // case KI:
736
0
                default:
737
0
                    assert(adjacentFaceDir[centerIJK.face][face2] == KI);
738
0
                    edge0 = &v2;
739
0
                    edge1 = &v0;
740
0
                    break;
741
0
            }
742
743
            // find the intersection and add the lat/lng point to the result
744
0
            Vec2d inter;
745
0
            _v2dIntersect(&orig2d0, &orig2d1, edge0, edge1, &inter);
746
            /*
747
            If a point of intersection occurs at a hexagon vertex, then each
748
            adjacent hexagon edge will lie completely on a single icosahedron
749
            face, and no additional vertex is required.
750
            */
751
0
            bool isIntersectionAtVertex = _v2dAlmostEquals(&orig2d0, &inter) ||
752
0
                                          _v2dAlmostEquals(&orig2d1, &inter);
753
0
            if (!isIntersectionAtVertex) {
754
0
                _hex2dToGeo(&inter, centerIJK.face, adjRes, 1,
755
0
                            &g->verts[g->numVerts]);
756
0
                g->numVerts++;
757
0
            }
758
0
        }
759
760
        // convert vertex to lat/lng and add to the result
761
        // vert == start + NUM_HEX_VERTS is only used to test for possible
762
        // intersection on last edge
763
0
        if (vert < start + NUM_HEX_VERTS) {
764
0
            Vec2d vec;
765
0
            _ijkToHex2d(&fijk.coord, &vec);
766
0
            _hex2dToGeo(&vec, fijk.face, adjRes, 1, &g->verts[g->numVerts]);
767
0
            g->numVerts++;
768
0
        }
769
770
0
        lastFace = fijk.face;
771
0
        lastOverage = overage;
772
0
    }
773
0
}
774
775
/**
776
 * Get the vertices of a cell as substrate FaceIJK addresses
777
 *
778
 * @param fijk The FaceIJK address of the cell.
779
 * @param res The H3 resolution of the cell. This may be adjusted if
780
 *            necessary for the substrate grid resolution.
781
 * @param fijkVerts Output array for the vertices
782
 */
783
0
void _faceIjkToVerts(FaceIJK *fijk, int *res, FaceIJK *fijkVerts) {
784
    // the vertexes of an origin-centered cell in a Class II resolution on a
785
    // substrate grid with aperture sequence 33r. The aperture 3 gets us the
786
    // vertices, and the 3r gets us back to Class II.
787
    // vertices listed ccw from the i-axes
788
0
    CoordIJK vertsCII[NUM_HEX_VERTS] = {
789
0
        {2, 1, 0},  // 0
790
0
        {1, 2, 0},  // 1
791
0
        {0, 2, 1},  // 2
792
0
        {0, 1, 2},  // 3
793
0
        {1, 0, 2},  // 4
794
0
        {2, 0, 1}   // 5
795
0
    };
796
797
    // the vertexes of an origin-centered cell in a Class III resolution on a
798
    // substrate grid with aperture sequence 33r7r. The aperture 3 gets us the
799
    // vertices, and the 3r7r gets us to Class II.
800
    // vertices listed ccw from the i-axes
801
0
    CoordIJK vertsCIII[NUM_HEX_VERTS] = {
802
0
        {5, 4, 0},  // 0
803
0
        {1, 5, 0},  // 1
804
0
        {0, 5, 4},  // 2
805
0
        {0, 1, 5},  // 3
806
0
        {4, 0, 5},  // 4
807
0
        {5, 0, 1}   // 5
808
0
    };
809
810
    // get the correct set of substrate vertices for this resolution
811
0
    CoordIJK *verts;
812
0
    if (isResolutionClassIII(*res))
813
0
        verts = vertsCIII;
814
0
    else
815
0
        verts = vertsCII;
816
817
    // adjust the center point to be in an aperture 33r substrate grid
818
    // these should be composed for speed
819
0
    _downAp3(&fijk->coord);
820
0
    _downAp3r(&fijk->coord);
821
822
    // if res is Class III we need to add a cw aperture 7 to get to
823
    // icosahedral Class II
824
0
    if (isResolutionClassIII(*res)) {
825
0
        _downAp7r(&fijk->coord);
826
0
        *res += 1;
827
0
    }
828
829
    // The center point is now in the same substrate grid as the origin
830
    // cell vertices. Add the center point substate coordinates
831
    // to each vertex to translate the vertices to that cell.
832
0
    for (int v = 0; v < NUM_HEX_VERTS; v++) {
833
0
        fijkVerts[v].face = fijk->face;
834
0
        _ijkAdd(&fijk->coord, &verts[v], &fijkVerts[v].coord);
835
0
        _ijkNormalize(&fijkVerts[v].coord);
836
0
    }
837
0
}
838
839
/**
840
 * Adjusts a FaceIJK address in place so that the resulting cell address is
841
 * relative to the correct icosahedral face.
842
 *
843
 * @param fijk The FaceIJK address of the cell.
844
 * @param res The H3 resolution of the cell.
845
 * @param pentLeading4 Whether or not the cell is a pentagon with a leading
846
 *        digit 4.
847
 * @param substrate Whether or not the cell is in a substrate grid.
848
 * @return 0 if on original face (no overage); 1 if on face edge (only occurs
849
 *         on substrate grids); 2 if overage on new face interior
850
 */
851
Overage _adjustOverageClassII(FaceIJK *fijk, int res, int pentLeading4,
852
0
                              int substrate) {
853
0
    Overage overage = NO_OVERAGE;
854
855
0
    CoordIJK *ijk = &fijk->coord;
856
857
    // get the maximum dimension value; scale if a substrate grid
858
0
    int maxDim = maxDimByCIIres[res];
859
0
    if (substrate) maxDim *= 3;
860
861
    // check for overage
862
0
    if (substrate && ijk->i + ijk->j + ijk->k == maxDim)  // on edge
863
0
        overage = FACE_EDGE;
864
0
    else if (ijk->i + ijk->j + ijk->k > maxDim)  // overage
865
0
    {
866
0
        overage = NEW_FACE;
867
868
0
        const FaceOrientIJK *fijkOrient;
869
0
        if (ijk->k > 0) {
870
0
            if (ijk->j > 0)  // jk "quadrant"
871
0
                fijkOrient = &faceNeighbors[fijk->face][JK];
872
0
            else  // ik "quadrant"
873
0
            {
874
0
                fijkOrient = &faceNeighbors[fijk->face][KI];
875
876
                // adjust for the pentagonal missing sequence
877
0
                if (pentLeading4) {
878
                    // translate origin to center of pentagon
879
0
                    CoordIJK origin;
880
0
                    _setIJK(&origin, maxDim, 0, 0);
881
0
                    CoordIJK tmp;
882
0
                    _ijkSub(ijk, &origin, &tmp);
883
                    // rotate to adjust for the missing sequence
884
0
                    _ijkRotate60cw(&tmp);
885
                    // translate the origin back to the center of the triangle
886
0
                    _ijkAdd(&tmp, &origin, ijk);
887
0
                }
888
0
            }
889
0
        } else  // ij "quadrant"
890
0
            fijkOrient = &faceNeighbors[fijk->face][IJ];
891
892
0
        fijk->face = fijkOrient->face;
893
894
        // rotate and translate for adjacent face
895
0
        for (int i = 0; i < fijkOrient->ccwRot60; i++) _ijkRotate60ccw(ijk);
896
897
0
        CoordIJK transVec = fijkOrient->translate;
898
0
        int unitScale = unitScaleByCIIres[res];
899
0
        if (substrate) unitScale *= 3;
900
0
        _ijkScale(&transVec, unitScale);
901
0
        _ijkAdd(ijk, &transVec, ijk);
902
0
        _ijkNormalize(ijk);
903
904
        // overage points on pentagon boundaries can end up on edges
905
0
        if (substrate && ijk->i + ijk->j + ijk->k == maxDim)  // on edge
906
0
            overage = FACE_EDGE;
907
0
    }
908
909
0
    return overage;
910
0
}
911
912
/**
913
 * Adjusts a FaceIJK address for a pentagon vertex in a substrate grid in
914
 * place so that the resulting cell address is relative to the correct
915
 * icosahedral face.
916
 *
917
 * @param fijk The FaceIJK address of the cell.
918
 * @param res The H3 resolution of the cell.
919
 */
920
0
Overage _adjustPentVertOverage(FaceIJK *fijk, int res) {
921
0
    int pentLeading4 = 0;
922
0
    Overage overage;
923
0
    do {
924
0
        overage = _adjustOverageClassII(fijk, res, pentLeading4, 1);
925
0
    } while (overage == NEW_FACE);
926
0
    return overage;
927
0
}
928
929
/**
930
 * Encodes a coordinate on the sphere to the corresponding icosahedral face and
931
 * containing the squared euclidean distance to that face center.
932
 *
933
 * @param g The spherical coordinates to encode.
934
 * @param face The icosahedral face containing the spherical coordinates.
935
 * @param sqd The squared euclidean distance to its icosahedral face center.
936
 */
937
0
void _geoToClosestFace(const LatLng *g, int *face, double *sqd) {
938
0
    Vec3d v3d;
939
0
    _geoToVec3d(g, &v3d);
940
941
    // determine the icosahedron face
942
0
    *face = 0;
943
    // The distance between two farthest points is 2.0, therefore the square of
944
    // the distance between two points should always be less or equal than 4.0 .
945
0
    *sqd = 5.0;
946
0
    for (int f = 0; f < NUM_ICOSA_FACES; ++f) {
947
0
        double sqdT = _pointSquareDist(&faceCenterPoint[f], &v3d);
948
0
        if (sqdT < *sqd) {
949
0
            *face = f;
950
0
            *sqd = sqdT;
951
0
        }
952
0
    }
953
0
}