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
5.56k
#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
473
void _hex2dToGeo(const Vec2d *v, int face, int res, int substrate, LatLng *g) {
438
    // calculate (r, theta) in hex2d
439
473
    double r = _v2dMag(v);
440
441
473
    if (r < EPSILON) {
442
0
        *g = faceCenterGeo[face];
443
0
        return;
444
0
    }
445
446
473
    double theta = atan2(v->y, v->x);
447
448
    // scale for current resolution length u
449
5.56k
    for (int i = 0; i < res; i++) r /= M_SQRT7;
450
451
    // scale accordingly if this is a substrate grid
452
473
    if (substrate) {
453
473
        r /= 3.0;
454
473
        if (isResolutionClassIII(res)) r /= M_SQRT7;
455
473
    }
456
457
473
    r *= RES0_U_GNOMONIC;
458
459
    // perform inverse gnomonic scaling of r
460
473
    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
473
    if (!substrate && isResolutionClassIII(res))
465
0
        theta = _posAngleRads(theta + M_AP7_ROT_RADS);
466
467
    // find theta as an azimuth
468
473
    theta = _posAngleRads(faceAxesAzRadsCII[face][0] - theta);
469
470
    // now find the point at (r,theta) from the face center
471
473
    _geoAzDistanceRads(&faceCenterGeo[face], theta, r, g);
472
473
}
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
0
void _faceIjkToGeo(const FaceIJK *h, int res, LatLng *g) {
483
0
    Vec2d v;
484
0
    _ijkToHex2d(&h->coord, &v);
485
0
    _hex2dToGeo(&v, h->face, res, 0, g);
486
0
}
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
6
                                int length, CellBoundary *g) {
500
6
    int adjRes = res;
501
6
    FaceIJK centerIJK = *h;
502
6
    FaceIJK fijkVerts[NUM_PENT_VERTS];
503
6
    _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
6
    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
6
    g->numVerts = 0;
513
6
    FaceIJK lastFijk;
514
12
    for (int vert = start; vert < start + length + additionalIteration;
515
6
         vert++) {
516
6
        int v = vert % NUM_PENT_VERTS;
517
518
6
        FaceIJK fijk = fijkVerts[v];
519
520
6
        _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
6
        if (isResolutionClassIII(res) && vert > start) {
526
            // find hex2d of the two vertexes on the last face
527
528
0
            FaceIJK tmpFijk = fijk;
529
530
0
            Vec2d orig2d0;
531
0
            _ijkToHex2d(&lastFijk.coord, &orig2d0);
532
533
0
            int currentToLastDir = adjacentFaceDir[tmpFijk.face][lastFijk.face];
534
535
0
            const FaceOrientIJK *fijkOrient =
536
0
                &faceNeighbors[tmpFijk.face][currentToLastDir];
537
538
0
            tmpFijk.face = fijkOrient->face;
539
0
            CoordIJK *ijk = &tmpFijk.coord;
540
541
            // rotate and translate for adjacent face
542
0
            for (int i = 0; i < fijkOrient->ccwRot60; i++) _ijkRotate60ccw(ijk);
543
544
0
            CoordIJK transVec = fijkOrient->translate;
545
0
            _ijkScale(&transVec, unitScaleByCIIres[adjRes] * 3);
546
0
            _ijkAdd(ijk, &transVec, ijk);
547
0
            _ijkNormalize(ijk);
548
549
0
            Vec2d orig2d1;
550
0
            _ijkToHex2d(ijk, &orig2d1);
551
552
            // find the appropriate icosa face edge vertexes
553
0
            int maxDim = maxDimByCIIres[adjRes];
554
0
            Vec2d v0 = {3.0 * maxDim, 0.0};
555
0
            Vec2d v1 = {-1.5 * maxDim, 3.0 * M_SQRT3_2 * maxDim};
556
0
            Vec2d v2 = {-1.5 * maxDim, -3.0 * M_SQRT3_2 * maxDim};
557
558
0
            Vec2d *edge0;
559
0
            Vec2d *edge1;
560
0
            switch (adjacentFaceDir[tmpFijk.face][fijk.face]) {
561
0
                case IJ:
562
0
                    edge0 = &v0;
563
0
                    edge1 = &v1;
564
0
                    break;
565
0
                case JK:
566
0
                    edge0 = &v1;
567
0
                    edge1 = &v2;
568
0
                    break;
569
0
                case KI:
570
0
                default:
571
0
                    assert(adjacentFaceDir[tmpFijk.face][fijk.face] == KI);
572
0
                    edge0 = &v2;
573
0
                    edge1 = &v0;
574
0
                    break;
575
0
            }
576
577
            // find the intersection and add the lat/lng point to the result
578
0
            Vec2d inter;
579
0
            _v2dIntersect(&orig2d0, &orig2d1, edge0, edge1, &inter);
580
0
            _hex2dToGeo(&inter, tmpFijk.face, adjRes, 1,
581
0
                        &g->verts[g->numVerts]);
582
0
            g->numVerts++;
583
0
        }
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
6
        if (vert < start + NUM_PENT_VERTS) {
589
6
            Vec2d vec;
590
6
            _ijkToHex2d(&fijk.coord, &vec);
591
6
            _hex2dToGeo(&vec, fijk.face, adjRes, 1, &g->verts[g->numVerts]);
592
6
            g->numVerts++;
593
6
        }
594
595
6
        lastFijk = fijk;
596
6
    }
597
6
}
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
6
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
6
    CoordIJK vertsCII[NUM_PENT_VERTS] = {
613
6
        {2, 1, 0},  // 0
614
6
        {1, 2, 0},  // 1
615
6
        {0, 2, 1},  // 2
616
6
        {0, 1, 2},  // 3
617
6
        {1, 0, 2},  // 4
618
6
    };
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
6
    CoordIJK vertsCIII[NUM_PENT_VERTS] = {
625
6
        {5, 4, 0},  // 0
626
6
        {1, 5, 0},  // 1
627
6
        {0, 5, 4},  // 2
628
6
        {0, 1, 5},  // 3
629
6
        {4, 0, 5},  // 4
630
6
    };
