Coverage Report

Created: 2023-09-25 06:53

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