• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

uber / h3-java / #578

07 Nov 2025 01:19AM UTC coverage: 94.157% (-0.3%) from 94.437%
#578

Pull #192

github

web-flow
Merge a03ed1db5 into c833c4237
Pull Request #192: Prepare for v4.4.0

167 of 185 branches covered (90.27%)

Branch coverage included in aggregate %.

19 of 22 new or added lines in 2 files covered. (86.36%)

9 existing lines in 1 file now uncovered.

542 of 568 relevant lines covered (95.42%)

0.95 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.08
/src/main/java/com/uber/h3core/H3Core.java
1
/*
2
 * Copyright 2017-2019, 2022-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
package com.uber.h3core;
17

18
import static java.lang.Math.toDegrees;
19
import static java.lang.Math.toRadians;
20

21
import com.uber.h3core.exceptions.H3Exception;
22
import com.uber.h3core.util.CoordIJ;
23
import com.uber.h3core.util.LatLng;
24
import java.io.IOException;
25
import java.util.ArrayList;
26
import java.util.Collection;
27
import java.util.List;
28
import java.util.stream.Collectors;
29
import java.util.stream.IntStream;
30

31
/**
32
 * H3Core provides all functions of the H3 API.
33
 *
34
 * <p>This class is thread safe and can be used as a singleton.
35
 *
36
 * <p>Any function in this class may throw {@link H3Exception}.
37
 */