631
632
    // get the correct set of substrate vertices for this resolution
633
6
    CoordIJK *verts;
634
6
    if (isResolutionClassIII(*res))
635
3
        verts = vertsCIII;
636
3
    else
637
3
        verts = vertsCII;
638
639
    // adjust the center point to be in an aperture 33r substrate grid
640
    // these should be composed for speed
641
6
    _downAp3(&fijk->coord);
642
6
    _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
6
    if (isResolutionClassIII(*res)) {
647
3
        _downAp7r(&fijk->coord);
648
3
        *res += 1;
649
3
    }
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
36
    for (int v = 0; v < NUM_PENT_VERTS; v++) {
655
30
        fijkVerts[v].face = fijk->face;
656
30
        _ijkAdd(&fijk->coord, &verts[v], &fijkVerts[v].coord);
657
30
        _ijkNormalize(&fijkVerts[v].coord);
658
30
    }
659
6
}
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
467
                            CellBoundary *g) {
673
467
    int adjRes = res;
674
467
    FaceIJK centerIJK = *h;
675
467
    FaceIJK fijkVerts[NUM_HEX_VERTS];
676
467
    _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
467
    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
467
    g->numVerts = 0;
686
467
    int lastFace = -1;
687
467
    Overage lastOverage = NO_OVERAGE;
688
934
    for (int vert = start; vert < start + length + additionalIteration;
689
467
         vert++) {
690
467
        int v = vert % NUM_HEX_VERTS;
691
692
467
        FaceIJK fijk = fijkVerts[v];
693
694
467
        const int pentLeading4 = 0;
695
467
        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
467
        if (isResolutionClassIII(res) && vert > start &&
707
467
            fijk.face != lastFace && lastOverage != FACE_EDGE) {
708
            // find hex2d of the two vertexes on original face
709
0
            int lastV = (v + 5) % NUM_HEX_VERTS;
710
0
            Vec2d orig2d0;
711
0
            _ijkToHex2d(&fijkVerts[lastV].coord, &orig2d0);
712
713
0
            Vec2d orig2d1;
714
0
            _ijkToHex2d(&fijkVerts[v].coord, &orig2d1);
715
716
            // find the appropriate icosa face edge vertexes
717
0
            int maxDim = maxDimByCIIres[adjRes];
718
0
            Vec2d v0 = {3.0 * maxDim, 0.0};
719
0
            Vec2d v1 = {-1.5 * maxDim, 3.0 * M_SQRT3_2 * maxDim};
720
0
            Vec2d v2 = {-1.5 * maxDim, -3.0 * M_SQRT3_2 * maxDim};
721
722
0
            int face2 = ((lastFace == centerIJK.face) ? fijk.face : lastFace);
723
0
            Vec2d *edge0;
724
0
            Vec2d *edge1;
725
0
            switch (adjacentFaceDir[centerIJK.face][face2]) {
726
0
                case IJ:
727
0
                    edge0 = &v0;
728
0
                    edge1 = &v1;
729
0
                    break;
730
0
                case JK:
731
0
                    edge0 = &v1;
732
0
                    edge1 = &v2;
733
0
                    break;
734
                // case KI:
735
0
                default:
736
0
                    assert(adjacentFaceDir[centerIJK.face][face2] == KI);
737
0
                    edge0 = &v2;
738
0
                    edge1 = &v0;
739
0
                    break;
740
0
            }
741
742
            // find the intersection and add the lat/lng point to the result
743
0
            Vec2d inter;
744
0
            _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
0
            bool isIntersectionAtVertex = _v2dAlmostEquals(&orig2d0, &inter) ||
751
0
                                          _v2dAlmostEquals(&orig2d1, &inter);
752
0
            if (!isIntersectionAtVertex) {
753
0
                _hex2dToGeo(&inter, centerIJK.face, adjRes, 1,
754
0
                            &g->verts[g->numVerts]);
755
0
                g->numVerts++;
756
0
            }
757
0
        }
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
467
        if (vert < start + NUM_HEX_VERTS) {
763
467
            Vec2d vec;
764
467
            _ijkToHex2d(&fijk.coord, &vec);
765
467
            _hex2dToGeo(&vec, fijk.face, adjRes, 1, &g->verts[g->numVerts]);
766
467
            g->numVerts++;
767
467
        }
768
769
467
        lastFace = fijk.face;
770
467
        lastOverage = overage;
771
467
    }
772
467
}
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
467
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
467
    CoordIJK vertsCII[NUM_HEX_VERTS] = {
788
467
        {2, 1, 0},  // 0
789
467
        {1, 2, 0},  // 1
790
467
        {0, 2, 1},  // 2
791
467
        {0, 1, 2},  // 3
792
467
        {1, 0, 2},  // 4
793
467
        {2, 0, 1}   // 5
794
467
    };
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
467
    CoordIJK vertsCIII[NUM_HEX_VERTS] = {
801
467
        {5, 4, 0},  // 0
802
467
        {1, 5, 0},  // 1
803
467
        {0, 5, 4},  // 2
804
467
        {0, 1, 5},  // 3
805
467
        {4, 0, 5},  // 4
806
467
        {5, 0, 1}   // 5
807
467
    };
