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