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