808
809
    // get the correct set of substrate vertices for this resolution
810
467
    CoordIJK *verts;
811
467
    if (isResolutionClassIII(*res))
812
352
        verts = vertsCIII;
813
115
    else
814
115
        verts = vertsCII;
815
816
    // adjust the center point to be in an aperture 33r substrate grid
817
    // these should be composed for speed
818
467
    _downAp3(&fijk->coord);
819
467
    _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
467
    if (isResolutionClassIII(*res)) {
824
352
        _downAp7r(&fijk->coord);
825
352
        *res += 1;
826
352
    }
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
3.26k
    for (int v = 0; v < NUM_HEX_VERTS; v++) {
832
2.80k
        fijkVerts[v].face = fijk->face;
833
2.80k
        _ijkAdd(&fijk->coord, &verts[v], &fijkVerts[v].coord);
834
2.80k
        _ijkNormalize(&fijkVerts[v].coord);
835
2.80k
    }
836
467
}
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.36k
                              int substrate) {
852
9.36k
    Overage overage = NO_OVERAGE;
853
854
9.36k
    CoordIJK *ijk = &fijk->coord;
855
856
    // get the maximum dimension value; scale if a substrate grid
857
9.36k
    int maxDim = maxDimByCIIres[res];
858
9.36k
    if (substrate) maxDim *= 3;
859
860
    // check for overage
861
9.36k
    if (substrate && ijk->i + ijk->j + ijk->k == maxDim)  // on edge
862
45
        overage = FACE_EDGE;
863
9.31k
    else if (ijk->i + ijk->j + ijk->k > maxDim)  // overage
864
4.36k
    {
865
4.36k
        overage = NEW_FACE;
866
867
4.36k
        const FaceOrientIJK *fijkOrient;
868
4.36k
        if (ijk->k > 0) {
869
3.49k
            if (ijk->j > 0)  // jk "quadrant"
870
1.12k
                fijkOrient = &faceNeighbors[fijk->face][JK];
871
2.36k
            else  // ik "quadrant"
872
2.36k
            {
873
2.36k
                fijkOrient = &faceNeighbors[fijk->face][KI];
874
875
                // adjust for the pentagonal missing sequence
876
2.36k
                if (pentLeading4) {
877
                    // translate origin to center of pentagon
878
644
                    CoordIJK origin;
879
644
                    _setIJK(&origin, maxDim, 0, 0);
880
644
                    CoordIJK tmp;
881
644
                    _ijkSub(ijk, &origin, &tmp);
882
                    // rotate to adjust for the missing sequence
883
644
                    _ijkRotate60cw(&tmp);
884
                    // translate the origin back to the center of the triangle
885
644
                    _ijkAdd(&tmp, &origin, ijk);
886
644
                }
887
2.36k
            }
888
3.49k
        } else  // ij "quadrant"
889
875
            fijkOrient = &faceNeighbors[fijk->face][IJ];
890
891
4.36k
        fijk->face = fijkOrient->face;
892
893
        // rotate and translate for adjacent face
894
19.1k
        for (int i = 0; i < fijkOrient->ccwRot60; i++) _ijkRotate60ccw(ijk);
895
896
4.36k
        CoordIJK transVec = fijkOrient->translate;
897
4.36k
        int unitScale = unitScaleByCIIres[res];
898
4.36k
        if (substrate) unitScale *= 3;
899
4.36k
        _ijkScale(&transVec, unitScale);
900
4.36k
        _ijkAdd(ijk, &transVec, ijk);
901
4.36k
        _ijkNormalize(ijk);
902
903
        // overage points on pentagon boundaries can end up on edges
904
4.36k
        if (substrate && ijk->i + ijk->j + ijk->k == maxDim)  // on edge
905
3
            overage = FACE_EDGE;
906
4.36k
    }
907
908
9.36k
    return overage;
909
9.36k
}
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
6
Overage _adjustPentVertOverage(FaceIJK *fijk, int res) {
920
6
    int pentLeading4 = 0;
921
6
    Overage overage;
922
14
    do {
923
14
        overage = _adjustOverageClassII(fijk, res, pentLeading4, 1);
924
14
    } while (overage == NEW_FACE);
925
6
    return overage;
926
6
}
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
}