38
public class H3Core {
1✔
39
  // These constants are from h3api.h and h3Index.h
40
  /** Maximum number of vertices for an H3 index */
41
  private static final int MAX_CELL_BNDRY_VERTS = 10;
42

43
  private static final int NUM_BASE_CELLS = 122;
44
  private static final int NUM_PENTAGONS = 12;
45

46
  // Constants for the resolution bits in an H3 index.
47
  private static final long H3_RES_OFFSET = 52L;
48
  private static final long H3_RES_MASK = 0xfL << H3_RES_OFFSET;
49
  private static final long H3_RES_MASK_NEGATIVE = ~H3_RES_MASK;
50

51
  /**
52
   * Mask for the indexing digits in an H3 index.
53
   *
54
   * <p>The digits are offset by 0, so no shift is needed in the constant.
55
   */
56
  private static final long H3_DIGIT_MASK = 0x1fffffffffffL;
57

58
  private static final long INVALID_INDEX = 0L;
59

60
  /** Native implementation of the H3 library. */
61
  private final NativeMethods h3Api;
62

63
  /**
64
   * Create by unpacking the H3 native library to disk and loading it. The library will attempt to
65
   * detect the correct operating system and architecture of native library to unpack.
66
   *
67
   * @throws SecurityException Loading the library was not allowed by the SecurityManager.
68
   * @throws UnsatisfiedLinkError The library could not be loaded
69
   * @throws IOException The library could not be extracted to disk.
70
   */
71
  public static H3Core newInstance() throws IOException {
72
    NativeMethods h3Api = H3CoreLoader.loadNatives();
1✔
73
    return new H3Core(h3Api);
1✔
74
  }
75

76
  /**
77
   * Create by unpacking the H3 native library to disk and loading it. The library will attempt to
78
   * extract the native library matching the given arguments to disk.
79
   *
80
   * @throws SecurityException Loading the library was not allowed by the SecurityManager.
81
   * @throws UnsatisfiedLinkError The library could not be loaded
82
   * @throws IOException The library could not be extracted to disk.
83
   */
84
  public static H3Core newInstance(H3CoreLoader.OperatingSystem os, String arch)
85
      throws IOException {
86
    NativeMethods h3Api = H3CoreLoader.loadNatives(os, arch);
1✔
87
    return new H3Core(h3Api);
1✔
88
  }
89

90
  /**
91
   * Create by using the H3 native library already installed on the system.
92
   *
93
   * @throws SecurityException The library could not be loaded
94
   * @throws UnsatisfiedLinkError The library could not be loaded
95
   */
96
  public static H3Core newSystemInstance() {
97
    NativeMethods h3Api = H3CoreLoader.loadSystemNatives();
×
98
    return new H3Core(h3Api);
×
99
  }
100

101
  /** Construct with the given NativeMethods, from {@link H3CoreLoader}. */
102
  private H3Core(NativeMethods h3Api) {
1✔
103
    this.h3Api = h3Api;
1✔
104
  }
1✔
105

106
  /** Returns true if this is a valid H3 cell index. */
107
  public boolean isValidCell(long h3) {
108
    return h3Api.isValidCell(h3);
1✔
109
  }
110

111
  /** Returns true if this is a valid H3 cell index. */
112
  public boolean isValidCell(String h3Address) {
113
    return isValidCell(stringToH3(h3Address));
1✔
114
  }
115

116
  /** Returns true if this is a valid H3 index. */
117
  public boolean isValidIndex(long h3) {
118
    return h3Api.isValidIndex(h3);
1✔
119
  }
120

121
  /** Returns true if this is a valid H3 index. */
122
  public boolean isValidIndex(String h3Address) {
123
    return isValidIndex(stringToH3(h3Address));
1✔
124
  }
125

126
  /** Construct a cell index from component parts */
127
  public long constructCell(int res, int baseCell, List<Integer> digits) {
128
    int[] digitsArray = digits.stream().mapToInt(Integer::intValue).toArray();
1✔
129
    if (digitsArray.length < res) {
1✔
130
      throw new IllegalArgumentException(
1✔
131
          String.format(
1✔
132
              "Number of provided digits is too few, must be at least %d, was %d",
133
              res - 1, digitsArray.length));
1✔
134
    }
135
    if (digitsArray.length > 15) {
1!
NEW
136
      throw new IllegalArgumentException(
×
NEW
137
          String.format(
×
138
              "Additional unused digits provided, must be at most 15 but was %d",
NEW
139
              digitsArray.length));
×
140
    }
141
    return h3Api.constructCell(res, baseCell, digitsArray);
1✔
142
  }
143

144
  /** Construct a cell index from component parts */
145
  public String constructCellAddress(int res, int baseCell, List<Integer> digits) {
146
    return h3ToString(constructCell(res, baseCell, digits));
1✔
147
  }
148

149
  /** Returns the base cell number for this index. */
150
  public int getBaseCellNumber(long h3) {
151
    return h3Api.getBaseCellNumber(h3);
1✔
152
  }
153

154
  /** Returns the base cell number for this index. */
155
  public int getBaseCellNumber(String h3Address) {
156
    return getBaseCellNumber(stringToH3(h3Address));
1✔
157
  }
158

159
  /** Returns <code>true</code> if this index is one of twelve pentagons per resolution. */
160
  public boolean isPentagon(long h3) {
161
    return h3Api.isPentagon(h3);
1✔
162
  }
163

164
  /** Returns <code>true</code> if this index is one of twelve pentagons per resolution. */
165
  public boolean isPentagon(String h3Address) {
166
    return isPentagon(stringToH3(h3Address));
1✔
167
  }
168

169
  /**
170
   * Find the H3 index of the resolution <code>res</code> cell containing the lat/lon (in degrees)
171
   *
172
   * @param lat Latitude in degrees.
173
   * @param lng Longitude in degrees.
174
   * @param res Resolution, 0 &lt;= res &lt;= 15
175
   * @return The H3 index.
176
   */
177
  public long latLngToCell(double lat, double lng, int res) {
178
    checkResolution(res);
1✔
179
    return h3Api.latLngToCell(toRadians(lat), toRadians(lng), res);
1✔
180
  }
181

182
  /**
183
   * Find the H3 index of the resolution <code>res</code> cell containing the lat/lon (in degrees)
184
   *
185
   * @param lat Latitude in degrees.
186
   * @param lng Longitude in degrees.
187
   * @param res Resolution, 0 &lt;= res &lt;= 15
188
   * @return The H3 index.
189
   */
190
  public String latLngToCellAddress(double lat, double lng, int res) {
191
    return h3ToString(latLngToCell(lat, lng, res));
1✔
192
  }
193

194
  /** Find the latitude, longitude (both in degrees) center point of the cell. */
195
  public LatLng cellToLatLng(long h3) {
196
    double[] coords = new double[2];
1✔
197
    h3Api.cellToLatLng(h3, coords);
1✔
198
    LatLng out = new LatLng(toDegrees(coords[0]), toDegrees(coords[1]));
1✔
199
    return out;
1✔
200
  }
201

202
  /** Find the latitude, longitude (degrees) center point of the cell. */
203
  public LatLng cellToLatLng(String h3Address) {
204
    return cellToLatLng(stringToH3(h3Address));
1✔
205
  }
206

207
  /** Find the cell boundary in latitude, longitude (degrees) coordinates for the cell */
208
  public List<LatLng> cellToBoundary(long h3) {
209
    double[] verts = new double[MAX_CELL_BNDRY_VERTS * 2];
1✔
210
    int numVerts = h3Api.cellToBoundary(h3, verts);
1✔
211
    List<LatLng> out = new ArrayList<>(numVerts);
1✔
212
    for (int i = 0; i < numVerts; i++) {
1✔
213
      LatLng coord = new LatLng(toDegrees(verts[i * 2]), toDegrees(verts[(i * 2) + 1]));
1✔
214
      out.add(coord);
1✔
215
    }
216
    return out;
1✔
217
  }
218

219
  /** Find the cell boundary in latitude, longitude (degrees) coordinates for the cell */
220
  public List<LatLng> cellToBoundary(String h3Address) {
221
    return cellToBoundary(stringToH3(h3Address));
1✔
222
  }
223

224
  /**
225
   * Neighboring indexes in all directions.
226
   *
227
   * @param h3Address Origin index
228
   * @param k Number of rings around the origin
229
   */
230
  public List<String> gridDisk(String h3Address, int k) {
231
    return h3ToStringList(gridDisk(stringToH3(h3Address), k));
1✔
232
  }
233

234
  /**
235
   * Neighboring indexes in all directions.
236
   *
237
   * @param h3 Origin index
238
   * @param k Number of rings around the origin
239
   */
240
  public List<Long> gridDisk(long h3, int k) {
241
    int sz = longToIntSize(h3Api.maxGridDiskSize(k));
1✔
242

243
    long[] out = new long[sz];
1✔
244

245
    h3Api.gridDisk(h3, k, out);
1✔
246

247
    return nonZeroLongArrayToList(out);
1✔
248
  }
249

250
  /**
251
   * Neighboring indexes in all directions, ordered by distance from the origin index.
252
   *
253
   * @param h3Address Origin index
254
   * @param k Number of rings around the origin
255
   * @return A list of rings, each of which is a list of addresses. The rings are in order from
256
   *     closest to origin to farthest.
257
   */
258
  public List<List<String>> gridDiskDistances(String h3Address, int k) {
259
    List<List<Long>> rings = gridDiskDistances(stringToH3(h3Address), k);
1✔
260

261
    return rings.stream().map(this::h3ToStringList).collect(Collectors.toList());
1✔
262
  }
263

264
  /**
265
   * Neighboring indexes in all directions, ordered by distance from the origin index.
266
   *
267
   * @param h3 Origin index
268
   * @param k Number of rings around the origin
269
   * @return A list of rings, each of which is a list of addresses. The rings are in order from
270
   *     closest to origin to farthest.
271
   */
272
  public List<List<Long>> gridDiskDistances(long h3, int k) {
273
    int sz = longToIntSize(h3Api.maxGridDiskSize(k));
1✔
274

275
    long[] out = new long[sz];
1✔
276
    int[] distances = new int[sz];
1✔
277

278
    h3Api.gridDiskDistances(h3, k, out, distances);
1✔
279

280
    List<List<Long>> ret = new ArrayList<>(k + 1);
1✔
281

282
    for (int i = 0; i <= k; i++) {
1✔
283
      ret.add(new ArrayList<>());
1✔
284
    }
285

286
    for (int i = 0; i < sz; i++) {
1✔
287
      long nextH3 = out[i];
1✔
288
      if (nextH3 != INVALID_INDEX) {
1✔
289
        ret.get(distances[i]).add(nextH3);
1✔
290
      }
291
    }
292

293
    return ret;
1✔
294
  }
295

296
  /**
297
   * Returns in order neighbor traversal.
298
   *
299
   * @param h3Address Origin hexagon index
300
   * @param k Number of rings around the origin
301
   * @return A list of rings, each of which is a list of addresses. The rings are in order from
302
   *     closest to origin to farthest.
303
   */
304
  public List<List<String>> gridDiskUnsafe(String h3Address, int k) {
305
    List<List<Long>> rings = gridDiskUnsafe(stringToH3(h3Address), k);
1✔
306

307
    return rings.stream().map(this::h3ToStringList).collect(Collectors.toList());
1✔
308
  }
309

310
  /**
311
   * Returns in order neighbor traversal.
312
   *
313
   * @param h3 Origin hexagon index
314
   * @param k Number of rings around the origin
315
   * @return A list of rings, each of which is a list of addresses. The rings are in order from
316
   *     closest to origin to farthest.
317
   */
318
  public List<List<Long>> gridDiskUnsafe(long h3, int k) {
319
    int sz = longToIntSize(h3Api.maxGridDiskSize(k));
1✔
320

321
    long[] out = new long[sz];
1✔
322

323
    h3Api.gridDiskUnsafe(h3, k, out);
1✔
324

325
    List<List<Long>> ret = new ArrayList<>(k + 1);
1✔
326

327
    List<Long> ring = null;
1✔
328
    int currentK = 0;
1✔
329
    int nextRing = 0;
1✔
330

331
    for (int i = 0; i < sz; i++) {
1✔
332
      // Check if we've reached the index of the next ring.
333
      if (i == nextRing) {
1✔
334
        ring = new ArrayList<>();
1✔
335
        ret.add(ring);
1✔
336

337
        // Determine the start index of the next ring.
338
        // k=0 is a special case of size 1.
339
        if (currentK == 0) {
1✔
340
          nextRing = 1;
1✔
341
        } else {
342
          nextRing += (6 * currentK);
1✔
343
        }
344
        currentK++;
1✔
345
      }
346

347
      long h = out[i];
1✔
348
      ring.add(h);
1✔
349
    }
350

351
    return ret;
1✔
352
  }
353

354
  /**
355
   * Returns in order neighbor traversal, of indexes with distance of <code>k</code>.
356
   *
357
   * @param h3Address Origin index
358
   * @param k Number of rings around the origin
359
   * @return All indexes <code>k</code> away from the origin
360
   */
361
  public List<String> gridRing(String h3Address, int k) {
362
    return h3ToStringList(gridRing(stringToH3(h3Address), k));
1✔
363
  }
364

365
  /**
366
   * Returns in order neighbor traversal, of indexes with distance of <code>k</code>.
367
   *
368
   * @param h3 Origin index
369
   * @param k Number of rings around the origin
370
   * @return All indexes <code>k</code> away from the origin
371
   */
372
  public List<Long> gridRing(long h3, int k) {
373
    int sz = k == 0 ? 1 : 6 * k;
1✔
374

375
    long[] out = new long[sz];
1✔
376

377
    h3Api.gridRing(h3, k, out);
1✔
378

379
    return nonZeroLongArrayToList(out);
1✔
380
  }
381

382
  /**
383
   * Returns in order neighbor traversal, of indexes with distance of <code>k</code>.
384
   *
385
   * @param h3Address Origin index
386
   * @param k Number of rings around the origin
387
   * @return All indexes <code>k</code> away from the origin
388
   */
389
  public List<String> gridRingUnsafe(String h3Address, int k) {
390
    return h3ToStringList(gridRingUnsafe(stringToH3(h3Address), k));
1✔
391
  }
392

393
  /**
394
   * Returns in order neighbor traversal, of indexes with distance of <code>k</code>.
395
   *
396
   * @param h3 Origin index
397
   * @param k Number of rings around the origin
398
   * @return All indexes <code>k</code> away from the origin
399
   */
400
  public List<Long> gridRingUnsafe(long h3, int k) {
401
    int sz = k == 0 ? 1 : 6 * k;
1✔
402

403
    long[] out = new long[sz];
1✔
404

405
    h3Api.gridRingUnsafe(h3, k, out);
1✔
406

407
    return nonZeroLongArrayToList(out);
1✔
408
  }
409

410
  /**
411
   * Returns the distance between <code>a</code> and <code>b</code>. This is the grid distance, or
412
   * distance expressed in number of H3 cells.
413
   *
414
   * <p>In some cases H3 cannot compute the distance between two indexes. This can happen because:
415
   *
416
   * <ul>
417
   *   <li>The indexes are not comparable (difference resolutions, etc)
418
   *   <li>The distance is greater than the H3 core library supports
419
   *   <li>The H3 library does not support finding the distance between the two cells, because of
420
   *       pentagonal distortion.
421
   * </ul>
422
   *
423
   * @param a An H3 index
424
   * @param b Another H3 index
425
   * @return Distance between the two in grid cells
426
   */
427
  public long gridDistance(String a, String b) {
428
    return gridDistance(stringToH3(a), stringToH3(b));
1✔
429
  }
430

431
  /**
432
   * Returns the distance between <code>a</code> and <code>b</code>. This is the grid distance, or
433
   * distance expressed in number of H3 cells.
434
   *
435
   * <p>In some cases H3 cannot compute the distance between two indexes. This can happen because:
436
   *
437
   * <ul>
438
   *   <li>The indexes are not comparable (difference resolutions, etc)
439
   *   <li>The distance is greater than the H3 core library supports
440
   *   <li>The H3 library does not support finding the distance between the two cells, because of
441
   *       pentagonal distortion.
442
   * </ul>
443
   *
444
   * @param a An H3 index
445
   * @param b Another H3 index
446
   * @return Distance between the two in grid cells
447
   */
448
  public long gridDistance(long a, long b) {
449
    return h3Api.gridDistance(a, b);
1✔
450
  }
451

452
  /**
453
   * Converts <code>h3</code> to IJ coordinates in a local coordinate space defined by <code>origin
454
   * </code>.
455
   *
456
   * <p>The local IJ coordinate space may have deleted regions and warping due to pentagon
457
   * distortion. IJ coordinates are only comparable if they came from the same origin.
458
   *
459
   * <p>This function is experimental, and its output is not guaranteed to be compatible across
460
   * different versions of H3.
461
   *
462
   * @param origin Anchoring index for the local coordinate space.
463
   * @param h3 Index to find the coordinates of.
464
   * @return Coordinates for <code>h3</code> in the local coordinate space.
465
   */
466
  public CoordIJ cellToLocalIj(long origin, long h3) {
467
    final int[] coords = new int[2];
1✔
468
    h3Api.cellToLocalIj(origin, h3, coords);
1✔
469
    return new CoordIJ(coords[0], coords[1]);
1✔
470
  }
471

472
  /**
473
   * Converts <code>h3Address</code> to IJ coordinates in a local coordinate space defined by <code>
474
   * originAddress</code>.
475
   *
476
   * <p>The local IJ coordinate space may have deleted regions and warping due to pentagon
477
   * distortion. IJ coordinates are only comparable if they came from the same origin.
478
   *
479
   * <p>This function is experimental, and its output is not guaranteed to be compatible across
480
   * different versions of H3.
481
   *
482
   * @param originAddress Anchoring index for the local coordinate space.
483
   * @param h3Address Index to find the coordinates of.
484
   * @return Coordinates for <code>h3</code> in the local coordinate space.
485
   */
486
  public CoordIJ cellToLocalIj(String originAddress, String h3Address) {
487
    return cellToLocalIj(stringToH3(originAddress), stringToH3(h3Address));
1✔
488
  }
489

490
  /**
491
   * Converts the IJ coordinates to an index, using a local IJ coordinate space anchored by <code>
492
   * origin</code>.
493
   *
494
   * <p>The local IJ coordinate space may have deleted regions and warping due to pentagon
495
   * distortion. IJ coordinates are only comparable if they came from the same origin.
496
   *
497
   * <p>This function is experimental, and its output is not guaranteed to be compatible across
498
   * different versions of H3.
499
   *
500
   * @param origin Anchoring index for the local coordinate space.
501
   * @param ij Coordinates in the local IJ coordinate space.
502
   * @return Index represented by <code>ij</code>
503
   */
504
  public long localIjToCell(long origin, CoordIJ ij) {
505
    return h3Api.localIjToCell(origin, ij.i, ij.j);
1✔
506
  }
507

508
  /**
509
   * Converts the IJ coordinates to an index, using a local IJ coordinate space anchored by <code>
510
   * origin</code>.
511
   *
512
   * <p>The local IJ coordinate space may have deleted regions and warping due to pentagon
513
   * distortion. IJ coordinates are only comparable if they came from the same origin.
514
   *
515
   * <p>This function is experimental, and its output is not guaranteed to be compatible across
516
   * different versions of H3.
517
   *
518
   * @param originAddress Anchoring index for the local coordinate space.
519
   * @param ij Coordinates in the local IJ coordinate space.
520
   * @return Index represented by <code>ij</code>
521
   */
522
  public String localIjToCell(String originAddress, CoordIJ ij) {
523
    return h3ToString(localIjToCell(stringToH3(originAddress), ij));
1✔
524
  }
525

526
  /**
527
   * Given two H3 indexes, return the line of indexes between them (inclusive of endpoints).
528
   *
529
   * <p>This function may fail to find the line between two indexes, for example if they are very
530
   * far apart. It may also fail when finding distances for indexes on opposite sides of a pentagon.
531
   *
532
   * <p>Notes:
533
   *
534
   * <ul>
535
   *   <li>The specific output of this function should not be considered stable across library
536
   *       versions. The only guarantees the library provides are that the line length will be
537
   *       `h3Distance(start, end) + 1` and that every index in the line will be a neighbor of the
538
   *       preceding index.
539
   *   <li>Lines are drawn in grid space, and may not correspond exactly to either Cartesian lines
540
   *       or great arcs.
541
   * </ul>
542
   *
543
   * @param startAddress Start index of the line
544
   * @param endAddress End index of the line
545
   * @return Indexes making up the line.
546
   */
547
  public List<String> gridPathCells(String startAddress, String endAddress) {
548
    return h3ToStringList(gridPathCells(stringToH3(startAddress), stringToH3(endAddress)));
1✔
549
  }
550

551
  /**
552
   * Given two H3 indexes, return the line of indexes between them (inclusive of endpoints).
553
   *
554
   * <p>This function may fail to find the line between two indexes, for example if they are very
555
   * far apart. It may also fail when finding distances for indexes on opposite sides of a pentagon.
556
   *
557
   * <p>Notes:
558
   *
559
   * <ul>
560
   *   <li>The specific output of this function should not be considered stable across library
561
   *       versions. The only guarantees the library provides are that the line length will be
562
   *       `h3Distance(start, end) + 1` and that every index in the line will be a neighbor of the
563
   *       preceding index.
564
   *   <li>Lines are drawn in grid space, and may not correspond exactly to either Cartesian lines
565
   *       or great arcs.
566
   * </ul>
567
   *
568
   * @param start Start index of the line
569
   * @param end End index of the line
570
   * @return Indexes making up the line.
571
   */
572
  public List<Long> gridPathCells(long start, long end) {
573
    int size = longToIntSize(h3Api.gridPathCellsSize(start, end));
1✔
574

575
    long[] results = new long[size];
1✔
576
    h3Api.gridPathCells(start, end, results);
1✔
577

578
    return nonZeroLongArrayToList(results);
1✔
579
  }
580

581
  /**
582
   * Finds indexes within the given geopolygon.
583
   *
584
   * @param points Outline geopolygon
585
   * @param holes Geopolygons of any internal holes
586
   * @param res Resolution of the desired indexes
587
   */
588
  public List<String> polygonToCellAddressesExperimental(
589
      List<LatLng> points, List<List<LatLng>> holes, int res, PolygonToCellsFlags flags) {
UNCOV
590
    return h3ToStringList(polygonToCellsExperimental(points, holes, res, flags));
×
591
  }
592

593
  /**
594
   * Finds indexes within the given geopolygon.
595
   *
596
   * @param points Outline geopolygon
597
   * @param holes Geopolygon of any internal holes
598
   * @param res Resolution of the desired indexes
599
   * @throws IllegalArgumentException Invalid resolution
600
   */
601
  public List<Long> polygonToCellsExperimental(
602
      List<LatLng> points, List<List<LatLng>> holes, int res, PolygonToCellsFlags flags) {
603
    checkResolution(res);
1✔
604

605
    // pack the data for use by the polyfill JNI call
606
    double[] verts = new double[points.size() * 2];
1✔
607
    packGeofenceVertices(verts, points, 0);
1✔
608
    int[] holeSizes = new int[0];
1✔
609
    double[] holeVerts = new double[0];
1✔
610
    if (holes != null) {
1!
UNCOV
611
      int holesSize = holes.size();
×
UNCOV
612
      holeSizes = new int[holesSize];
×
UNCOV
613
      int totalSize = 0;
×
UNCOV
614
      for (int i = 0; i < holesSize; i++) {
×
UNCOV
615
        int holeSize = holes.get(i).size() * 2;
×
UNCOV
616
        totalSize += holeSize;
×
617
        // Note we are storing the number of doubles
UNCOV
618
        holeSizes[i] = holeSize;
×
619
      }
UNCOV
620
      holeVerts = new double[totalSize];
×
621
      int offset = 0;
×
622
      for (int i = 0; i < holesSize; i++) {
×
623
        offset = packGeofenceVertices(holeVerts, holes.get(i), offset);
×
624
      }
625
    }
626

627
    int flagsInt = flags.toInt();
1✔
628
    int sz =
1✔
629
        longToIntSize(
1✔
630
            h3Api.maxPolygonToCellsSizeExperimental(verts, holeSizes, holeVerts, res, flagsInt));
1✔
631

632
    long[] results = new long[sz];
1✔
633

634
    h3Api.polygonToCellsExperimental(verts, holeSizes, holeVerts, res, flagsInt, results);
1✔
635

636
    return nonZeroLongArrayToList(results);
1✔
637
  }
638

639
  /**
640
   * Finds indexes within the given geopolygon.
641
   *
642
   * @param points Outline geopolygon
643
   * @param holes Geopolygons of any internal holes
644
   * @param res Resolution of the desired indexes
645
   */
646
  public List<String> polygonToCellAddresses(
647
      List<LatLng> points, List<List<LatLng>> holes, int res) {
648
    return h3ToStringList(polygonToCells(points, holes, res));
1✔
649
  }
650

651
  /**
652
   * Finds indexes within the given geopolygon.
653
   *
654
   * @param points Outline geopolygon
655
   * @param holes Geopolygon of any internal holes
656
   * @param res Resolution of the desired indexes
657
   * @throws IllegalArgumentException Invalid resolution
658
   */
659
  public List<Long> polygonToCells(List<LatLng> points, List<List<LatLng>> holes, int res) {
660
    checkResolution(res);
1✔
661

662
    // pack the data for use by the polyfill JNI call
663
    double[] verts = new double[points.size() * 2];
1✔
664
    packGeofenceVertices(verts, points, 0);
1✔
665
    int[] holeSizes = new int[0];
1✔
666
    double[] holeVerts = new double[0];
1✔
667
    if (holes != null) {
1✔
668
      int holesSize = holes.size();
1✔
669
      holeSizes = new int[holesSize];
1✔
670
      int totalSize = 0;
1✔
671
      for (int i = 0; i < holesSize; i++) {
1✔
672
        int holeSize = holes.get(i).size() * 2;
1✔
673
        totalSize += holeSize;
1✔
674
        // Note we are storing the number of doubles
675
        holeSizes[i] = holeSize;
1✔
676
      }
677
      holeVerts = new double[totalSize];
1✔
678
      int offset = 0;
1✔
679
      for (int i = 0; i < holesSize; i++) {
1✔
680
        offset = packGeofenceVertices(holeVerts, holes.get(i), offset);
1✔
681
      }
682
    }
683

684
    int flags = 0;
1✔
685
    int sz = longToIntSize(h3Api.maxPolygonToCellsSize(verts, holeSizes, holeVerts, res, flags));
1✔
686

687
    long[] results = new long[sz];
1✔
688

689
    h3Api.polygonToCells(verts, holeSizes, holeVerts, res, flags, results);
1✔
690

691
    return nonZeroLongArrayToList(results);
1✔
692
  }
693

694
  /**
695
   * Interleave the pairs in the given double array.
696
   *
697
   * @return Next offset to begin filling from
698
   */
699
  private static int packGeofenceVertices(double[] arr, List<LatLng> original, int offset) {
700
    int newOffset = (original.size() * 2) + offset;
1✔
701
    assert arr.length >= newOffset;
1!
702

703
    for (int i = 0, size = original.size(); i < size; i++) {
1✔
704
      LatLng coord = original.get(i);
1✔
705

706
      int firstOffset = (i * 2) + offset;
1✔
707
      int secondOffset = firstOffset + 1;
1✔
708
      arr[firstOffset] = toRadians(coord.lat);
1✔
709
      arr[secondOffset] = toRadians(coord.lng);
1✔
710
    }
711

712
    return newOffset;
1✔
713
  }
714

715
  /** Create polygons from a set of contiguous indexes */
716
  public List<List<List<LatLng>>> cellAddressesToMultiPolygon(
717
      Collection<String> h3Addresses, boolean geoJson) {
718
    List<Long> indices = stringToH3List(h3Addresses);
1✔
719

720
    return cellsToMultiPolygon(indices, geoJson);
1✔
721
  }
722

723
  /** Create polygons from a set of contiguous indexes */
724
  public List<List<List<LatLng>>> cellsToMultiPolygon(Collection<Long> h3, boolean geoJson) {
725
    long[] h3AsArray = collectionToLongArray(h3);
1✔
726

727
    ArrayList<List<List<LatLng>>> result = new ArrayList<>();
1✔
728

729
    h3Api.cellsToLinkedMultiPolygon(h3AsArray, result);
1✔
730

731
    // For each polygon
732
    for (List<List<LatLng>> loops : result) {
1✔
733
      // For each loop within the polygon (first being the outline,
734
      // further loops being "holes" or exclusions in the polygon.)
735
      for (List<LatLng> loop : loops) {
1✔
736
        // For each coordinate in the loop, we need to convert to degrees,
737
        // and ensure the correct ordering (whether geoJson or not.)
738
        for (int vectorInLoop = 0, size = loop.size(); vectorInLoop < size; vectorInLoop++) {
1✔
739
          final LatLng v = loop.get(vectorInLoop);
1✔
740
          final double origLat = toDegrees(v.lat);
1✔
741
          final double origLng = toDegrees(v.lng);
1✔
742

743
          final LatLng replacement = new LatLng(origLat, origLng);
1✔
744

745
          loop.set(vectorInLoop, replacement);
1✔
746
        }
747

748
        if (geoJson && loop.size() > 0) {
1!
749
          // geoJson requires closing the loop
750
          loop.add(loop.get(0));
1✔
751
        }
752
      }
1✔
753
    }
1✔
754

755
    return result;
1✔
756
  }
757

758
  /** Returns the resolution of the provided index */
759
  public int getResolution(String h3Address) {
760
    return getResolution(stringToH3(h3Address));
1✔
761
  }
762

763
  /** Returns the resolution of the provided index. */
764
  public int getResolution(long h3) {
765
    return (int) ((h3 & H3_RES_MASK) >> H3_RES_OFFSET);
1✔
766
  }
767

768
  /**
769
   * Returns the indexing digit of the index at `res`
770
   *
771
   * @param h3 H3 index.
772
   * @param res Resolution of the digit, <code>1 &lt;= res &lt;= 15</code>
773
   * @throws IllegalArgumentException <code>res</code> is not between 0 and 15, inclusive.
774
   */
775
  public int getIndexDigit(String h3Address, int res) {
776
    return getIndexDigit(stringToH3(h3Address), res);
1✔
777
  }
778

779
  /**
780
   * Returns the indexing digit of the index at `res`
781
   *
782
   * @param h3 H3 index.
783
   * @param res Resolution of the digit, <code>1 &lt;= res &lt;= 15</code>
784
   * @throws IllegalArgumentException <code>res</code> is not between 0 and 15, inclusive.
785
   */
786
  public int getIndexDigit(long h3, int res) {
787
    if (res < 1 || res > 15) {
1✔
788
      throw new IllegalArgumentException(
1✔
789
          String.format("resolution %d is out of range (must be 1 <= res <= 15)", res));
1✔
790
    }
791
    return (int) ((h3 >> ((15 - res) * 3)) & 7);
1✔
792
  }
793

794
  /**
795
   * Returns the parent of the index at the given resolution.
796
   *
797
   * @param h3 H3 index.
798
   * @param res Resolution of the parent, <code>0 &lt;= res &lt;= h3GetResolution(h3)</code>
799
   * @throws IllegalArgumentException <code>res</code> is not between 0 and the resolution of <code>
800
   *     h3</code>, inclusive.
801
   */
802
  public long cellToParent(long h3, int res) {
803
    // This is a ported version of h3ToParent from h3core.
804

805
    int childRes = (int) ((h3 & H3_RES_MASK) >> H3_RES_OFFSET);
1✔
806
    if (res < 0 || res > childRes) {
1✔
807
      throw new IllegalArgumentException(
1✔
808
          String.format("res (%d) must be between 0 and %d, inclusive", res, childRes));
1✔
809
    } else if (res == childRes) {
1✔
810
      return h3;
1✔
811
    }
812

813
    // newRes is the bits that need to be set to set the given resolution.
814
    long newRes = (long) res << H3_RES_OFFSET;
1✔
815
    long digitMaskForRes = H3_DIGIT_MASK;
1✔
816
    for (int i = 0; i < res; i++) {
1✔
817
      digitMaskForRes >>= 3L;
1✔
818
    }
819

820
    return (h3 & H3_RES_MASK_NEGATIVE) | newRes | digitMaskForRes;
1✔
821
  }
822

823
  /**
824
   * Returns the parent of the index at the given resolution.
825
   *
826
   * @param h3Address H3 index.
827
   * @param res Resolution of the parent, <code>0 &lt;= res &lt;= h3GetResolution(h3)</code>
828
   */
829
  public String cellToParentAddress(String h3Address, int res) {
830
    long parent = cellToParent(stringToH3(h3Address), res);
1✔
831
    return h3ToString(parent);
1✔
832
  }
833

834
  /**
835
   * Provides the children of the index at the given resolution.
836
   *
837
   * @param childRes Resolution of the children
838
   */
839
  public List<String> cellToChildren(String h3Address, int childRes) {
840
    return h3ToStringList(cellToChildren(stringToH3(h3Address), childRes));
1✔
841
  }
842

843
  /**
844
   * Provides the children of the index at the given resolution.
845
   *
846
   * @param h3 H3 index.
847
   * @param childRes Resolution of the children
848
   * @throws IllegalArgumentException Invalid resolution
849
   */
850
  public List<Long> cellToChildren(long h3, int childRes) {
851
    checkResolution(childRes);
1✔
852

853
    int sz = longToIntSize(h3Api.cellToChildrenSize(h3, childRes));
1✔
854

855
    long[] out = new long[sz];
1✔
856

857
    h3Api.cellToChildren(h3, childRes, out);
1✔
858

859
    return nonZeroLongArrayToList(out);
1✔
860
  }
861

862
  /**
863
   * Returns the center child at the given resolution.
864
   *
865
   * @param h3 Parent H3 index
866
   * @param childRes Resolution of the child
867
   * @throws IllegalArgumentException Invalid resolution (e.g. coarser than the parent)
868
   */
869
  public String cellToCenterChild(String h3, int childRes) {
870
    return h3ToString(cellToCenterChild(stringToH3(h3), childRes));
1✔
871
  }
872

873
  /** Returns the number of children the cell index has at the given resolution. */
874
  public long cellToChildrenSize(long cell, int childRes) {
875
    return h3Api.cellToChildrenSize(cell, childRes);
1✔
876
  }
877

878
  /** Returns the number of children the cell index has at the given resolution. */
879
  public long cellToChildrenSize(String cellAddress, int childRes) {
880
    return cellToChildrenSize(stringToH3(cellAddress), childRes);
1✔
881
  }
882

883
  /**
884
   * Returns the center child at the given resolution.
885
   *
886
   * @param h3 Parent H3 index
887
   * @param childRes Resolution of the child
888
   * @throws IllegalArgumentException Invalid resolution (e.g. coarser than the parent)
889
   */
890
  public long cellToCenterChild(long h3, int childRes) {
891
    checkResolution(childRes);
1✔
892

893
    long result = h3Api.cellToCenterChild(h3, childRes);
1✔
894

895
    return result;
1✔
896
  }
897

898
  /**
899
   * Determines if an index is Class III or Class II.
900
   *
901
   * @return <code>true</code> if the index is Class III
902
   */
903
  public boolean isResClassIII(String h3Address) {
904
    return isResClassIII(stringToH3(h3Address));
1✔
905
  }
906

907
  /**
908
   * Determines if an index is Class III or Class II.
909
   *
910
   * @param h3 H3 index.
911
   * @return <code>true</code> if the index is Class III
912
   */
913
  public boolean isResClassIII(long h3) {
914
    return getResolution(h3) % 2 != 0;
1✔
915
  }
916

917
  /** Returns a compacted set of indexes, at possibly coarser resolutions. */
918
  public List<String> compactCellAddresses(Collection<String> h3Addresses) {
919
    List<Long> h3 = stringToH3List(h3Addresses);
1✔
920
    List<Long> compacted = compactCells(h3);
1✔
921
    return h3ToStringList(compacted);
1✔
922
  }
923

924
  /** Returns a compacted set of indexes, at possibly coarser resolutions. */
925
  public List<Long> compactCells(Collection<Long> h3) {
926
    int sz = h3.size();
1✔
927

928
    long[] h3AsArray = collectionToLongArray(h3);
1✔
929

930
    long[] out = new long[sz];
1✔
931

932
    h3Api.compactCells(h3AsArray, out);
1✔
933

934
    return nonZeroLongArrayToList(out);
1✔
935
  }
936

937
  /** Uncompacts all the given indexes to resolution <code>res</code>. */
938
  public List<String> uncompactCellAddresses(Collection<String> h3Addresses, int res) {
939
    List<Long> h3 = stringToH3List(h3Addresses);
1✔
940
    List<Long> uncompacted = uncompactCells(h3, res);
1✔
941
    return h3ToStringList(uncompacted);
1✔
942
  }
943

944
  /** Uncompacts all the given indexes to resolution <code>res</code>. */
945
  public List<Long> uncompactCells(Collection<Long> h3, int res) {
946
    checkResolution(res);
1✔
947

948
    long[] h3AsArray = collectionToLongArray(h3);
1✔
949

950
    int sz = longToIntSize(h3Api.uncompactCellsSize(h3AsArray, res));
1✔
951

952
    long[] out = new long[sz];
1✔
953

954
    h3Api.uncompactCells(h3AsArray, res, out);
1✔
955

956
    return nonZeroLongArrayToList(out);
1✔
957
  }
958

959
  /**
960
   * Converts from <code>long</code> representation of an index to <code>String</code>
961
   * representation.
962
   */
963
  public String h3ToString(long h3) {
964
    return Long.toHexString(h3);
1✔
965
  }
966

967
  /**
968
   * Converts from <code>String</code> representation of an index to <code>long</code>
969
   * representation.
970
   */
971
  public long stringToH3(String h3Address) {
972
    return Long.parseUnsignedLong(h3Address, 16);
1✔
973
  }
974

975
  /**
976
   * Calculates the area of the given H3 cell.
977
   *
978
   * @param h3Address Cell to find the area of.
979
   * @param unit Unit to calculate the area in.
980
   * @return Cell area in the given units.
981
   */
982
  public double cellArea(String h3Address, AreaUnit unit) {
983
    return cellArea(stringToH3(h3Address), unit);
1✔
984
  }
985

986
  /**
987
   * Calculates the area of the given H3 cell.
988
   *
989
   * @param h3 Cell to find the area of.
990
   * @param unit Unit to calculate the area in.
991
   * @return Cell area in the given units.
992
   */
993
  public double cellArea(long h3, AreaUnit unit) {
994
    if (unit == AreaUnit.rads2) return h3Api.cellAreaRads2(h3);
1✔
995
    else if (unit == AreaUnit.km2) return h3Api.cellAreaKm2(h3);
1✔
996
    else if (unit == AreaUnit.m2) return h3Api.cellAreaM2(h3);
1✔
997
    else throw new IllegalArgumentException(String.format("Invalid unit: %s", unit));
1✔
998
  }
999

1000
  /**
1001
   * Return the distance along the sphere between two points.
1002
   *
1003
   * @param a First point
1004
   * @param b Second point
1005
   * @param unit Unit to return the distance in.
1006
   * @return Distance from point <code>a</code> to point <code>b</code>
1007
   */
1008
  public double greatCircleDistance(LatLng a, LatLng b, LengthUnit unit) {
1009
    double lat1 = toRadians(a.lat);
1✔
1010
    double lng1 = toRadians(a.lng);
1✔
1011
    double lat2 = toRadians(b.lat);
1✔
1012
    double lng2 = toRadians(b.lng);
1✔
1013

1014
    if (unit == LengthUnit.rads) return h3Api.greatCircleDistanceRads(lat1, lng1, lat2, lng2);
1✔
1015
    else if (unit == LengthUnit.km) return h3Api.greatCircleDistanceKm(lat1, lng1, lat2, lng2);
1✔
1016
    else if (unit == LengthUnit.m) return h3Api.greatCircleDistanceM(lat1, lng1, lat2, lng2);
1✔
1017
    else throw new IllegalArgumentException(String.format("Invalid unit: %s", unit));
1✔
1018
  }
1019

1020
  /**
1021
   * Calculate the edge length of the given H3 edge.
1022
   *
1023
   * @param edgeAddress Edge to find the edge length of.
1024
   * @param unit Unit of measure to use.
1025
   * @return Length of the given edge.
1026
   */
1027
  public double edgeLength(String edgeAddress, LengthUnit unit) {
1028
    return edgeLength(stringToH3(edgeAddress), unit);
1✔
1029
  }
1030

1031
  /**
1032
   * Calculate the edge length of the given H3 edge.
1033
   *
1034
   * @param edge Edge to find the edge length of.
1035
   * @param unit Unit of measure to use.
1036
   * @return Length of the given edge.
1037
   */
1038
  public double edgeLength(long edge, LengthUnit unit) {
1039
    if (unit == LengthUnit.rads) return h3Api.edgeLengthRads(edge);
1✔
1040
    else if (unit == LengthUnit.km) return h3Api.edgeLengthKm(edge);
1✔
1041
    else if (unit == LengthUnit.m) return h3Api.edgeLengthM(edge);
1✔
1042
    else throw new IllegalArgumentException(String.format("Invalid unit: %s", unit));
1✔
1043
  }
1044

1045
  /**
1046
   * Returns the average area in <code>unit</code> for indexes at resolution <code>res</code>.
1047
   *
1048
   * @throws IllegalArgumentException Invalid parameter value
1049
   */
1050
  public double getHexagonAreaAvg(int res, AreaUnit unit) {
1051
    checkResolution(res);
1✔
1052
    if (unit == AreaUnit.km2) return h3Api.getHexagonAreaAvgKm2(res);
1✔
1053
    else if (unit == AreaUnit.m2) return h3Api.getHexagonAreaAvgM2(res);
1✔
1054
    else throw new IllegalArgumentException(String.format("Invalid unit: %s", unit));
1✔
1055
  }
1056

1057
  /**
1058
   * Returns the average edge length in <code>unit</code> for indexes at resolution <code>res</code>
1059
   * .
1060
   *
1061
   * @throws IllegalArgumentException Invalid parameter value
1062
   */
1063
  public double getHexagonEdgeLengthAvg(int res, LengthUnit unit) {
1064
    checkResolution(res);
1✔
1065
    if (unit == LengthUnit.km) return h3Api.getHexagonEdgeLengthAvgKm(res);
1✔
1066
    else if (unit == LengthUnit.m) return h3Api.getHexagonEdgeLengthAvgM(res);
1✔
1067
    else throw new IllegalArgumentException(String.format("Invalid unit: %s", unit));
1✔
1068
  }
1069

1070
  /**
1071
   * Returns the number of unique H3 indexes at resolution <code>res</code>.
1072
   *
1073
   * @throws IllegalArgumentException Invalid resolution
1074
   */
1075
  public long getNumCells(int res) {
1076
    checkResolution(res);
1✔
1077
    return h3Api.getNumCells(res);
1✔
1078
  }
1079

1080
  /** Returns a collection of all base cells (H3 indexes are resolution 0). */
1081
  public Collection<String> getRes0CellAddresses() {
1082
    return h3ToStringList(getRes0Cells());
1✔
1083
  }
1084

1085
  /** Returns a collection of all base cells (H3 indexes are resolution 0). */
1086
  public Collection<Long> getRes0Cells() {
1087
    long[] indexes = new long[NUM_BASE_CELLS];
1✔
1088
    h3Api.getRes0Cells(indexes);
1✔
1089
    return nonZeroLongArrayToList(indexes);
1✔
1090
  }
1091

1092
  /**
1093
   * Returns a collection of all topologically pentagonal cells at the given resolution.
1094
   *
1095
   * @throws IllegalArgumentException Invalid resolution.
1096
   */
1097
  public Collection<String> getPentagonAddresses(int res) {
1098
    return h3ToStringList(getPentagons(res));
1✔
1099
  }
1100

1101
  /**
1102
   * Returns a collection of all topologically pentagonal cells at the given resolution.
1103
   *
1104
   * @throws IllegalArgumentException Invalid resolution.
1105
   */
1106
  public Collection<Long> getPentagons(int res) {
1107
    checkResolution(res);
1✔
1108
    long[] indexes = new long[NUM_PENTAGONS];
1✔
1109
    h3Api.getPentagons(res, indexes);
1✔
1110
    return nonZeroLongArrayToList(indexes);
1✔
1111
  }
1112

1113
  /** Returns <code>true</code> if the two indexes are neighbors. */
1114
  public boolean areNeighborCells(long a, long b) {
1115
    return h3Api.areNeighborCells(a, b);
1✔
1116
  }
1117

1118
  /** Returns <code>true</code> if the two indexes are neighbors. */
1119
  public boolean areNeighborCells(String a, String b) {
1120
    return areNeighborCells(stringToH3(a), stringToH3(b));
1✔
1121
  }
1122

1123
  /**
1124
   * Returns a unidirectional edge index representing <code>a</code> towards <code>b</code>.
1125
   *
1126
   * @throws IllegalArgumentException The indexes are not neighbors.
1127
   */
1128
  public long cellsToDirectedEdge(long a, long b) {
1129
    return h3Api.cellsToDirectedEdge(a, b);
1✔
1130
  }
1131

1132
  /**
1133
   * Returns a unidirectional edge index representing <code>a</code> towards <code>b</code>.
1134
   *
1135
   * @throws IllegalArgumentException The indexes are not neighbors.
1136
   */
1137
  public String cellsToDirectedEdge(String a, String b) {
1138
    return h3ToString(cellsToDirectedEdge(stringToH3(a), stringToH3(b)));
1✔
1139
  }
1140

1141
  /** Returns <code>true</code> if the given index is a valid unidirectional edge. */
1142
  public boolean isValidDirectedEdge(long h3) {
1143
    return h3Api.isValidDirectedEdge(h3);
1✔
1144
  }
1145

1146
  /** Returns <code>true</code> if the given index is a valid unidirectional edge. */
1147
  public boolean isValidDirectedEdge(String h3) {
1148
    return isValidDirectedEdge(stringToH3(h3));
1✔
1149
  }
1150

1151
  /** Returns the origin index of the given unidirectional edge. */
1152
  public long getDirectedEdgeOrigin(long h3) {
1153
    return h3Api.getDirectedEdgeOrigin(h3);
1✔
1154
  }
1155

1156
  /** Returns the origin index of the given unidirectional edge. */
1157
  public String getDirectedEdgeOrigin(String h3) {
1158
    return h3ToString(getDirectedEdgeOrigin(stringToH3(h3)));
1✔
1159
  }
1160

1161
  /** Returns the destination index of the given unidirectional edge. */
1162
  public long getDirectedEdgeDestination(long h3) {
1163
    return h3Api.getDirectedEdgeDestination(h3);
1✔
1164
  }
1165

1166
  /** Returns the destination index of the given unidirectional edge. */
1167
  public String getDirectedEdgeDestination(String h3) {
1168
    return h3ToString(getDirectedEdgeDestination(stringToH3(h3)));
1✔
1169
  }
1170

1171
  /**
1172
   * Returns the origin and destination indexes (in that order) of the given unidirectional edge.
1173
   */
1174
  public List<Long> directedEdgeToCells(long h3) {
1175
    long[] results = new long[2];
1✔
1176

1177
    // TODO: could be a pair type
1178
    h3Api.directedEdgeToCells(h3, results);
1✔
1179

1180
    return nonZeroLongArrayToList(results);
1✔
1181
  }
1182

1183
  /**
1184
   * Returns the origin and destination indexes (in that order) of the given unidirectional edge.
1185
   */
1186
  public List<String> directedEdgeToCells(String h3) {
1187
    return h3ToStringList(directedEdgeToCells(stringToH3(h3)));
1✔
1188
  }
1189

1190
  /** Returns all unidirectional edges originating from the given index. */
1191
  public List<Long> originToDirectedEdges(long h3) {
1192
    long[] results = new long[6];
1✔
1193

1194
    h3Api.originToDirectedEdges(h3, results);
1✔
1195

1196
    return nonZeroLongArrayToList(results);
1✔
1197
  }
1198

1199
  /** Returns all unidirectional edges originating from the given index. */
1200
  public List<String> originToDirectedEdges(String h3) {
1201
    return h3ToStringList(originToDirectedEdges(stringToH3(h3)));
1✔
1202
  }
1203

1204
  /** Returns a list of coordinates representing the given edge. */
1205
  public List<LatLng> directedEdgeToBoundary(long h3) {
1206
    double[] verts = new double[MAX_CELL_BNDRY_VERTS * 2];
1✔
1207
    int numVerts = h3Api.directedEdgeToBoundary(h3, verts);
1✔
1208
    List<LatLng> out = new ArrayList<>(numVerts);
1✔
1209
    for (int i = 0; i < numVerts; i++) {
1✔
1210
      LatLng coord = new LatLng(toDegrees(verts[i * 2]), toDegrees(verts[(i * 2) + 1]));
1✔
1211
      out.add(coord);
1✔
1212
    }
1213
    return out;
1✔
1214
  }
1215

1216
  /** Returns a list of coordinates representing the given edge. */
1217
  public List<LatLng> directedEdgeToBoundary(String h3) {
1218
    return directedEdgeToBoundary(stringToH3(h3));
1✔
1219
  }
1220

1221
  /**
1222
   * Find all icosahedron faces intersected by a given H3 index, represented as integers from 0-19.
1223
   *
1224
   * @param h3 Index to find icosahedron faces for.
1225
   * @return A collection of faces intersected by the index.
1226
   */
1227
  public Collection<Integer> getIcosahedronFaces(String h3) {
1228
    return getIcosahedronFaces(stringToH3(h3));
1✔
1229
  }
1230

1231
  /**
1232
   * Find all icosahedron faces intersected by a given H3 index, represented as integers from 0-19.
1233
   *
1234
   * @param h3 Index to find icosahedron faces for.
1235
   * @return A collection of faces intersected by the index.
1236
   */
1237
  public Collection<Integer> getIcosahedronFaces(long h3) {
1238
    int maxFaces = h3Api.maxFaceCount(h3);
1✔
1239
    int[] faces = new int[maxFaces];
1✔
1240

1241
    h3Api.getIcosahedronFaces(h3, faces);
1✔
1242

1243
    return IntStream.of(faces).filter(f -> f != -1).boxed().collect(Collectors.toList());
1✔
1244
  }
1245

1246
  public long cellToVertex(long h3, int vertexNum) {
1247
    return h3Api.cellToVertex(h3, vertexNum);
1✔
1248
  }
1249

1250
  public String cellToVertex(String h3Address, int vertexNum) {
1251
    return h3ToString(h3Api.cellToVertex(stringToH3(h3Address), vertexNum));
1✔
1252
  }
1253

1254
  public List<Long> cellToVertexes(long h3) {
1255
    long[] results = new long[6];
1✔
1256
    h3Api.cellToVertexes(h3, results);
1✔
1257
    return nonZeroLongArrayToList(results);
1✔
1258
  }
1259

1260
  public List<String> cellToVertexes(String h3Address) {
1261
    return h3ToStringList(cellToVertexes(stringToH3(h3Address)));
1✔
1262
  }
1263

1264
  public LatLng vertexToLatLng(long h3) {
1265
    double[] results = new double[2];
1✔
1266
    h3Api.vertexToLatLng(h3, results);
1✔
1267
    return new LatLng(toDegrees(results[0]), toDegrees(results[1]));
1✔
1268
  }
1269

1270
  public LatLng vertexToLatLng(String h3Address) {
1271
    return vertexToLatLng(stringToH3(h3Address));
1✔
1272
  }
1273

1274
  public boolean isValidVertex(long h3) {
1275
    return h3Api.isValidVertex(h3);
1✔
1276
  }
1277

1278
  public boolean isValidVertex(String h3Address) {
1279
    return h3Api.isValidVertex(stringToH3(h3Address));
1✔
1280
  }
1281

1282
  /**
1283
   * Returns the position of the child cell within an ordered list of all children of the cell's
1284
   * parent at the specified resolution parentRes.
1285
   */
1286
  public long cellToChildPos(String childAddress, int parentRes) {
1287
    return cellToChildPos(stringToH3(childAddress), parentRes);
1✔
1288
  }
1289

1290
  /**
1291
   * Returns the position of the child cell within an ordered list of all children of the cell's
1292
   * parent at the specified resolution parentRes.
1293
   */
1294
  public long cellToChildPos(long child, int parentRes) {
1295
    return h3Api.cellToChildPos(child, parentRes);
1✔
1296
  }
1297

1298
  /**
1299
   * Returns the child cell at a given position within an ordered list of all children of parent at
1300
   * the specified resolution childRes.
1301
   */
1302
  public long childPosToCell(long childPos, long parent, int childRes) {
1303
    return h3Api.childPosToCell(childPos, parent, childRes);
1✔
1304
  }
1305

1306
  /**
1307
   * Returns the child cell at a given position within an ordered list of all children of parent at
1308
   * the specified resolution childRes.
1309
   */
1310
  public String childPosToCell(long childPos, String parentAddress, int childRes) {
1311
    return h3ToString(childPosToCell(childPos, stringToH3(parentAddress), childRes));
1✔
1312
  }
1313

1314
  /** Transforms a collection of H3 indexes in string form to a list of H3 indexes in long form. */
1315
  private List<Long> stringToH3List(Collection<String> collection) {
1316
    return collection.stream().map(this::stringToH3).collect(Collectors.toList());
1✔
1317
  }
1318

1319
  /** Transforms a list of H3 indexes in long form to a list of H3 indexes in string form. */
1320
  private List<String> h3ToStringList(Collection<Long> collection) {
1321
    return collection.stream().map(this::h3ToString).collect(Collectors.toList());
1✔
1322
  }
1323

1324
  /** Creates a new list with all non-zero elements of the array as members. */
1325
  private static List<Long> nonZeroLongArrayToList(long[] out) {
1326
    List<Long> ret = new ArrayList<>();
1✔
1327

1328
    for (int i = 0; i < out.length; i++) {
1✔
1329
      long h = out[i];
1✔
1330
      if (h != 0) {
1✔
1331
        ret.add(h);
1✔
1332
      }
1333
    }
1334

1335
    return ret;
1✔
1336
  }
1337

1338
  /** Returns an array of <code>long</code> with the contents of the collection. */
1339
  private static long[] collectionToLongArray(Collection<Long> collection) {
1340
    return collection.stream().mapToLong(Long::longValue).toArray();
1✔
1341
  }
1342

1343
  /**
1344
   * @throws IllegalArgumentException <code>res</code> is not a valid H3 resolution.
1345
   */
1346
  private static void checkResolution(int res) {
1347
    if (res < 0 || res > 15) {
1✔
1348
      throw new IllegalArgumentException(
1✔
1349
          String.format("resolution %d is out of range (must be 0 <= res <= 15)", res));
1✔
1350
    }
1351
  }
1✔
1352

1353
  /**
1354
   * @throws IllegalArgumentException <code>sz</code> cannot be losslessly cast to an <code>int
1355
   *     </code>
1356
   */
1357
  private static int longToIntSize(long sz) {
1358
    if (sz < 0 || sz > Integer.MAX_VALUE) {
1!
1359
      throw new IllegalArgumentException(String.format("size %d is out of range", sz));
1✔
1360
    }
1361
    return (int) sz;
1✔
1362
  }
1363
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc