1 /*
2 * Copyright 2016-2020 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 utility.c
17 * @brief Miscellaneous useful functions.
18 */
19
20 #include "utility.h"
21
22 #include <assert.h>
23 #include <inttypes.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <time.h>
28
29 #include "coordijk.h"
30 #include "h3Index.h"
31 #include "h3api.h"
32
error(const char * msg)33 void error(const char* msg) {
34 fflush(stdout);
35 fflush(stderr);
36 fprintf(stderr, "ERROR: %s.\n", msg);
37 exit(1);
38 }
39
40 /**
41 * Prints the H3Index
42 */
h3Print(H3Index h)43 void h3Print(H3Index h) { printf("%" PRIx64, h); }
44
45 /**
46 * Prints the H3Index and a newline
47 */
h3Println(H3Index h)48 void h3Println(H3Index h) { printf("%" PRIx64 "\n", h); }
49
50 /**
51 * Prints the CoordIJK
52 */
coordIjkPrint(const CoordIJK * c)53 void coordIjkPrint(const CoordIJK* c) {
54 printf("[%d, %d, %d]", c->i, c->j, c->k);
55 }
56
57 /**
58 * Assumes `str` is big enough to hold the result.
59 */
geoToStringRads(const GeoCoord * p,char * str)60 void geoToStringRads(const GeoCoord* p, char* str) {
61 sprintf(str, "(%.4lf, %.4lf)", p->lat, p->lon);
62 }
63
64 /**
65 * Assumes `str` is big enough to hold the result.
66 */
geoToStringDegs(const GeoCoord * p,char * str)67 void geoToStringDegs(const GeoCoord* p, char* str) {
68 sprintf(str, "(%.9lf, %.9lf)", H3_EXPORT(radsToDegs)(p->lat),
69 H3_EXPORT(radsToDegs)(p->lon));
70 }
71
72 /**
73 * Assumes `str` is big enough to hold the result.
74 */
geoToStringDegsNoFmt(const GeoCoord * p,char * str)75 void geoToStringDegsNoFmt(const GeoCoord* p, char* str) {
76 sprintf(str, "%.9lf %.9lf", H3_EXPORT(radsToDegs)(p->lat),
77 H3_EXPORT(radsToDegs)(p->lon));
78 }
79
geoPrint(const GeoCoord * p)80 void geoPrint(const GeoCoord* p) {
81 char buff[BUFF_SIZE];
82 geoToStringDegs(p, buff);
83 printf("%s", buff);
84 }
85
geoPrintln(const GeoCoord * p)86 void geoPrintln(const GeoCoord* p) {
87 geoPrint(p);
88 printf("\n");
89 }
90
geoPrintNoFmt(const GeoCoord * p)91 void geoPrintNoFmt(const GeoCoord* p) {
92 char buff[BUFF_SIZE];
93 geoToStringDegsNoFmt(p, buff);
94 printf("%s", buff);
95 }
96
geoPrintlnNoFmt(const GeoCoord * p)97 void geoPrintlnNoFmt(const GeoCoord* p) {
98 geoPrintNoFmt(p);
99 printf("\n");
100 }
101
geoBoundaryPrint(const GeoBoundary * b)102 void geoBoundaryPrint(const GeoBoundary* b) {
103 char buff[BUFF_SIZE];
104 printf("{");
105 for (int v = 0; v < b->numVerts; v++) {
106 geoToStringDegsNoFmt(&b->verts[v], buff);
107 printf("%s ", buff);
108 }
109 printf("}");
110 }
111
geoBoundaryPrintln(const GeoBoundary * b)112 void geoBoundaryPrintln(const GeoBoundary* b) {
113 char buff[BUFF_SIZE];
114 printf("{\n");
115 for (int v = 0; v < b->numVerts; v++) {
116 geoToStringDegsNoFmt(&b->verts[v], buff);
117 printf(" %s\n", buff);
118 }
119 printf("}\n");
120 }
121
122 /**
123 * Assumes `f` is open and ready for reading.
124 * @return 0 on success, EOF on EOF
125 */
readBoundary(FILE * f,GeoBoundary * b)126 int readBoundary(FILE* f, GeoBoundary* b) {
127 char buff[BUFF_SIZE];
128
129 // get the first line, which should be a "{"
130 if (!fgets(buff, BUFF_SIZE, f)) {
131 if (feof(stdin)) {
132 return EOF;
133 } else {
134 printf("reading GeoBoundary from input");
135 return -1;
136 }
137 }
138
139 if (buff[0] != '{') {
140 printf("missing GeoBoundary {");
141 return -2;
142 }
143
144 // now read the verts
145
146 b->numVerts = 0;
147 while (1) {
148 if (!fgets(buff, BUFF_SIZE, f)) {
149 printf("reading GeoBoundary from input");
150 return -3;
151 }
152
153 if (buff[0] == '}') {
154 if (b->numVerts == 0) {
155 printf("reading empty cell boundary");
156 return -4;
157 } else {
158 break;
159 }
160 }
161
162 if (b->numVerts == MAX_CELL_BNDRY_VERTS) {
163 printf("too many vertices in GeoBoundary from input");
164 return -5;
165 }
166
167 double latDegs, lonDegs;
168 if (sscanf(buff, "%lf %lf", &latDegs, &lonDegs) != 2) {
169 printf("parsing GeoBoundary from input");
170 return -6;
171 }
172
173 setGeoDegs(&b->verts[b->numVerts], latDegs, lonDegs);
174 b->numVerts++;
175 }
176
177 return 0;
178 }
179
180 /**
181 * Move nonzero elements to the front of array `a` of length `n`.
182 *
183 * Loop invariant: Everything *before* `i` or *after* `j` is "done".
184 * Move `i` and `j` inwards until they equal, and exit.
185 * You can move `i` forward until there's a zero in front of it.
186 * You can move `j` backward until there's a nonzero to the left of it.
187 * Anything to the right of `j` is "junk" that can be reallocated.
188 *
189 * Before:
190 * | a | b | 0 | c | d | ... |
191 * ^ ^
192 * i j
193 * After:
194 * | a | b | d | c | d | ... |
195 * ^ ^
196 * i j
197 *
198 * todo: should this function be in the public API?
199 * todo: add tests for this function
200 *
201 * @param a H3Index array to whose elements will be moved
202 * @param n length of the input array
203 * @return number of nonzero elements (length of new array); can reallocate
204 * memory after this point
205 */
packNonzeros(H3Index * a,size_t n)206 size_t packNonzeros(H3Index* a, size_t n) {
207 size_t i = 0;
208 size_t j = n;
209
210 while (i < j) {
211 // move j to the left until the first nonzero
212 if (a[j - 1] == 0) {
213 j -= 1;
214 continue;
215 }
216
217 // move i to the right until the first zero
218 if (a[i] != 0) {
219 i += 1;
220 continue;
221 }
222
223 // if we get to this point, we know:
224 // a[i] == 0
225 // a[j-1] != 0
226 // i < j
227 // so we can swap! (actually, move a[j-1] -> a[i])
228 a[i] = a[j - 1];
229 j -= 1;
230 }
231
232 return i;
233 }
234
235 /**
236 * Array of all cells at a given resolution.
237 *
238 * @param res resolution
239 *
240 * @return array of H3 cells at resolution res
241 */
getCellsAtRes(int res)242 H3Index* getCellsAtRes(int res) {
243 int num0 = H3_EXPORT(res0IndexCount)();
244 H3Index* cells0 = calloc(num0, sizeof(H3Index));
245 H3_EXPORT(getRes0Indexes)(cells0);
246
247 int numRes = H3_EXPORT(maxUncompactSize)(cells0, num0, res);
248
249 H3Index* cellsRes = calloc(numRes, sizeof(H3Index));
250 H3_EXPORT(uncompact)(cells0, num0, cellsRes, numRes, res);
251
252 free(cells0);
253
254 numRes = packNonzeros(cellsRes, numRes);
255 cellsRes = realloc(cellsRes, numRes * sizeof(H3Index));
256
257 return cellsRes;
258 }
259
260 /**
261 * Apply callback to every cell for a given resolution, and sum the results.
262 */
mapSumAllCells_double(int res,double (* callback)(H3Index))263 double mapSumAllCells_double(int res, double (*callback)(H3Index)) {
264 H3Index* cells = getCellsAtRes(res);
265 int N = H3_EXPORT(numHexagons)(res);
266
267 double total = 0.0;
268 for (int i = 0; i < N; i++) {
269 total += (*callback)(cells[i]);
270 }
271 free(cells);
272
273 return total;
274 }
275
276 /**
277 * Apply callback for every unidirectional edge at the given resolution.
278 */
iterateAllUnidirectionalEdgesAtRes(int res,void (* callback)(H3Index))279 void iterateAllUnidirectionalEdgesAtRes(int res, void (*callback)(H3Index)) {
280 H3Index* cells = getCellsAtRes(res);
281 int N = H3_EXPORT(numHexagons)(res);
282
283 for (int i = 0; i < N; i++) {
284 H3Index edges[6] = {H3_NULL};
285 int isPentagon = H3_EXPORT(h3IsPentagon)(cells[i]);
286 H3_EXPORT(getH3UnidirectionalEdgesFromHexagon)(cells[i], edges);
287
288 for (int j = 0; j < 6; j++) {
289 if (isPentagon && j == 0) continue;
290 (*callback)(edges[j]);
291 }
292 }
293 free(cells);
294 }
295
296 /**
297 * Call the callback for every index at the given resolution.
298 */
iterateAllIndexesAtRes(int res,void (* callback)(H3Index))299 void iterateAllIndexesAtRes(int res, void (*callback)(H3Index)) {
300 iterateAllIndexesAtResPartial(res, callback, NUM_BASE_CELLS);
301 }
302
303 /**
304 * Call the callback for every index at the given resolution in base
305 * cell 0 up to the given base cell number.
306 */
iterateAllIndexesAtResPartial(int res,void (* callback)(H3Index),int baseCells)307 void iterateAllIndexesAtResPartial(int res, void (*callback)(H3Index),
308 int baseCells) {
309 assert(baseCells <= NUM_BASE_CELLS);
310 for (int i = 0; i < baseCells; i++) {
311 iterateBaseCellIndexesAtRes(res, callback, i);
312 }
313 }
314
315 /**
316 * Call the callback for every index at the given resolution in a
317 * specific base cell
318 */
iterateBaseCellIndexesAtRes(int res,void (* callback)(H3Index),int baseCell)319 void iterateBaseCellIndexesAtRes(int res, void (*callback)(H3Index),
320 int baseCell) {
321 H3Index bc;
322 setH3Index(&bc, 0, baseCell, 0);
323 int childrenSz = H3_EXPORT(maxUncompactSize)(&bc, 1, res);
324 H3Index* children = calloc(childrenSz, sizeof(H3Index));
325 H3_EXPORT(uncompact)(&bc, 1, children, childrenSz, res);
326
327 for (int j = 0; j < childrenSz; j++) {
328 if (children[j] == H3_NULL) {
329 continue;
330 }
331
332 (*callback)(children[j]);
333 }
334
335 free(children);
336 }
337
338 /**
339 * Generates a random lat/lon pair.
340 *
341 * @param g Lat/lon will be placed here.
342 */
randomGeo(GeoCoord * g)343 void randomGeo(GeoCoord* g) {
344 static int init = 0;
345 if (!init) {
346 srand((unsigned int)time(0));
347 init = 1;
348 }
349
350 g->lat = H3_EXPORT(degsToRads)(
351 (((float)rand() / (float)(RAND_MAX)) * 180.0) - 90.0);
352 g->lon = H3_EXPORT(degsToRads)((float)rand() / (float)(RAND_MAX)) * 360.0;
353 }
354
355 /**
356 * Returns the number of non-invalid indexes in the array.
357 */
countActualHexagons(H3Index * hexagons,int numHexagons)358 int countActualHexagons(H3Index* hexagons, int numHexagons) {
359 int actualNumHexagons = 0;
360 for (int i = 0; i < numHexagons; i++) {
361 if (hexagons[i] != H3_NULL) {
362 actualNumHexagons++;
363 }
364 }
365 return actualNumHexagons;
366 }
367