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